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