3 static unsigned char inputLocation = 0; // Current index in text input
5 static double calc(const char input[CALC_BUFFER_SIZE +1]) // Finds value of input char array, relatively small and fast I think
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
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
13 bool dashAsMinus = false; // Kind of a hacky solution to determining whether to treat a dash as a minus sign or a negative sign
15 while(inputLocation < CALC_BUFFER_SIZE + 1)
17 char digit = input[inputLocation];
19 if(inputLocation == 0 && input[inputLocation] == CALC_CHAR_SUB && input[inputLocation + 1] == CALC_CHAR_BEG)
21 tokens[tokenCount].raw.num = 0;
22 tokens[tokenCount].isNum = true;
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 */
32 inputToken[inputTokenLocation] = input[inputLocation];
38 if(inputTokenLocation != 0)
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++)
47 inputTokenLocation = 0;
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;
60 switch(input[inputLocation])
68 tokens[tokenCount].raw.op.priority = CALC_PRIO_ADD;
71 tokens[tokenCount].raw.op.priority = CALC_PRIO_SUB;
74 tokens[tokenCount].raw.op.priority = CALC_PRIO_MUL;
77 tokens[tokenCount].raw.op.priority = CALC_PRIO_DIV;
80 tokens[tokenCount].raw.op.priority = CALC_PRIO_EXP;
81 tokens[tokenCount].raw.op.ltr = false;
94 tokens[tokenCount].isNum = true;
95 tokens[tokenCount].raw.num = CALC_VALU_EUL;
99 tokens[tokenCount].isNum = true;
100 tokens[tokenCount].raw.num = CALC_VALU_PI;
105 inputLocation = CALC_BUFFER_SIZE;
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
119 unsigned char numBrackets = 0; // The number of parenthesis
121 for(unsigned char i = 0; i < tokenCount; i++)
125 output[outputLocation] = tokens[i];
128 else if(tokens[i].raw.op.c == CALC_CHAR_BEG)
130 opstack[opstackLocation] = tokens[i];
133 else if(tokens[i].raw.op.c == CALC_CHAR_END)
135 while(opstack[opstackLocation - 1].raw.op.c != CALC_CHAR_BEG)
137 output[outputLocation] = opstack[opstackLocation - 1];
145 else if(tokens[i].raw.op.priority == 0)
147 opstack[opstackLocation] = tokens[i];
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)
158 output[outputLocation] = opstack[opstackLocation - 1];
162 opstack[opstackLocation] = tokens[i];
167 tokenCount -= numBrackets;
169 for(signed char i = opstackLocation - 1; i >= 0; i--)
171 output[outputLocation] = opstack[i];
176 double answer[CALC_BUFFER_SIZE];
177 unsigned char answerLocation = 0;
179 for(unsigned char i = 0; i < tokenCount; i++)
183 answer[answerLocation] = output[i].raw.num;
188 if(output[i].raw.op.priority == 0)
190 if (answerLocation < 1) { /* not handled here -- ERROR? */ } else
191 if(answerLocation >= 1)
193 double (*op)(double);
194 switch(output[i].raw.op.c)
224 continue; /* invalid input */
226 answer[answerLocation - 1] = op(answer[answerLocation - 1]);
230 else if(answerLocation >= 2)
232 switch(output[i].raw.op.c)
235 answer[answerLocation - 2] += answer[answerLocation - 1];
238 answer[answerLocation - 2] -= answer[answerLocation - 1];
241 answer[answerLocation - 2] *= answer[answerLocation - 1];
244 answer[answerLocation - 2] /= answer[answerLocation - 1];
247 answer[answerLocation - 2] = pow(answer[answerLocation - 2], answer[answerLocation - 1]);
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
266 static int process_input(const uint16_t keycode, const uint8_t mods, const keyevent_t event)
268 /* handle even when no key was pressed */
273 /* QMK should handle those */
283 /* when shift key is pressed handle characters differently */
284 char characterPressed;
285 if((get_mods() & MODS_SHIFT_MASK))
290 characterPressed = CALC_CHAR_BEG;
293 characterPressed = CALC_CHAR_END;
296 characterPressed = CALC_CHAR_ADD;
299 characterPressed = CALC_CHAR_ADD;
302 characterPressed = CALC_CHAR_EXP;
305 characterPressed = CALC_CHAR_MUL;
308 characterPressed = CALC_CHAR_MUL;
311 characterPressed = CALC_CHAR_ASN;
314 characterPressed = CALC_CHAR_ACS;
317 characterPressed = CALC_CHAR_ATN;
320 characterPressed = CALC_CHAR_LOG;
326 return characterPressed;
329 /* normal key handling: shift not pressed */
332 if (keycode == KC_KP_0 || keycode == KC_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';
344 return characterPressed = CALC_CHAR_SUB;
347 return characterPressed = CALC_CHAR_DIV;
349 return characterPressed = CALC_CHAR_SIN;
351 return characterPressed = CALC_CHAR_COS;
353 return characterPressed = CALC_CHAR_TAN;
355 return characterPressed = CALC_CHAR_SQT;
357 return characterPressed = CALC_CHAR_LGE;
360 return characterPressed = CALC_CHAR_DEC;
362 return characterPressed = CALC_CHAR_PI;
364 return characterPressed = CALC_CHAR_EUL;
380 bool process_record_user(uint16_t keycode, keyrecord_t* record)
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
385 if((biton32(layer_state) == CALC_LAYER && CALC_FORCE_NUM_LOCK_INSIDE_CALC) || (biton32(layer_state) != CALC_LAYER && CALC_FORCE_NUM_LOCK_OUTSIDE_CALC))
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);
392 if(numpadKeyPressed && !(host_keyboard_leds() & (1 << USB_LED_NUM_LOCK)))
395 send_keyboard_report();
400 if(biton32(layer_state) != CALC_LAYER) { return true; }
402 int action = process_input(keycode, get_mods(), record->event);
410 if(inputLocation > 0)
413 text[inputLocation] = '\0';
414 backspaceText[0] = (char)8;
415 backspaceText[1] = '\0';
416 send_string(backspaceText);
420 for(int i = 0; i < inputLocation; i++)
422 backspaceText[i] = (char)8;
424 send_string(backspaceText);
425 dtostrf(calc(text), CALC_PRINT_SIZE, CALC_PRINT_SIZE, text);
427 for(unsigned char i = 0; i < CALC_BUFFER_SIZE; i++)
430 backspaceText[i] = '\0';
435 for(unsigned char i = 0; i < CALC_BUFFER_SIZE; i++)
438 backspaceText[i] = '\0';
441 layer_off(CALC_LAYER);
446 char characterPressed = (char)action;
448 if(inputLocation < CALC_BUFFER_SIZE)
450 text[inputLocation] = characterPressed;
453 char characterToSend[2];
454 characterToSend[0] = characterPressed;
455 characterToSend[1] = '\0';
457 send_string(characterToSend);