diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 0000000..ac2e541 --- /dev/null +++ b/src/parser.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include + +#include "parser.h" + +/* CDT del parser */ +struct parser { + /** tipificación para cada caracter */ + const unsigned *classes; + /** definición de estados */ + const struct parser_definition *def; + + /* estado actual */ + unsigned state; + + /* evento que se retorna */ + struct parser_event e1; + /* evento que se retorna */ + struct parser_event e2; +}; + +void +parser_destroy(struct parser *p) { + if(p != NULL) { + free(p); + } +} + +struct parser * +parser_init(const unsigned *classes, + const struct parser_definition *def) { + struct parser *ret = malloc(sizeof(*ret)); + if(ret != NULL) { + memset(ret, 0, sizeof(*ret)); + ret->classes = classes; + ret->def = def; + ret->state = def->start_state; + } + return ret; +} + +void +parser_reset(struct parser *p) { + p->state = p->def->start_state; +} + +const struct parser_event * +parser_feed(struct parser *p, const uint8_t c) { + const unsigned type = p->classes[c]; + + p->e1.next = p->e2.next = 0; + + const struct parser_state_transition *state = p->def->states[p->state]; + const size_t n = p->def->states_n[p->state]; + bool matched = false; + + for(unsigned i = 0; i < n ; i++) { + const int when = state[i].when; + if (state[i].when <= 0xFF) { + matched = (c == when); + } else if(state[i].when == ANY) { + matched = true; + } else if(state[i].when > 0xFF) { + matched = (type & when); + } else { + matched = false; + } + + if(matched) { + state[i].act1(&p->e1, c); + if(state[i].act2 != NULL) { + p->e1.next = &p->e2; + state[i].act2(&p->e2, c); + } + p->state = state[i].dest; + break; + } + } + return &p->e1; +} + + +static const unsigned classes[0xFF] = {0x00}; + +const unsigned * +parser_no_classes(void) { + return classes; +} + diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 0000000..0a79804 --- /dev/null +++ b/src/parser.h @@ -0,0 +1,93 @@ +#ifndef PARSER_H_00180a6350a1fbe79f133adf0a96eb6685c242b6 +#define PARSER_H_00180a6350a1fbe79f133adf0a96eb6685c242b6 + +/** + * parser.c -- pequeño motor para parsers/lexers. + * + * El usuario describe estados y transiciones. + * Las transiciones contienen una condición, un estado destino y acciones. + * + * El usuario provee al parser con bytes y éste retona eventos que pueden + * servir para delimitar tokens o accionar directamente. + */ +#include +#include + +/** + * Evento que retorna el parser. + * Cada tipo de evento tendrá sus reglas en relación a data. + */ +struct parser_event { + /** tipo de evento */ + unsigned type; + /** caracteres asociados al evento */ + uint8_t data[3]; + /** cantidad de datos en el buffer `data' */ + uint8_t n; + + /** lista de eventos: si es diferente de null ocurrieron varios eventos */ + struct parser_event *next; +}; + +/** describe una transición entre estados */ +struct parser_state_transition { + /* condición: un caracter o una clase de caracter. Por ej: '\r' */ + int when; + /** descriptor del estado destino cuando se cumple la condición */ + unsigned dest; + /** acción 1 que se ejecuta cuando la condición es verdadera. requerida. */ + void (*act1)(struct parser_event *ret, const uint8_t c); + /** otra acción opcional */ + void (*act2)(struct parser_event *ret, const uint8_t c); +}; + +/** predicado para utilizar en `when' que retorna siempre true */ +static const unsigned ANY = 1 << 9; + +/** declaración completa de una máquina de estados */ +struct parser_definition { + /** cantidad de estados */ + const unsigned states_count; + /** por cada estado, sus transiciones */ + const struct parser_state_transition **states; + /** cantidad de estados por transición */ + const size_t *states_n; + + /** estado inicial */ + const unsigned start_state; +}; + +/** + * inicializa el parser. + * + * `classes`: caracterización de cada caracter (256 elementos) + */ +struct parser * +parser_init (const unsigned *classes, + const struct parser_definition *def); + +/** destruye el parser */ +void +parser_destroy (struct parser *p); + +/** permite resetear el parser al estado inicial */ +void +parser_reset (struct parser *p); + +/** + * el usuario alimenta el parser con un caracter, y el parser retorna un evento + * de parsing. Los eventos son reusado entre llamadas por lo que si se desea + * capturar los datos se debe clonar. + */ +const struct parser_event * +parser_feed (struct parser *p, const uint8_t c); + +/** + * En caso de la aplicacion no necesite clases caracteres, se + * provee dicho arreglo para ser usando en `parser_init' + */ +const unsigned * +parser_no_classes(void); + + +#endif diff --git a/src/parser_test.c b/src/parser_test.c new file mode 100644 index 0000000..c662c96 --- /dev/null +++ b/src/parser_test.c @@ -0,0 +1,115 @@ +#include +#include +#include + +#include "parser.h" + +// definición de maquina + +enum states { + S0, + S1 +}; + +enum event_type { + FOO, + BAR, +}; + +static void +foo(struct parser_event *ret, const uint8_t c) { + ret->type = FOO; + ret->n = 1; + ret->data[0] = c; +} + +static void +bar(struct parser_event *ret, const uint8_t c) { + ret->type = BAR; + ret->n = 1; + ret->data[0] = c; +} + +static const struct parser_state_transition ST_S0 [] = { + {.when = 'F', .dest = S0, .act1 = foo,}, + {.when = 'f', .dest = S0, .act1 = foo,}, + {.when = ANY, .dest = S1, .act1 = bar,}, +}; +static const struct parser_state_transition ST_S1 [] = { + {.when = 'F', .dest = S0, .act1 = foo,}, + {.when = 'f', .dest = S0, .act1 = foo,}, + {.when = ANY, .dest = S1, .act1 = bar,}, +}; + +static const struct parser_state_transition *states [] = { + ST_S0, + ST_S1, +}; + +#define N(x) (sizeof(x)/sizeof((x)[0])) + +static const size_t states_n [] = { + N(ST_S0), + N(ST_S1), +}; + +static struct parser_definition definition = { + .states_count = N(states), + .states = states, + .states_n = states_n, + .start_state = S0, +}; + +//// TEST + +static void +assert_eq(const unsigned type, const int c, const struct parser_event *e) { + ck_assert_ptr_eq (0, e->next); + ck_assert_uint_eq(1, e->n); + ck_assert_uint_eq(type, e->type); + ck_assert_uint_eq(c, e->data[0]); + +} + +START_TEST (test_basic) { + struct parser *parser = parser_init(parser_no_classes(), &definition); + assert_eq(FOO, 'f', parser_feed(parser, 'f')); + assert_eq(FOO, 'F', parser_feed(parser, 'F')); + assert_eq(BAR, 'B', parser_feed(parser, 'B')); + assert_eq(BAR, 'b', parser_feed(parser, 'b')); + + parser_destroy(parser); +} +END_TEST + +Suite * +suite(void) { + Suite *s; + TCase *tc; + + s = suite_create("parser_utils"); + + /* Core test case */ + tc = tcase_create("parser_utils"); + + tcase_add_test(tc, test_basic); + suite_add_tcase(s, tc); + + return s; +} + +int +main(void) { + int number_failed; + Suite *s; + SRunner *sr; + + s = suite(); + sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} +