1use super::sexpr::*;
2use super::*;
3use crate::{anyhow_expr, bail, bail_expr};
4
5pub fn parse_switch(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
6 const ERR_STR: &str =
7 "switch expects triples of params: <key match> <action> <break|fallthrough>";
8
9 let mut cases = vec![];
10
11 let mut params = ac_params.iter();
12 loop {
13 let Some(key_match) = params.next() else {
14 break;
15 };
16 let Some(action) = params.next() else {
17 bail!("{ERR_STR}\nMissing <action> and <break|fallthrough> for the final triple");
18 };
19 let Some(break_or_fallthrough_expr) = params.next() else {
20 bail!("{ERR_STR}\nMissing <break|fallthrough> for the final triple");
21 };
22
23 let Some(key_match) = key_match.list(s.vars()) else {
24 bail_expr!(key_match, "{ERR_STR}\n<key match> must be a list")
25 };
26 let mut ops = vec![];
27 for op in key_match.iter() {
28 parse_switch_case_bool(1, op, &mut ops, s)?;
29 }
30
31 let action = parse_action(action, s)?;
32
33 let Some(break_or_fallthrough) = break_or_fallthrough_expr.atom(s.vars()) else {
34 bail_expr!(
35 break_or_fallthrough_expr,
36 "{ERR_STR}\nthis must be one of: break, fallthrough"
37 );
38 };
39 let break_or_fallthrough = match break_or_fallthrough {
40 "break" => BreakOrFallthrough::Break,
41 "fallthrough" => BreakOrFallthrough::Fallthrough,
42 _ => bail_expr!(
43 break_or_fallthrough_expr,
44 "{ERR_STR}\nthis must be one of: break, fallthrough"
45 ),
46 };
47 cases.push((s.a.sref_vec(ops), action, break_or_fallthrough));
48 }
49 Ok(s.a.sref(Action::Switch(s.a.sref(Switch {
50 cases: s.a.sref_vec(cases),
51 }))))
52}
53
54pub fn parse_switch_case_bool(
55 depth: u8,
56 op_expr: &SExpr,
57 ops: &mut Vec<OpCode>,
58 s: &ParserState,
59) -> Result<()> {
60 if ops.len() > MAX_OPCODE_LEN as usize {
61 bail_expr!(
62 op_expr,
63 "maximum key match size of {MAX_OPCODE_LEN} items is exceeded"
64 );
65 }
66 if usize::from(depth) > MAX_BOOL_EXPR_DEPTH {
67 bail_expr!(
68 op_expr,
69 "maximum key match expression depth {MAX_BOOL_EXPR_DEPTH} is exceeded"
70 );
71 }
72 if let Some(a) = op_expr.atom(s.vars()) {
73 let osc = str_to_oscode(a).ok_or_else(|| anyhow_expr!(op_expr, "invalid key name"))?;
74 ops.push(OpCode::new_key(osc.into()));
75 Ok(())
76 } else {
77 let l = op_expr
78 .list(s.vars())
79 .expect("must be a list, checked atom");
80 if l.is_empty() {
81 bail_expr!(op_expr, "switch logic cannot contain empty lists inside");
82 }
83 #[derive(PartialEq)]
84 enum AllowedListOps {
85 Or,
86 And,
87 Not,
88 KeyHistory,
89 KeyTiming,
90 Input,
91 InputHistory,
92 Layer,
93 BaseLayer,
94 }
95 #[derive(Copy, Clone)]
96 enum InputType {
97 Real,
98 Virtual,
99 }
100 impl InputType {
101 fn to_row(self) -> u8 {
102 match self {
103 InputType::Real => 0,
104 InputType::Virtual => 1,
105 }
106 }
107 }
108 let op = l[0]
109 .atom(s.vars())
110 .and_then(|s| match s {
111 "or" => Some(AllowedListOps::Or),
112 "and" => Some(AllowedListOps::And),
113 "not" => Some(AllowedListOps::Not),
114 "key-history" => Some(AllowedListOps::KeyHistory),
115 "key-timing" => Some(AllowedListOps::KeyTiming),
116 "input" => Some(AllowedListOps::Input),
117 "input-history" => Some(AllowedListOps::InputHistory),
118 "layer" => Some(AllowedListOps::Layer),
119 "base-layer" => Some(AllowedListOps::BaseLayer),
120 _ => None,
121 })
122 .ok_or_else(|| {
123 anyhow_expr!(
124 op_expr,
125 "lists inside switch logic must begin with one of:\n\
126 or | and | not | key-history | key-timing\n\
127 | input | input-history | layer | base-layer",
128 )
129 })?;
130
131 match op {
132 AllowedListOps::KeyHistory => {
133 if l.len() != 3 {
134 bail_expr!(
135 op_expr,
136 "key-history must have 2 parameters: key, key-recency"
137 );
138 }
139 let osc = l[1]
140 .atom(s.vars())
141 .and_then(str_to_oscode)
142 .ok_or_else(|| anyhow_expr!(&l[1], "invalid key name"))?;
143 let key_recency = parse_u8_with_range(&l[2], s, "key-recency", 1, 8)? - 1;
144 ops.push(OpCode::new_key_history(osc.into(), key_recency));
145 Ok(())
146 }
147 AllowedListOps::Input => {
148 if l.len() != 3 {
149 bail_expr!(
150 op_expr,
151 "input must have 2 parameters: key-type(virtual|real), key"
152 );
153 }
154
155 let input_type = match l[1]
156 .atom(s.vars())
157 .ok_or_else(|| anyhow_expr!(&l[1], "key-type must be virtual|real"))?
158 {
159 "real" => InputType::Real,
160 "fake" | "virtual" => InputType::Virtual,
161 _ => bail_expr!(op_expr, "key-type must be virtual|real"),
162 };
163 let input = match input_type {
164 InputType::Real => {
165 let key = l[2].atom(s.vars()).ok_or_else(|| {
166 anyhow_expr!(&l[2], "input key name must not be a list")
167 })?;
168 u16::from(
169 str_to_oscode(key)
170 .ok_or_else(|| anyhow_expr!(&l[2], "invalid input key name"))?,
171 )
172 }
173 InputType::Virtual => parse_vkey_coord(&l[2], s)?.y,
174 };
175 let (op1, op2) = OpCode::new_active_input((input_type.to_row(), input));
176 ops.extend(&[op1, op2]);
177 Ok(())
178 }
179 AllowedListOps::InputHistory => {
180 if l.len() != 4 {
181 bail_expr!(op_expr, "input-history must have 3 parameters: key-type(virtual|real), key, key-recency");
182 }
183
184 let input_type = match l[1]
185 .atom(s.vars())
186 .ok_or_else(|| anyhow_expr!(&l[1], "key-type must be virtual|real"))?
187 {
188 "real" => InputType::Real,
189 "fake" | "virtual" => InputType::Virtual,
190 _ => bail_expr!(&l[1], "key-type must be virtual|real"),
191 };
192 let input = match input_type {
193 InputType::Real => {
194 let key = l[2].atom(s.vars()).ok_or_else(|| {
195 anyhow_expr!(&l[2], "input key name must not be a list")
196 })?;
197 u16::from(
198 str_to_oscode(key)
199 .ok_or_else(|| anyhow_expr!(&l[2], "invalid input key name"))?,
200 )
201 }
202 InputType::Virtual => parse_vkey_coord(&l[2], s)?.y,
203 };
204 let key_recency = parse_u8_with_range(&l[3], s, "key-recency", 1, 8)? - 1;
205 let (op1, op2) =
206 OpCode::new_historical_input((input_type.to_row(), input), key_recency);
207 ops.extend(&[op1, op2]);
208 Ok(())
209 }
210 AllowedListOps::KeyTiming => {
211 if l.len() != 4 {
212 bail_expr!(
213 op_expr,
214 "key-timing must have 3 parameters: key-recency, lt|gt|less-than|greater-than, milliseconds (0-65535)"
215 );
216 }
217 let nth_key = parse_u8_with_range(&l[1], s, "key-recency", 1, 8)? - 1;
218 let ticks_since = parse_u16(&l[3], s, "milliseconds")?;
219 match l[2].atom(s.vars()).ok_or_else(|| {
220 anyhow_expr!(
221 &l[2],
222 "key-timing 2nd parameter must be one of: lt|gt|less-than|greater-than"
223 )
224 })? {
225 "less-than" | "lt" => {
226 ops.push(OpCode::new_ticks_since_lt(nth_key, ticks_since));
227 }
228 "greater-than" | "gt" => {
229 ops.push(OpCode::new_ticks_since_gt(nth_key, ticks_since));
230 }
231 _ => {
232 bail_expr!(
233 &l[2],
234 "key-timing 2nd parameter must be one of: lt|gt|less-than|greater-than"
235 );
236 }
237 };
238 s.switch_max_key_timing
239 .set(std::cmp::max(s.switch_max_key_timing.get(), ticks_since));
240 Ok(())
241 }
242 AllowedListOps::Layer | AllowedListOps::BaseLayer => {
243 if l.len() != 2 {
244 bail_expr!(
245 op_expr,
246 "{} must have 1 parameter: layer-name",
247 match op {
248 AllowedListOps::Layer => "layer",
249 AllowedListOps::BaseLayer => "base-layer",
250 _ => unreachable!(),
251 }
252 );
253 }
254 let layer = l[1]
255 .atom(s.vars())
256 .and_then(|atom| s.layer_idxs.get(atom))
257 .map(|idx| {
258 assert!(*idx < MAX_LAYERS);
259 *idx as u16
260 })
261 .ok_or_else(|| anyhow_expr!(&l[1], "not a known layer name"))?;
262 let (op1, op2) = match op {
263 AllowedListOps::Layer => OpCode::new_layer(layer),
264 AllowedListOps::BaseLayer => OpCode::new_base_layer(layer),
265 _ => unreachable!(),
266 };
267 ops.extend(&[op1, op2]);
268 Ok(())
269 }
270 AllowedListOps::Or | AllowedListOps::And | AllowedListOps::Not => {
271 let op = match op {
272 AllowedListOps::Or => BooleanOperator::Or,
273 AllowedListOps::And => BooleanOperator::And,
274 AllowedListOps::Not => BooleanOperator::Not,
275 _ => unreachable!(),
276 };
277 let placeholder_index = ops.len() as u16;
279 ops.push(OpCode::new_bool(op, placeholder_index));
280 for op in l.iter().skip(1) {
281 parse_switch_case_bool(depth + 1, op, ops, s)?;
282 }
283 if ops.len() > usize::from(MAX_OPCODE_LEN) {
284 bail_expr!(op_expr, "switch logic length has been exceeded");
285 }
286 ops[placeholder_index as usize] = OpCode::new_bool(op, ops.len() as u16);
287 Ok(())
288 }
289 }
290 }
291}