123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- #include "doogle999.h"
- static unsigned char inputLocation = 0; // Current index in text input
- static double calc(const char input[CALC_BUFFER_SIZE +1]) // Finds value of input char array, relatively small and fast I think
- {
- char inputToken[CALC_BUFFER_SIZE + 1]; // Input buffer, used when a single token (generally a number) takes up more
- unsigned char inputTokenLocation = 0, inputLocation = 0; // Keep track of indices
- 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
- unsigned char tokenCount = 0; // Keep track of index
- bool dashAsMinus = false; // Kind of a hacky solution to determining whether to treat a dash as a minus sign or a negative sign
- while(inputLocation < CALC_BUFFER_SIZE + 1)
- {
- char digit = input[inputLocation];
- if(inputLocation == 0 && input[inputLocation] == CALC_CHAR_SUB && input[inputLocation + 1] == CALC_CHAR_BEG)
- {
- tokens[tokenCount].raw.num = 0;
- tokens[tokenCount].isNum = true;
- tokenCount++;
- dashAsMinus = true;
- }
- if ((digit >= '0' && digit <= '9') || /* valid digit */
- (inputTokenLocation != 0 && input[inputLocation] == CALC_CHAR_DEC) || /* valid floating point */
- (!dashAsMinus && inputTokenLocation == 0 && input[inputLocation] == CALC_CHAR_SUB)) /* - is negative sign */
- {
- inputToken[inputTokenLocation] = input[inputLocation];
- inputTokenLocation++;
- inputLocation++;
- continue;
- }
- if(inputTokenLocation != 0)
- {
- // 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
- tokens[tokenCount].raw.num = atof(inputToken);
- tokens[tokenCount].isNum = true;
- for(unsigned char i = 0; i < inputTokenLocation + 1; i++)
- {
- inputToken[i] = '\0';
- }
- inputTokenLocation = 0;
- tokenCount++;
- dashAsMinus = true;
- continue;
- }
- /* inputTokenLocation == 0 */
- tokens[tokenCount].isNum = false;
- tokens[tokenCount].raw.op.c = input[inputLocation];
- tokens[tokenCount].raw.op.priority = 0;
- tokens[tokenCount].raw.op.ltr = true;
- dashAsMinus = false;
- switch(input[inputLocation])
- {
- case CALC_CHAR_BEG:
- break;
- case CALC_CHAR_END:
- dashAsMinus = true;
- break;
- case CALC_CHAR_ADD:
- tokens[tokenCount].raw.op.priority = CALC_PRIO_ADD;
- break;
- case CALC_CHAR_SUB:
- tokens[tokenCount].raw.op.priority = CALC_PRIO_SUB;
- break;
- case CALC_CHAR_MUL:
- tokens[tokenCount].raw.op.priority = CALC_PRIO_MUL;
- break;
- case CALC_CHAR_DIV:
- tokens[tokenCount].raw.op.priority = CALC_PRIO_DIV;
- break;
- case CALC_CHAR_EXP:
- tokens[tokenCount].raw.op.priority = CALC_PRIO_EXP;
- tokens[tokenCount].raw.op.ltr = false;
- break;
- case CALC_CHAR_SIN:
- case CALC_CHAR_COS:
- case CALC_CHAR_TAN:
- case CALC_CHAR_ASN:
- case CALC_CHAR_ACS:
- case CALC_CHAR_ATN:
- case CALC_CHAR_LGE:
- case CALC_CHAR_LOG:
- case CALC_CHAR_SQT:
- break;
- case CALC_CHAR_EUL:
- tokens[tokenCount].isNum = true;
- tokens[tokenCount].raw.num = CALC_VALU_EUL;
- dashAsMinus = true;
- break;
- case CALC_CHAR_PI:
- tokens[tokenCount].isNum = true;
- tokens[tokenCount].raw.num = CALC_VALU_PI;
- dashAsMinus = true;
- break;
- case '\0':
- tokenCount--;
- inputLocation = CALC_BUFFER_SIZE;
- break;
- default:
- tokenCount--;
- break;
- }
- tokenCount++;
- inputLocation++;
- }
- struct Token output[CALC_BUFFER_SIZE + 1]; // Final output tokens before evaluation
- struct Token opstack[CALC_BUFFER_SIZE + 1]; // Stack of operators
- unsigned char outputLocation = 0, opstackLocation = 0; // Keep track of indices
- unsigned char numBrackets = 0; // The number of parenthesis
- for(unsigned char i = 0; i < tokenCount; i++)
- {
- if(tokens[i].isNum)
- {
- output[outputLocation] = tokens[i];
- outputLocation++;
- }
- else if(tokens[i].raw.op.c == CALC_CHAR_BEG)
- {
- opstack[opstackLocation] = tokens[i];
- opstackLocation++;
- }
- else if(tokens[i].raw.op.c == CALC_CHAR_END)
- {
- while(opstack[opstackLocation - 1].raw.op.c != CALC_CHAR_BEG)
- {
- output[outputLocation] = opstack[opstackLocation - 1];
- outputLocation++;
- opstackLocation--;
- }
- opstackLocation--;
- numBrackets += 2;
- }
- else if(tokens[i].raw.op.priority == 0)
- {
- opstack[opstackLocation] = tokens[i];
- opstackLocation++;
- }
- else
- {
- while(opstackLocation != 0
- && (opstack[opstackLocation - 1].raw.op.priority == 0
- || tokens[i].raw.op.priority < opstack[opstackLocation - 1].raw.op.priority
- || (tokens[i].raw.op.priority == opstack[opstackLocation - 1].raw.op.priority && opstack[opstackLocation - 1].raw.op.ltr))
- && opstack[opstackLocation - 1].raw.op.c != CALC_CHAR_BEG)
- {
- output[outputLocation] = opstack[opstackLocation - 1];
- outputLocation++;
- opstackLocation--;
- }
- opstack[opstackLocation] = tokens[i];
- opstackLocation++;
- }
- }
- tokenCount -= numBrackets;
- for(signed char i = opstackLocation - 1; i >= 0; i--)
- {
- output[outputLocation] = opstack[i];
- outputLocation++;
- opstackLocation--;
- }
- double answer[CALC_BUFFER_SIZE];
- unsigned char answerLocation = 0;
- for(unsigned char i = 0; i < tokenCount; i++)
- {
- if(output[i].isNum)
- {
- answer[answerLocation] = output[i].raw.num;
- answerLocation++;
- continue;
- }
- if(output[i].raw.op.priority == 0)
- {
- if (answerLocation < 1) { /* not handled here -- ERROR? */ } else
- if(answerLocation >= 1)
- {
- double (*op)(double);
- switch(output[i].raw.op.c)
- {
- case CALC_CHAR_SIN:
- op = sin;
- break;
- case CALC_CHAR_COS:
- op = cos;
- break;
- case CALC_CHAR_TAN:
- op = tan;
- break;
- case CALC_CHAR_ASN:
- op = asin;
- break;
- case CALC_CHAR_ACS:
- op = acos;
- break;
- case CALC_CHAR_ATN:
- op = atan;
- break;
- case CALC_CHAR_LGE:
- op = log;
- break;
- case CALC_CHAR_LOG:
- op = log10;
- break;
- case CALC_CHAR_SQT:
- op = sqrt;
- break;
- default:
- continue; /* invalid input */
- }
- answer[answerLocation - 1] = op(answer[answerLocation - 1]);
- }
- }
- /* priority != 0 */
- else if(answerLocation >= 2)
- {
- switch(output[i].raw.op.c)
- {
- case CALC_CHAR_ADD:
- answer[answerLocation - 2] += answer[answerLocation - 1];
- break;
- case CALC_CHAR_SUB:
- answer[answerLocation - 2] -= answer[answerLocation - 1];
- break;
- case CALC_CHAR_MUL:
- answer[answerLocation - 2] *= answer[answerLocation - 1];
- break;
- case CALC_CHAR_DIV:
- answer[answerLocation - 2] /= answer[answerLocation - 1];
- break;
- case CALC_CHAR_EXP:
- answer[answerLocation - 2] = pow(answer[answerLocation - 2], answer[answerLocation - 1]);
- break;
- }
- answerLocation--;
- }
- }
- return answer[0];
- }
- /*
- * @returns 0 when nothing should happen and QMK should work as usual
- * @returns -1 when invalid input was given, QMK should ignore it
- * @returns -2 when BSP should be done
- * @returns -3 when CALC should be done
- * @returns -4 when ENDCALC should be done
- * @returns positive value of CALC_* when normal input was processed
- */
- static int process_input(const uint16_t keycode, const uint8_t mods, const keyevent_t event)
- {
- /* handle even when no key was pressed */
- if(!event.pressed)
- {
- switch(keycode)
- {
- /* QMK should handle those */
- case KC_RSFT:
- case KC_LSFT:
- return 0;
- break;
- }
- /* ??? ignore */
- return -1;
- }
- /* when shift key is pressed handle characters differently */
- char characterPressed;
- if((get_mods() & MODS_SHIFT_MASK))
- {
- switch(keycode)
- {
- case KC_9:
- characterPressed = CALC_CHAR_BEG;
- break;
- case KC_0:
- characterPressed = CALC_CHAR_END;
- break;
- case KC_EQUAL:
- characterPressed = CALC_CHAR_ADD;
- break;
- case KC_KP_PLUS:
- characterPressed = CALC_CHAR_ADD;
- break;
- case KC_6:
- characterPressed = CALC_CHAR_EXP;
- break;
- case KC_8:
- characterPressed = CALC_CHAR_MUL;
- break;
- case KC_KP_ASTERISK:
- characterPressed = CALC_CHAR_MUL;
- break;
- case KC_S:
- characterPressed = CALC_CHAR_ASN;
- break;
- case KC_C:
- characterPressed = CALC_CHAR_ACS;
- break;
- case KC_T:
- characterPressed = CALC_CHAR_ATN;
- break;
- case KC_L:
- characterPressed = CALC_CHAR_LOG;
- break;
- default:
- return -1;
- break;
- }
- return characterPressed;
- }
- /* normal key handling: shift not pressed */
- /* digits */
- if (keycode == KC_KP_0 || keycode == KC_0) {
- return '0';
- } else if (keycode >= KC_KP_1 && keycode <= KC_KP_9) {
- return keycode - KC_KP_1 +1 + '0';
- } else if (keycode >= KC_1 && keycode <= KC_9) {
- return keycode - KC_1 +1 + '0';
- }
- /* other tokens */
- switch (keycode) {
- case KC_MINUS:
- case KC_KP_MINUS:
- return characterPressed = CALC_CHAR_SUB;
- case KC_SLASH:
- case KC_KP_SLASH:
- return characterPressed = CALC_CHAR_DIV;
- case KC_S:
- return characterPressed = CALC_CHAR_SIN;
- case KC_C:
- return characterPressed = CALC_CHAR_COS;
- case KC_T:
- return characterPressed = CALC_CHAR_TAN;
- case KC_Q:
- return characterPressed = CALC_CHAR_SQT;
- case KC_L:
- return characterPressed = CALC_CHAR_LGE;
- case KC_DOT:
- case KC_KP_DOT:
- return characterPressed = CALC_CHAR_DEC;
- case KC_P:
- return characterPressed = CALC_CHAR_PI;
- case KC_E:
- return characterPressed = CALC_CHAR_EUL;
- case KC_BSPC:
- return -2;
- case KC_RSFT:
- return 0;
- case KC_LSFT:
- return 0;
- case CALC:
- return -3;
- case ENDCALC:
- return -4;
- default:
- return -1;
- }
- }
- bool process_record_user(uint16_t keycode, keyrecord_t* record)
- {
- static char text[CALC_BUFFER_SIZE + 1]; // Used to store input and then output when ready to print
- 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
- if((biton32(layer_state) == CALC_LAYER && CALC_FORCE_NUM_LOCK_INSIDE_CALC) || (biton32(layer_state) != CALC_LAYER && CALC_FORCE_NUM_LOCK_OUTSIDE_CALC))
- {
- bool numpadKeyPressed = record->event.pressed &&
- !(get_mods() & MODS_SHIFT_MASK) &&
- /* KC_KP_1, KC_KP_2, ..., KC_KP_0, KC_KP_DOT */
- (keycode >= KC_KP_1 && keycode <= KC_KP_DOT);
- if(numpadKeyPressed && !(host_keyboard_leds() & (1 << USB_LED_NUM_LOCK)))
- {
- add_key(KC_NLCK);
- send_keyboard_report();
- del_key(KC_NLCK);
- }
- }
- if(biton32(layer_state) != CALC_LAYER) { return true; }
- int action = process_input(keycode, get_mods(), record->event);
- switch(action)
- {
- case 0:
- return true;
- case -1:
- return false;
- case -2:
- if(inputLocation > 0)
- {
- inputLocation--;
- text[inputLocation] = '\0';
- backspaceText[0] = (char)8;
- backspaceText[1] = '\0';
- send_string(backspaceText);
- }
- return false;
- case -3:
- for(int i = 0; i < inputLocation; i++)
- {
- backspaceText[i] = (char)8;
- }
- send_string(backspaceText);
- dtostrf(calc(text), CALC_PRINT_SIZE, CALC_PRINT_SIZE, text);
- send_string(text);
- for(unsigned char i = 0; i < CALC_BUFFER_SIZE; i++)
- {
- text[i] = '\0';
- backspaceText[i] = '\0';
- }
- inputLocation = 0;
- return false;
- case -4:
- for(unsigned char i = 0; i < CALC_BUFFER_SIZE; i++)
- {
- text[i] = '\0';
- backspaceText[i] = '\0';
- }
- inputLocation = 0;
- layer_off(CALC_LAYER);
- return false;
- default:
- break;
- }
- char characterPressed = (char)action;
- if(inputLocation < CALC_BUFFER_SIZE)
- {
- text[inputLocation] = characterPressed;
- inputLocation++;
- char characterToSend[2];
- characterToSend[0] = characterPressed;
- characterToSend[1] = '\0';
- send_string(characterToSend);
- }
- return false;
- }
|