aboutsummaryrefslogtreecommitdiff
path: root/users/doogle999/doogle999.c
diff options
context:
space:
mode:
Diffstat (limited to 'users/doogle999/doogle999.c')
-rw-r--r--users/doogle999/doogle999.c460
1 files changed, 460 insertions, 0 deletions
diff --git a/users/doogle999/doogle999.c b/users/doogle999/doogle999.c
new file mode 100644
index 000000000..320de7cff
--- /dev/null
+++ b/users/doogle999/doogle999.c
@@ -0,0 +1,460 @@
1#include "doogle999.h"
2
3static unsigned char inputLocation = 0; // Current index in text input
4
5static 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 */
266static 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
380bool 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} \ No newline at end of file