]> git.donarmstrong.com Git - qmk_firmware.git/blob - users/doogle999/doogle999.c
Made a userspace that allows you to use your keyboard as an order of operations based...
[qmk_firmware.git] / users / doogle999 / doogle999.c
1 #include "doogle999.h"
2
3 static unsigned char inputLocation = 0; // Current index in text input
4
5 static double calc(const char input[CALC_BUFFER_SIZE +1]) // Finds value of input char array, relatively small and fast I think
6 {
7   char inputToken[CALC_BUFFER_SIZE + 1]; // Input buffer, used when a single token (generally a number) takes up more
8   unsigned char inputTokenLocation = 0, inputLocation = 0; // Keep track of indices
9
10   struct Token tokens[CALC_BUFFER_SIZE + 1]; // Input, converted to tokens, one extra large to accomodate for possible negative sign then open parenthesis as first character
11   unsigned char tokenCount = 0; // Keep track of index
12
13   bool dashAsMinus = false; // Kind of a hacky solution to determining whether to treat a dash as a minus sign or a negative sign
14
15   while(inputLocation < CALC_BUFFER_SIZE + 1)
16   {
17     char digit = input[inputLocation];
18
19     if(inputLocation == 0 && input[inputLocation] == CALC_CHAR_SUB && input[inputLocation + 1] == CALC_CHAR_BEG)
20     {
21       tokens[tokenCount].raw.num = 0;
22       tokens[tokenCount].isNum = true;
23
24       tokenCount++;
25       dashAsMinus = true;
26     }
27
28     if ((digit >= '0' && digit <= '9') || /* valid digit */
29         (inputTokenLocation != 0 && input[inputLocation] == CALC_CHAR_DEC) || /* valid floating point */
30         (!dashAsMinus && inputTokenLocation == 0 && input[inputLocation] == CALC_CHAR_SUB)) /* - is negative sign */
31     {
32       inputToken[inputTokenLocation] = input[inputLocation];
33       inputTokenLocation++;
34       inputLocation++;
35       continue;
36     }
37
38     if(inputTokenLocation != 0)
39     {
40       // sscanf(inputToken, "%lf", &tokens[tokenCount].raw.num); // I would like to use sscanf here, but the small version of stdio.h on the chip doesn't allow sscanf or its sister functions to be used to process floats
41       tokens[tokenCount].raw.num = atof(inputToken);
42       tokens[tokenCount].isNum = true;
43       for(unsigned char i = 0; i < inputTokenLocation + 1; i++)
44       {
45         inputToken[i] = '\0';
46       }
47       inputTokenLocation = 0;
48       tokenCount++;
49       dashAsMinus = true;
50       continue;
51     }
52
53     /* inputTokenLocation == 0 */
54     tokens[tokenCount].isNum = false;
55     tokens[tokenCount].raw.op.c = input[inputLocation];
56     tokens[tokenCount].raw.op.priority = 0;
57     tokens[tokenCount].raw.op.ltr = true;
58     dashAsMinus = false;
59
60     switch(input[inputLocation])
61     {
62       case CALC_CHAR_BEG:
63         break;
64       case CALC_CHAR_END:
65         dashAsMinus = true;
66         break;
67       case CALC_CHAR_ADD:
68         tokens[tokenCount].raw.op.priority = CALC_PRIO_ADD;
69         break;
70       case CALC_CHAR_SUB:
71         tokens[tokenCount].raw.op.priority = CALC_PRIO_SUB;
72         break;
73       case CALC_CHAR_MUL:
74         tokens[tokenCount].raw.op.priority = CALC_PRIO_MUL;
75         break;
76       case CALC_CHAR_DIV:
77         tokens[tokenCount].raw.op.priority = CALC_PRIO_DIV;
78         break;
79       case CALC_CHAR_EXP:
80         tokens[tokenCount].raw.op.priority = CALC_PRIO_EXP;
81         tokens[tokenCount].raw.op.ltr = false;
82         break;
83       case CALC_CHAR_SIN:
84       case CALC_CHAR_COS:
85       case CALC_CHAR_TAN:
86       case CALC_CHAR_ASN:
87       case CALC_CHAR_ACS:
88       case CALC_CHAR_ATN:
89       case CALC_CHAR_LGE:
90       case CALC_CHAR_LOG:
91       case CALC_CHAR_SQT:
92         break;
93       case CALC_CHAR_EUL:
94         tokens[tokenCount].isNum = true;
95         tokens[tokenCount].raw.num = CALC_VALU_EUL;
96         dashAsMinus = true;
97         break;
98       case CALC_CHAR_PI:
99         tokens[tokenCount].isNum = true;
100         tokens[tokenCount].raw.num = CALC_VALU_PI;
101         dashAsMinus = true;
102         break;
103       case '\0':
104         tokenCount--;
105         inputLocation = CALC_BUFFER_SIZE;
106         break;
107       default:
108         tokenCount--;
109         break;
110     }
111     tokenCount++;
112     inputLocation++;
113   }
114
115   struct Token output[CALC_BUFFER_SIZE + 1]; // Final output tokens before evaluation
116   struct Token opstack[CALC_BUFFER_SIZE + 1]; // Stack of operators
117   unsigned char outputLocation = 0, opstackLocation = 0; // Keep track of indices
118
119   unsigned char numBrackets = 0; // The number of parenthesis
120
121   for(unsigned char i = 0; i < tokenCount; i++)
122   {
123     if(tokens[i].isNum)
124     {
125       output[outputLocation] = tokens[i];
126       outputLocation++;
127     }
128     else if(tokens[i].raw.op.c == CALC_CHAR_BEG)
129     {
130       opstack[opstackLocation] = tokens[i];
131       opstackLocation++;
132     }
133     else if(tokens[i].raw.op.c == CALC_CHAR_END)
134     {
135       while(opstack[opstackLocation - 1].raw.op.c != CALC_CHAR_BEG)
136       {
137         output[outputLocation] = opstack[opstackLocation - 1];
138         outputLocation++;
139         opstackLocation--;
140       }
141       opstackLocation--;
142
143       numBrackets += 2;
144     }
145     else if(tokens[i].raw.op.priority == 0)
146     {
147       opstack[opstackLocation] = tokens[i];
148       opstackLocation++;
149     }
150     else
151     {
152       while(opstackLocation != 0
153         && (opstack[opstackLocation - 1].raw.op.priority == 0
154           || tokens[i].raw.op.priority < opstack[opstackLocation - 1].raw.op.priority
155           || (tokens[i].raw.op.priority == opstack[opstackLocation - 1].raw.op.priority && opstack[opstackLocation - 1].raw.op.ltr))
156         && opstack[opstackLocation - 1].raw.op.c != CALC_CHAR_BEG)
157       {
158         output[outputLocation] = opstack[opstackLocation - 1];
159         outputLocation++;
160         opstackLocation--;
161       }
162       opstack[opstackLocation] = tokens[i];
163       opstackLocation++;
164     }
165   }
166
167   tokenCount -= numBrackets;
168
169   for(signed char i = opstackLocation - 1; i >= 0; i--)
170   {
171     output[outputLocation] = opstack[i];
172     outputLocation++;
173     opstackLocation--;
174   }
175
176   double answer[CALC_BUFFER_SIZE];
177   unsigned char answerLocation = 0;
178
179   for(unsigned char i = 0; i < tokenCount; i++)
180   {
181     if(output[i].isNum)
182     {
183       answer[answerLocation] = output[i].raw.num;
184       answerLocation++;
185       continue;
186     }
187
188     if(output[i].raw.op.priority == 0)
189     {
190       if (answerLocation < 1) { /* not handled here -- ERROR? */ } else
191       if(answerLocation >= 1)
192       {
193         double (*op)(double);
194         switch(output[i].raw.op.c)
195         {
196         case CALC_CHAR_SIN:
197           op = sin;
198           break;
199         case CALC_CHAR_COS:
200           op = cos;
201           break;
202         case CALC_CHAR_TAN:
203           op = tan;
204           break;
205         case CALC_CHAR_ASN:
206           op = asin;
207           break;
208         case CALC_CHAR_ACS:
209           op = acos;
210           break;
211         case CALC_CHAR_ATN:
212           op = atan;
213           break;
214         case CALC_CHAR_LGE:
215           op = log;
216           break;
217         case CALC_CHAR_LOG:
218           op = log10;
219           break;
220         case CALC_CHAR_SQT:
221           op = sqrt;
222           break;
223         default:
224           continue; /* invalid input */
225         }
226         answer[answerLocation - 1] = op(answer[answerLocation - 1]);
227       }
228     }
229     /* priority != 0 */
230     else if(answerLocation >= 2)
231     {
232       switch(output[i].raw.op.c)
233       {
234       case CALC_CHAR_ADD:
235         answer[answerLocation - 2] += answer[answerLocation - 1];
236         break;
237       case CALC_CHAR_SUB:
238         answer[answerLocation - 2] -= answer[answerLocation - 1];
239         break;
240       case CALC_CHAR_MUL:
241         answer[answerLocation - 2] *= answer[answerLocation - 1];
242         break;
243       case CALC_CHAR_DIV:
244         answer[answerLocation - 2] /= answer[answerLocation - 1];
245         break;
246       case CALC_CHAR_EXP:
247         answer[answerLocation - 2] = pow(answer[answerLocation - 2], answer[answerLocation - 1]);
248         break;
249       }
250
251       answerLocation--;
252     }
253   }
254
255   return answer[0];
256 }
257
258 /*
259  * @returns  0 when nothing should happen and QMK should work as usual
260  * @returns -1 when invalid input was given, QMK should ignore it
261  * @returns -2 when BSP should be done
262  * @returns -3 when CALC should be done
263  * @returns -4 when ENDCALC should be done
264  * @returns positive value of CALC_* when normal input was processed
265  */
266 static int process_input(const uint16_t keycode, const uint8_t mods, const keyevent_t event)
267 {
268   /* handle even when no key was pressed */
269   if(!event.pressed)
270   {
271     switch(keycode)
272     {
273       /* QMK should handle those */
274       case KC_RSFT:
275       case KC_LSFT:
276         return 0;
277         break;
278     }
279     /* ??? ignore */
280     return -1;
281   }
282
283   /* when shift key is pressed handle characters differently */
284   char characterPressed;
285   if((get_mods() & MODS_SHIFT_MASK))
286   {
287     switch(keycode)
288     {
289       case KC_9:
290         characterPressed = CALC_CHAR_BEG;
291         break;
292       case KC_0:
293         characterPressed = CALC_CHAR_END;
294         break;
295       case KC_EQUAL:
296         characterPressed = CALC_CHAR_ADD;
297         break;
298       case KC_KP_PLUS:
299         characterPressed = CALC_CHAR_ADD;
300         break;
301       case KC_6:
302         characterPressed = CALC_CHAR_EXP;
303         break;
304       case KC_8:
305         characterPressed = CALC_CHAR_MUL;
306         break;
307       case KC_KP_ASTERISK:
308         characterPressed = CALC_CHAR_MUL;
309         break;
310       case KC_S:
311         characterPressed = CALC_CHAR_ASN;
312         break;
313       case KC_C:
314         characterPressed = CALC_CHAR_ACS;
315         break;
316       case KC_T:
317         characterPressed = CALC_CHAR_ATN;
318         break;
319       case KC_L:
320         characterPressed = CALC_CHAR_LOG;
321         break;
322       default:
323         return -1;
324         break;
325     }
326     return characterPressed;
327   }
328
329   /* normal key handling:  shift not pressed */
330
331   /* digits */
332   if (keycode == KC_KP_0 || keycode == KC_0) {
333     return '0';
334   } else if (keycode >= KC_KP_1 && keycode <= KC_KP_9) {
335     return keycode - KC_KP_1 +1 + '0';
336   } else if (keycode >= KC_1 && keycode <= KC_9) {
337     return keycode - KC_1 +1 + '0';
338   }
339
340   /* other tokens */
341   switch (keycode) {
342     case KC_MINUS:
343     case KC_KP_MINUS:
344       return characterPressed = CALC_CHAR_SUB;
345     case KC_SLASH:
346     case KC_KP_SLASH:
347       return characterPressed = CALC_CHAR_DIV;
348     case KC_S:
349       return characterPressed = CALC_CHAR_SIN;
350     case KC_C:
351       return characterPressed = CALC_CHAR_COS;
352     case KC_T:
353       return characterPressed = CALC_CHAR_TAN;
354     case KC_Q:
355       return characterPressed = CALC_CHAR_SQT;
356     case KC_L:
357       return characterPressed = CALC_CHAR_LGE;
358     case KC_DOT:
359     case KC_KP_DOT:
360       return characterPressed = CALC_CHAR_DEC;
361     case KC_P:
362       return characterPressed = CALC_CHAR_PI;
363     case KC_E:
364       return characterPressed = CALC_CHAR_EUL;
365     case KC_BSPC:
366       return -2;
367     case KC_RSFT:
368       return 0;
369     case KC_LSFT:
370       return 0;
371     case CALC:
372       return -3;
373     case ENDCALC:
374       return -4;
375     default:
376       return -1;
377   }
378 }
379
380 bool process_record_user(uint16_t keycode, keyrecord_t* record)
381 {
382         static char text[CALC_BUFFER_SIZE + 1]; // Used to store input and then output when ready to print
383         static char backspaceText[CALC_BUFFER_SIZE + 1]; // Pretty dumb waste of memory because only backspace characters, used with send_string to backspace and remove input
384
385         if((biton32(layer_state) == CALC_LAYER && CALC_FORCE_NUM_LOCK_INSIDE_CALC) || (biton32(layer_state) != CALC_LAYER && CALC_FORCE_NUM_LOCK_OUTSIDE_CALC))
386         {
387                 bool numpadKeyPressed = record->event.pressed &&
388                         !(get_mods() & MODS_SHIFT_MASK) &&
389                         /* KC_KP_1, KC_KP_2, ..., KC_KP_0, KC_KP_DOT */
390                         (keycode >= KC_KP_1 && keycode <= KC_KP_DOT);
391
392                 if(numpadKeyPressed && !(host_keyboard_leds() & (1 << USB_LED_NUM_LOCK)))
393                 {
394                         add_key(KC_NLCK);
395                         send_keyboard_report();
396                         del_key(KC_NLCK);
397                 }
398         }
399
400         if(biton32(layer_state) != CALC_LAYER) { return true; }
401
402         int action = process_input(keycode, get_mods(), record->event);
403         switch(action)
404         {
405         case 0:
406                 return true;
407         case -1:
408                 return false;
409         case -2:
410                 if(inputLocation > 0)
411                 {
412                         inputLocation--;
413                         text[inputLocation] = '\0';
414                         backspaceText[0] = (char)8;
415                         backspaceText[1] = '\0';
416                         send_string(backspaceText);
417                 }
418                 return false;
419         case -3:
420                 for(int i = 0; i < inputLocation; i++)
421                 {
422                         backspaceText[i] = (char)8;
423                 }
424                 send_string(backspaceText);
425                 dtostrf(calc(text), CALC_PRINT_SIZE, CALC_PRINT_SIZE, text);
426                 send_string(text);
427                 for(unsigned char i = 0; i < CALC_BUFFER_SIZE; i++)
428                 {
429                         text[i] = '\0';
430                         backspaceText[i] = '\0';
431                 }
432                 inputLocation = 0;
433                 return false;
434         case -4:
435                 for(unsigned char i = 0; i < CALC_BUFFER_SIZE; i++)
436                 {
437                         text[i] = '\0';
438                         backspaceText[i] = '\0';
439                 }
440                 inputLocation = 0;
441                 layer_off(CALC_LAYER);
442                 return false;
443         default:
444                 break;
445         }
446         char characterPressed = (char)action;
447
448         if(inputLocation < CALC_BUFFER_SIZE)
449         {
450                 text[inputLocation] = characterPressed;
451                 inputLocation++;
452
453                 char characterToSend[2];
454                 characterToSend[0] = characterPressed;
455                 characterToSend[1] = '\0';
456
457                 send_string(characterToSend);
458         }
459         return false;
460 }