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