parser_utils.c -- factory de ciertos parsers típicos

Provee parsers reusables, como por ejemplo para verificar que
un string es igual a otro de forma case insensitive.
This commit is contained in:
Juan F. Codagnone 2017-10-22 19:32:41 -03:00 committed by Santiago Lo Coco
parent cfb8f2de05
commit aa1c136ad9
4 changed files with 266 additions and 0 deletions

20
src/foo.dot Normal file
View File

@ -0,0 +1,20 @@
digraph g {
rankdir=LR;
size= "8.27,11.69";
node [shape = circle];
S0 [label = "0", shape = doublecircle];
S1 [label = "1"];
S2 [label = "2"];
EQ [label = "EQ", shape = doublecircle];
NEQ [label = "NEQ", shape = doublecircle];
S0 -> S1 [label= "'f', 'F'\neq(c)"];
S0 -> NEQ [label="ANY\nneq(c)"];
S1 -> S2 [label= "'o', 'O'\neq(c)"];
S1 -> NEQ [label="ANY\nneq(c)"];
S2 -> EQ [label= "'o', 'O\neq(c)'"];
S2 -> NEQ [label="ANY\nneq(c)"];
EQ -> NEQ [label= "ANY\nneq(c)"];
}

144
src/parser_utils.c Normal file
View File

@ -0,0 +1,144 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "parser_utils.h"
const char *
parser_utils_strcmpi_event(const enum string_cmp_event_types type) {
const char *ret;
switch(type) {
case STRING_CMP_MAYEQ:
ret = "wait(c)";
break;
case STRING_CMP_EQ:
ret = "eq(c)";
break;
case STRING_CMP_NEQ:
ret = "neq(c)";
break;
}
return ret;
}
static void
may_eq(struct parser_event *ret, const uint8_t c) {
ret->type = STRING_CMP_MAYEQ;
ret->n = 1;
ret->data[0] = c;
}
static void
eq(struct parser_event *ret, const uint8_t c) {
ret->type = STRING_CMP_EQ;
ret->n = 1;
ret->data[0] = c;
}
static void
neq(struct parser_event *ret, const uint8_t c) {
ret->type = STRING_CMP_NEQ;
ret->n = 1;
ret->data[0] = c;
}
/*
* para comparar "foo" (length 3) necesitamos 3 + 2 estados.
* Los útimos dos, son el sumidero de comparación fallida, y
* el estado donde se llegó a la comparación completa.
*
* static const struct parser_state_transition ST_0 [] = {
* {.when = 'F', .dest = 1, .action1 = may_eq, },
* {.when = 'f', .dest = 1, .action1 = may_eq, },
* {.when = ANY, .dest = NEQ, .action1 = neq,},
* };
* static const struct parser_state_transition ST_1 [] = {
* {.when = 'O', .dest = 2, .action1 = may_eq, },
* {.when = 'o', .dest = 2, .action1 = may_eq, },
* {.when = ANY, .dest = NEQ, .action1 = neq,},
* };
* static const struct parser_state_transition ST_2 [] = {
* {.when = 'O', .dest = EQ, .action1 = eq, },
* {.when = 'o', .dest = EQ, .action1 = eq, },
* {.when = ANY, .dest = NEQ, .action1 = neq,},
* };
* static const struct parser_state_transition ST_EQ (3) [] = {
* {.when = ANY, .dest = NEQ, .action1 = neq,},
* };
* static const struct parser_state_transition ST_NEQ (4) [] = {
* {.when = ANY, .dest = NEQ, .action1 = neq,},
* };
*
*/
struct parser_definition
parser_utils_strcmpi(const char *s) {
const size_t n = strlen(s);
struct parser_state_transition **states = calloc(n + 2, sizeof(*states));
size_t *nstates = calloc(n + 2, sizeof(*nstates));
struct parser_state_transition *transitions= calloc(3 *(n + 2),
sizeof(*transitions));
if(states == NULL || nstates == NULL || transitions == NULL) {
free(states);
free(nstates);
free(transitions);
struct parser_definition def = {
.start_state = 0,
.states_count = 0,
.states = NULL,
.states_n = NULL,
};
return def;
}
// estados fijos
const size_t st_eq = n;
const size_t st_neq = n + 1;
for(size_t i = 0; i < n; i++) {
const size_t dest = (i + 1 == n) ? st_eq : i + 1;
transitions[i * 3 + 0].when = tolower(s[i]);
transitions[i * 3 + 0].dest = dest;
transitions[i * 3 + 0].act1 = i + 1 == n ? eq : may_eq;
transitions[i * 3 + 1].when = toupper(s[i]);
transitions[i * 3 + 1].dest = dest;
transitions[i * 3 + 1].act1 = i + 1 == n ? eq : may_eq;
transitions[i * 3 + 2].when = ANY;
transitions[i * 3 + 2].dest = st_neq;
transitions[i * 3 + 2].act1 = neq;
states [i] = transitions + (i * 3 + 0);
nstates [i] = 3;
}
// EQ
transitions[(n + 0) * 3].when = ANY;
transitions[(n + 0) * 3].dest = st_neq;
transitions[(n + 0) * 3].act1 = neq;
states [(n + 0)] = transitions + ((n + 0) * 3 + 0);
nstates [(n + 0)] = 1;
// NEQ
transitions[(n + 1) * 3].when = ANY;
transitions[(n + 1) * 3].dest = st_neq;
transitions[(n + 1) * 3].act1 = neq;
states [(n + 1)] = transitions + ((n + 1) * 3 + 0);
nstates [(n + 1)] = 1;
struct parser_definition def = {
.start_state = 0,
.states_count = n + 2,
.states = (const struct parser_state_transition **) states,
.states_n = (const size_t *) nstates,
};
return def;
}
void
parser_utils_strcmpi_destroy(const struct parser_definition *p) {
free((void *)p->states[0]);
free((void *)p->states);
free((void *)p->states_n);
}

