1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use alloc::collections::VecDeque;
use alloc::vec;
use alloc::vec::Vec;
use crate::Variable;
/// An operation that can be reflected on
pub trait OperationReflect: Sized {
/// Type of the op codes for this operation
type OpCode;
/// Get the opcode for this operation
fn op_code(&self) -> Self::OpCode;
/// Get the list of arguments for this operation. If not all arguments are [`Variable`], returns
/// `None` instead.
fn args(&self) -> Option<Vec<Variable>> {
None
}
/// Create typed operation from an opcode and a list of arguments. Returns `None` if not all
/// arguments are [`Variable`].
#[allow(unused)]
fn from_code_and_args(op_code: Self::OpCode, args: &[Variable]) -> Option<Self> {
None
}
/// Whether this operation is commutative (arguments can be freely reordered). Ignored for
/// single argument operations.
fn is_commutative(&self) -> bool {
false
}
/// Whether this operation is pure (has no side effects). Things like uniform/plane operations
/// are considered impure, because they affect other units.
fn is_pure(&self) -> bool {
false
}
}
/// A type that represents an operation's arguments
pub trait OperationArgs: Sized {
/// Construct this type from a list of arguments. If not all arguments are [`Variable`], returns
/// `None`
#[allow(unused)]
fn from_args(args: &[Variable]) -> Option<Self> {
None
}
/// Turns this type into a flat list of arguments. If not all arguments are [`Variable`],
/// returns `None`
fn as_args(&self) -> Option<Vec<Variable>> {
None
}
}
impl OperationArgs for Variable {
fn from_args(args: &[Variable]) -> Option<Self> {
Some(args[0])
}
fn as_args(&self) -> Option<Vec<Variable>> {
Some(vec![*self])
}
}
/// Types that can be destructured into and created from a list of [`Variable`]s.
pub trait FromArgList: Sized {
/// Creates this type from a list of variables. This works like a parse stream, where consumed
/// variables are popped from the front.
fn from_arg_list(args: &mut VecDeque<Variable>) -> Self;
/// Turns this type into a list of [`Variable`]s.
fn as_arg_list(&self) -> impl IntoIterator<Item = Variable>;
}
impl FromArgList for Variable {
fn from_arg_list(args: &mut VecDeque<Variable>) -> Self {
args.pop_front().expect("Missing variable from arg list")
}
fn as_arg_list(&self) -> impl IntoIterator<Item = Variable> {
[*self]
}
}
impl FromArgList for Vec<Variable> {
fn from_arg_list(args: &mut VecDeque<Variable>) -> Self {
core::mem::take(args).into_iter().collect()
}
fn as_arg_list(&self) -> impl IntoIterator<Item = Variable> {
self.iter().cloned()
}
}
impl FromArgList for bool {
fn from_arg_list(args: &mut VecDeque<Variable>) -> Self {
args.pop_front()
.expect("Missing variable from arg list")
.as_const()
.unwrap()
.as_bool()
}
fn as_arg_list(&self) -> impl IntoIterator<Item = Variable> {
[(*self).into()]
}
}
impl FromArgList for u32 {
fn from_arg_list(args: &mut VecDeque<Variable>) -> Self {
args.pop_front()
.expect("Missing variable from arg list")
.as_const()
.unwrap()
.as_u32()
}
fn as_arg_list(&self) -> impl IntoIterator<Item = Variable> {
[(*self).into()]
}
}
impl FromArgList for usize {
fn from_arg_list(args: &mut VecDeque<Variable>) -> Self {
args.pop_front()
.expect("Missing variable from arg list")
.as_const()
.unwrap()
.as_usize()
}
fn as_arg_list(&self) -> impl IntoIterator<Item = Variable> {
[(*self).into()]
}
}