source: mainline/uspace/app/vcalc/vcalc.c@ 103db908

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 103db908 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 13.3 KB
Line 
1/*
2 * Copyright (c) 2016 Martin Decky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup vcalc
30 * @{
31 */
32/** @file
33 *
34 * Inspired by the code released at https://github.com/osgroup/HelenOSProject
35 *
36 */
37
38#include <stdbool.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <io/pixel.h>
42#include <task.h>
43#include <window.h>
44#include <grid.h>
45#include <button.h>
46#include <label.h>
47#include <ctype.h>
48#include <stdlib.h>
49#include <str.h>
50
51#define NAME "vcalc"
52
53#define NULL_DISPLAY "."
54
55#define SYNTAX_ERROR_DISPLAY "syntax error"
56#define NUMERIC_ERROR_DISPLAY "numerical error"
57#define UNKNOWN_ERROR_DISPLAY "unknown error"
58
59typedef enum {
60 STATE_INITIAL = 0,
61 STATE_FINISH,
62 STATE_ERROR,
63 STATE_DIGIT,
64 STATE_VALUE
65} parser_state_t;
66
67typedef enum {
68 ERROR_SYNTAX = 0,
69 ERROR_NUMERIC
70} error_type_t;
71
72typedef enum {
73 OPERATOR_NONE = 0,
74 OPERATOR_ADD,
75 OPERATOR_SUB,
76 OPERATOR_MUL,
77 OPERATOR_DIV
78} operator_t;
79
80typedef enum {
81 ITEM_VALUE = 0,
82 ITEM_OPERATOR
83} stack_item_type_t;
84
85typedef struct {
86 link_t link;
87
88 stack_item_type_t type;
89 union {
90 int64_t value;
91 operator_t operator;
92 } data;
93} stack_item_t;
94
95static char *expr = NULL;
96static label_t *display;
97
98static bool is_digit(char c)
99{
100 return ((c >= '0') && (c <= '9'));
101}
102
103static int get_digit(char c)
104{
105 assert(is_digit(c));
106
107 return (c - '0');
108}
109
110static bool is_plus(char c)
111{
112 return (c == '+');
113}
114
115static bool is_minus(char c)
116{
117 return (c == '-');
118}
119
120static bool is_finish(char c)
121{
122 return (c == 0);
123}
124
125static operator_t get_operator(char c)
126{
127 switch (c) {
128 case '+':
129 return OPERATOR_ADD;
130 case '-':
131 return OPERATOR_SUB;
132 case '*':
133 return OPERATOR_MUL;
134 case '/':
135 return OPERATOR_DIV;
136 default:
137 return OPERATOR_NONE;
138 }
139}
140
141static bool is_operator(char c)
142{
143 return (get_operator(c) != OPERATOR_NONE);
144}
145
146static bool stack_push_value(list_t *stack, int64_t value, bool value_neg)
147{
148 stack_item_t *item = malloc(sizeof(stack_item_t));
149 if (!item)
150 return false;
151
152 link_initialize(&item->link);
153 item->type = ITEM_VALUE;
154
155 if (value_neg)
156 item->data.value = -value;
157 else
158 item->data.value = value;
159
160 list_prepend(&item->link, stack);
161
162 return true;
163}
164
165static bool stack_push_operator(list_t *stack, operator_t operator)
166{
167 stack_item_t *item = malloc(sizeof(stack_item_t));
168 if (!item)
169 return false;
170
171 link_initialize(&item->link);
172 item->type = ITEM_OPERATOR;
173 item->data.operator = operator;
174 list_prepend(&item->link, stack);
175
176 return true;
177}
178
179static bool stack_pop_value(list_t *stack, int64_t *value)
180{
181 link_t *link = list_first(stack);
182 if (!link)
183 return false;
184
185 stack_item_t *item = list_get_instance(link, stack_item_t, link);
186 if (item->type != ITEM_VALUE)
187 return false;
188
189 *value = item->data.value;
190
191 list_remove(link);
192 free(item);
193
194 return true;
195}
196
197static bool stack_pop_operator(list_t *stack, operator_t *operator)
198{
199 link_t *link = list_first(stack);
200 if (!link)
201 return false;
202
203 stack_item_t *item = list_get_instance(link, stack_item_t, link);
204 if (item->type != ITEM_OPERATOR)
205 return false;
206
207 *operator = item->data.operator;
208
209 list_remove(link);
210 free(item);
211
212 return true;
213}
214
215static void stack_cleanup(list_t *stack)
216{
217 while (!list_empty(stack)) {
218 link_t *link = list_first(stack);
219 if (link) {
220 stack_item_t *item = list_get_instance(link, stack_item_t,
221 link);
222
223 list_remove(link);
224 free(item);
225 }
226 }
227}
228
229static bool compute(int64_t a, operator_t operator, int64_t b, int64_t *value)
230{
231 switch (operator) {
232 case OPERATOR_ADD:
233 *value = a + b;
234 break;
235 case OPERATOR_SUB:
236 *value = a - b;
237 break;
238 case OPERATOR_MUL:
239 *value = a * b;
240 break;
241 case OPERATOR_DIV:
242 if (b == 0)
243 return false;
244
245 *value = a / b;
246 break;
247 default:
248 return false;
249 }
250
251 return true;
252}
253
254static unsigned int get_priority(operator_t operator)
255{
256 switch (operator) {
257 case OPERATOR_ADD:
258 return 0;
259 case OPERATOR_SUB:
260 return 0;
261 case OPERATOR_MUL:
262 return 1;
263 case OPERATOR_DIV:
264 return 1;
265 default:
266 return 0;
267 }
268}
269
270static void evaluate(list_t *stack, int64_t *value, parser_state_t *state,
271 error_type_t *error_type)
272{
273 while (!list_empty(stack)) {
274 if (!stack_pop_value(stack, value)) {
275 *state = STATE_ERROR;
276 *error_type = ERROR_SYNTAX;
277 break;
278 }
279
280 if (!list_empty(stack)) {
281 operator_t operator;
282 if (!stack_pop_operator(stack, &operator)) {
283 *state = STATE_ERROR;
284 *error_type = ERROR_SYNTAX;
285 break;
286 }
287
288 int64_t value_a;
289 if (!stack_pop_value(stack, &value_a)) {
290 *state = STATE_ERROR;
291 *error_type = ERROR_SYNTAX;
292 break;
293 }
294
295 if (!compute(value_a, operator, *value, value)) {
296 *state = STATE_ERROR;
297 *error_type = ERROR_NUMERIC;
298 break;
299 }
300
301 if (!stack_push_value(stack, *value, false)) {
302 *state = STATE_ERROR;
303 *error_type = ERROR_SYNTAX;
304 break;
305 }
306 }
307 }
308}
309
310static void display_update(void)
311{
312 if (expr != NULL)
313 display->rewrite(&display->widget, (void *) expr);
314 else
315 display->rewrite(&display->widget, (void *) NULL_DISPLAY);
316}
317
318static void display_error(error_type_t error_type)
319{
320 if (expr != NULL) {
321 free(expr);
322 expr = NULL;
323 }
324
325 switch (error_type) {
326 case ERROR_SYNTAX:
327 display->rewrite(&display->widget, (void *) SYNTAX_ERROR_DISPLAY);
328 break;
329 case ERROR_NUMERIC:
330 display->rewrite(&display->widget, (void *) NUMERIC_ERROR_DISPLAY);
331 break;
332 default:
333 display->rewrite(&display->widget, (void *) UNKNOWN_ERROR_DISPLAY);
334 }
335}
336
337static void on_btn_click(widget_t *widget, void *data)
338{
339 const char *subexpr = (const char *) widget_get_data(widget);
340
341 if (expr != NULL) {
342 char *new_expr;
343
344 asprintf(&new_expr, "%s%s", expr, subexpr);
345 free(expr);
346 expr = new_expr;
347 } else
348 expr = str_dup(subexpr);
349
350 display_update();
351}
352
353static void on_c_click(widget_t *widget, void *data)
354{
355 if (expr != NULL) {
356 free(expr);
357 expr = NULL;
358 }
359
360 display_update();
361}
362
363static void on_eval_click(widget_t *widget, void *data)
364{
365 if (expr == NULL)
366 return;
367
368 list_t stack;
369 list_initialize(&stack);
370
371 error_type_t error_type = ERROR_SYNTAX;
372 size_t i = 0;
373 parser_state_t state = STATE_INITIAL;
374 int64_t value = 0;
375 bool value_neg = false;
376 operator_t last_operator = OPERATOR_NONE;
377
378 while ((state != STATE_FINISH) && (state != STATE_ERROR)) {
379 switch (state) {
380 case STATE_INITIAL:
381 if (is_digit(expr[i])) {
382 value = get_digit(expr[i]);
383 i++;
384 state = STATE_VALUE;
385 } else if (is_plus(expr[i])) {
386 i++;
387 value_neg = false;
388 state = STATE_DIGIT;
389 } else if (is_minus(expr[i])) {
390 i++;
391 value_neg = true;
392 state = STATE_DIGIT;
393 } else
394 state = STATE_ERROR;
395 break;
396
397 case STATE_DIGIT:
398 if (is_digit(expr[i])) {
399 value = get_digit(expr[i]);
400 i++;
401 state = STATE_VALUE;
402 } else
403 state = STATE_ERROR;
404 break;
405
406 case STATE_VALUE:
407 if (is_digit(expr[i])) {
408 value *= 10;
409 value += get_digit(expr[i]);
410 i++;
411 } else if (is_operator(expr[i])) {
412 if (!stack_push_value(&stack, value, value_neg)) {
413 state = STATE_ERROR;
414 break;
415 }
416
417 value = 0;
418 value_neg = false;
419
420 operator_t operator = get_operator(expr[i]);
421
422 if (get_priority(operator) <= get_priority(last_operator)) {
423 evaluate(&stack, &value, &state, &error_type);
424 if (state == STATE_ERROR)
425 break;
426
427 if (!stack_push_value(&stack, value, value_neg)) {
428 state = STATE_ERROR;
429 break;
430 }
431 }
432
433 if (!stack_push_operator(&stack, operator)) {
434 state = STATE_ERROR;
435 break;
436 }
437
438 last_operator = operator;
439 i++;
440 state = STATE_DIGIT;
441 } else if (is_finish(expr[i])) {
442 if (!stack_push_value(&stack, value, value_neg)) {
443 state = STATE_ERROR;
444 break;
445 }
446
447 state = STATE_FINISH;
448 } else
449 state = STATE_ERROR;
450 break;
451
452 default:
453 state = STATE_ERROR;
454 }
455 }
456
457 evaluate(&stack, &value, &state, &error_type);
458 stack_cleanup(&stack);
459
460 if (state == STATE_ERROR) {
461 display_error(error_type);
462 return;
463 }
464
465 free(expr);
466 asprintf(&expr, "%" PRId64, value);
467 display_update();
468}
469
470int main(int argc, char *argv[])
471{
472 if (argc < 2) {
473 printf("%s: Compositor server not specified.\n", NAME);
474 return 1;
475 }
476
477 window_t *main_window = window_open(argv[1], NULL,
478 WINDOW_MAIN | WINDOW_DECORATED | WINDOW_RESIZEABLE, NAME);
479 if (!main_window) {
480 printf("%s: Cannot open main window.\n", NAME);
481 return 2;
482 }
483
484 pixel_t grd_bg = PIXEL(255, 240, 240, 240);
485
486 pixel_t btn_bg = PIXEL(255, 0, 0, 0);
487 pixel_t btn_fg = PIXEL(200, 200, 200, 200);
488
489 pixel_t lbl_bg = PIXEL(255, 240, 240, 240);
490 pixel_t lbl_fg = PIXEL(255, 0, 0, 0);
491
492 grid_t *grid = create_grid(window_root(main_window), NULL, 4, 5, grd_bg);
493
494 display = create_label(NULL, NULL, NULL_DISPLAY, 16, lbl_bg, lbl_fg);
495
496 button_t *btn_1 = create_button(NULL, "1", "1", 16, btn_bg, btn_fg,
497 lbl_fg);
498 button_t *btn_2 = create_button(NULL, "2", "2", 16, btn_bg, btn_fg,
499 lbl_fg);
500 button_t *btn_3 = create_button(NULL, "3", "3", 16, btn_bg, btn_fg,
501 lbl_fg);
502 button_t *btn_4 = create_button(NULL, "4", "4", 16, btn_bg, btn_fg,
503 lbl_fg);
504 button_t *btn_5 = create_button(NULL, "5", "5", 16, btn_bg, btn_fg,
505 lbl_fg);
506 button_t *btn_6 = create_button(NULL, "6", "6", 16, btn_bg, btn_fg,
507 lbl_fg);
508 button_t *btn_7 = create_button(NULL, "7", "7", 16, btn_bg, btn_fg,
509 lbl_fg);
510 button_t *btn_8 = create_button(NULL, "8", "8", 16, btn_bg, btn_fg,
511 lbl_fg);
512 button_t *btn_9 = create_button(NULL, "9", "9", 16, btn_bg, btn_fg,
513 lbl_fg);
514 button_t *btn_0 = create_button(NULL, "0", "0", 16, btn_bg, btn_fg,
515 lbl_fg);
516
517 button_t *btn_add = create_button(NULL, "+", "+", 16, btn_bg, btn_fg,
518 lbl_fg);
519 button_t *btn_sub = create_button(NULL, "-", "-", 16, btn_bg, btn_fg,
520 lbl_fg);
521 button_t *btn_mul = create_button(NULL, "*", "*", 16, btn_bg, btn_fg,
522 lbl_fg);
523 button_t *btn_div = create_button(NULL, "/", "/", 16, btn_bg, btn_fg,
524 lbl_fg);
525
526 button_t *btn_eval = create_button(NULL, NULL, "=", 16, btn_bg, btn_fg,
527 lbl_fg);
528 button_t *btn_c = create_button(NULL, NULL, "C", 16, btn_bg, btn_fg,
529 lbl_fg);
530
531 if ((!grid) || (!display) || (!btn_1) || (!btn_2) || (!btn_3) ||
532 (!btn_4) || (!btn_5) || (!btn_6) || (!btn_7) || (!btn_8) ||
533 (!btn_9) || (!btn_0) || (!btn_add) || (!btn_sub) || (!btn_mul) ||
534 (!btn_div) || (!btn_eval) || (!btn_c)) {
535 window_close(main_window);
536 printf("%s: Cannot create widgets.\n", NAME);
537 return 3;
538 }
539
540 sig_connect(&btn_1->clicked, &btn_1->widget, on_btn_click);
541 sig_connect(&btn_2->clicked, &btn_2->widget, on_btn_click);
542 sig_connect(&btn_3->clicked, &btn_3->widget, on_btn_click);
543 sig_connect(&btn_4->clicked, &btn_4->widget, on_btn_click);
544 sig_connect(&btn_5->clicked, &btn_5->widget, on_btn_click);
545 sig_connect(&btn_6->clicked, &btn_6->widget, on_btn_click);
546 sig_connect(&btn_7->clicked, &btn_7->widget, on_btn_click);
547 sig_connect(&btn_8->clicked, &btn_8->widget, on_btn_click);
548 sig_connect(&btn_9->clicked, &btn_9->widget, on_btn_click);
549 sig_connect(&btn_0->clicked, &btn_0->widget, on_btn_click);
550
551 sig_connect(&btn_add->clicked, &btn_add->widget, on_btn_click);
552 sig_connect(&btn_sub->clicked, &btn_sub->widget, on_btn_click);
553 sig_connect(&btn_div->clicked, &btn_div->widget, on_btn_click);
554 sig_connect(&btn_mul->clicked, &btn_mul->widget, on_btn_click);
555
556 sig_connect(&btn_eval->clicked, &btn_eval->widget, on_eval_click);
557 sig_connect(&btn_c->clicked, &btn_c->widget, on_c_click);
558
559 grid->add(grid, &display->widget, 0, 0, 4, 1);
560
561 grid->add(grid, &btn_1->widget, 0, 1, 1, 1);
562 grid->add(grid, &btn_2->widget, 1, 1, 1, 1);
563 grid->add(grid, &btn_3->widget, 2, 1, 1, 1);
564 grid->add(grid, &btn_add->widget, 3, 1, 1, 1);
565
566 grid->add(grid, &btn_4->widget, 0, 2, 1, 1);
567 grid->add(grid, &btn_5->widget, 1, 2, 1, 1);
568 grid->add(grid, &btn_6->widget, 2, 2, 1, 1);
569 grid->add(grid, &btn_sub->widget, 3, 2, 1, 1);
570
571 grid->add(grid, &btn_7->widget, 0, 3, 1, 1);
572 grid->add(grid, &btn_8->widget, 1, 3, 1, 1);
573 grid->add(grid, &btn_9->widget, 2, 3, 1, 1);
574 grid->add(grid, &btn_mul->widget, 3, 3, 1, 1);
575
576 grid->add(grid, &btn_c->widget, 0, 4, 1, 1);
577 grid->add(grid, &btn_0->widget, 1, 4, 1, 1);
578 grid->add(grid, &btn_eval->widget, 2, 4, 1, 1);
579 grid->add(grid, &btn_div->widget, 3, 4, 1, 1);
580
581 window_resize(main_window, 0, 0, 400, 400, WINDOW_PLACEMENT_ANY);
582 window_exec(main_window);
583
584 task_retval(0);
585 async_manager();
586
587 return 0;
588}
Note: See TracBrowser for help on using the repository browser.