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