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