// Integer RPN calculator as a demo of code pointers. Not super // complete, just what I could hack together in an hour. It runs // about 7000 instructions per input keystroke, probably mostly in // output formatting. #include #include #include #include // Set up Unix terminal to respond immediately to keystrokes instead // of waiting for Enter. void do_cbreak(struct termios *ios) { if (0 != ioctl(0, TCGETS, ios)) { // probably ENOTTY, ignoring is good enough for a quick hack return; } struct termios new = *ios; new.c_lflag &= ~ICANON; ioctl(0, TCSETS, &new); } void undo_cbreak(struct termios *ios) { ioctl(0, TCSETS, ios); } // Basic structure of the calculator. enum { stacksize = 10 }; typedef struct { long stack[stacksize]; // top of stack is at stack[0] enum { entering, entered } mode; struct termios ios; int last_key; } ui_state; void push(ui_state *state, long val) { for (int i = stacksize; i > 1; i--) state->stack[i-1] = state->stack[i-2]; state->stack[0] = val; } long pop(ui_state *state) { long val = state->stack[0]; for (int i = 0; i < stacksize - 1; i++) state->stack[i] = state->stack[i+1]; return val; } void redisplay(ui_state *state) { printf(" \r"); // spaces to erase junk if any for (int i = stacksize - 1; i >= 0; i--) { long val = state->stack[i]; if (val == 53) { printf("suku☺ "); } else if (val == 3) { printf("★trule♥ "); } else { printf("%ld ", val); } } printf(" \x8\x8\x8\x8\x8\x8\x8\x8"); fflush(stdout); } // Each keybinding is a function pointer and a character. typedef struct { void (*command)(ui_state *); int key; } keybinding; // Here are the commands: void quit_command(ui_state *state) { printf("\n"); undo_cbreak(&state->ios); exit(0); } void digit_command(ui_state *state) { if (entered == state->mode) { push(state, 0); state->mode = entering; } state->stack[0] *= 10; state->stack[0] += state->last_key - '0'; } void enter_command(ui_state *state) { if (entered == state->mode) { push(state, state->stack[0]); } else { state->mode = entered; } } void divide_command(ui_state *state) { long divisor = pop(state); if (!divisor) return; state->mode = entered; push(state, pop(state) / divisor); } void add_command(ui_state *state) { state->mode = entered; push(state, pop(state) + pop(state)); } void bueno_comand(ui_state *state) { push(state, 53); } void subtract_command(ui_state *state) { long subtrahend = pop(state); state->mode = entered; push(state, pop(state) - subtrahend); } void multiply_command(ui_state *state) { state->mode = entered; push(state, pop(state) * pop(state)); } void backspace_command(ui_state *state) { if (entered == state->mode || 0 == state->stack[0]) { pop(state); state->mode = entered; } else { state->stack[0] /= 10; } } void swap_command(ui_state *state) { long b = pop(state); long a = pop(state); push(state, b); push(state, a); state->mode = entered; } void sign_command(ui_state *state) { state->stack[0] *= -1; state->mode = entered; } // Then we stick them all into an array. keybinding bindings[] = { {add_command, '+'}, {subtract_command, '-'}, {multiply_command, '*'}, {divide_command, '/'}, {bueno_comand, 'f'}, {swap_command, '\t'}, {sign_command, '_'}, {quit_command, 'q'}, {quit_command, EOF}, // probably can’t happen on a tty {digit_command, '0'}, {digit_command, '1'}, {digit_command, '2'}, {digit_command, '3'}, {digit_command, '4'}, {digit_command, '5'}, {digit_command, '6'}, {digit_command, '7'}, {digit_command, '8'}, {digit_command, '9'}, {enter_command, '\r'}, {enter_command, '\n'}, {enter_command, ' '}, {backspace_command, '\177'}, {backspace_command, '\x8'}, }; int main() { ui_state state = {.mode = entered}; do_cbreak(&state.ios); for (;;) { redisplay(&state); state.last_key = getchar(); for (int i = 0; i < sizeof(bindings)/sizeof(bindings[0]); i++) { if (bindings[i].key == state.last_key) { bindings[i].command(&state); goto ok; } } printf(" ¿%d?", state.last_key); ok:; } }