HelenOS sources
This source file includes following definitions.
- wnd_close
- wnd_kbd_event
- is_digit
- get_digit
- is_plus
- is_minus
- is_finish
- get_operator
- is_operator
- stack_push_value
- stack_push_operator
- stack_pop_value
- stack_pop_operator
- stack_cleanup
- compute
- get_priority
- evaluate
- display_update
- display_error
- calc_pb_clicked
- calc_clear_clicked
- calc_eval_clicked
- calc_button_create
- print_syntax
- main
#include <ctype.h>
#include <io/kbd_event.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <str.h>
#include <ui/entry.h>
#include <ui/fixed.h>
#include <ui/pbutton.h>
#include <ui/ui.h>
#include <ui/window.h>
#define NAME "calculator"
#define NULL_DISPLAY "0"
#define SYNTAX_ERROR_DISPLAY "Syntax error"
#define NUMERIC_ERROR_DISPLAY "Numerical error"
#define UNKNOWN_ERROR_DISPLAY "Unknown error"
#define EXPR_MAX_LEN 22
typedef enum {
STATE_INITIAL = 0,
STATE_FINISH,
STATE_ERROR,
STATE_DIGIT,
STATE_VALUE
} parser_state_t;
typedef enum {
ERROR_SYNTAX = 0,
ERROR_NUMERIC
} error_type_t;
typedef enum {
OPERATOR_NONE = 0,
OPERATOR_ADD,
OPERATOR_SUB,
OPERATOR_MUL,
OPERATOR_DIV
} operator_t;
typedef enum {
ITEM_VALUE = 0,
ITEM_OPERATOR
} stack_item_type_t;
typedef struct {
link_t link;
stack_item_type_t type;
union {
int64_t value;
operator_t operator;
} data;
} stack_item_t;
typedef struct {
ui_t *ui;
ui_pbutton_t *btn_eval;
ui_pbutton_t *btn_clear;
ui_pbutton_t *btn_add;
ui_pbutton_t *btn_sub;
ui_pbutton_t *btn_mul;
ui_pbutton_t *btn_div;
ui_pbutton_t *btn_0;
ui_pbutton_t *btn_1;
ui_pbutton_t *btn_2;
ui_pbutton_t *btn_3;
ui_pbutton_t *btn_4;
ui_pbutton_t *btn_5;
ui_pbutton_t *btn_6;
ui_pbutton_t *btn_7;
ui_pbutton_t *btn_8;
ui_pbutton_t *btn_9;
} calc_t;
static void calc_pb_clicked(ui_pbutton_t *, void *);
static void calc_eval_clicked(ui_pbutton_t *, void *);
static void calc_clear_clicked(ui_pbutton_t *, void *);
static ui_pbutton_cb_t calc_pbutton_cb = {
.clicked = calc_pb_clicked
};
static ui_pbutton_cb_t calc_clear_cb = {
.clicked = calc_clear_clicked
};
static ui_pbutton_cb_t calc_eval_cb = {
.clicked = calc_eval_clicked
};
static void wnd_close(ui_window_t *, void *);
static void wnd_kbd_event(ui_window_t *, void *, kbd_event_t *);
static ui_window_cb_t window_cb = {
.close = wnd_close,
.kbd = wnd_kbd_event
};
static void wnd_close(ui_window_t *window, void *arg)
{
calc_t *calc = (calc_t *) arg;
ui_quit(calc->ui);
}
static void wnd_kbd_event(ui_window_t *window, void *arg, kbd_event_t *event)
{
calc_t *calc = (calc_t *) arg;
switch (event->key) {
case KC_ENTER:
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_eval);
else
ui_pbutton_release(calc->btn_eval);
break;
case KC_BACKSPACE:
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_clear);
else
ui_pbutton_release(calc->btn_clear);
break;
case KC_MINUS:
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_sub);
else
ui_pbutton_release(calc->btn_sub);
break;
case KC_EQUALS:
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_add);
else
ui_pbutton_release(calc->btn_add);
break;
case KC_SLASH:
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_div);
else
ui_pbutton_release(calc->btn_div);
break;
case KC_0:
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_0);
else
ui_pbutton_release(calc->btn_0);
break;
case KC_1:
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_1);
else
ui_pbutton_release(calc->btn_1);
break;
case KC_2:
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_2);
else
ui_pbutton_release(calc->btn_2);
break;
case KC_3:
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_3);
else
ui_pbutton_release(calc->btn_3);
break;
case KC_4:
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_4);
else
ui_pbutton_release(calc->btn_4);
break;
case KC_5:
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_5);
else
ui_pbutton_release(calc->btn_5);
break;
case KC_6:
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_6);
else
ui_pbutton_release(calc->btn_6);
break;
case KC_7:
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_7);
else
ui_pbutton_release(calc->btn_7);
break;
case KC_8:
if ((event->mods & KM_SHIFT) != 0) {
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_mul);
else
ui_pbutton_release(calc->btn_mul);
} else {
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_8);
else
ui_pbutton_release(calc->btn_8);
}
break;
case KC_9:
if (event->type == KEY_PRESS)
ui_pbutton_press(calc->btn_9);
else
ui_pbutton_release(calc->btn_9);
break;
default:
break;
}
}
static char *expr = NULL;
static ui_entry_t *display;
static bool is_digit(char c)
{
return ((c >= '0') && (c <= '9'));
}
static int get_digit(char c)
{
assert(is_digit(c));
return (c - '0');
}
static bool is_plus(char c)
{
return (c == '+');
}
static bool is_minus(char c)
{
return (c == '-');
}
static bool is_finish(char c)
{
return (c == 0);
}
static operator_t get_operator(char c)
{
switch (c) {
case '+':
return OPERATOR_ADD;
case '-':
return OPERATOR_SUB;
case '*':
return OPERATOR_MUL;
case '/':
return OPERATOR_DIV;
default:
return OPERATOR_NONE;
}
}
static bool is_operator(char c)
{
return (get_operator(c) != OPERATOR_NONE);
}
static bool stack_push_value(list_t *stack, int64_t value, bool value_neg)
{
stack_item_t *item = malloc(sizeof(stack_item_t));
if (!item)
return false;
link_initialize(&item->link);
item->type = ITEM_VALUE;
if (value_neg)
item->data.value = -value;
else
item->data.value = value;
list_prepend(&item->link, stack);
return true;
}
static bool stack_push_operator(list_t *stack, operator_t operator)
{
stack_item_t *item = malloc(sizeof(stack_item_t));
if (!item)
return false;
link_initialize(&item->link);
item->type = ITEM_OPERATOR;
item->data.operator = operator;
list_prepend(&item->link, stack);
return true;
}
static bool stack_pop_value(list_t *stack, int64_t *value)
{
link_t *link = list_first(stack);
if (!link)
return false;
stack_item_t *item = list_get_instance(link, stack_item_t, link);
if (item->type != ITEM_VALUE)
return false;
*value = item->data.value;
list_remove(link);
free(item);
return true;
}
static bool stack_pop_operator(list_t *stack, operator_t *operator)
{
link_t *link = list_first(stack);
if (!link)
return false;
stack_item_t *item = list_get_instance(link, stack_item_t, link);
if (item->type != ITEM_OPERATOR)
return false;
*operator = item->data.operator;
list_remove(link);
free(item);
return true;
}
static void stack_cleanup(list_t *stack)
{
while (!list_empty(stack)) {
link_t *link = list_first(stack);
if (link) {
stack_item_t *item = list_get_instance(link, stack_item_t,
link);
list_remove(link);
free(item);
}
}
}
static bool compute(int64_t a, operator_t operator, int64_t b, int64_t *value)
{
switch (operator) {
case OPERATOR_ADD:
*value = a + b;
break;
case OPERATOR_SUB:
*value = a - b;
break;
case OPERATOR_MUL:
*value = a * b;
break;
case OPERATOR_DIV:
if (b == 0)
return false;
*value = a / b;
break;
default:
return false;
}
return true;
}
static unsigned int get_priority(operator_t operator)
{
switch (operator) {
case OPERATOR_ADD:
return 0;
case OPERATOR_SUB:
return 0;
case OPERATOR_MUL:
return 1;
case OPERATOR_DIV:
return 1;
default:
return 0;
}
}
static void evaluate(list_t *stack, int64_t *value, parser_state_t *state,
error_type_t *error_type)
{
while (!list_empty(stack)) {
if (!stack_pop_value(stack, value)) {
*state = STATE_ERROR;
*error_type = ERROR_SYNTAX;
break;
}
if (!list_empty(stack)) {
operator_t operator;
if (!stack_pop_operator(stack, &operator)) {
*state = STATE_ERROR;
*error_type = ERROR_SYNTAX;
break;
}
int64_t value_a;
if (!stack_pop_value(stack, &value_a)) {
*state = STATE_ERROR;
*error_type = ERROR_SYNTAX;
break;
}
if (!compute(value_a, operator, *value, value)) {
*state = STATE_ERROR;
*error_type = ERROR_NUMERIC;
break;
}
if (!stack_push_value(stack, *value, false)) {
*state = STATE_ERROR;
*error_type = ERROR_SYNTAX;
break;
}
}
}
}
static void display_update(void)
{
if (expr != NULL)
(void) ui_entry_set_text(display, (void *) expr);
else
(void) ui_entry_set_text(display, (void *) NULL_DISPLAY);
ui_entry_paint(display);
}
static void display_error(error_type_t error_type)
{
if (expr != NULL) {
free(expr);
expr = NULL;
}
switch (error_type) {
case ERROR_SYNTAX:
(void) ui_entry_set_text(display,
(void *) SYNTAX_ERROR_DISPLAY);
break;
case ERROR_NUMERIC:
(void) ui_entry_set_text(display,
(void *) NUMERIC_ERROR_DISPLAY);
break;
default:
(void) ui_entry_set_text(display,
(void *) UNKNOWN_ERROR_DISPLAY);
break;
}
ui_entry_paint(display);
}
static void calc_pb_clicked(ui_pbutton_t *pbutton, void *arg)
{
const char *subexpr = (const char *) arg;
if (expr != NULL) {
char *new_expr;
if (str_length(expr) < EXPR_MAX_LEN) {
asprintf(&new_expr, "%s%s", expr, subexpr);
free(expr);
expr = new_expr;
}
} else {
expr = str_dup(subexpr);
}
display_update();
}
static void calc_clear_clicked(ui_pbutton_t *pbutton, void *arg)
{
if (expr != NULL) {
free(expr);
expr = NULL;
}
display_update();
}
static void calc_eval_clicked(ui_pbutton_t *pbutton, void *arg)
{
if (expr == NULL)
return;
list_t stack;
list_initialize(&stack);
error_type_t error_type = ERROR_SYNTAX;
size_t i = 0;
parser_state_t state = STATE_INITIAL;
int64_t value = 0;
bool value_neg = false;
operator_t last_operator = OPERATOR_NONE;
while ((state != STATE_FINISH) && (state != STATE_ERROR)) {
switch (state) {
case STATE_INITIAL:
if (is_digit(expr[i])) {
value = get_digit(expr[i]);
i++;
state = STATE_VALUE;
} else if (is_plus(expr[i])) {
i++;
value_neg = false;
state = STATE_DIGIT;
} else if (is_minus(expr[i])) {
i++;
value_neg = true;
state = STATE_DIGIT;
} else
state = STATE_ERROR;
break;
case STATE_DIGIT:
if (is_digit(expr[i])) {
value = get_digit(expr[i]);
i++;
state = STATE_VALUE;
} else
state = STATE_ERROR;
break;
case STATE_VALUE:
if (is_digit(expr[i])) {
value *= 10;
value += get_digit(expr[i]);
i++;
} else if (is_operator(expr[i])) {
if (!stack_push_value(&stack, value, value_neg)) {
state = STATE_ERROR;
break;
}
value = 0;
value_neg = false;
operator_t operator = get_operator(expr[i]);
if (get_priority(operator) <= get_priority(last_operator)) {
evaluate(&stack, &value, &state, &error_type);
if (state == STATE_ERROR)
break;
if (!stack_push_value(&stack, value, value_neg)) {
state = STATE_ERROR;
break;
}
}
if (!stack_push_operator(&stack, operator)) {
state = STATE_ERROR;
break;
}
last_operator = operator;
i++;
state = STATE_DIGIT;
} else if (is_finish(expr[i])) {
if (!stack_push_value(&stack, value, value_neg)) {
state = STATE_ERROR;
break;
}
state = STATE_FINISH;
} else
state = STATE_ERROR;
break;
default:
state = STATE_ERROR;
}
}
evaluate(&stack, &value, &state, &error_type);
stack_cleanup(&stack);
if (state == STATE_ERROR) {
display_error(error_type);
return;
}
free(expr);
asprintf(&expr, "%" PRId64, value);
display_update();
}
static errno_t calc_button_create(ui_resource_t *ui_res, ui_fixed_t *fixed,
int x, int y, const char *text, ui_pbutton_cb_t *cb, void *arg,
ui_pbutton_t **rbutton)
{
ui_pbutton_t *pb;
gfx_rect_t rect;
errno_t rc;
rc = ui_pbutton_create(ui_res, text, &pb);
if (rc != EOK) {
printf("Error creating button.\n");
return rc;
}
ui_pbutton_set_cb(pb, cb, arg);
rect.p0.x = 10 + 60 * x;
rect.p0.y = 90 + 45 * y;
rect.p1.x = 60 + 60 * x;
rect.p1.y = 125 + 45 * y;
ui_pbutton_set_rect(pb, &rect);
rc = ui_fixed_add(fixed, ui_pbutton_ctl(pb));
if (rc != EOK) {
printf("Error adding control to layout.\n");
return rc;
}
if (rbutton != NULL)
*rbutton = pb;
return EOK;
}
static void print_syntax(void)
{
printf("Syntax: %s [-d <display-spec>]\n", NAME);
}
int main(int argc, char *argv[])
{
const char *display_spec = UI_DISPLAY_DEFAULT;
ui_t *ui;
ui_resource_t *ui_res;
ui_fixed_t *fixed;
ui_wnd_params_t params;
ui_window_t *window;
gfx_rect_t rect;
calc_t calc;
errno_t rc;
int i;
i = 1;
while (i < argc) {
if (str_cmp(argv[i], "-d") == 0) {
++i;
if (i >= argc) {
printf("Argument missing.\n");
print_syntax();
return 1;
}
display_spec = argv[i++];
} else {
printf("Invalid option '%s'.\n", argv[i]);
print_syntax();
return 1;
}
}
rc = ui_create(display_spec, &ui);
if (rc != EOK) {
printf("Error creating UI on display %s.\n", display_spec);
return rc;
}
ui_wnd_params_init(¶ms);
params.caption = "Calculator";
params.rect.p0.x = 0;
params.rect.p0.y = 0;
params.rect.p1.x = 250;
params.rect.p1.y = 270;
rc = ui_window_create(ui, ¶ms, &window);
if (rc != EOK) {
printf("Error creating window.\n");
return rc;
}
ui_window_set_cb(window, &window_cb, (void *) &calc);
calc.ui = ui;
ui_res = ui_window_get_res(window);
rc = ui_fixed_create(&fixed);
if (rc != EOK) {
printf("Error creating fixed layout.\n");
return rc;
}
rc = ui_entry_create(ui_res, NULL_DISPLAY, &display);
if (rc != EOK) {
printf("Error creating text lentry.\n");
return rc;
}
rect.p0.x = 15;
rect.p0.y = 45;
rect.p1.x = 235;
rect.p1.y = 70;
ui_entry_set_rect(display, &rect);
ui_entry_set_halign(display, gfx_halign_right);
rc = ui_fixed_add(fixed, ui_entry_ctl(display));
if (rc != EOK) {
printf("Error adding control to layout.\n");
return rc;
}
rc = calc_button_create(ui_res, fixed, 0, 0, "7", &calc_pbutton_cb,
(void *) "7", &calc.btn_7);
if (rc != EOK)
return rc;
rc = calc_button_create(ui_res, fixed, 1, 0, "8", &calc_pbutton_cb,
(void *) "8", &calc.btn_8);
if (rc != EOK)
return rc;
rc = calc_button_create(ui_res, fixed, 2, 0, "9", &calc_pbutton_cb,
(void *) "9", &calc.btn_9);
if (rc != EOK)
return rc;
rc = calc_button_create(ui_res, fixed, 3, 0, "/", &calc_pbutton_cb,
(void *) "/", &calc.btn_div);
if (rc != EOK)
return rc;
rc = calc_button_create(ui_res, fixed, 0, 1, "4", &calc_pbutton_cb,
(void *) "4", &calc.btn_4);
if (rc != EOK)
return rc;
rc = calc_button_create(ui_res, fixed, 1, 1, "5", &calc_pbutton_cb,
(void *) "5", &calc.btn_5);
if (rc != EOK)
return rc;
rc = calc_button_create(ui_res, fixed, 2, 1, "6", &calc_pbutton_cb,
(void *) "6", &calc.btn_6);
if (rc != EOK)
return rc;
rc = calc_button_create(ui_res, fixed, 3, 1, "*", &calc_pbutton_cb,
(void *) "*", &calc.btn_mul);
if (rc != EOK)
return rc;
rc = calc_button_create(ui_res, fixed, 0, 2, "1", &calc_pbutton_cb,
(void *) "1", &calc.btn_1);
if (rc != EOK)
return rc;
rc = calc_button_create(ui_res, fixed, 1, 2, "2", &calc_pbutton_cb,
(void *) "2", &calc.btn_2);
if (rc != EOK)
return rc;
rc = calc_button_create(ui_res, fixed, 2, 2, "3", &calc_pbutton_cb,
(void *) "3", &calc.btn_3);
if (rc != EOK)
return rc;
rc = calc_button_create(ui_res, fixed, 3, 2, "-", &calc_pbutton_cb,
(void *) "-", &calc.btn_sub);
if (rc != EOK)
return rc;
rc = calc_button_create(ui_res, fixed, 0, 3, "0", &calc_pbutton_cb,
(void *) "0", &calc.btn_0);
if (rc != EOK)
return rc;
rc = calc_button_create(ui_res, fixed, 1, 3, "C", &calc_clear_cb,
(void *) "C", &calc.btn_clear);
if (rc != EOK)
return rc;
rc = calc_button_create(ui_res, fixed, 2, 3, "=", &calc_eval_cb,
(void *) "=", &calc.btn_eval);
if (rc != EOK)
return rc;
rc = calc_button_create(ui_res, fixed, 3, 3, "+", &calc_pbutton_cb,
(void *) "+", &calc.btn_add);
if (rc != EOK)
return rc;
ui_pbutton_set_default(calc.btn_eval, true);
ui_window_add(window, ui_fixed_ctl(fixed));
rc = ui_window_paint(window);
if (rc != EOK) {
printf("Error painting window.\n");
return rc;
}
ui_run(ui);
ui_window_destroy(window);
ui_destroy(ui);
return 0;
}
HelenOS homepage, sources at GitHub