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!(
182 op_expr,
183 "input-history must have 3 parameters: key-type(virtual|real), key, key-recency"
184 );
185 }
186
187 let input_type = match l[1]
188 .atom(s.vars())
189 .ok_or_else(|| anyhow_expr!(&l[1], "key-type must be virtual|real"))?
190 {
191 "real" => InputType::Real,
192 "fake" | "virtual" => InputType::Virtual,
193 _ => bail_expr!(&l[1], "key-type must be virtual|real"),
194 };
195 let input = match input_type {
196 InputType::Real => {
197 let key = l[2].atom(s.vars()).ok_or_else(|| {
198 anyhow_expr!(&l[2], "input key name must not be a list")
199 })?;
200 u16::from(
201 str_to_oscode(key)
202 .ok_or_else(|| anyhow_expr!(&l[2], "invalid input key name"))?,
203 )
204 }
205 InputType::Virtual => parse_vkey_coord(&l[2], s)?.y,
206 };
207 let key_recency = parse_u8_with_range(&l[3], s, "key-recency", 1, 8)? - 1;
208 let (op1, op2) =
209 OpCode::new_historical_input((input_type.to_row(), input), key_recency);
210 ops.extend(&[op1, op2]);
211 Ok(())
212 }
213 AllowedListOps::KeyTiming => {
214 if l.len() != 4 {
215 bail_expr!(
216 op_expr,
217 "key-timing must have 3 parameters: key-recency, lt|gt|less-than|greater-than, milliseconds (0-65535)"
218 );
219 }
220 let nth_key = parse_u8_with_range(&l[1], s, "key-recency", 1, 8)? - 1;
221 let ticks_since = parse_u16(&l[3], s, "milliseconds")?;
222 match l[2].atom(s.vars()).ok_or_else(|| {
223 anyhow_expr!(
224 &l[2],
225 "key-timing 2nd parameter must be one of: lt|gt|less-than|greater-than"
226 )
227 })? {
228 "less-than" | "lt" => {
229 ops.push(OpCode::new_ticks_since_lt(nth_key, ticks_since));
230 }
231 "greater-than" | "gt" => {
232 ops.push(OpCode::new_ticks_since_gt(nth_key, ticks_since));
233 }
234 _ => {
235 bail_expr!(
236 &l[2],
237 "key-timing 2nd parameter must be one of: lt|gt|less-than|greater-than"
238 );
239 }
240 };
241 s.switch_max_key_timing
242 .set(std::cmp::max(s.switch_max_key_timing.get(), ticks_since));
243 Ok(())
244 }
245 AllowedListOps::Layer | AllowedListOps::BaseLayer => {
246 if l.len() != 2 {
247 bail_expr!(
248 op_expr,
249 "{} must have 1 parameter: layer-name",
250 match op {
251 AllowedListOps::Layer => "layer",
252 AllowedListOps::BaseLayer => "base-layer",
253 _ => unreachable!(),
254 }
255 );
256 }
257 let layer = l[1]
258 .atom(s.vars())
259 .and_then(|atom| s.layer_idxs.get(atom))
260 .map(|idx| {
261 assert!(*idx < MAX_LAYERS);
262 *idx as u16
263 })
264 .ok_or_else(|| anyhow_expr!(&l[1], "not a known layer name"))?;
265 let (op1, op2) = match op {
266 AllowedListOps::Layer => OpCode::new_layer(layer),
267 AllowedListOps::BaseLayer => OpCode::new_base_layer(layer),
268 _ => unreachable!(),
269 };
270 ops.extend(&[op1, op2]);
271 Ok(())
272 }
273 AllowedListOps::Or | AllowedListOps::And | AllowedListOps::Not => {
274 let op = match op {
275 AllowedListOps::Or => BooleanOperator::Or,
276 AllowedListOps::And => BooleanOperator::And,
277 AllowedListOps::Not => BooleanOperator::Not,
278 _ => unreachable!(),
279 };
280 let placeholder_index = ops.len() as u16;
282 ops.push(OpCode::new_bool(op, placeholder_index));
283 for op in l.iter().skip(1) {
284 parse_switch_case_bool(depth + 1, op, ops, s)?;
285 }
286 if ops.len() > usize::from(MAX_OPCODE_LEN) {
287 bail_expr!(op_expr, "switch logic length has been exceeded");
288 }
289 ops[placeholder_index as usize] = OpCode::new_bool(op, ops.len() as u16);
290 Ok(())
291 }
292 }
293 }
294}