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:
parent
cfb8f2de05
commit
aa1c136ad9
|
@ -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)"];
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue