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| public_value(&storage, *condition))
511 .collect::<Result<Vec<_>>>()?;
512 let mut actions = Vec::new();
513 let mut action_provenance = Vec::new();
514 for action in &rule.actions {
515 public_actions(&storage, *action, &mut actions)?;
516 public_action_provenance(&storage, *action, &mut action_provenance)?;
517 }
518 program
519 .provenance
520 .as_mut()
521 .expect("parsed programs retain provenance")
522 .rules
523 .push(RuleProvenance {
524 span: rule.span,
525 conditions: rule
526 .conditions
527 .iter()
528 .map(|condition| {
529 storage.values.get(*condition).and_then(|value| value.span)
530 })
531 .collect(),
532 actions: action_provenance,
533 });
534 program.rules.push(Rule {
535 name: rule.name.clone(),
536 disabled: rule.disabled,
537 event,
538 conditions: conditions.into_iter().map(Condition::new).collect(),
539 actions,
540 });
541 }
542 Ok(program)
543 }
544
545 pub(crate) fn to_wir(&self) -> Result<wir::Program> {
546 let mut storage = wir::Program {
547 settings: self.settings.clone(),
548 ..Default::default()
549 };
550
551 for file in &self.files {
552 storage.add_file(file.clone());
553 }
554
555 let mut globals = HashMap::new();
556 for (position, variable) in self.global_variables.iter().enumerate() {
557 let declaration = self.declaration_provenance(
558 |provenance| &provenance.global_variables,
559 self.global_variables.len(),
560 position,
561 );
562 let id = storage.global_variables.push(wir::WorkshopVariable {
563 name: variable.name.clone(),
564 index: variable.index.unwrap_or(position as u32),
565 span: declaration.span,
566 name_span: declaration.name_span,
567 });
568 globals.insert(variable.name.clone(), id);
569 }
570 let mut players = HashMap::new();
571 for (position, variable) in self.player_variables.iter().enumerate() {
572 let declaration = self.declaration_provenance(
573 |provenance| &provenance.player_variables,
574 self.player_variables.len(),
575 position,
576 );
577 let id = storage.player_variables.push(wir::WorkshopVariable {
578 name: variable.name.clone(),
579 index: variable.index.unwrap_or(position as u32),
580 span: declaration.span,
581 name_span: declaration.name_span,
582 });
583 players.insert(variable.name.clone(), id);
584 }
585 let mut subroutines = HashMap::new();
586 for (position, subroutine) in self.subroutines.iter().enumerate() {
587 let declaration = self.declaration_provenance(
588 |provenance| &provenance.subroutines,
589 self.subroutines.len(),
590 position,
591 );
592 let id = storage.subroutines.push(wir::WorkshopSubroutine {
593 name: subroutine.name.clone(),
594 index: subroutine.index.unwrap_or(position as u32),
595 span: declaration.span,
596 name_span: declaration.name_span,
597 });
598 subroutines.insert(subroutine.name.clone(), id);
599 }
600
601 for (rule_index, rule) in self.rules.iter().enumerate() {
602 let event = wir_event(&rule.event, &subroutines)?;
603 let conditions = rule
604 .conditions
605 .iter()
606 .enumerate()
607 .map(|(condition_index, condition)| {
608 if condition.disabled {
609 return Err(WorkshopError::Unsupported {
610 message: "disabled conditions are not representable by the canonical storage model"
611 .to_string(),
612 span: self.condition_span(rule_index, condition_index),
613 });
614 }
615 wir_value(
616 &condition.value,
617 &mut storage,
618 &globals,
619 &players,
620 &subroutines,
621 )
622 .inspect(|&value| {
623 if let Some(span) = self.condition_span(rule_index, condition_index) {
624 storage.values.get_mut(value).unwrap().span = Some(span);
625 }
626 })
627 })
628 .collect::<Result<Vec<_>>>()?;
629 let mut actions = Vec::new();
630 let mut position = 0;
631 lower_actions(
632 &rule.actions,
633 &mut position,
634 &mut actions,
635 &mut storage,
636 &globals,
637 &players,
638 &subroutines,
639 )?;
640 if let Some(provenance) = self
641 .rule_provenance(rule_index)
642 .filter(|provenance| provenance.actions.len() == rule.actions.len())
643 {
644 let mut public_position = 0;
645 apply_action_provenance(
646 &mut storage,
647 &actions,
648 &provenance.actions,
649 &mut public_position,
650 )?;
651 }
652 if position != rule.actions.len() {
653 return Err(WorkshopError::Malformed {
654 message: "unexpected control-flow terminator in rule actions".to_string(),
655 span: None,
656 });
657 }
658 storage.rules.push(wir::Rule {
659 name: rule.name.clone(),
660 span: self.rule_span(rule_index),
661 name_span: None,
662 disabled: rule.disabled,
663 event,
664 conditions,
665 actions,
666 });
667 }
668 Ok(storage)
669 }
670}
671
672fn fit<T: Default>(items: &mut Vec<T>, len: usize) {
673 items.truncate(len);
674 items.resize_with(len, T::default);
675}
676
677fn public_event(storage: &wir::Program, event: &wir::Event) -> Result<Event> {
678 Ok(match event {
679 wir::Event::Global => Event::Global,
680 wir::Event::EachPlayer => Event::EachPlayer,
681 wir::Event::EachPlayerWithFilters { team, target } => Event::EachPlayerWithFilters {
682 team: public_team(*team),
683 target: public_target(target),
684 },
685 wir::Event::Player { kind, team, target } => Event::Player {
686 kind: public_player_event(*kind),
687 team: public_team(*team),
688 target: public_target(target),
689 },
690 wir::Event::Subroutine(id) => Event::Subroutine(
691 storage
692 .subroutines
693 .get(*id)
694 .ok_or_else(|| malformed_id("subroutine", id.index()))?
695 .name
696 .clone(),
697 ),
698 })
699}
700
701fn public_team(team: wir::EventTeam) -> EventTeam {
702 match team {
703 wir::EventTeam::All => EventTeam::All,
704 wir::EventTeam::Team1 => EventTeam::Team1,
705 wir::EventTeam::Team2 => EventTeam::Team2,
706 }
707}
708
709fn public_target(target: &wir::EventTarget) -> EventTarget {
710 match target {
711 wir::EventTarget::All => EventTarget::All,
712 wir::EventTarget::Slot(slot) => EventTarget::Slot(*slot),
713 wir::EventTarget::Hero(hero) => EventTarget::Hero(hero.clone()),
714 }
715}
716
717fn public_player_event(kind: wir::PlayerEventKind) -> PlayerEventKind {
718 match kind {
719 wir::PlayerEventKind::DealtDamage => PlayerEventKind::DealtDamage,
720 wir::PlayerEventKind::DealtFinalBlow => PlayerEventKind::DealtFinalBlow,
721 wir::PlayerEventKind::DealtHealing => PlayerEventKind::DealtHealing,
722 wir::PlayerEventKind::DealtKnockback => PlayerEventKind::DealtKnockback,
723 wir::PlayerEventKind::Died => PlayerEventKind::Died,
724 wir::PlayerEventKind::EarnedElimination => PlayerEventKind::EarnedElimination,
725 wir::PlayerEventKind::Joined => PlayerEventKind::Joined,
726 wir::PlayerEventKind::Left => PlayerEventKind::Left,
727 wir::PlayerEventKind::ReceivedHealing => PlayerEventKind::ReceivedHealing,
728 wir::PlayerEventKind::ReceivedKnockback => PlayerEventKind::ReceivedKnockback,
729 wir::PlayerEventKind::TookDamage => PlayerEventKind::TookDamage,
730 }
731}
732
733fn public_value(storage: &wir::Program, id: wir::ValueId) -> Result<Value> {
734 let node = storage
735 .values
736 .get(id)
737 .ok_or_else(|| malformed_id("value", id.index()))?;
738 Ok(match &node.value {
739 wir::Value::Number { value, .. } => Value::Number(*value),
740 wir::Value::String(value) => Value::String(value.clone()),
741 wir::Value::LocalizedString(value) => Value::LocalizedString(value.clone()),
742 wir::Value::Bool(value) => Value::Bool(*value),
743 wir::Value::Null => Value::Null,
744 wir::Value::Array(values) => Value::Array(
745 values
746 .iter()
747 .map(|value| public_value(storage, *value))
748 .collect::<Result<Vec<_>>>()?,
749 ),
750 wir::Value::Vector { x, y, z } => Value::Vector {
751 x: Box::new(public_value(storage, *x)?),
752 y: Box::new(public_value(storage, *y)?),
753 z: Box::new(public_value(storage, *z)?),
754 },
755 wir::Value::Enum { value_type, value } => Value::Enum {
756 value_type: value_type.clone(),
757 value: value.clone(),
758 },
759 wir::Value::GlobalVariable(id) => Value::GlobalVariable(
760 storage
761 .global_variables
762 .get(*id)
763 .ok_or_else(|| malformed_id("global variable", id.index()))?
764 .name
765 .clone(),
766 ),
767 wir::Value::PlayerVariable { player, variable } => Value::PlayerVariable {
768 player: Box::new(public_value(storage, *player)?),
769 variable: storage
770 .player_variables
771 .get(*variable)
772 .ok_or_else(|| malformed_id("player variable", variable.index()))?
773 .name
774 .clone(),
775 },
776 wir::Value::Subroutine(id) => Value::Subroutine(
777 storage
778 .subroutines
779 .get(*id)
780 .ok_or_else(|| malformed_id("subroutine", id.index()))?
781 .name
782 .clone(),
783 ),
784 wir::Value::EventPlayer => Value::EventPlayer,
785 wir::Value::Call { name, args } => Value::Call {
786 name: name.clone(),
787 args: args
788 .iter()
789 .map(|arg| public_value(storage, *arg))
790 .collect::<Result<Vec<_>>>()?,
791 },
792 })
793}
794
795fn public_actions(
796 storage: &wir::Program,
797 id: wir::ActionId,
798 output: &mut Vec<Action>,
799) -> Result<()> {
800 let action = storage
801 .actions
802 .get(id)
803 .ok_or_else(|| malformed_id("action", id.index()))?;
804 match action {
805 wir::Action::SetGlobalVariable {
806 variable, value, ..
807 } => output.push(Action::SetGlobalVariable {
808 variable: storage
809 .global_variables
810 .get(*variable)
811 .ok_or_else(|| malformed_id("global variable", variable.index()))?
812 .name
813 .clone(),
814 value: public_value(storage, *value)?,
815 }),
816 wir::Action::ModifyGlobalVariable {
817 variable,
818 op,
819 value,
820 ..
821 } => output.push(Action::ModifyGlobalVariable {
822 variable: storage
823 .global_variables
824 .get(*variable)
825 .ok_or_else(|| malformed_id("global variable", variable.index()))?
826 .name
827 .clone(),
828 op: public_modify(*op),
829 value: public_value(storage, *value)?,
830 }),
831 wir::Action::SetPlayerVariable {
832 player,
833 variable,
834 value,
835 ..
836 } => output.push(Action::SetPlayerVariable {
837 player: public_value(storage, *player)?,
838 variable: storage
839 .player_variables
840 .get(*variable)
841 .ok_or_else(|| malformed_id("player variable", variable.index()))?
842 .name
843 .clone(),
844 value: public_value(storage, *value)?,
845 }),
846 wir::Action::ModifyPlayerVariable {
847 player,
848 variable,
849 op,
850 value,
851 ..
852 } => output.push(Action::ModifyPlayerVariable {
853 player: public_value(storage, *player)?,
854 variable: storage
855 .player_variables
856 .get(*variable)
857 .ok_or_else(|| malformed_id("player variable", variable.index()))?
858 .name
859 .clone(),
860 op: public_modify(*op),
861 value: public_value(storage, *value)?,
862 }),
863 wir::Action::AssignMember {
864 target, op, value, ..
865 } => output.push(Action::AssignMember {
866 target: public_value(storage, *target)?,
867 op: op.map(public_modify),
868 value: public_value(storage, *value)?,
869 }),
870 wir::Action::CallSubroutine { subroutine, .. } => output.push(Action::CallSubroutine {
871 subroutine: storage
872 .subroutines
873 .get(*subroutine)
874 .ok_or_else(|| malformed_id("subroutine", subroutine.index()))?
875 .name
876 .clone(),
877 }),
878 wir::Action::If {
879 branches,
880 else_body,
881 ..
882 } => {
883 for (index, branch) in branches.iter().enumerate() {
884 output.push(if index == 0 {
885 Action::If {
886 condition: public_value(storage, branch.condition)?,
887 }
888 } else {
889 Action::ElseIf {
890 condition: public_value(storage, branch.condition)?,
891 }
892 });
893 for action in &branch.body {
894 public_actions(storage, *action, output)?;
895 }
896 }
897 if let Some(body) = else_body {
898 output.push(Action::Else);
899 for action in body {
900 public_actions(storage, *action, output)?;
901 }
902 }
903 output.push(Action::End);
904 }
905 wir::Action::While {
906 condition, body, ..
907 } => {
908 output.push(Action::While {
909 condition: public_value(storage, *condition)?,
910 });
911 for action in body {
912 public_actions(storage, *action, output)?;
913 }
914 output.push(Action::End);
915 }
916 wir::Action::ForGlobalVariable {
917 variable,
918 start,
919 stop,
920 step,
921 body,
922 ..
923 } => {
924 output.push(Action::ForGlobalVariable {
925 variable: storage
926 .global_variables
927 .get(*variable)
928 .ok_or_else(|| malformed_id("global variable", variable.index()))?
929 .name
930 .clone(),
931 start: public_value(storage, *start)?,
932 stop: public_value(storage, *stop)?,
933 step: public_value(storage, *step)?,
934 });
935 for action in body {
936 public_actions(storage, *action, output)?;
937 }
938 output.push(Action::End);
939 }
940 wir::Action::ForPlayerVariable {
941 player,
942 variable,
943 start,
944 stop,
945 step,
946 body,
947 ..
948 } => {
949 output.push(Action::ForPlayerVariable {
950 player: public_value(storage, *player)?,
951 variable: storage
952 .player_variables
953 .get(*variable)
954 .ok_or_else(|| malformed_id("player variable", variable.index()))?
955 .name
956 .clone(),
957 start: public_value(storage, *start)?,
958 stop: public_value(storage, *stop)?,
959 step: public_value(storage, *step)?,
960 });
961 for action in body {
962 public_actions(storage, *action, output)?;
963 }
964 output.push(Action::End);
965 }
966 wir::Action::Call { name, args, .. } => output.push(Action::Call {
967 name: name.clone(),
968 args: args
969 .iter()
970 .map(|arg| public_value(storage, *arg))
971 .collect::<Result<Vec<_>>>()?,
972 }),
973 }
974 Ok(())
975}
976
977fn public_action_provenance(
978 storage: &wir::Program,
979 id: wir::ActionId,
980 output: &mut Vec<ActionProvenance>,
981) -> Result<()> {
982 let action = storage
983 .actions
984 .get(id)
985 .ok_or_else(|| malformed_id("action", id.index()))?;
986 let push = |output: &mut Vec<ActionProvenance>, arguments: &[wir::ValueId]| {
987 output.push(ActionProvenance {
988 span: action.span(),
989 arguments: arguments
990 .iter()
991 .map(|value| storage.values.get(*value).and_then(|value| value.span))
992 .collect(),
993 });
994 };
995 let push_without_span = |output: &mut Vec<ActionProvenance>, arguments: &[wir::ValueId]| {
996 output.push(ActionProvenance {
997 span: None,
998 arguments: arguments
999 .iter()
1000 .map(|value| storage.values.get(*value).and_then(|value| value.span))
1001 .collect(),
1002 });
1003 };
1004 match action {
1005 wir::Action::SetGlobalVariable { value, .. }
1006 | wir::Action::ModifyGlobalVariable { value, .. } => push(output, &[*value]),
1007 wir::Action::SetPlayerVariable { player, value, .. }
1008 | wir::Action::ModifyPlayerVariable { player, value, .. } => {
1009 push(output, &[*player, *value])
1010 }
1011 wir::Action::AssignMember { target, value, .. } => push(output, &[*target, *value]),
1012 wir::Action::CallSubroutine { .. } => push(output, &[]),
1013 wir::Action::If {
1014 branches,
1015 else_body,
1016 ..
1017 } => {
1018 for (index, branch) in branches.iter().enumerate() {
1019 if index == 0 {
1020 push(output, &[branch.condition]);
1021 } else {
1022 push_without_span(output, &[branch.condition]);
1023 }
1024 for action in &branch.body {
1025 public_action_provenance(storage, *action, output)?;
1026 }
1027 if index + 1 == branches.len() && else_body.is_none() {
1028 push_without_span(output, &[]);
1029 }
1030 }
1031 if let Some(body) = else_body {
1032 push_without_span(output, &[]);
1033 for action in body {
1034 public_action_provenance(storage, *action, output)?;
1035 }
1036 push_without_span(output, &[]);
1037 }
1038 }
1039 wir::Action::While {
1040 condition, body, ..
1041 } => {
1042 push(output, &[*condition]);
1043 for action in body {
1044 public_action_provenance(storage, *action, output)?;
1045 }
1046 push_without_span(output, &[]);
1047 }
1048 wir::Action::ForGlobalVariable {
1049 start,
1050 stop,
1051 step,
1052 body,
1053 ..
1054 } => {
1055 push(output, &[*start, *stop, *step]);
1056 for action in body {
1057 public_action_provenance(storage, *action, output)?;
1058 }
1059 push_without_span(output, &[]);
1060 }
1061 wir::Action::ForPlayerVariable {
1062 player,
1063 start,
1064 stop,
1065 step,
1066 body,
1067 ..
1068 } => {
1069 push(output, &[*player, *start, *stop, *step]);
1070 for action in body {
1071 public_action_provenance(storage, *action, output)?;
1072 }
1073 push_without_span(output, &[]);
1074 }
1075 wir::Action::Call { args, .. } => push(output, args),
1076 }
1077 Ok(())
1078}
1079
1080fn lower_actions(
1081 actions: &[Action],
1082 position: &mut usize,
1083 output: &mut Vec<wir::ActionId>,
1084 storage: &mut wir::Program,
1085 globals: &HashMap<String, wir::GlobalVarId>,
1086 players: &HashMap<String, wir::PlayerVarId>,
1087 subroutines: &HashMap<String, wir::SubroutineId>,
1088) -> Result<()> {
1089 while *position < actions.len() {
1090 match &actions[*position] {
1091 Action::ElseIf { .. } | Action::Else | Action::End => return Ok(()),
1092 Action::If { condition } => {
1093 *position += 1;
1094 let mut branches = vec![wir::IfBranch {
1095 condition: wir_value(condition, storage, globals, players, subroutines)?,
1096 body: Vec::new(),
1097 }];
1098 lower_actions(
1099 actions,
1100 position,
1101 &mut branches[0].body,
1102 storage,
1103 globals,
1104 players,
1105 subroutines,
1106 )?;
1107 while let Some(Action::ElseIf { condition }) = actions.get(*position) {
1108 *position += 1;
1109 let mut body = Vec::new();
1110 lower_actions(
1111 actions,
1112 position,
1113 &mut body,
1114 storage,
1115 globals,
1116 players,
1117 subroutines,
1118 )?;
1119 branches.push(wir::IfBranch {
1120 condition: wir_value(condition, storage, globals, players, subroutines)?,
1121 body,
1122 });
1123 }
1124 let else_body = if matches!(actions.get(*position), Some(Action::Else)) {
1125 *position += 1;
1126 let mut body = Vec::new();
1127 lower_actions(
1128 actions,
1129 position,
1130 &mut body,
1131 storage,
1132 globals,
1133 players,
1134 subroutines,
1135 )?;
1136 Some(body)
1137 } else {
1138 None
1139 };
1140 if !matches!(actions.get(*position), Some(Action::End)) {
1141 return Err(WorkshopError::Malformed {
1142 message: "control-flow action is missing End".to_string(),
1143 span: None,
1144 });
1145 }
1146 *position += 1;
1147 output.push(storage.actions.push(wir::Action::If {
1148 branches,
1149 else_body,
1150 span: None,
1151 }));
1152 }
1153 Action::While { condition } => {
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 require_end(actions, position)?;
1166 let condition = wir_value(condition, storage, globals, players, subroutines)?;
1167 output.push(storage.actions.push(wir::Action::While {
1168 condition,
1169 body,
1170 span: None,
1171 }));
1172 }
1173 Action::ForGlobalVariable {
1174 variable,
1175 start,
1176 stop,
1177 step,
1178 } => {
1179 *position += 1;
1180 let mut body = Vec::new();
1181 lower_actions(
1182 actions,
1183 position,
1184 &mut body,
1185 storage,
1186 globals,
1187 players,
1188 subroutines,
1189 )?;
1190 require_end(actions, position)?;
1191 let variable = *globals
1192 .get(variable)
1193 .ok_or_else(|| unknown_name("global variable", variable))?;
1194 let start = wir_value(start, storage, globals, players, subroutines)?;
1195 let stop = wir_value(stop, storage, globals, players, subroutines)?;
1196 let step = wir_value(step, storage, globals, players, subroutines)?;
1197 output.push(storage.actions.push(wir::Action::ForGlobalVariable {
1198 variable,
1199 start,
1200 stop,
1201 step,
1202 body,
1203 span: None,
1204 target_span: None,
1205 }));
1206 }
1207 Action::ForPlayerVariable {
1208 player,
1209 variable,
1210 start,
1211 stop,
1212 step,
1213 } => {
1214 *position += 1;
1215 let mut body = Vec::new();
1216 lower_actions(
1217 actions,
1218 position,
1219 &mut body,
1220 storage,
1221 globals,
1222 players,
1223 subroutines,
1224 )?;
1225 require_end(actions, position)?;
1226 let player = wir_value(player, storage, globals, players, subroutines)?;
1227 let variable = *players
1228 .get(variable)
1229 .ok_or_else(|| unknown_name("player variable", variable))?;
1230 let start = wir_value(start, storage, globals, players, subroutines)?;
1231 let stop = wir_value(stop, storage, globals, players, subroutines)?;
1232 let step = wir_value(step, storage, globals, players, subroutines)?;
1233 output.push(storage.actions.push(wir::Action::ForPlayerVariable {
1234 player,
1235 variable,
1236 start,
1237 stop,
1238 step,
1239 body,
1240 span: None,
1241 }));
1242 }
1243 action => {
1244 *position += 1;
1245 let lowered = wir_action(action, storage, globals, players, subroutines)?;
1246 output.push(lowered);
1247 }
1248 }
1249 }
1250 Ok(())
1251}
1252
1253fn apply_action_provenance(
1254 storage: &mut wir::Program,
1255 actions: &[wir::ActionId],
1256 provenance: &[ActionProvenance],
1257 position: &mut usize,
1258) -> Result<()> {
1259 for id in actions {
1260 let action = storage
1261 .actions
1262 .get(*id)
1263 .cloned()
1264 .ok_or_else(|| malformed_id("action", id.index()))?;
1265 match action {
1266 wir::Action::If {
1267 branches,
1268 else_body,
1269 ..
1270 } => {
1271 let source = provenance.get(*position).cloned().unwrap_or_default();
1272 *position += 1;
1273 apply_action_source(storage, *id, &source);
1274 for (branch_index, branch) in branches.iter().enumerate() {
1275 if branch_index > 0 {
1276 let source = provenance.get(*position).cloned().unwrap_or_default();
1277 *position += 1;
1278 set_value_span(
1279 storage,
1280 branch.condition,
1281 source.arguments.first().copied().flatten(),
1282 );
1283 }
1284 apply_action_provenance(storage, &branch.body, provenance, position)?;
1285 }
1286 if let Some(body) = else_body {
1287 *position += 1;
1288 apply_action_provenance(storage, &body, provenance, position)?;
1289 }
1290 *position += 1;
1291 }
1292 wir::Action::While {
1293 condition, body, ..
1294 } => {
1295 let source = provenance.get(*position).cloned().unwrap_or_default();
1296 *position += 1;
1297 apply_action_source(storage, *id, &source);
1298 apply_action_provenance(storage, &body, provenance, position)?;
1299 *position += 1;
1300 set_value_span(
1301 storage,
1302 condition,
1303 source.arguments.first().copied().flatten(),
1304 );
1305 }
1306 wir::Action::ForGlobalVariable {
1307 start,
1308 stop,
1309 step,
1310 body,
1311 ..
1312 } => {
1313 let source = provenance.get(*position).cloned().unwrap_or_default();
1314 *position += 1;
1315 apply_action_source(storage, *id, &source);
1316 apply_action_provenance(storage, &body, provenance, position)?;
1317 *position += 1;
1318 for (value, span) in [start, stop, step].into_iter().zip(source.arguments) {
1319 set_value_span(storage, value, span);
1320 }
1321 }
1322 wir::Action::ForPlayerVariable {
1323 player,
1324 start,
1325 stop,
1326 step,
1327 body,
1328 ..
1329 } => {
1330 let source = provenance.get(*position).cloned().unwrap_or_default();
1331 *position += 1;
1332 apply_action_source(storage, *id, &source);
1333 apply_action_provenance(storage, &body, provenance, position)?;
1334 *position += 1;
1335 for (value, span) in [player, start, stop, step]
1336 .into_iter()
1337 .zip(source.arguments)
1338 {
1339 set_value_span(storage, value, span);
1340 }
1341 }
1342 _ => {
1343 let source = provenance.get(*position).cloned().unwrap_or_default();
1344 *position += 1;
1345 apply_action_source(storage, *id, &source);
1346 }
1347 }
1348 }
1349 Ok(())
1350}
1351
1352fn apply_action_source(storage: &mut wir::Program, id: wir::ActionId, source: &ActionProvenance) {
1353 let arguments = source.arguments.clone();
1354 if let Some(action) = storage.actions.get_mut(id) {
1355 match action {
1356 wir::Action::SetGlobalVariable { span, .. }
1357 | wir::Action::ModifyGlobalVariable { span, .. }
1358 | wir::Action::SetPlayerVariable { span, .. }
1359 | wir::Action::ModifyPlayerVariable { span, .. }
1360 | wir::Action::AssignMember { span, .. }
1361 | wir::Action::CallSubroutine { span, .. }
1362 | wir::Action::If { span, .. }
1363 | wir::Action::While { span, .. }
1364 | wir::Action::ForGlobalVariable { span, .. }
1365 | wir::Action::ForPlayerVariable { span, .. }
1366 | wir::Action::Call { span, .. } => *span = source.span,
1367 }
1368 }
1369 let value_ids = storage
1370 .actions
1371 .get(id)
1372 .map(action_value_ids)
1373 .unwrap_or_default();
1374 for (value, span) in value_ids.into_iter().zip(arguments) {
1375 set_value_span(storage, value, span);
1376 }
1377}
1378
1379fn action_value_ids(action: &wir::Action) -> Vec<wir::ValueId> {
1380 match action {
1381 wir::Action::SetGlobalVariable { value, .. }
1382 | wir::Action::ModifyGlobalVariable { value, .. } => vec![*value],
1383 wir::Action::SetPlayerVariable { player, value, .. }
1384 | wir::Action::ModifyPlayerVariable { player, value, .. } => vec![*player, *value],
1385 wir::Action::AssignMember { target, value, .. } => vec![*target, *value],
1386 wir::Action::If { branches, .. } => {
1387 branches.iter().map(|branch| branch.condition).collect()
1388 }
1389 wir::Action::While { condition, .. } => vec![*condition],
1390 wir::Action::ForGlobalVariable {
1391 start, stop, step, ..
1392 } => vec![*start, *stop, *step],
1393 wir::Action::ForPlayerVariable {
1394 player,
1395 start,
1396 stop,
1397 step,
1398 ..
1399 } => vec![*player, *start, *stop, *step],
1400 wir::Action::Call { args, .. } => args.clone(),
1401 wir::Action::CallSubroutine { .. } => Vec::new(),
1402 }
1403}
1404
1405fn set_value_span(
1406 storage: &mut wir::Program,
1407 value: wir::ValueId,
1408 span: Option<crate::source::Span>,
1409) {
1410 if let Some(node) = storage.values.get_mut(value) {
1411 node.span = span;
1412 }
1413}
1414
1415fn require_end(actions: &[Action], position: &mut usize) -> Result<()> {
1416 if !matches!(actions.get(*position), Some(Action::End)) {
1417 return Err(WorkshopError::Malformed {
1418 message: "control-flow action is missing End".to_string(),
1419 span: None,
1420 });
1421 }
1422 *position += 1;
1423 Ok(())
1424}
1425
1426fn wir_action(
1427 action: &Action,
1428 storage: &mut wir::Program,
1429 globals: &HashMap<String, wir::GlobalVarId>,
1430 players: &HashMap<String, wir::PlayerVarId>,
1431 subroutines: &HashMap<String, wir::SubroutineId>,
1432) -> Result<wir::ActionId> {
1433 let action = match action {
1434 Action::SetGlobalVariable { variable, value } => wir::Action::SetGlobalVariable {
1435 variable: *globals
1436 .get(variable)
1437 .ok_or_else(|| unknown_name("global variable", variable))?,
1438 value: wir_value(value, storage, globals, players, subroutines)?,
1439 span: None,
1440 target_span: None,
1441 },
1442 Action::ModifyGlobalVariable {
1443 variable,
1444 op,
1445 value,
1446 } => wir::Action::ModifyGlobalVariable {
1447 variable: *globals
1448 .get(variable)
1449 .ok_or_else(|| unknown_name("global variable", variable))?,
1450 op: wir_modify(*op),
1451 value: wir_value(value, storage, globals, players, subroutines)?,
1452 span: None,
1453 target_span: None,
1454 },
1455 Action::SetPlayerVariable {
1456 player,
1457 variable,
1458 value,
1459 } => wir::Action::SetPlayerVariable {
1460 player: wir_value(player, storage, globals, players, subroutines)?,
1461 variable: *players
1462 .get(variable)
1463 .ok_or_else(|| unknown_name("player variable", variable))?,
1464 value: wir_value(value, storage, globals, players, subroutines)?,
1465 span: None,
1466 target_span: None,
1467 },
1468 Action::ModifyPlayerVariable {
1469 player,
1470 variable,
1471 op,
1472 value,
1473 } => wir::Action::ModifyPlayerVariable {
1474 player: wir_value(player, storage, globals, players, subroutines)?,
1475 variable: *players
1476 .get(variable)
1477 .ok_or_else(|| unknown_name("player variable", variable))?,
1478 op: wir_modify(*op),
1479 value: wir_value(value, storage, globals, players, subroutines)?,
1480 span: None,
1481 target_span: None,
1482 },
1483 Action::AssignMember { target, op, value } => wir::Action::AssignMember {
1484 target: wir_value(target, storage, globals, players, subroutines)?,
1485 op: op.map(wir_modify),
1486 value: wir_value(value, storage, globals, players, subroutines)?,
1487 span: None,
1488 },
1489 Action::CallSubroutine { subroutine } => wir::Action::CallSubroutine {
1490 subroutine: *subroutines
1491 .get(subroutine)
1492 .ok_or_else(|| unknown_name("subroutine", subroutine))?,
1493 span: None,
1494 callee_span: None,
1495 },
1496 Action::Disabled { .. } => {
1497 return Err(WorkshopError::Unsupported {
1498 message: "disabled actions are not representable by the canonical storage model"
1499 .to_string(),
1500 span: None,
1501 });
1502 }
1503 Action::Call { name, args } => wir::Action::Call {
1504 name: name.clone(),
1505 args: args
1506 .iter()
1507 .map(|arg| wir_value(arg, storage, globals, players, subroutines))
1508 .collect::<Result<Vec<_>>>()?,
1509 span: None,
1510 },
1511 Action::ElseIf { .. }
1512 | Action::Else
1513 | Action::End
1514 | Action::If { .. }
1515 | Action::While { .. }
1516 | Action::ForGlobalVariable { .. }
1517 | Action::ForPlayerVariable { .. } => {
1518 unreachable!("structured actions are lowered by lower_actions")
1519 }
1520 };
1521 Ok(storage.actions.push(action))
1522}
1523
1524fn wir_value(
1525 value: &Value,
1526 storage: &mut wir::Program,
1527 globals: &HashMap<String, wir::GlobalVarId>,
1528 players: &HashMap<String, wir::PlayerVarId>,
1529 subroutines: &HashMap<String, wir::SubroutineId>,
1530) -> Result<wir::ValueId> {
1531 let value = match value {
1532 Value::Number(value) => wir::Value::Number {
1533 value: *value,
1534 text: crate::core::format::format_number(*value),
1535 },
1536 Value::String(value) => wir::Value::String(value.clone()),
1537 Value::LocalizedString(value) => wir::Value::LocalizedString(value.clone()),
1538 Value::Bool(value) => wir::Value::Bool(*value),
1539 Value::Null => wir::Value::Null,
1540 Value::Array(values) => wir::Value::Array(
1541 values
1542 .iter()
1543 .map(|value| wir_value(value, storage, globals, players, subroutines))
1544 .collect::<Result<Vec<_>>>()?,
1545 ),
1546 Value::Vector { x, y, z } => wir::Value::Vector {
1547 x: wir_value(x, storage, globals, players, subroutines)?,
1548 y: wir_value(y, storage, globals, players, subroutines)?,
1549 z: wir_value(z, storage, globals, players, subroutines)?,
1550 },
1551 Value::Enum { value_type, value } => wir::Value::Enum {
1552 value_type: value_type.clone(),
1553 value: value.clone(),
1554 },
1555 Value::GlobalVariable(name) => wir::Value::GlobalVariable(
1556 *globals
1557 .get(name)
1558 .ok_or_else(|| unknown_name("global variable", name))?,
1559 ),
1560 Value::PlayerVariable { player, variable } => wir::Value::PlayerVariable {
1561 player: wir_value(player, storage, globals, players, subroutines)?,
1562 variable: *players
1563 .get(variable)
1564 .ok_or_else(|| unknown_name("player variable", variable))?,
1565 },
1566 Value::Subroutine(name) => wir::Value::Subroutine(
1567 *subroutines
1568 .get(name)
1569 .ok_or_else(|| unknown_name("subroutine", name))?,
1570 ),
1571 Value::EventPlayer => wir::Value::EventPlayer,
1572 Value::Call { name, args } => wir::Value::Call {
1573 name: name.clone(),
1574 args: args
1575 .iter()
1576 .map(|arg| wir_value(arg, storage, globals, players, subroutines))
1577 .collect::<Result<Vec<_>>>()?,
1578 },
1579 };
1580 Ok(storage.values.push(wir::ValueNode::new(value, None)))
1581}
1582
1583fn wir_event(
1584 event: &Event,
1585 subroutines: &HashMap<String, wir::SubroutineId>,
1586) -> Result<wir::Event> {
1587 Ok(match event {
1588 Event::Global => wir::Event::Global,
1589 Event::EachPlayer => wir::Event::EachPlayer,
1590 Event::EachPlayerWithFilters { team, target } => wir::Event::EachPlayerWithFilters {
1591 team: wir_team(*team),
1592 target: wir_target(target),
1593 },
1594 Event::Player { kind, team, target } => wir::Event::Player {
1595 kind: wir_player_event(*kind),
1596 team: wir_team(*team),
1597 target: wir_target(target),
1598 },
1599 Event::Subroutine(name) => wir::Event::Subroutine(
1600 *subroutines
1601 .get(name)
1602 .ok_or_else(|| unknown_name("subroutine", name))?,
1603 ),
1604 })
1605}
1606
1607fn wir_team(team: EventTeam) -> wir::EventTeam {
1608 match team {
1609 EventTeam::All => wir::EventTeam::All,
1610 EventTeam::Team1 => wir::EventTeam::Team1,
1611 EventTeam::Team2 => wir::EventTeam::Team2,
1612 }
1613}
1614
1615fn wir_target(target: &EventTarget) -> wir::EventTarget {
1616 match target {
1617 EventTarget::All => wir::EventTarget::All,
1618 EventTarget::Slot(slot) => wir::EventTarget::Slot(*slot),
1619 EventTarget::Hero(hero) => wir::EventTarget::Hero(hero.clone()),
1620 }
1621}
1622
1623fn wir_player_event(kind: PlayerEventKind) -> wir::PlayerEventKind {
1624 match kind {
1625 PlayerEventKind::DealtDamage => wir::PlayerEventKind::DealtDamage,
1626 PlayerEventKind::DealtFinalBlow => wir::PlayerEventKind::DealtFinalBlow,
1627 PlayerEventKind::DealtHealing => wir::PlayerEventKind::DealtHealing,
1628 PlayerEventKind::DealtKnockback => wir::PlayerEventKind::DealtKnockback,
1629 PlayerEventKind::Died => wir::PlayerEventKind::Died,
1630 PlayerEventKind::EarnedElimination => wir::PlayerEventKind::EarnedElimination,
1631 PlayerEventKind::Joined => wir::PlayerEventKind::Joined,
1632 PlayerEventKind::Left => wir::PlayerEventKind::Left,
1633 PlayerEventKind::ReceivedHealing => wir::PlayerEventKind::ReceivedHealing,
1634 PlayerEventKind::ReceivedKnockback => wir::PlayerEventKind::ReceivedKnockback,
1635 PlayerEventKind::TookDamage => wir::PlayerEventKind::TookDamage,
1636 }
1637}
1638
1639fn public_modify(op: wir::ModifyOp) -> ModifyOp {
1640 match op {
1641 wir::ModifyOp::Add => ModifyOp::Add,
1642 wir::ModifyOp::Subtract => ModifyOp::Subtract,
1643 wir::ModifyOp::Multiply => ModifyOp::Multiply,
1644 wir::ModifyOp::Divide => ModifyOp::Divide,
1645 wir::ModifyOp::Modulo => ModifyOp::Modulo,
1646 wir::ModifyOp::Min => ModifyOp::Min,
1647 wir::ModifyOp::Max => ModifyOp::Max,
1648 wir::ModifyOp::RaiseToPower => ModifyOp::RaiseToPower,
1649 wir::ModifyOp::AppendToArray => ModifyOp::AppendToArray,
1650 wir::ModifyOp::RemoveFromArrayByValue => ModifyOp::RemoveFromArrayByValue,
1651 wir::ModifyOp::RemoveFromArrayByIndex => ModifyOp::RemoveFromArrayByIndex,
1652 }
1653}
1654
1655fn wir_modify(op: ModifyOp) -> wir::ModifyOp {
1656 match op {
1657 ModifyOp::Add => wir::ModifyOp::Add,
1658 ModifyOp::Subtract => wir::ModifyOp::Subtract,
1659 ModifyOp::Multiply => wir::ModifyOp::Multiply,
1660 ModifyOp::Divide => wir::ModifyOp::Divide,
1661 ModifyOp::Modulo => wir::ModifyOp::Modulo,
1662 ModifyOp::Min => wir::ModifyOp::Min,
1663 ModifyOp::Max => wir::ModifyOp::Max,
1664 ModifyOp::RaiseToPower => wir::ModifyOp::RaiseToPower,
1665 ModifyOp::AppendToArray => wir::ModifyOp::AppendToArray,
1666 ModifyOp::RemoveFromArrayByValue => wir::ModifyOp::RemoveFromArrayByValue,
1667 ModifyOp::RemoveFromArrayByIndex => wir::ModifyOp::RemoveFromArrayByIndex,
1668 }
1669}
1670
1671fn malformed_id(kind: &str, index: usize) -> WorkshopError {
1672 WorkshopError::Malformed {
1673 message: format!("dangling {kind} {index}"),
1674 span: None,
1675 }
1676}
1677
1678fn unknown_name(kind: &str, name: &str) -> WorkshopError {
1679 WorkshopError::Malformed {
1680 message: format!("unknown {kind} '{name}'"),
1681 span: None,
1682 }
1683}
1684
1685#[derive(Debug, Clone, PartialEq, Eq)]
1687pub struct Variable {
1688 pub name: String,
1689 pub index: Option<u32>,
1691}
1692
1693impl Variable {
1694 pub fn new(name: impl Into<String>) -> Self {
1695 Self {
1696 name: name.into(),
1697 index: None,
1698 }
1699 }
1700
1701 pub fn with_index(name: impl Into<String>, index: u32) -> Self {
1702 Self {
1703 name: name.into(),
1704 index: Some(index),
1705 }
1706 }
1707}
1708
1709#[derive(Debug, Clone, PartialEq, Eq)]
1711pub struct Subroutine {
1712 pub name: String,
1713 pub index: Option<u32>,
1715}
1716
1717impl Subroutine {
1718 pub fn new(name: impl Into<String>) -> Self {
1719 Self {
1720 name: name.into(),
1721 index: None,
1722 }
1723 }
1724
1725 pub fn with_index(name: impl Into<String>, index: u32) -> Self {
1726 Self {
1727 name: name.into(),
1728 index: Some(index),
1729 }
1730 }
1731}
1732
1733#[derive(Debug, Clone)]
1735pub struct Rule {
1736 pub name: String,
1737 pub disabled: bool,
1738 pub event: Event,
1739 pub conditions: Vec<Condition>,
1740 pub actions: Vec<Action>,
1741}
1742
1743impl Rule {
1744 pub fn new(name: impl Into<String>, event: Event) -> Self {
1745 Self {
1746 name: name.into(),
1747 disabled: false,
1748 event,
1749 conditions: Vec::new(),
1750 actions: Vec::new(),
1751 }
1752 }
1753
1754 pub fn condition(mut self, condition: impl Into<Condition>) -> Self {
1755 self.conditions.push(condition.into());
1756 self
1757 }
1758
1759 pub fn action(mut self, action: Action) -> Self {
1760 self.actions.push(action);
1761 self
1762 }
1763}
1764
1765#[derive(Debug, Clone)]
1767pub struct Condition {
1768 pub value: Value,
1769 pub disabled: bool,
1770}
1771
1772impl Condition {
1773 pub fn new(value: Value) -> Self {
1774 Self {
1775 value,
1776 disabled: false,
1777 }
1778 }
1779
1780 pub fn disabled(value: Value) -> Self {
1781 Self {
1782 value,
1783 disabled: true,
1784 }
1785 }
1786}
1787
1788impl From<Value> for Condition {
1789 fn from(value: Value) -> Self {
1790 Self::new(value)
1791 }
1792}
1793
1794fn action_argument_count(action: &Action) -> usize {
1795 match action {
1796 Action::SetGlobalVariable { .. }
1797 | Action::ModifyGlobalVariable { .. }
1798 | Action::If { .. }
1799 | Action::ElseIf { .. }
1800 | Action::While { .. } => 1,
1801 Action::SetPlayerVariable { .. } | Action::ModifyPlayerVariable { .. } => 2,
1802 Action::AssignMember { .. } => 2,
1803 Action::ForGlobalVariable { .. } => 3,
1804 Action::ForPlayerVariable { .. } => 4,
1805 Action::Call { args, .. } => args.len(),
1806 Action::CallSubroutine { .. } | Action::Else | Action::End | Action::Disabled { .. } => 0,
1807 }
1808}
1809
1810#[derive(Debug, Clone, PartialEq, Eq)]
1812pub enum Event {
1813 Global,
1814 EachPlayer,
1815 EachPlayerWithFilters {
1816 team: EventTeam,
1817 target: EventTarget,
1818 },
1819 Player {
1820 kind: PlayerEventKind,
1821 team: EventTeam,
1822 target: EventTarget,
1823 },
1824 Subroutine(String),
1825}
1826
1827#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1828pub enum EventTeam {
1829 All,
1830 Team1,
1831 Team2,
1832}
1833
1834#[derive(Debug, Clone, PartialEq, Eq)]
1835pub enum EventTarget {
1836 All,
1837 Slot(u8),
1838 Hero(String),
1839}
1840
1841#[non_exhaustive]
1846#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1847pub enum PlayerEventKind {
1848 DealtDamage,
1849 DealtFinalBlow,
1850 DealtHealing,
1851 DealtKnockback,
1852 Died,
1853 EarnedElimination,
1854 Joined,
1855 Left,
1856 ReceivedHealing,
1857 ReceivedKnockback,
1858 TookDamage,
1859}
1860
1861#[derive(Debug, Clone)]
1864pub enum Action {
1865 SetGlobalVariable {
1866 variable: String,
1867 value: Value,
1868 },
1869 ModifyGlobalVariable {
1870 variable: String,
1871 op: ModifyOp,
1872 value: Value,
1873 },
1874 SetPlayerVariable {
1875 player: Value,
1876 variable: String,
1877 value: Value,
1878 },
1879 ModifyPlayerVariable {
1880 player: Value,
1881 variable: String,
1882 op: ModifyOp,
1883 value: Value,
1884 },
1885 AssignMember {
1886 target: Value,
1887 op: Option<ModifyOp>,
1888 value: Value,
1889 },
1890 CallSubroutine {
1891 subroutine: String,
1892 },
1893 If {
1894 condition: Value,
1895 },
1896 ElseIf {
1897 condition: Value,
1898 },
1899 Else,
1900 While {
1901 condition: Value,
1902 },
1903 ForGlobalVariable {
1904 variable: String,
1905 start: Value,
1906 stop: Value,
1907 step: Value,
1908 },
1909 ForPlayerVariable {
1910 player: Value,
1911 variable: String,
1912 start: Value,
1913 stop: Value,
1914 step: Value,
1915 },
1916 End,
1917 Disabled {
1918 action: Box<Action>,
1919 },
1920 Call {
1921 name: String,
1922 args: Vec<Value>,
1923 },
1924}
1925
1926impl Action {
1927 pub fn disabled(action: Action) -> Self {
1929 Self::Disabled {
1930 action: Box::new(action),
1931 }
1932 }
1933
1934 pub fn call(name: impl Into<String>, args: impl IntoIterator<Item = Value>) -> Self {
1936 Self::Call {
1937 name: name.into(),
1938 args: args.into_iter().collect(),
1939 }
1940 }
1941}
1942
1943#[non_exhaustive]
1948#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1949pub enum ModifyOp {
1950 Add,
1951 Subtract,
1952 Multiply,
1953 Divide,
1954 Modulo,
1955 Min,
1956 Max,
1957 RaiseToPower,
1958 AppendToArray,
1959 RemoveFromArrayByValue,
1960 RemoveFromArrayByIndex,
1961}
1962
1963#[derive(Debug, Clone)]
1965pub enum Value {
1966 Number(f64),
1967 String(String),
1968 LocalizedString(String),
1969 Bool(bool),
1970 Null,
1971 Array(Vec<Value>),
1972 Vector {
1973 x: Box<Value>,
1974 y: Box<Value>,
1975 z: Box<Value>,
1976 },
1977 Enum {
1978 value_type: String,
1979 value: String,
1980 },
1981 GlobalVariable(String),
1982 PlayerVariable {
1983 player: Box<Value>,
1984 variable: String,
1985 },
1986 Subroutine(String),
1987 EventPlayer,
1988 Call {
1989 name: String,
1990 args: Vec<Value>,
1991 },
1992}
1993
1994impl Value {
1995 pub fn number(value: f64) -> Self {
1997 Self::Number(value)
1998 }
1999
2000 pub fn string(value: impl Into<String>) -> Self {
2002 Self::String(value.into())
2003 }
2004
2005 pub fn global_variable(name: impl Into<String>) -> Self {
2006 Self::GlobalVariable(name.into())
2007 }
2008
2009 pub fn player_variable(player: Value, name: impl Into<String>) -> Self {
2010 Self::PlayerVariable {
2011 player: Box::new(player),
2012 variable: name.into(),
2013 }
2014 }
2015
2016 pub fn call(name: impl Into<String>, args: impl IntoIterator<Item = Value>) -> Self {
2018 Self::Call {
2019 name: name.into(),
2020 args: args.into_iter().collect(),
2021 }
2022 }
2023}
2024
2025impl From<bool> for Value {
2026 fn from(value: bool) -> Self {
2027 Self::Bool(value)
2028 }
2029}
2030
2031impl From<f64> for Value {
2032 fn from(value: f64) -> Self {
2033 Self::Number(value)
2034 }
2035}
2036
2037impl From<f32> for Value {
2038 fn from(value: f32) -> Self {
2039 Self::Number(f64::from(value))
2040 }
2041}
2042
2043macro_rules! impl_integer_value {
2044 ($($type:ty),+ $(,)?) => {
2045 $(
2046 impl From<$type> for Value {
2047 fn from(value: $type) -> Self {
2048 Self::Number(value as f64)
2049 }
2050 }
2051 )+
2052 };
2053}
2054
2055impl_integer_value!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
2056
2057impl From<String> for Value {
2058 fn from(value: String) -> Self {
2059 Self::String(value)
2060 }
2061}
2062
2063impl From<&str> for Value {
2064 fn from(value: &str) -> Self {
2065 Self::String(value.to_string())
2066 }
2067}
2068
2069impl<T: Into<Value>> From<Vec<T>> for Value {
2070 fn from(values: Vec<T>) -> Self {
2071 Self::Array(values.into_iter().map(Into::into).collect())
2072 }
2073}
2074
2075impl<T: Into<Value>, const N: usize> From<[T; N]> for Value {
2076 fn from(values: [T; N]) -> Self {
2077 Self::Array(values.into_iter().map(Into::into).collect())
2078 }
2079}
2080
2081include!(concat!(env!("OUT_DIR"), "/typed_api.rs"));