39
src/parser_utils.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef PARSER_UTILS_H_c2f29bb6482d34fc6f94a09046bbd65a5f668acf
#define PARSER_UTILS_H_c2f29bb6482d34fc6f94a09046bbd65a5f668acf
/*
* parser_utils.c -- factory de ciertos parsers típicos
*
* Provee parsers reusables, como por ejemplo para verificar que
* un string es igual a otro de forma case insensitive.
*/
#include "parser.h"
enum string_cmp_event_types {
STRING_CMP_MAYEQ,
/** hay posibilidades de que el string sea igual */
STRING_CMP_EQ,
/** NO hay posibilidades de que el string sea igual */
STRING_CMP_NEQ,
};
const char *
parser_utils_strcmpi_event(const enum string_cmp_event_types type);
/*
* Crea un parser que verifica que los caracteres recibidos forment el texto
* descripto por `s'.
*
* Si se recibe el evento `STRING_CMP_NEQ' el texto entrado no matchea.
*/
struct parser_definition
parser_utils_strcmpi(const char *s);
/**
* libera recursos asociado a una llamada de `parser_utils_strcmpi'
*/
void
parser_utils_strcmpi_destroy(const struct parser_definition *p);
#endif

63
src/parser_utils_test.c Normal file
View File

@ -0,0 +1,63 @@
#include <stdio.h>
#include <stdlib.h>
#include <check.h>
#include "parser_utils.h"
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_eq) {
const struct parser_definition d = parser_utils_strcmpi("foo");
struct parser *parser = parser_init(parser_no_classes(), &d);
assert_eq(STRING_CMP_MAYEQ, 'f', parser_feed(parser, 'f'));
assert_eq(STRING_CMP_MAYEQ, 'O', parser_feed(parser, 'O'));
assert_eq(STRING_CMP_EQ, 'o', parser_feed(parser, 'o'));
assert_eq(STRING_CMP_NEQ, 'X', parser_feed(parser, 'X'));
assert_eq(STRING_CMP_NEQ, 'y', parser_feed(parser, 'y'));
parser_destroy(parser);
parser_utils_strcmpi_destroy(&d);
}
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_eq);
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;
}