1use std::collections::HashMap;
4
5use crate::core::error::{Result, WorkshopError};
6use crate::settings::Settings;
7use crate::source::{FileId, SourceDocument, SourceFile, Span};
8use crate::wir;
9
10mod source_map;
11pub use source_map::{MAPPED_TEXT_V1, MappedText, SourceMap, SourceMapError, TEXT_V1};
12
13#[derive(Debug, Clone, Default)]
15pub struct Program {
16 pub settings: Option<Settings>,
17 pub global_variables: Vec<Variable>,
18 pub player_variables: Vec<Variable>,
19 pub subroutines: Vec<Subroutine>,
20 pub rules: Vec<Rule>,
21 files: Vec<SourceFile>,
22 provenance: Option<Box<ProgramProvenance>>,
23}
24
25#[derive(Debug, Clone, Default)]
26struct ProgramProvenance {
27 global_variables: Vec<DeclarationProvenance>,
28 player_variables: Vec<DeclarationProvenance>,
29 subroutines: Vec<DeclarationProvenance>,
30 rules: Vec<RuleProvenance>,
31}
32
33#[derive(Debug, Clone, Copy, Default)]
34struct DeclarationProvenance {
35 span: Option<Span>,
36 name_span: Option<Span>,
37}
38
39#[derive(Debug, Clone, Default)]
40struct RuleProvenance {
41 span: Option<crate::source::Span>,
42 name: Option<Span>,
44 event_name: Option<Span>,
46 conditions: Vec<ValueProvenance>,
47 actions: Vec<ActionProvenance>,
48}
49
50#[derive(Debug, Clone, Default)]
51struct ActionProvenance {
52 span: Option<Span>,
53 identifier: Option<Span>,
56 arguments: Vec<ValueProvenance>,
57}
58
59#[derive(Debug, Clone, Default)]
62struct ValueProvenance {
63 span: Option<Span>,
64 identifier: Option<Span>,
67 children: Vec<ValueProvenance>,
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72#[non_exhaustive]
73pub enum SourceMappingError {
74 UnknownFile(FileId),
75 InvalidSpan(Span),
76 InvalidRule(usize),
77 InvalidCondition {
78 rule: usize,
79 condition: usize,
80 },
81 InvalidAction {
82 rule: usize,
83 action: usize,
84 },
85 InvalidActionArgument {
86 rule: usize,
87 action: usize,
88 argument: usize,
89 },
90 InvalidGlobalVariable(usize),
91 InvalidPlayerVariable(usize),
92 InvalidSubroutine(usize),
93}
94
95impl std::fmt::Display for SourceMappingError {
96 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 match self {
98 Self::UnknownFile(file) => {
99 write!(formatter, "source span references unknown file {file}")
100 }
101 Self::InvalidSpan(span) => write!(formatter, "invalid source span {span:?}"),
102 Self::InvalidRule(rule) => write!(formatter, "invalid rule index {rule}"),
103 Self::InvalidCondition { rule, condition } => {
104 write!(
105 formatter,
106 "invalid condition index {condition} in rule {rule}"
107 )
108 }
109 Self::InvalidAction { rule, action } => {
110 write!(formatter, "invalid action index {action} in rule {rule}")
111 }
112 Self::InvalidActionArgument {
113 rule,
114 action,
115 argument,
116 } => write!(
117 formatter,
118 "invalid argument index {argument} in action {action} of rule {rule}"
119 ),
120 Self::InvalidGlobalVariable(variable) => {
121 write!(formatter, "invalid global variable index {variable}")
122 }
123 Self::InvalidPlayerVariable(variable) => {
124 write!(formatter, "invalid player variable index {variable}")
125 }
126 Self::InvalidSubroutine(subroutine) => {
127 write!(formatter, "invalid subroutine index {subroutine}")
128 }
129 }
130 }
131}
132
133impl std::error::Error for SourceMappingError {}
134
135impl Program {
136 pub fn new() -> Self {
137 Self::default()
138 }
139
140 pub fn add_file(&mut self, mut file: SourceFile) -> FileId {
142 let id = FileId::from_index(self.files.len());
143 file.bind_file(id);
144 self.files.push(file);
145 id
146 }
147
148 pub fn global_variable(&mut self, variable: Variable) -> &mut Self {
149 self.global_variables.push(variable);
150 self
151 }
152
153 pub fn player_variable(&mut self, variable: Variable) -> &mut Self {
154 self.player_variables.push(variable);
155 self
156 }
157
158 pub fn subroutine(&mut self, subroutine: Subroutine) -> &mut Self {
159 self.subroutines.push(subroutine);
160 self
161 }
162
163 pub fn rule(&mut self, rule: Rule) -> &mut Self {
164 self.rules.push(rule);
165 self
166 }
167
168 pub fn source(&self, file: FileId) -> Option<&SourceDocument> {
170 self.files.get(file.index()).and_then(SourceFile::source)
171 }
172
173 pub fn set_rule_span(
175 &mut self,
176 rule: usize,
177 span: Option<Span>,
178 ) -> std::result::Result<(), SourceMappingError> {
179 self.validate_span(span)?;
180 self.rule_provenance_mut(rule)?.span = span;
181 Ok(())
182 }
183
184 pub fn set_condition_span(
186 &mut self,
187 rule: usize,
188 condition: usize,
189 span: Option<Span>,
190 ) -> std::result::Result<(), SourceMappingError> {
191 self.validate_span(span)?;
192 let condition_count = self
193 .rules
194 .get(rule)
195 .ok_or(SourceMappingError::InvalidRule(rule))?
196 .conditions
197 .len();
198 if condition >= condition_count {
199 return Err(SourceMappingError::InvalidCondition { rule, condition });
200 }
201 let rule_data = self.rule_provenance_mut(rule)?;
202 fit(&mut rule_data.conditions, condition_count);
203 rule_data.conditions[condition].span = span;
204 Ok(())
205 }
206
207 pub fn set_action_span(
209 &mut self,
210 rule: usize,
211 action: usize,
212 span: Option<Span>,
213 ) -> std::result::Result<(), SourceMappingError> {
214 self.validate_span(span)?;
215 let action_count = self
216 .rules
217 .get(rule)
218 .ok_or(SourceMappingError::InvalidRule(rule))?
219 .actions
220 .len();
221 if action >= action_count {
222 return Err(SourceMappingError::InvalidAction { rule, action });
223 }
224 let rule_data = self.rule_provenance_mut(rule)?;
225 fit(&mut rule_data.actions, action_count);
226 rule_data.actions[action].span = span;
227 Ok(())
228 }
229
230 pub fn set_action_argument_span(
232 &mut self,
233 rule: usize,
234 action: usize,
235 argument: usize,
236 span: Option<Span>,
237 ) -> std::result::Result<(), SourceMappingError> {
238 self.validate_span(span)?;
239 let action_value = self
240 .rules
241 .get(rule)
242 .ok_or(SourceMappingError::InvalidRule(rule))?
243 .actions
244 .get(action)
245 .ok_or(SourceMappingError::InvalidAction { rule, action })?;
246 let argument_count = action_argument_count(action_value);
247 if argument >= argument_count {
248 return Err(SourceMappingError::InvalidActionArgument {
249 rule,
250 action,
251 argument,
252 });
253 }
254 let action_data = self.action_provenance_mut(rule, action)?;
255 action_data
256 .arguments
257 .resize_with(argument + 1, ValueProvenance::default);
258 action_data.arguments[argument].span = span;
259 Ok(())
260 }
261
262 pub fn set_global_variable_spans(
264 &mut self,
265 variable: usize,
266 span: Option<Span>,
267 name_span: Option<Span>,
268 ) -> std::result::Result<(), SourceMappingError> {
269 self.validate_span(span)?;
270 self.validate_span(name_span)?;
271 if variable >= self.global_variables.len() {
272 return Err(SourceMappingError::InvalidGlobalVariable(variable));
273 }
274 let variable_count = self.global_variables.len();
275 let provenance = self.provenance_mut();
276 fit(&mut provenance.global_variables, variable_count);
277 provenance.global_variables[variable] = DeclarationProvenance { span, name_span };
278 Ok(())
279 }
280
281 pub fn set_player_variable_spans(
283 &mut self,
284 variable: usize,
285 span: Option<Span>,
286 name_span: Option<Span>,
287 ) -> std::result::Result<(), SourceMappingError> {
288 self.validate_span(span)?;
289 self.validate_span(name_span)?;
290 if variable >= self.player_variables.len() {
291 return Err(SourceMappingError::InvalidPlayerVariable(variable));
292 }
293 let variable_count = self.player_variables.len();
294 let provenance = self.provenance_mut();
295 fit(&mut provenance.player_variables, variable_count);
296 provenance.player_variables[variable] = DeclarationProvenance { span, name_span };
297 Ok(())
298 }
299
300 pub fn set_subroutine_spans(
302 &mut self,
303 subroutine: usize,
304 span: Option<Span>,
305 name_span: Option<Span>,
306 ) -> std::result::Result<(), SourceMappingError> {
307 self.validate_span(span)?;
308 self.validate_span(name_span)?;
309 if subroutine >= self.subroutines.len() {
310 return Err(SourceMappingError::InvalidSubroutine(subroutine));
311 }
312 let subroutine_count = self.subroutines.len();
313 let provenance = self.provenance_mut();
314 fit(&mut provenance.subroutines, subroutine_count);
315 provenance.subroutines[subroutine] = DeclarationProvenance { span, name_span };
316 Ok(())
317 }
318
319 pub fn rule_span(&self, rule: usize) -> Option<crate::source::Span> {
325 self.rule_provenance(rule)?.span
326 }
327
328 pub fn condition_span(&self, rule: usize, condition: usize) -> Option<crate::source::Span> {
330 self.condition_provenance(rule, condition)?.span
331 }
332
333 pub fn action_span(&self, rule: usize, action: usize) -> Option<crate::source::Span> {
335 self.action_provenance(rule, action)?.span
336 }
337
338 pub fn action_argument_span(
340 &self,
341 rule: usize,
342 action: usize,
343 argument: usize,
344 ) -> Option<crate::source::Span> {
345 self.action_provenance(rule, action)?
346 .arguments
347 .get(argument)?
348 .span
349 }
350
351 pub fn global_variable_name_span(&self, variable: usize) -> Option<Span> {
358 self.declaration_name_span(
359 |provenance| &provenance.global_variables,
360 self.global_variables.len(),
361 variable,
362 )
363 }
364
365 pub fn player_variable_name_span(&self, variable: usize) -> Option<Span> {
368 self.declaration_name_span(
369 |provenance| &provenance.player_variables,
370 self.player_variables.len(),
371 variable,
372 )
373 }
374
375 pub fn subroutine_name_span(&self, subroutine: usize) -> Option<Span> {
378 self.declaration_name_span(
379 |provenance| &provenance.subroutines,
380 self.subroutines.len(),
381 subroutine,
382 )
383 }
384
385 pub fn action_identifier_span(&self, rule: usize, action: usize) -> Option<Span> {
397 self.action_provenance(rule, action)?.identifier
398 }
399
400 pub fn rule_name_span(&self, rule: usize) -> Option<Span> {
403 self.rule_provenance(rule)?.name
404 }
405
406 pub fn rule_event_name_span(&self, rule: usize) -> Option<Span> {
410 self.rule_provenance(rule)?.event_name
411 }
412
413 pub fn condition_value_span(
428 &self,
429 rule: usize,
430 condition: usize,
431 path: &[usize],
432 ) -> Option<crate::source::Span> {
433 let mut value = self.condition_provenance(rule, condition)?;
434 for &index in path {
435 value = value.children.get(index)?;
436 }
437 value.identifier.or(value.span)
438 }
439
440 pub fn action_argument_value_span(
450 &self,
451 rule: usize,
452 action: usize,
453 argument: usize,
454 path: &[usize],
455 ) -> Option<crate::source::Span> {
456 let mut value = self
457 .action_provenance(rule, action)?
458 .arguments
459 .get(argument)?;
460 for &index in path {
461 value = value.children.get(index)?;
462 }
463 value.identifier.or(value.span)
464 }
465
466 pub fn edit_source(
469 &self,
470 span: crate::source::Span,
471 replacement: impl Into<String>,
472 ) -> std::result::Result<crate::source::SourceEdit, crate::source::SourceEditError> {
473 self.source(span.file)
474 .ok_or(crate::source::SourceEditError::InvalidRange)?
475 .edit_span(span, replacement)
476 }
477
478 pub fn validate(&self) -> std::result::Result<(), WorkshopError> {
480 let storage = self.to_wir()?;
481 storage
482 .validate()
483 .map_err(|error| WorkshopError::Malformed {
484 message: error.to_string(),
485 span: error.span(),
486 })
487 }
488
489 pub fn semantic_issues(
492 &self,
493 catalog: &crate::catalog::Catalog,
494 ) -> Vec<crate::rules::SemanticIssue> {
495 crate::analysis::semantic::inspect(self, catalog)
496 }
497
498 pub fn dump(&self) -> String {
500 self.to_wir().map_or_else(
501 |error| format!("invalid program: {error}"),
502 |program| program.dump(),
503 )
504 }
505
506 fn rule_provenance(&self, rule: usize) -> Option<&RuleProvenance> {
507 let recorded = &self.provenance.as_deref()?.rules;
508 if recorded.len() != self.rules.len() {
509 return None;
510 }
511 recorded.get(rule)
512 }
513
514 fn action_provenance(&self, rule: usize, action: usize) -> Option<&ActionProvenance> {
515 let recorded = self.rule_provenance(rule)?;
516 if recorded.actions.len() != self.rules[rule].actions.len() {
517 return None;
518 }
519 recorded.actions.get(action)
520 }
521
522 fn condition_provenance(&self, rule: usize, condition: usize) -> Option<&ValueProvenance> {
523 let recorded = self.rule_provenance(rule)?;
524 if recorded.conditions.len() != self.rules[rule].conditions.len() {
525 return None;
526 }
527 recorded.conditions.get(condition)
528 }
529
530 fn declaration_name_span(
531 &self,
532 recorded: impl Fn(&ProgramProvenance) -> &[DeclarationProvenance],
533 count: usize,
534 position: usize,
535 ) -> Option<Span> {
536 let declaration = self.declaration_provenance(recorded, count, position);
537 declaration.name_span.or(declaration.span)
538 }
539
540 fn declaration_provenance(
541 &self,
542 recorded: impl Fn(&ProgramProvenance) -> &[DeclarationProvenance],
543 count: usize,
544 position: usize,
545 ) -> DeclarationProvenance {
546 self.provenance
547 .as_deref()
548 .map(recorded)
549 .filter(|recorded| recorded.len() == count)
550 .and_then(|recorded| recorded.get(position))
551 .copied()
552 .unwrap_or_default()
553 }
554
555 fn validate_span(&self, span: Option<Span>) -> std::result::Result<(), SourceMappingError> {
556 let Some(span) = span else {
557 return Ok(());
558 };
559 if !span.is_valid() {
560 return Err(SourceMappingError::InvalidSpan(span));
561 }
562 if self.files.get(span.file.index()).is_none() {
563 return Err(SourceMappingError::UnknownFile(span.file));
564 }
565 Ok(())
566 }
567
568 fn provenance_mut(&mut self) -> &mut ProgramProvenance {
569 self.provenance
570 .get_or_insert_with(|| Box::new(ProgramProvenance::default()))
571 .as_mut()
572 }
573
574 fn rule_provenance_mut(
575 &mut self,
576 rule: usize,
577 ) -> std::result::Result<&mut RuleProvenance, SourceMappingError> {
578 if rule >= self.rules.len() {
579 return Err(SourceMappingError::InvalidRule(rule));
580 }
581 let rule_count = self.rules.len();
582 let provenance = self.provenance_mut();
583 fit(&mut provenance.rules, rule_count);
584 Ok(&mut provenance.rules[rule])
585 }
586
587 fn action_provenance_mut(
588 &mut self,
589 rule: usize,
590 action: usize,
591 ) -> std::result::Result<&mut ActionProvenance, SourceMappingError> {
592 let action_count = self
593 .rules
594 .get(rule)
595 .ok_or(SourceMappingError::InvalidRule(rule))?
596 .actions
597 .len();
598 if action >= action_count {
599 return Err(SourceMappingError::InvalidAction { rule, action });
600 }
601 let rule_data = self.rule_provenance_mut(rule)?;
602 fit(&mut rule_data.actions, action_count);
603 Ok(&mut rule_data.actions[action])
604 }
605
606 pub(crate) fn from_wir(storage: wir::Program) -> Result<Self> {
607 let mut program = Self {
608 settings: storage.settings.clone(),
609 global_variables: storage
610 .global_variables
611 .iter()
612 .map(|variable| Variable::with_index(variable.name.clone(), variable.index))
613 .collect(),
614 player_variables: storage
615 .player_variables
616 .iter()
617 .map(|variable| Variable::with_index(variable.name.clone(), variable.index))
618 .collect(),
619 subroutines: storage
620 .subroutines
621 .iter()
622 .map(|subroutine| Subroutine::with_index(subroutine.name.clone(), subroutine.index))
623 .collect(),
624 rules: Vec::with_capacity(storage.rules.len()),
625 files: storage.files.iter().cloned().collect(),
626 provenance: Some(Box::new(ProgramProvenance {
627 global_variables: storage
628 .global_variables
629 .iter()
630 .map(|variable| DeclarationProvenance {
631 span: variable.span,
632 name_span: variable.name_span,
633 })
634 .collect(),
635 player_variables: storage
636 .player_variables
637 .iter()
638 .map(|variable| DeclarationProvenance {
639 span: variable.span,
640 name_span: variable.name_span,
641 })
642 .collect(),
643 subroutines: storage
644 .subroutines
645 .iter()
646 .map(|subroutine| DeclarationProvenance {
647 span: subroutine.span,
648 name_span: subroutine.name_span,
649 })
650 .collect(),
651 rules: Vec::with_capacity(storage.rules.len()),
652 })),
653 };
654 for rule in storage.rules.iter() {
655 let event = public_event(&storage, &rule.event)?;
656 let conditions = rule
657 .conditions
658 .iter()
659 .map(|condition| {
660 Ok(Condition {
661 value: public_value(&storage, condition.value)?,
662 disabled: condition.disabled,
663 })
664 })
665 .collect::<Result<Vec<_>>>()?;
666 let mut actions = Vec::new();
667 let mut action_provenance = Vec::new();
668 for action in &rule.actions {
669 public_actions(&storage, *action, &mut actions)?;
670 public_action_provenance(&storage, *action, &mut action_provenance)?;
671 }
672 let event_name = match &rule.event {
673 wir::Event::Subroutine { name_span, .. } => *name_span,
674 _ => None,
675 };
676 program
677 .provenance
678 .as_mut()
679 .expect("parsed programs retain provenance")
680 .rules
681 .push(RuleProvenance {
682 span: rule.span,
683 name: rule.name_span,
684 event_name,
685 conditions: rule
686 .conditions
687 .iter()
688 .map(|condition| value_provenance(&storage, condition.value))
689 .collect(),
690 actions: action_provenance,
691 });
692 program.rules.push(Rule {
693 name: rule.name.clone(),
694 disabled: rule.disabled,
695 event,
696 conditions,
697 actions,
698 });
699 }
700 Ok(program)
701 }
702
703 pub(crate) fn to_wir(&self) -> Result<wir::Program> {
704 let mut storage = wir::Program {
705 settings: self.settings.clone(),
706 ..Default::default()
707 };
708
709 for file in &self.files {
710 storage.add_file(file.clone());
711 }
712
713 let mut globals = HashMap::new();
714 for (position, variable) in self.global_variables.iter().enumerate() {
715 let declaration = self.declaration_provenance(
716 |provenance| &provenance.global_variables,
717 self.global_variables.len(),
718 position,
719 );
720 let id = storage.global_variables.push(wir::WorkshopVariable {
721 name: variable.name.clone(),
722 index: variable.index.unwrap_or(position as u32),
723 span: declaration.span,
724 name_span: declaration.name_span,
725 });
726 globals.insert(variable.name.clone(), id);
727 }
728 let mut players = HashMap::new();
729 for (position, variable) in self.player_variables.iter().enumerate() {
730 let declaration = self.declaration_provenance(
731 |provenance| &provenance.player_variables,
732 self.player_variables.len(),
733 position,
734 );
735 let id = storage.player_variables.push(wir::WorkshopVariable {
736 name: variable.name.clone(),
737 index: variable.index.unwrap_or(position as u32),
738 span: declaration.span,
739 name_span: declaration.name_span,
740 });
741 players.insert(variable.name.clone(), id);
742 }
743 let mut subroutines = HashMap::new();
744 for (position, subroutine) in self.subroutines.iter().enumerate() {
745 let declaration = self.declaration_provenance(
746 |provenance| &provenance.subroutines,
747 self.subroutines.len(),
748 position,
749 );
750 let id = storage.subroutines.push(wir::WorkshopSubroutine {
751 name: subroutine.name.clone(),
752 index: subroutine.index.unwrap_or(position as u32),
753 span: declaration.span,
754 name_span: declaration.name_span,
755 });
756 subroutines.insert(subroutine.name.clone(), id);
757 }
758
759 for (rule_index, rule) in self.rules.iter().enumerate() {
760 let mut event = wir_event(&rule.event, &subroutines)?;
761 if let wir::Event::Subroutine { name_span, .. } = &mut event {
762 *name_span = self
763 .rule_provenance(rule_index)
764 .and_then(|provenance| provenance.event_name);
765 }
766 let conditions = rule
767 .conditions
768 .iter()
769 .enumerate()
770 .map(|(condition_index, condition)| {
771 wir_value(
772 &condition.value,
773 &mut storage,
774 &globals,
775 &players,
776 &subroutines,
777 )
778 .inspect(|&value| {
779 if let Some(provenance) =
780 self.condition_provenance(rule_index, condition_index)
781 {
782 apply_value_provenance(&mut storage, value, provenance);
783 }
784 })
785 .map(|value| wir::Condition {
786 value,
787 disabled: condition.disabled,
788 })
789 })
790 .collect::<Result<Vec<_>>>()?;
791 let mut actions = Vec::new();
792 let mut position = 0;
793 lower_actions(
794 &rule.actions,
795 &mut position,
796 &mut actions,
797 &mut storage,
798 &globals,
799 &players,
800 &subroutines,
801 )?;
802 if let Some(provenance) = self
803 .rule_provenance(rule_index)
804 .filter(|provenance| provenance.actions.len() == rule.actions.len())
805 {
806 let mut public_position = 0;
807 apply_action_provenance(
808 &mut storage,
809 &actions,
810 &provenance.actions,
811 &mut public_position,
812 )?;
813 }
814 if position != rule.actions.len() {
815 return Err(WorkshopError::Malformed {
816 message: "unexpected control-flow terminator in rule actions".to_string(),
817 span: None,
818 });
819 }
820 storage.rules.push(wir::Rule {
821 name: rule.name.clone(),
822 span: self.rule_span(rule_index),
823 name_span: self
824 .rule_provenance(rule_index)
825 .and_then(|provenance| provenance.name),
826 disabled: rule.disabled,
827 event,
828 conditions,
829 actions,
830 });
831 }
832 Ok(storage)
833 }
834}
835
836fn fit<T: Default>(items: &mut Vec<T>, len: usize) {
837 items.truncate(len);
838 items.resize_with(len, T::default);
839}
840
841fn public_event(storage: &wir::Program, event: &wir::Event) -> Result<Event> {
842 Ok(match event {
843 wir::Event::Global => Event::Global,
844 wir::Event::EachPlayer => Event::EachPlayer,
845 wir::Event::EachPlayerWithFilters { team, target } => Event::EachPlayerWithFilters {
846 team: public_team(*team),
847 target: public_target(target),
848 },
849 wir::Event::Player { kind, team, target } => Event::Player {
850 kind: public_player_event(*kind),
851 team: public_team(*team),
852 target: public_target(target),
853 },
854 wir::Event::Subroutine { subroutine, .. } => Event::Subroutine(
855 storage
856 .subroutines
857 .get(*subroutine)
858 .ok_or_else(|| malformed_id("subroutine", subroutine.index()))?
859 .name
860 .clone(),
861 ),
862 })
863}
864
865fn public_team(team: wir::EventTeam) -> EventTeam {
866 match team {
867 wir::EventTeam::All => EventTeam::All,
868 wir::EventTeam::Team1 => EventTeam::Team1,
869 wir::EventTeam::Team2 => EventTeam::Team2,
870 }
871}
872
873fn public_target(target: &wir::EventTarget) -> EventTarget {
874 match target {
875 wir::EventTarget::All => EventTarget::All,
876 wir::EventTarget::Slot(slot) => EventTarget::Slot(*slot),
877 wir::EventTarget::Hero(hero) => EventTarget::Hero(hero.clone()),
878 }
879}
880
881fn public_player_event(kind: wir::PlayerEventKind) -> PlayerEventKind {
882 match kind {
883 wir::PlayerEventKind::DealtDamage => PlayerEventKind::DealtDamage,
884 wir::PlayerEventKind::DealtFinalBlow => PlayerEventKind::DealtFinalBlow,
885 wir::PlayerEventKind::DealtHealing => PlayerEventKind::DealtHealing,
886 wir::PlayerEventKind::DealtKnockback => PlayerEventKind::DealtKnockback,
887 wir::PlayerEventKind::Died => PlayerEventKind::Died,
888 wir::PlayerEventKind::EarnedElimination => PlayerEventKind::EarnedElimination,
889 wir::PlayerEventKind::Joined => PlayerEventKind::Joined,
890 wir::PlayerEventKind::Left => PlayerEventKind::Left,
891 wir::PlayerEventKind::ReceivedHealing => PlayerEventKind::ReceivedHealing,
892 wir::PlayerEventKind::ReceivedKnockback => PlayerEventKind::ReceivedKnockback,
893 wir::PlayerEventKind::TookDamage => PlayerEventKind::TookDamage,
894 }
895}
896
897fn public_value(storage: &wir::Program, id: wir::ValueId) -> Result<Value> {
898 let node = storage
899 .values
900 .get(id)
901 .ok_or_else(|| malformed_id("value", id.index()))?;
902 Ok(match &node.value {
903 wir::Value::Number { value, .. } => Value::Number(*value),
904 wir::Value::String(value) => Value::String(value.clone()),
905 wir::Value::LocalizedString(value) => Value::LocalizedString(value.clone()),
906 wir::Value::Bool(value) => Value::Bool(*value),
907 wir::Value::Null => Value::Null,
908 wir::Value::Array(values) => Value::Array(
909 values
910 .iter()
911 .map(|value| public_value(storage, *value))
912 .collect::<Result<Vec<_>>>()?,
913 ),
914 wir::Value::Vector { x, y, z } => Value::Vector {
915 x: Box::new(public_value(storage, *x)?),
916 y: Box::new(public_value(storage, *y)?),
917 z: Box::new(public_value(storage, *z)?),
918 },
919 wir::Value::Enum { value_type, value } => Value::Enum {
920 value_type: value_type.clone(),
921 value: value.clone(),
922 },
923 wir::Value::GlobalVariable(id) => Value::GlobalVariable(
924 storage
925 .global_variables
926 .get(*id)
927 .ok_or_else(|| malformed_id("global variable", id.index()))?
928 .name
929 .clone(),
930 ),
931 wir::Value::PlayerVariable { player, variable } => Value::PlayerVariable {
932 player: Box::new(public_value(storage, *player)?),
933 variable: storage
934 .player_variables
935 .get(*variable)
936 .ok_or_else(|| malformed_id("player variable", variable.index()))?
937 .name
938 .clone(),
939 },
940 wir::Value::Subroutine(id) => Value::Subroutine(
941 storage
942 .subroutines
943 .get(*id)
944 .ok_or_else(|| malformed_id("subroutine", id.index()))?
945 .name
946 .clone(),
947 ),
948 wir::Value::EventPlayer => Value::EventPlayer,
949 wir::Value::Call { name, args } => Value::Call {
950 name: name.clone(),
951 args: args
952 .iter()
953 .map(|arg| public_value(storage, *arg))
954 .collect::<Result<Vec<_>>>()?,
955 },
956 })
957}
958
959fn public_actions(
960 storage: &wir::Program,
961 id: wir::ActionId,
962 output: &mut Vec<Action>,
963) -> Result<()> {
964 let action = storage
965 .actions
966 .get(id)
967 .ok_or_else(|| malformed_id("action", id.index()))?;
968 match action {
969 wir::Action::SetGlobalVariable {
970 variable, value, ..
971 } => output.push(Action::SetGlobalVariable {
972 variable: storage
973 .global_variables
974 .get(*variable)
975 .ok_or_else(|| malformed_id("global variable", variable.index()))?
976 .name
977 .clone(),
978 value: public_value(storage, *value)?,
979 }),
980 wir::Action::ModifyGlobalVariable {
981 variable,
982 op,
983 value,
984 ..
985 } => output.push(Action::ModifyGlobalVariable {
986 variable: storage
987 .global_variables
988 .get(*variable)
989 .ok_or_else(|| malformed_id("global variable", variable.index()))?
990 .name
991 .clone(),
992 op: public_modify(*op),
993 value: public_value(storage, *value)?,
994 }),
995 wir::Action::SetPlayerVariable {
996 player,
997 variable,
998 value,
999 ..
1000 } => output.push(Action::SetPlayerVariable {
1001 player: public_value(storage, *player)?,
1002 variable: storage
1003 .player_variables
1004 .get(*variable)
1005 .ok_or_else(|| malformed_id("player variable", variable.index()))?
1006 .name
1007 .clone(),
1008 value: public_value(storage, *value)?,
1009 }),
1010 wir::Action::ModifyPlayerVariable {
1011 player,
1012 variable,
1013 op,
1014 value,
1015 ..
1016 } => output.push(Action::ModifyPlayerVariable {
1017 player: public_value(storage, *player)?,
1018 variable: storage
1019 .player_variables
1020 .get(*variable)
1021 .ok_or_else(|| malformed_id("player variable", variable.index()))?
1022 .name
1023 .clone(),
1024 op: public_modify(*op),
1025 value: public_value(storage, *value)?,
1026 }),
1027 wir::Action::AssignMember {
1028 target, op, value, ..
1029 } => output.push(Action::AssignMember {
1030 target: public_value(storage, *target)?,
1031 op: op.map(public_modify),
1032 value: public_value(storage, *value)?,
1033 }),
1034 wir::Action::CallSubroutine { subroutine, .. } => output.push(Action::CallSubroutine {
1035 subroutine: storage
1036 .subroutines
1037 .get(*subroutine)
1038 .ok_or_else(|| malformed_id("subroutine", subroutine.index()))?
1039 .name
1040 .clone(),
1041 }),
1042 wir::Action::If {
1043 branches,
1044 else_body,
1045 ..
1046 } => {
1047 for (index, branch) in branches.iter().enumerate() {
1048 output.push(if index == 0 {
1049 Action::If {
1050 condition: public_value(storage, branch.condition)?,
1051 }
1052 } else {
1053 Action::ElseIf {
1054 condition: public_value(storage, branch.condition)?,
1055 }
1056 });
1057 for action in &branch.body {
1058 public_actions(storage, *action, output)?;
1059 }
1060 }
1061 if let Some(body) = else_body {
1062 output.push(Action::Else);
1063 for action in body {
1064 public_actions(storage, *action, output)?;
1065 }
1066 }
1067 output.push(Action::End);
1068 }
1069 wir::Action::While {
1070 condition, body, ..
1071 } => {
1072 output.push(Action::While {
1073 condition: public_value(storage, *condition)?,
1074 });
1075 for action in body {
1076 public_actions(storage, *action, output)?;
1077 }
1078 output.push(Action::End);
1079 }
1080 wir::Action::ForGlobalVariable {
1081 variable,
1082 start,
1083 stop,
1084 step,
1085 body,
1086 ..
1087 } => {
1088 output.push(Action::ForGlobalVariable {
1089 variable: storage
1090 .global_variables
1091 .get(*variable)
1092 .ok_or_else(|| malformed_id("global variable", variable.index()))?
1093 .name
1094 .clone(),
1095 start: public_value(storage, *start)?,
1096 stop: public_value(storage, *stop)?,
1097 step: public_value(storage, *step)?,
1098 });
1099 for action in body {
1100 public_actions(storage, *action, output)?;
1101 }
1102 output.push(Action::End);
1103 }
1104 wir::Action::ForPlayerVariable {
1105 player,
1106 variable,
1107 start,
1108 stop,
1109 step,
1110 body,
1111 ..
1112 } => {
1113 output.push(Action::ForPlayerVariable {
1114 player: public_value(storage, *player)?,
1115 variable: storage
1116 .player_variables
1117 .get(*variable)
1118 .ok_or_else(|| malformed_id("player variable", variable.index()))?
1119 .name
1120 .clone(),
1121 start: public_value(storage, *start)?,
1122 stop: public_value(storage, *stop)?,
1123 step: public_value(storage, *step)?,
1124 });
1125 for action in body {
1126 public_actions(storage, *action, output)?;
1127 }
1128 output.push(Action::End);
1129 }
1130 wir::Action::Disabled { action, .. } => {
1131 let first = output.len();
1132 public_actions(storage, *action, output)?;
1133 let inner = output.remove(first);
1134 output.insert(first, Action::disabled(inner));
1135 }
1136 wir::Action::Call { name, args, .. } => output.push(Action::Call {
1137 name: name.clone(),
1138 args: args
1139 .iter()
1140 .map(|arg| public_value(storage, *arg))
1141 .collect::<Result<Vec<_>>>()?,
1142 }),
1143 }
1144 Ok(())
1145}
1146
1147fn public_action_provenance(
1148 storage: &wir::Program,
1149 id: wir::ActionId,
1150 output: &mut Vec<ActionProvenance>,
1151) -> Result<()> {
1152 let action = storage
1153 .actions
1154 .get(id)
1155 .ok_or_else(|| malformed_id("action", id.index()))?;
1156 let identifier = action_identifier(action);
1157 let push = |output: &mut Vec<ActionProvenance>, arguments: &[wir::ValueId]| {
1158 output.push(ActionProvenance {
1159 span: action.span(),
1160 identifier,
1161 arguments: arguments
1162 .iter()
1163 .map(|value| value_provenance(storage, *value))
1164 .collect(),
1165 });
1166 };
1167 let push_without_span = |output: &mut Vec<ActionProvenance>, arguments: &[wir::ValueId]| {
1168 output.push(ActionProvenance {
1169 span: None,
1170 identifier: None,
1171 arguments: arguments
1172 .iter()
1173 .map(|value| value_provenance(storage, *value))
1174 .collect(),
1175 });
1176 };
1177 match action {
1178 wir::Action::SetGlobalVariable { value, .. }
1179 | wir::Action::ModifyGlobalVariable { value, .. } => push(output, &[*value]),
1180 wir::Action::SetPlayerVariable { player, value, .. }
1181 | wir::Action::ModifyPlayerVariable { player, value, .. } => {
1182 push(output, &[*player, *value])
1183 }
1184 wir::Action::AssignMember { target, value, .. } => push(output, &[*target, *value]),
1185 wir::Action::CallSubroutine { .. } => push(output, &[]),
1186 wir::Action::If {
1187 branches,
1188 else_body,
1189 ..
1190 } => {
1191 for (index, branch) in branches.iter().enumerate() {
1192 if index == 0 {
1193 push(output, &[branch.condition]);
1194 } else {
1195 push_without_span(output, &[branch.condition]);
1196 }
1197 for action in &branch.body {
1198 public_action_provenance(storage, *action, output)?;
1199 }
1200 if index + 1 == branches.len() && else_body.is_none() {
1201 push_without_span(output, &[]);
1202 }
1203 }
1204 if let Some(body) = else_body {
1205 push_without_span(output, &[]);
1206 for action in body {
1207 public_action_provenance(storage, *action, output)?;
1208 }
1209 push_without_span(output, &[]);
1210 }
1211 }
1212 wir::Action::While {
1213 condition, body, ..
1214 } => {
1215 push(output, &[*condition]);
1216 for action in body {
1217 public_action_provenance(storage, *action, output)?;
1218 }
1219 push_without_span(output, &[]);
1220 }
1221 wir::Action::ForGlobalVariable {
1222 start,
1223 stop,
1224 step,
1225 body,
1226 ..
1227 } => {
1228 push(output, &[*start, *stop, *step]);
1229 for action in body {
1230 public_action_provenance(storage, *action, output)?;
1231 }
1232 push_without_span(output, &[]);
1233 }
1234 wir::Action::ForPlayerVariable {
1235 player,
1236 start,
1237 stop,
1238 step,
1239 body,
1240 ..
1241 } => {
1242 push(output, &[*player, *start, *stop, *step]);
1243 for action in body {
1244 public_action_provenance(storage, *action, output)?;
1245 }
1246 push_without_span(output, &[]);
1247 }
1248 wir::Action::Disabled { action, .. } => {
1249 public_action_provenance(storage, *action, output)?;
1250 }
1251 wir::Action::Call { args, .. } => push(output, args),
1252 }
1253 Ok(())
1254}
1255
1256fn lower_actions(
1257 actions: &[Action],
1258 position: &mut usize,
1259 output: &mut Vec<wir::ActionId>,
1260 storage: &mut wir::Program,
1261 globals: &HashMap<String, wir::GlobalVarId>,
1262 players: &HashMap<String, wir::PlayerVarId>,
1263 subroutines: &HashMap<String, wir::SubroutineId>,
1264) -> Result<()> {
1265 while *position < actions.len() {
1266 let (disabled, current) = match &actions[*position] {
1267 Action::Disabled { action } => (true, action.as_ref()),
1268 action => (false, action),
1269 };
1270 if disabled
1271 && matches!(
1272 current,
1273 Action::ElseIf { .. } | Action::Else | Action::End | Action::Disabled { .. }
1274 )
1275 {
1276 return Err(WorkshopError::Unsupported {
1277 message: "the disabled modifier applies to a single executable action".to_string(),
1278 span: None,
1279 });
1280 }
1281 match current {
1282 Action::ElseIf { .. } | Action::Else | Action::End => return Ok(()),
1283 Action::If { condition } => {
1284 *position += 1;
1285 let mut branches = vec![wir::IfBranch {
1286 condition: wir_value(condition, storage, globals, players, subroutines)?,
1287 body: Vec::new(),
1288 }];
1289 lower_actions(
1290 actions,
1291 position,
1292 &mut branches[0].body,
1293 storage,
1294 globals,
1295 players,
1296 subroutines,
1297 )?;
1298 while let Some(Action::ElseIf { condition }) = actions.get(*position) {
1299 *position += 1;
1300 let mut body = Vec::new();
1301 lower_actions(
1302 actions,
1303 position,
1304 &mut body,
1305 storage,
1306 globals,
1307 players,
1308 subroutines,
1309 )?;
1310 branches.push(wir::IfBranch {
1311 condition: wir_value(condition, storage, globals, players, subroutines)?,
1312 body,
1313 });
1314 }
1315 let else_body = if matches!(actions.get(*position), Some(Action::Else)) {
1316 *position += 1;
1317 let mut body = Vec::new();
1318 lower_actions(
1319 actions,
1320 position,
1321 &mut body,
1322 storage,
1323 globals,
1324 players,
1325 subroutines,
1326 )?;
1327 Some(body)
1328 } else {
1329 None
1330 };
1331 if !matches!(actions.get(*position), Some(Action::End)) {
1332 return Err(WorkshopError::Malformed {
1333 message: "control-flow action is missing End".to_string(),
1334 span: None,
1335 });
1336 }
1337 *position += 1;
1338 output.push(storage.actions.push(wir::Action::If {
1339 branches,
1340 else_body,
1341 span: None,
1342 }));
1343 }
1344 Action::While { condition } => {
1345 *position += 1;
1346 let mut body = Vec::new();
1347 lower_actions(
1348 actions,
1349 position,
1350 &mut body,
1351 storage,
1352 globals,
1353 players,
1354 subroutines,
1355 )?;
1356 require_end(actions, position)?;
1357 let condition = wir_value(condition, storage, globals, players, subroutines)?;
1358 output.push(storage.actions.push(wir::Action::While {
1359 condition,
1360 body,
1361 span: None,
1362 }));
1363 }
1364 Action::ForGlobalVariable {
1365 variable,
1366 start,
1367 stop,
1368 step,
1369 } => {
1370 *position += 1;
1371 let mut body = Vec::new();
1372 lower_actions(
1373 actions,
1374 position,
1375 &mut body,
1376 storage,
1377 globals,
1378 players,
1379 subroutines,
1380 )?;
1381 require_end(actions, position)?;
1382 let variable = *globals
1383 .get(variable)
1384 .ok_or_else(|| unknown_name("global variable", variable))?;
1385 let start = wir_value(start, storage, globals, players, subroutines)?;
1386 let stop = wir_value(stop, storage, globals, players, subroutines)?;
1387 let step = wir_value(step, storage, globals, players, subroutines)?;
1388 output.push(storage.actions.push(wir::Action::ForGlobalVariable {
1389 variable,
1390 start,
1391 stop,
1392 step,
1393 body,
1394 span: None,
1395 target_span: None,
1396 }));
1397 }
1398 Action::ForPlayerVariable {
1399 player,
1400 variable,
1401 start,
1402 stop,
1403 step,
1404 } => {
1405 *position += 1;
1406 let mut body = Vec::new();
1407 lower_actions(
1408 actions,
1409 position,
1410 &mut body,
1411 storage,
1412 globals,
1413 players,
1414 subroutines,
1415 )?;
1416 require_end(actions, position)?;
1417 let player = wir_value(player, storage, globals, players, subroutines)?;
1418 let variable = *players
1419 .get(variable)
1420 .ok_or_else(|| unknown_name("player variable", variable))?;
1421 let start = wir_value(start, storage, globals, players, subroutines)?;
1422 let stop = wir_value(stop, storage, globals, players, subroutines)?;
1423 let step = wir_value(step, storage, globals, players, subroutines)?;
1424 output.push(storage.actions.push(wir::Action::ForPlayerVariable {
1425 player,
1426 variable,
1427 start,
1428 stop,
1429 step,
1430 body,
1431 span: None,
1432 target_span: None,
1433 }));
1434 }
1435 action => {
1436 *position += 1;
1437 let lowered = wir_action(action, storage, globals, players, subroutines)?;
1438 output.push(lowered);
1439 }
1440 }
1441 if disabled {
1442 let action = output.pop().expect("a lowered action was just pushed");
1443 output.push(
1444 storage
1445 .actions
1446 .push(wir::Action::Disabled { action, span: None }),
1447 );
1448 }
1449 }
1450 Ok(())
1451}
1452
1453fn apply_action_provenance(
1454 storage: &mut wir::Program,
1455 actions: &[wir::ActionId],
1456 provenance: &[ActionProvenance],
1457 position: &mut usize,
1458) -> Result<()> {
1459 for id in actions {
1460 let action = storage
1461 .actions
1462 .get(*id)
1463 .cloned()
1464 .ok_or_else(|| malformed_id("action", id.index()))?;
1465 match action {
1466 wir::Action::If {
1467 branches,
1468 else_body,
1469 ..
1470 } => {
1471 let source = provenance.get(*position).cloned().unwrap_or_default();
1472 *position += 1;
1473 apply_action_source(storage, *id, &source);
1474 for (branch_index, branch) in branches.iter().enumerate() {
1475 if branch_index > 0 {
1476 let source = provenance.get(*position).cloned().unwrap_or_default();
1477 *position += 1;
1478 if let Some(provenance) = source.arguments.first() {
1479 apply_value_provenance(storage, branch.condition, provenance);
1480 }
1481 }
1482 apply_action_provenance(storage, &branch.body, provenance, position)?;
1483 }
1484 if let Some(body) = else_body {
1485 *position += 1;
1486 apply_action_provenance(storage, &body, provenance, position)?;
1487 }
1488 *position += 1;
1489 }
1490 wir::Action::While { body, .. } => {
1491 let source = provenance.get(*position).cloned().unwrap_or_default();
1492 *position += 1;
1493 apply_action_source(storage, *id, &source);
1494 apply_action_provenance(storage, &body, provenance, position)?;
1495 *position += 1;
1496 }
1497 wir::Action::ForGlobalVariable { body, .. }
1498 | wir::Action::ForPlayerVariable { body, .. } => {
1499 let source = provenance.get(*position).cloned().unwrap_or_default();
1500 *position += 1;
1501 apply_action_source(storage, *id, &source);
1502 apply_action_provenance(storage, &body, provenance, position)?;
1503 *position += 1;
1504 }
1505 wir::Action::Disabled { action, .. } => {
1506 let start = *position;
1507 apply_action_provenance(storage, &[action], provenance, position)?;
1508 let source = provenance.get(start).cloned().unwrap_or_default();
1509 if let Some(wir::Action::Disabled { span, .. }) = storage.actions.get_mut(*id) {
1510 *span = source.span;
1511 }
1512 }
1513 _ => {
1514 let source = provenance.get(*position).cloned().unwrap_or_default();
1515 *position += 1;
1516 apply_action_source(storage, *id, &source);
1517 }
1518 }
1519 }
1520 Ok(())
1521}
1522
1523fn apply_action_source(storage: &mut wir::Program, id: wir::ActionId, source: &ActionProvenance) {
1524 if let Some(action) = storage.actions.get_mut(id) {
1525 match action {
1526 wir::Action::SetGlobalVariable {
1527 span, target_span, ..
1528 }
1529 | wir::Action::ModifyGlobalVariable {
1530 span, target_span, ..
1531 }
1532 | wir::Action::SetPlayerVariable {
1533 span, target_span, ..
1534 }
1535 | wir::Action::ModifyPlayerVariable {
1536 span, target_span, ..
1537 }
1538 | wir::Action::ForGlobalVariable {
1539 span, target_span, ..
1540 }
1541 | wir::Action::ForPlayerVariable {
1542 span, target_span, ..
1543 } => {
1544 *span = source.span;
1545 *target_span = source.identifier;
1546 }
1547 wir::Action::CallSubroutine {
1548 span, callee_span, ..
1549 } => {
1550 *span = source.span;
1551 *callee_span = source.identifier;
1552 }
1553 wir::Action::AssignMember { span, .. }
1554 | wir::Action::If { span, .. }
1555 | wir::Action::While { span, .. }
1556 | wir::Action::Disabled { span, .. }
1557 | wir::Action::Call { span, .. } => *span = source.span,
1558 }
1559 }
1560 let value_ids = storage
1561 .actions
1562 .get(id)
1563 .map(action_value_ids)
1564 .unwrap_or_default();
1565 for (value, provenance) in value_ids.into_iter().zip(&source.arguments) {
1566 apply_value_provenance(storage, value, provenance);
1567 }
1568}
1569
1570fn action_value_ids(action: &wir::Action) -> Vec<wir::ValueId> {
1571 match action {
1572 wir::Action::SetGlobalVariable { value, .. }
1573 | wir::Action::ModifyGlobalVariable { value, .. } => vec![*value],
1574 wir::Action::SetPlayerVariable { player, value, .. }
1575 | wir::Action::ModifyPlayerVariable { player, value, .. } => vec![*player, *value],
1576 wir::Action::AssignMember { target, value, .. } => vec![*target, *value],
1577 wir::Action::If { branches, .. } => {
1578 branches.iter().map(|branch| branch.condition).collect()
1579 }
1580 wir::Action::While { condition, .. } => vec![*condition],
1581 wir::Action::ForGlobalVariable {
1582 start, stop, step, ..
1583 } => vec![*start, *stop, *step],
1584 wir::Action::ForPlayerVariable {
1585 player,
1586 start,
1587 stop,
1588 step,
1589 ..
1590 } => vec![*player, *start, *stop, *step],
1591 wir::Action::Call { args, .. } => args.clone(),
1592 wir::Action::CallSubroutine { .. } | wir::Action::Disabled { .. } => Vec::new(),
1593 }
1594}
1595
1596fn action_identifier(action: &wir::Action) -> Option<Span> {
1599 match action {
1600 wir::Action::SetGlobalVariable { target_span, .. }
1601 | wir::Action::ModifyGlobalVariable { target_span, .. }
1602 | wir::Action::SetPlayerVariable { target_span, .. }
1603 | wir::Action::ModifyPlayerVariable { target_span, .. }
1604 | wir::Action::ForGlobalVariable { target_span, .. }
1605 | wir::Action::ForPlayerVariable { target_span, .. } => *target_span,
1606 wir::Action::CallSubroutine { callee_span, .. } => *callee_span,
1607 _ => None,
1608 }
1609}
1610
1611fn value_provenance(storage: &wir::Program, id: wir::ValueId) -> ValueProvenance {
1614 let Some(node) = storage.values.get(id) else {
1615 return ValueProvenance::default();
1616 };
1617 ValueProvenance {
1618 span: node.span,
1619 identifier: node.identifier,
1620 children: wir_value_children(&node.value)
1621 .into_iter()
1622 .map(|child| value_provenance(storage, child))
1623 .collect(),
1624 }
1625}
1626
1627fn wir_value_children(value: &wir::Value) -> Vec<wir::ValueId> {
1630 match value {
1631 wir::Value::Array(values) => values.clone(),
1632 wir::Value::Vector { x, y, z } => vec![*x, *y, *z],
1633 wir::Value::PlayerVariable { player, .. } => vec![*player],
1634 wir::Value::Call { args, .. } => args.clone(),
1635 _ => Vec::new(),
1636 }
1637}
1638
1639fn apply_value_provenance(
1641 storage: &mut wir::Program,
1642 value: wir::ValueId,
1643 source: &ValueProvenance,
1644) {
1645 let Some(node) = storage.values.get(value) else {
1646 return;
1647 };
1648 let children = wir_value_children(&node.value);
1649 if let Some(node) = storage.values.get_mut(value) {
1650 node.span = source.span;
1651 node.identifier = source.identifier;
1652 }
1653 for (child, source) in children.into_iter().zip(&source.children) {
1654 apply_value_provenance(storage, child, source);
1655 }
1656}
1657
1658fn require_end(actions: &[Action], position: &mut usize) -> Result<()> {
1659 if !matches!(actions.get(*position), Some(Action::End)) {
1660 return Err(WorkshopError::Malformed {
1661 message: "control-flow action is missing End".to_string(),
1662 span: None,
1663 });
1664 }
1665 *position += 1;
1666 Ok(())
1667}
1668
1669fn wir_action(
1670 action: &Action,
1671 storage: &mut wir::Program,
1672 globals: &HashMap<String, wir::GlobalVarId>,
1673 players: &HashMap<String, wir::PlayerVarId>,
1674 subroutines: &HashMap<String, wir::SubroutineId>,
1675) -> Result<wir::ActionId> {
1676 let action = match action {
1677 Action::SetGlobalVariable { variable, value } => wir::Action::SetGlobalVariable {
1678 variable: *globals
1679 .get(variable)
1680 .ok_or_else(|| unknown_name("global variable", variable))?,
1681 value: wir_value(value, storage, globals, players, subroutines)?,
1682 span: None,
1683 target_span: None,
1684 },
1685 Action::ModifyGlobalVariable {
1686 variable,
1687 op,
1688 value,
1689 } => wir::Action::ModifyGlobalVariable {
1690 variable: *globals
1691 .get(variable)
1692 .ok_or_else(|| unknown_name("global variable", variable))?,
1693 op: wir_modify(*op),
1694 value: wir_value(value, storage, globals, players, subroutines)?,
1695 span: None,
1696 target_span: None,
1697 },
1698 Action::SetPlayerVariable {
1699 player,
1700 variable,
1701 value,
1702 } => wir::Action::SetPlayerVariable {
1703 player: wir_value(player, storage, globals, players, subroutines)?,
1704 variable: *players
1705 .get(variable)
1706 .ok_or_else(|| unknown_name("player variable", variable))?,
1707 value: wir_value(value, storage, globals, players, subroutines)?,
1708 span: None,
1709 target_span: None,
1710 },
1711 Action::ModifyPlayerVariable {
1712 player,
1713 variable,
1714 op,
1715 value,
1716 } => wir::Action::ModifyPlayerVariable {
1717 player: wir_value(player, storage, globals, players, subroutines)?,
1718 variable: *players
1719 .get(variable)
1720 .ok_or_else(|| unknown_name("player variable", variable))?,
1721 op: wir_modify(*op),
1722 value: wir_value(value, storage, globals, players, subroutines)?,
1723 span: None,
1724 target_span: None,
1725 },
1726 Action::AssignMember { target, op, value } => wir::Action::AssignMember {
1727 target: wir_value(target, storage, globals, players, subroutines)?,
1728 op: op.map(wir_modify),
1729 value: wir_value(value, storage, globals, players, subroutines)?,
1730 span: None,
1731 },
1732 Action::CallSubroutine { subroutine } => wir::Action::CallSubroutine {
1733 subroutine: *subroutines
1734 .get(subroutine)
1735 .ok_or_else(|| unknown_name("subroutine", subroutine))?,
1736 span: None,
1737 callee_span: None,
1738 },
1739 Action::Disabled { .. } => {
1740 unreachable!("disabled actions are lowered by lower_actions")
1741 }
1742 Action::Call { name, args } => wir::Action::Call {
1743 name: name.clone(),
1744 args: args
1745 .iter()
1746 .map(|arg| wir_value(arg, storage, globals, players, subroutines))
1747 .collect::<Result<Vec<_>>>()?,
1748 span: None,
1749 },
1750 Action::ElseIf { .. }
1751 | Action::Else
1752 | Action::End
1753 | Action::If { .. }
1754 | Action::While { .. }
1755 | Action::ForGlobalVariable { .. }
1756 | Action::ForPlayerVariable { .. } => {
1757 unreachable!("structured actions are lowered by lower_actions")
1758 }
1759 };
1760 Ok(storage.actions.push(action))
1761}
1762
1763fn wir_value(
1764 value: &Value,
1765 storage: &mut wir::Program,
1766 globals: &HashMap<String, wir::GlobalVarId>,
1767 players: &HashMap<String, wir::PlayerVarId>,
1768 subroutines: &HashMap<String, wir::SubroutineId>,
1769) -> Result<wir::ValueId> {
1770 let value = match value {
1771 Value::Number(value) => wir::Value::Number {
1772 value: *value,
1773 text: crate::core::format::format_number(*value),
1774 },
1775 Value::String(value) => wir::Value::String(value.clone()),
1776 Value::LocalizedString(value) => wir::Value::LocalizedString(value.clone()),
1777 Value::Bool(value) => wir::Value::Bool(*value),
1778 Value::Null => wir::Value::Null,
1779 Value::Array(values) => wir::Value::Array(
1780 values
1781 .iter()
1782 .map(|value| wir_value(value, storage, globals, players, subroutines))
1783 .collect::<Result<Vec<_>>>()?,
1784 ),
1785 Value::Vector { x, y, z } => wir::Value::Vector {
1786 x: wir_value(x, storage, globals, players, subroutines)?,
1787 y: wir_value(y, storage, globals, players, subroutines)?,
1788 z: wir_value(z, storage, globals, players, subroutines)?,
1789 },
1790 Value::Enum { value_type, value } => wir::Value::Enum {
1791 value_type: value_type.clone(),
1792 value: value.clone(),
1793 },
1794 Value::GlobalVariable(name) => wir::Value::GlobalVariable(
1795 *globals
1796 .get(name)
1797 .ok_or_else(|| unknown_name("global variable", name))?,
1798 ),
1799 Value::PlayerVariable { player, variable } => wir::Value::PlayerVariable {
1800 player: wir_value(player, storage, globals, players, subroutines)?,
1801 variable: *players
1802 .get(variable)
1803 .ok_or_else(|| unknown_name("player variable", variable))?,
1804 },
1805 Value::Subroutine(name) => wir::Value::Subroutine(
1806 *subroutines
1807 .get(name)
1808 .ok_or_else(|| unknown_name("subroutine", name))?,
1809 ),
1810 Value::EventPlayer => wir::Value::EventPlayer,
1811 Value::Call { name, args } => wir::Value::Call {
1812 name: name.clone(),
1813 args: args
1814 .iter()
1815 .map(|arg| wir_value(arg, storage, globals, players, subroutines))
1816 .collect::<Result<Vec<_>>>()?,
1817 },
1818 };
1819 Ok(storage.values.push(wir::ValueNode::new(value, None)))
1820}
1821
1822fn wir_event(
1823 event: &Event,
1824 subroutines: &HashMap<String, wir::SubroutineId>,
1825) -> Result<wir::Event> {
1826 Ok(match event {
1827 Event::Global => wir::Event::Global,
1828 Event::EachPlayer => wir::Event::EachPlayer,
1829 Event::EachPlayerWithFilters { team, target } => wir::Event::EachPlayerWithFilters {
1830 team: wir_team(*team),
1831 target: wir_target(target),
1832 },
1833 Event::Player { kind, team, target } => wir::Event::Player {
1834 kind: wir_player_event(*kind),
1835 team: wir_team(*team),
1836 target: wir_target(target),
1837 },
1838 Event::Subroutine(name) => wir::Event::Subroutine {
1839 subroutine: *subroutines
1840 .get(name)
1841 .ok_or_else(|| unknown_name("subroutine", name))?,
1842 name_span: None,
1843 },
1844 })
1845}
1846
1847fn wir_team(team: EventTeam) -> wir::EventTeam {
1848 match team {
1849 EventTeam::All => wir::EventTeam::All,
1850 EventTeam::Team1 => wir::EventTeam::Team1,
1851 EventTeam::Team2 => wir::EventTeam::Team2,
1852 }
1853}
1854
1855fn wir_target(target: &EventTarget) -> wir::EventTarget {
1856 match target {
1857 EventTarget::All => wir::EventTarget::All,
1858 EventTarget::Slot(slot) => wir::EventTarget::Slot(*slot),
1859 EventTarget::Hero(hero) => wir::EventTarget::Hero(hero.clone()),
1860 }
1861}
1862
1863fn wir_player_event(kind: PlayerEventKind) -> wir::PlayerEventKind {
1864 match kind {
1865 PlayerEventKind::DealtDamage => wir::PlayerEventKind::DealtDamage,
1866 PlayerEventKind::DealtFinalBlow => wir::PlayerEventKind::DealtFinalBlow,
1867 PlayerEventKind::DealtHealing => wir::PlayerEventKind::DealtHealing,
1868 PlayerEventKind::DealtKnockback => wir::PlayerEventKind::DealtKnockback,
1869 PlayerEventKind::Died => wir::PlayerEventKind::Died,
1870 PlayerEventKind::EarnedElimination => wir::PlayerEventKind::EarnedElimination,
1871 PlayerEventKind::Joined => wir::PlayerEventKind::Joined,
1872 PlayerEventKind::Left => wir::PlayerEventKind::Left,
1873 PlayerEventKind::ReceivedHealing => wir::PlayerEventKind::ReceivedHealing,
1874 PlayerEventKind::ReceivedKnockback => wir::PlayerEventKind::ReceivedKnockback,
1875 PlayerEventKind::TookDamage => wir::PlayerEventKind::TookDamage,
1876 }
1877}
1878
1879fn public_modify(op: wir::ModifyOp) -> ModifyOp {
1880 match op {
1881 wir::ModifyOp::Add => ModifyOp::Add,
1882 wir::ModifyOp::Subtract => ModifyOp::Subtract,
1883 wir::ModifyOp::Multiply => ModifyOp::Multiply,
1884 wir::ModifyOp::Divide => ModifyOp::Divide,
1885 wir::ModifyOp::Modulo => ModifyOp::Modulo,
1886 wir::ModifyOp::Min => ModifyOp::Min,
1887 wir::ModifyOp::Max => ModifyOp::Max,
1888 wir::ModifyOp::RaiseToPower => ModifyOp::RaiseToPower,
1889 wir::ModifyOp::AppendToArray => ModifyOp::AppendToArray,
1890 wir::ModifyOp::RemoveFromArrayByValue => ModifyOp::RemoveFromArrayByValue,
1891 wir::ModifyOp::RemoveFromArrayByIndex => ModifyOp::RemoveFromArrayByIndex,
1892 }
1893}
1894
1895fn wir_modify(op: ModifyOp) -> wir::ModifyOp {
1896 match op {
1897 ModifyOp::Add => wir::ModifyOp::Add,
1898 ModifyOp::Subtract => wir::ModifyOp::Subtract,
1899 ModifyOp::Multiply => wir::ModifyOp::Multiply,
1900 ModifyOp::Divide => wir::ModifyOp::Divide,
1901 ModifyOp::Modulo => wir::ModifyOp::Modulo,
1902 ModifyOp::Min => wir::ModifyOp::Min,
1903 ModifyOp::Max => wir::ModifyOp::Max,
1904 ModifyOp::RaiseToPower => wir::ModifyOp::RaiseToPower,
1905 ModifyOp::AppendToArray => wir::ModifyOp::AppendToArray,
1906 ModifyOp::RemoveFromArrayByValue => wir::ModifyOp::RemoveFromArrayByValue,
1907 ModifyOp::RemoveFromArrayByIndex => wir::ModifyOp::RemoveFromArrayByIndex,
1908 }
1909}
1910
1911fn malformed_id(kind: &str, index: usize) -> WorkshopError {
1912 WorkshopError::Malformed {
1913 message: format!("dangling {kind} {index}"),
1914 span: None,
1915 }
1916}
1917
1918fn unknown_name(kind: &str, name: &str) -> WorkshopError {
1919 WorkshopError::Malformed {
1920 message: format!("unknown {kind} '{name}'"),
1921 span: None,
1922 }
1923}
1924
1925#[derive(Debug, Clone, PartialEq, Eq)]
1927pub struct Variable {
1928 pub name: String,
1929 pub index: Option<u32>,
1931}
1932
1933impl Variable {
1934 pub fn new(name: impl Into<String>) -> Self {
1935 Self {
1936 name: name.into(),
1937 index: None,
1938 }
1939 }
1940
1941 pub fn with_index(name: impl Into<String>, index: u32) -> Self {
1942 Self {
1943 name: name.into(),
1944 index: Some(index),
1945 }
1946 }
1947}
1948
1949#[derive(Debug, Clone, PartialEq, Eq)]
1951pub struct Subroutine {
1952 pub name: String,
1953 pub index: Option<u32>,
1955}
1956
1957impl Subroutine {
1958 pub fn new(name: impl Into<String>) -> Self {
1959 Self {
1960 name: name.into(),
1961 index: None,
1962 }
1963 }
1964
1965 pub fn with_index(name: impl Into<String>, index: u32) -> Self {
1966 Self {
1967 name: name.into(),
1968 index: Some(index),
1969 }
1970 }
1971}
1972
1973#[derive(Debug, Clone)]
1975#[non_exhaustive]
1976pub struct Rule {
1977 pub name: String,
1978 pub disabled: bool,
1979 pub event: Event,
1980 pub conditions: Vec<Condition>,
1981 pub actions: Vec<Action>,
1982}
1983
1984impl Rule {
1985 pub fn new(name: impl Into<String>, event: Event) -> Self {
1986 Self {
1987 name: name.into(),
1988 disabled: false,
1989 event,
1990 conditions: Vec::new(),
1991 actions: Vec::new(),
1992 }
1993 }
1994
1995 pub fn condition(mut self, condition: impl Into<Condition>) -> Self {
1996 self.conditions.push(condition.into());
1997 self
1998 }
1999
2000 pub fn action(mut self, action: Action) -> Self {
2001 self.actions.push(action);
2002 self
2003 }
2004}
2005
2006#[derive(Debug, Clone)]
2008#[non_exhaustive]
2009pub struct Condition {
2010 pub value: Value,
2011 pub disabled: bool,
2012}
2013
2014impl Condition {
2015 pub fn new(value: Value) -> Self {
2016 Self {
2017 value,
2018 disabled: false,
2019 }
2020 }
2021
2022 pub fn disabled(value: Value) -> Self {
2023 Self {
2024 value,
2025 disabled: true,
2026 }
2027 }
2028}
2029
2030impl From<Value> for Condition {
2031 fn from(value: Value) -> Self {
2032 Self::new(value)
2033 }
2034}
2035
2036fn action_argument_values(action: &Action) -> Vec<&Value> {
2038 match action {
2039 Action::SetGlobalVariable { value, .. } | Action::ModifyGlobalVariable { value, .. } => {
2040 vec![value]
2041 }
2042 Action::SetPlayerVariable { player, value, .. }
2043 | Action::ModifyPlayerVariable { player, value, .. } => vec![player, value],
2044 Action::AssignMember { target, value, .. } => vec![target, value],
2045 Action::If { condition } | Action::ElseIf { condition } | Action::While { condition } => {
2046 vec![condition]
2047 }
2048 Action::ForGlobalVariable {
2049 start, stop, step, ..
2050 } => vec![start, stop, step],
2051 Action::ForPlayerVariable {
2052 player,
2053 start,
2054 stop,
2055 step,
2056 ..
2057 } => vec![player, start, stop, step],
2058 Action::Call { args, .. } => args.iter().collect(),
2059 Action::CallSubroutine { .. } | Action::Else | Action::End => Vec::new(),
2060 Action::Disabled { action } => action_argument_values(action),
2061 }
2062}
2063
2064fn value_children(value: &Value) -> Vec<&Value> {
2067 match value {
2068 Value::Array(values) => values.iter().collect(),
2069 Value::Vector { x, y, z } => vec![x.as_ref(), y.as_ref(), z.as_ref()],
2070 Value::PlayerVariable { player, .. } => vec![player.as_ref()],
2071 Value::Call { args, .. } => args.iter().collect(),
2072 _ => Vec::new(),
2073 }
2074}
2075
2076fn action_argument_count(action: &Action) -> usize {
2077 match action {
2078 Action::SetGlobalVariable { .. }
2079 | Action::ModifyGlobalVariable { .. }
2080 | Action::If { .. }
2081 | Action::ElseIf { .. }
2082 | Action::While { .. } => 1,
2083 Action::SetPlayerVariable { .. } | Action::ModifyPlayerVariable { .. } => 2,
2084 Action::AssignMember { .. } => 2,
2085 Action::ForGlobalVariable { .. } => 3,
2086 Action::ForPlayerVariable { .. } => 4,
2087 Action::Call { args, .. } => args.len(),
2088 Action::CallSubroutine { .. } | Action::Else | Action::End => 0,
2089 Action::Disabled { action } => action_argument_count(action),
2090 }
2091}
2092
2093#[derive(Debug, Clone, PartialEq, Eq)]
2095pub enum Event {
2096 Global,
2097 EachPlayer,
2098 EachPlayerWithFilters {
2099 team: EventTeam,
2100 target: EventTarget,
2101 },
2102 Player {
2103 kind: PlayerEventKind,
2104 team: EventTeam,
2105 target: EventTarget,
2106 },
2107 Subroutine(String),
2108}
2109
2110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2111pub enum EventTeam {
2112 All,
2113 Team1,
2114 Team2,
2115}
2116
2117#[derive(Debug, Clone, PartialEq, Eq)]
2118pub enum EventTarget {
2119 All,
2120 Slot(u8),
2121 Hero(String),
2122}
2123
2124#[non_exhaustive]
2129#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2130pub enum PlayerEventKind {
2131 DealtDamage,
2132 DealtFinalBlow,
2133 DealtHealing,
2134 DealtKnockback,
2135 Died,
2136 EarnedElimination,
2137 Joined,
2138 Left,
2139 ReceivedHealing,
2140 ReceivedKnockback,
2141 TookDamage,
2142}
2143
2144#[derive(Debug, Clone)]
2147pub enum Action {
2148 SetGlobalVariable {
2149 variable: String,
2150 value: Value,
2151 },
2152 ModifyGlobalVariable {
2153 variable: String,
2154 op: ModifyOp,
2155 value: Value,
2156 },
2157 SetPlayerVariable {
2158 player: Value,
2159 variable: String,
2160 value: Value,
2161 },
2162 ModifyPlayerVariable {
2163 player: Value,
2164 variable: String,
2165 op: ModifyOp,
2166 value: Value,
2167 },
2168 AssignMember {
2169 target: Value,
2170 op: Option<ModifyOp>,
2171 value: Value,
2172 },
2173 CallSubroutine {
2174 subroutine: String,
2175 },
2176 If {
2177 condition: Value,
2178 },
2179 ElseIf {
2180 condition: Value,
2181 },
2182 Else,
2183 While {
2184 condition: Value,
2185 },
2186 ForGlobalVariable {
2187 variable: String,
2188 start: Value,
2189 stop: Value,
2190 step: Value,
2191 },
2192 ForPlayerVariable {
2193 player: Value,
2194 variable: String,
2195 start: Value,
2196 stop: Value,
2197 step: Value,
2198 },
2199 End,
2200 Disabled {
2201 action: Box<Action>,
2202 },
2203 Call {
2204 name: String,
2205 args: Vec<Value>,
2206 },
2207}
2208
2209impl Action {
2210 pub fn disabled(action: Action) -> Self {
2212 Self::Disabled {
2213 action: Box::new(action),
2214 }
2215 }
2216
2217 pub fn call(name: impl Into<String>, args: impl IntoIterator<Item = Value>) -> Self {
2219 Self::Call {
2220 name: name.into(),
2221 args: args.into_iter().collect(),
2222 }
2223 }
2224}
2225
2226#[non_exhaustive]
2231#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2232pub enum ModifyOp {
2233 Add,
2234 Subtract,
2235 Multiply,
2236 Divide,
2237 Modulo,
2238 Min,
2239 Max,
2240 RaiseToPower,
2241 AppendToArray,
2242 RemoveFromArrayByValue,
2243 RemoveFromArrayByIndex,
2244}
2245
2246#[derive(Debug, Clone)]
2248pub enum Value {
2249 Number(f64),
2250 String(String),
2251 LocalizedString(String),
2252 Bool(bool),
2253 Null,
2254 Array(Vec<Value>),
2255 Vector {
2256 x: Box<Value>,
2257 y: Box<Value>,
2258 z: Box<Value>,
2259 },
2260 Enum {
2261 value_type: String,
2262 value: String,
2263 },
2264 GlobalVariable(String),
2265 PlayerVariable {
2266 player: Box<Value>,
2267 variable: String,
2268 },
2269 Subroutine(String),
2270 EventPlayer,
2271 Call {
2272 name: String,
2273 args: Vec<Value>,
2274 },
2275}
2276
2277impl Value {
2278 pub fn number(value: f64) -> Self {
2280 Self::Number(value)
2281 }
2282
2283 pub fn string(value: impl Into<String>) -> Self {
2285 Self::String(value.into())
2286 }
2287
2288 pub fn global_variable(name: impl Into<String>) -> Self {
2289 Self::GlobalVariable(name.into())
2290 }
2291
2292 pub fn player_variable(player: Value, name: impl Into<String>) -> Self {
2293 Self::PlayerVariable {
2294 player: Box::new(player),
2295 variable: name.into(),
2296 }
2297 }
2298
2299 pub fn call(name: impl Into<String>, args: impl IntoIterator<Item = Value>) -> Self {
2301 Self::Call {
2302 name: name.into(),
2303 args: args.into_iter().collect(),
2304 }
2305 }
2306}
2307
2308impl From<bool> for Value {
2309 fn from(value: bool) -> Self {
2310 Self::Bool(value)
2311 }
2312}
2313
2314impl From<f64> for Value {
2315 fn from(value: f64) -> Self {
2316 Self::Number(value)
2317 }
2318}
2319
2320impl From<f32> for Value {
2321 fn from(value: f32) -> Self {
2322 Self::Number(f64::from(value))
2323 }
2324}
2325
2326macro_rules! impl_integer_value {
2327 ($($type:ty),+ $(,)?) => {
2328 $(
2329 impl From<$type> for Value {
2330 fn from(value: $type) -> Self {
2331 Self::Number(value as f64)
2332 }
2333 }
2334 )+
2335 };
2336}
2337
2338impl_integer_value!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
2339
2340impl From<String> for Value {
2341 fn from(value: String) -> Self {
2342 Self::String(value)
2343 }
2344}
2345
2346impl From<&str> for Value {
2347 fn from(value: &str) -> Self {
2348 Self::String(value.to_string())
2349 }
2350}
2351
2352impl<T: Into<Value>> From<Vec<T>> for Value {
2353 fn from(values: Vec<T>) -> Self {
2354 Self::Array(values.into_iter().map(Into::into).collect())
2355 }
2356}
2357
2358impl<T: Into<Value>, const N: usize> From<[T; N]> for Value {
2359 fn from(values: [T; N]) -> Self {
2360 Self::Array(values.into_iter().map(Into::into).collect())
2361 }
2362}