diff options
Diffstat (limited to 'src/ragel')
-rw-r--r-- | src/ragel/fspec.h | 77 | ||||
-rw-r--r-- | src/ragel/fspec.rl | 329 | ||||
-rw-r--r-- | src/ragel/ragel.h | 244 | ||||
-rw-r--r-- | src/ragel/ragel.rl | 88 |
4 files changed, 107 insertions, 631 deletions
diff --git a/src/ragel/fspec.h b/src/ragel/fspec.h deleted file mode 100644 index 68998f4..0000000 --- a/src/ragel/fspec.h +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include <stddef.h> -#include <stdint.h> -#include <stdbool.h> - -struct fspec_bytes { - const uint8_t *data; - size_t size; -}; - -enum fspec_kind_bits { - FSPEC_KIND_IGNORE = 1<<0, - FSPEC_KIND_HEXADECIMAL = 1<<1, - FSPEC_KIND_ENCODING = 1<<2, -}; - -struct fspec_kind { - const char *name; - uint32_t flags; -}; - -enum fspec_array_type { - FSPEC_ARRAY_FIXED, - FSPEC_ARRAY_MATCH, - FSPEC_ARRAY_VAR, -}; - -struct fspec_array { - enum fspec_array_type type; - - union { - struct fspec_bytes match; - const char *var; - size_t nmemb; - }; -}; - -enum fspec_type_bits { - FSPEC_TYPE_SIGNED = 1<<0, - FSPEC_TYPE_CONTAINER = 1<<1, -}; - -struct fspec_type { - const char *name; - size_t size; - uint32_t flags; -}; - -struct fspec_field { - struct fspec_type type; - struct fspec_array array; - struct fspec_kind kind; - const char *name; -}; - -struct fspec_container { - const char *name; -}; - -struct fspec; -struct fspec { - struct { - void (*field)(struct fspec *fspec, const struct fspec_container *container, const struct fspec_field *field); - size_t (*read)(struct fspec *fspec, char *buf, const size_t size, const size_t nmemb); - } ops; - - struct { - // XXX: replace with ops.alloc, ops.free - // on dump.c we can then just provide implementation that still uses reasonable amount of static memory - // but we don't limit the code from working with regular dynamic memory - uint8_t *data; - size_t size; - } mem; -}; - -void fspec_parse(struct fspec *fspec); diff --git a/src/ragel/fspec.rl b/src/ragel/fspec.rl deleted file mode 100644 index 8493cf1..0000000 --- a/src/ragel/fspec.rl +++ /dev/null @@ -1,329 +0,0 @@ -#include "fspec.h" -#include "ragel.h" - -// It's pretty good base so far. -// ragel_search_str for typechecking variable delcaration is hack. -// State should have hashmap for fields/containers. -// -// XXX: Maybe drop whole container thing and just give field const char *parent; that points to keypath of container. -// Then we would have flat structure like, "foo, foo.var, foo.b, ..." - -static const struct fspec_container default_container = {0}; -static const struct fspec_field default_field = { .array.nmemb = 1 }; - -enum stack_type { - STACK_VAR, - STACK_STR, - STACK_NUM, -}; - -struct stack { - enum stack_type type; - - union { - struct fspec_bytes str; - const char *var; - uint64_t num; - }; -}; - -struct state { - struct ragel ragel; - struct stack stack; - struct fspec_field field; - struct fspec_container container; - size_t container_data_offset; -}; - -static const char* -stack_type_to_str(const enum stack_type type) -{ - switch (type) { - case STACK_VAR: return "var"; - case STACK_STR: return "str"; - case STACK_NUM: return "num"; - }; - - assert(0 && "should not happen"); - return "unknown"; -} - -static void -stack_check_type(const struct ragel *ragel, const struct stack *stack, const enum stack_type type) -{ - assert(ragel && stack); - - if (stack->type != type) - ragel_throw_error(ragel, "tried to get '%s' from stack, but the last pushed type was '%s'", stack_type_to_str(type), stack_type_to_str(stack->type)); -} - -static const char* -stack_get_var(const struct ragel *ragel, const struct stack *stack) -{ - assert(ragel && stack); - stack_check_type(ragel, stack, STACK_VAR); - return stack->var; -} - -static const struct fspec_bytes* -stack_get_str(const struct ragel *ragel, const struct stack *stack) -{ - assert(ragel && stack); - stack_check_type(ragel, stack, STACK_STR); - return &stack->str; -} - -static uint64_t -stack_get_num(const struct ragel *ragel, const struct stack *stack) -{ - assert(ragel && stack); - stack_check_type(ragel, stack, STACK_NUM); - return stack->num; -} - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) - -static void -fspec_type_from_str(const struct ragel *ragel, const char *str, struct fspec_type *out_type) -{ - assert(ragel && str); - - const struct fspec_type types[] = { - { .name = "u8", .size = sizeof(uint8_t) }, - { .name = "u16", .size = sizeof(uint16_t) }, - { .name = "u32", .size = sizeof(uint32_t) }, - { .name = "u64", .size = sizeof(uint64_t) }, - { .name = "s8", .size = sizeof(int8_t), .flags = FSPEC_TYPE_SIGNED }, - { .name = "s16", .size = sizeof(int16_t), .flags = FSPEC_TYPE_SIGNED }, - { .name = "s32", .size = sizeof(int32_t), .flags = FSPEC_TYPE_SIGNED }, - { .name = "s64", .size = sizeof(int64_t), .flags = FSPEC_TYPE_SIGNED }, - }; - - for (size_t i = 0; i < ARRAY_SIZE(types); ++i) { - if (strcmp(str, types[i].name)) - continue; - - *out_type = types[i]; - return; - } - - if (ragel_search_str(ragel, 0, str)) { - *out_type = (struct fspec_type){ .name = str, .flags = FSPEC_TYPE_CONTAINER }; - return; - } - - ragel_throw_error(ragel, "invalid type"); -} - -static void -fspec_kind_from_str(const struct ragel *ragel, const char *str, struct fspec_kind *out_kind) -{ - assert(ragel && str); - - const struct fspec_kind kinds[] = { - { .name = "pad", .flags = FSPEC_KIND_IGNORE }, - { .name = "hex", .flags = FSPEC_KIND_HEXADECIMAL }, - { .name = "ascii", .flags = FSPEC_KIND_ENCODING }, - { .name = "utf8", .flags = FSPEC_KIND_ENCODING }, - { .name = "sjis", .flags = FSPEC_KIND_ENCODING }, - }; - - for (size_t i = 0; i < ARRAY_SIZE(kinds); ++i) { - if (strcmp(str, kinds[i].name)) - continue; - - *out_kind = kinds[i]; - return; - } - - ragel_throw_error(ragel, "invalid kind"); -} - -static void -check_field_kind(const struct ragel *ragel, const struct fspec_field *field) -{ - assert(ragel && field); - - if ((field->kind.flags & FSPEC_KIND_ENCODING) && field->type.size != sizeof(uint8_t)) - ragel_throw_error(ragel, "invalid kind: %s kind only allowed for u8 and s8 types", field->kind.name); -} - -%%{ - # File specification parser. - - machine fspec; - variable p state.ragel.p; - variable pe state.ragel.pe; - variable eof state.ragel.eof; - write data noerror nofinal; - - action field { - fspec->ops.field(fspec, &state.container, &state.field); - } - - action field_kind { - fspec_kind_from_str(&state.ragel, stack_get_var(&state.ragel, &state.stack), &state.field.kind); - check_field_kind(&state.ragel, &state.field); - } - - action field_array { - switch (state.stack.type) { - case STACK_NUM: - state.field.array.type = FSPEC_ARRAY_FIXED; - state.field.array.nmemb = stack_get_num(&state.ragel, &state.stack); - break; - - case STACK_STR: - state.field.array.type = FSPEC_ARRAY_MATCH; - state.field.array.match = *stack_get_str(&state.ragel, &state.stack); - break; - - case STACK_VAR: - state.field.array.type = FSPEC_ARRAY_VAR; - state.field.array.var = stack_get_var(&state.ragel, &state.stack); - - if (!ragel_search_str(&state.ragel, state.container_data_offset, state.field.array.var)) - ragel_throw_error(&state.ragel, "undeclared variable '%s'", state.field.array.var); - break; - - default: - ragel_throw_error(&state.ragel, "array can't contain the stack type of '%s'", stack_type_to_str(state.stack.type)); - break; - } - } - - action field_name { - state.field.name = stack_get_var(&state.ragel, &state.stack); - } - - action field_type { - state.field = default_field; - fspec_type_from_str(&state.ragel, stack_get_var(&state.ragel, &state.stack), &state.field.type); - } - - action container_name { - state.container = default_container; - state.container.name = stack_get_var(&state.ragel, &state.stack); - state.container_data_offset = state.ragel.mem.cur - state.ragel.mem.data; - } - - action push_var { - state.stack.type = STACK_VAR; - state.stack.var = (char*)state.ragel.mem.cur; - } - - action push_hex { - state.stack.type = STACK_NUM; - state.stack.num = strtoll((char*)state.ragel.mem.cur, NULL, 16); - } - - action push_dec { - state.stack.type = STACK_NUM; - state.stack.num = strtoll((char*)state.ragel.mem.cur, NULL, 10); - } - - action push_str { - state.stack.type = STACK_STR; - state.stack.str.data = state.ragel.mem.cur; - state.stack.str.size = (state.ragel.mem.data + state.ragel.mem.written) - state.ragel.mem.cur; - } - - action convert_escape { - ragel_convert_escape(&state.ragel); - } - - action remove { - ragel_remove_last_data(&state.ragel); - } - - action finish { - ragel_finish_data(&state.ragel); - } - - action store { - ragel_store_data(&state.ragel); - } - - action begin { - ragel_begin_data(&state.ragel); - } - - action invalid_kind { - ragel_throw_error(&state.ragel, "invalid kind"); - } - - action invalid_type { - ragel_throw_error(&state.ragel, "invalid type"); - } - - action error { - ragel_throw_error(&state.ragel, "malformed input (machine failed here or in previous or next expression)"); - } - - action line { - ragel_advance_line(&state.ragel); - } - - # Semantic - ws = space; - valid = ^cntrl; - es = '\\'; - delim = ';'; - quote = ['"]; - bopen = '{'; - bclose = '}'; - newline = '\n'; - octal = [0-7]; - hex = '0x' <: xdigit+; - decimal = ([1-9] <: digit*) | '0'; - comment = '//' <: valid* :>> newline; - escape = es <: ('x' <: xdigit+ | [abfnrtv\\'"e] | octal{1,3}); - type = 'u8' | 'u16' | 'u32' | 'u64' | 's8' | 's16' | 's32' | 's64'; - kind = 'ascii' | 'utf8' | 'sjis' | 'hex' | 'pad'; - reserved = 'struct' | type | kind; - var = ((alpha | '_') <: (alnum | '_')*) - reserved; - - # Catchers - catch_var = var >begin $store %finish %push_var; - catch_struct = ('struct' $store ws+ >store <: var $store) >begin %finish %push_var; - catch_type = (catch_struct | type >begin $store %push_var %remove) $!invalid_type; - catch_hex = hex >begin $store %push_hex %remove; - catch_decimal = decimal >begin $store %push_dec %remove; - catch_string = quote <: (escape %convert_escape | print)* >begin $store %finish %push_str :>> quote; - catch_array = '[' <: (catch_hex | catch_decimal | catch_string | catch_var) :>> ']'; - catch_kind = '=' ws* <: kind >begin $store %push_var %remove $!invalid_kind; - - # Actions - field = catch_type %field_type ws+ <: catch_var %field_name ws* <: (catch_array %field_array ws*)? <: (catch_kind %field_kind ws*)? :>> delim %field; - container = catch_struct %container_name ws* :>> bopen <: (ws | comment | field)* :>> bclose ws* delim; - line = valid* :>> newline @line; - main := (ws | comment | container)* & line* $!error; -}%% - -void -fspec_parse(struct fspec *fspec) -{ - int cs; - %% write init; - - (void)fspec_en_main; - assert(fspec); - assert(fspec->ops.read); - assert(fspec->ops.field); - - struct state state = { - .ragel = { - .lineno = 1, - .mem = { - .data = fspec->mem.data, - .size = fspec->mem.size, - }, - }, - }; - - for (bool ok = true; ok;) { - const size_t bytes = fspec->ops.read(fspec, state.ragel.buf, 1, sizeof(state.ragel.buf)); - ok = ragel_confirm_input(&state.ragel, bytes); - %% write exec; - } -} diff --git a/src/ragel/ragel.h b/src/ragel/ragel.h index af06f4a..b2c7572 100644 --- a/src/ragel/ragel.h +++ b/src/ragel/ragel.h @@ -1,236 +1,30 @@ #pragma once -#include <stdlib.h> -#include <stdio.h> -#include <stdarg.h> -#include <stddef.h> +#include <stdint.h> #include <stdbool.h> -#include <string.h> -#include <ctype.h> -#include <assert.h> -#include <limits.h> -#include <err.h> -struct ragel { - struct { - uint8_t *data; // data\0another_data\0 - const uint8_t *cur; // data\0another_data\0cursor - size_t written, size; // amount of data written / size of data - } mem; +struct ragel_mem { + const char *data, *end; + bool binary; // binary input bit +}; - char buf[4096]; // block of input data +struct ragel { + struct ragel_mem input; // block of input data + uint64_t lineno; // current line const char *p, *pe, *eof; // see ragel doc - size_t lineno; // current line + const char *cl; // current line start + const char *name; // may be current file name for example + bool error; // error thrown bit }; -static inline void -ragel_get_current_line(const struct ragel *ragel, size_t *out_lineno, size_t *out_ls, size_t *out_le, size_t *out_ws, size_t *out_we) -{ - assert(out_ls && out_le && out_ws && out_we); - assert(ragel->p >= ragel->buf && ragel->pe >= ragel->p); - - size_t ls, le, ws, we; - size_t off = ragel->p - ragel->buf; - size_t lineno = ragel->lineno; - const size_t end = ragel->pe - ragel->buf; - - // rewind to first non-space - for (; off > 0 && (isspace(ragel->buf[off]) || !ragel->buf[off]); --off) { - if (lineno > 0 && ragel->buf[off] == '\n') - --lineno; - } - - for (ls = off; ls > 0 && ragel->buf[ls] != '\n'; --ls); // beginning of line - for (le = off; le < end && ragel->buf[le] != '\n'; ++le); // end of line - for (; ls < le && isspace(ragel->buf[ls]); ++ls); // strip leading whitespace - for (ws = off; ws > ls && isspace(ragel->buf[ws]); --ws); // rewind to first non-space - for (; ws > 0 && ws > ls && !isspace(ragel->buf[ws - 1]); --ws); // find word start - for (we = ws; we < le && !isspace(ragel->buf[we]); ++we); // find word ending - - assert(we >= ws && ws >= ls && le >= ls && le >= we); - *out_lineno = lineno; - *out_ls = ls; - *out_le = le; - *out_ws = ws; - *out_we = we; -} - -__attribute__((format(printf, 2, 3))) -static inline void -ragel_throw_error(const struct ragel *ragel, const char *fmt, ...) -{ - assert(ragel && fmt); - - size_t lineno, ls, le, ws, we; - ragel_get_current_line(ragel, &lineno, &ls, &le, &ws, &we); - assert(le - ls <= INT_MAX && ws - ls <= INT_MAX); - - char msg[255]; - va_list args; - va_start(args, fmt); - vsnprintf(msg, sizeof(msg), fmt, args); - va_end(args); - - const int indent = 8; - const size_t mark = (we - ws ? we - ws : 1), cur = (ragel->p - ragel->buf) - ws; - warnx("\x1b[37m%zu: \x1b[31merror: \x1b[0m%s\n%*s%.*s", lineno, msg, indent, "", (int)(le - ls), ragel->buf + ls); - fprintf(stderr, "%*s%*s\x1b[31m", indent, "", (int)(ws - ls), ""); - for (size_t i = 0; i < mark; ++i) fputs((i == cur ? "^" : "~"), stderr); - fputs("\x1b[0m\n", stderr); - - exit(EXIT_FAILURE); -} - -static inline void -ragel_bounds_check_data(const struct ragel *ragel, const size_t nmemb) -{ - assert(ragel); - - if (ragel->mem.size < nmemb || ragel->mem.written >= ragel->mem.size - nmemb) - ragel_throw_error(ragel, "data storage limit exceeded: %zu bytes exceeds the maximum store size of %zu bytes", ragel->mem.written, ragel->mem.size); -} - -static inline void -ragel_replace_data(struct ragel *ragel, const size_t nmemb, char replacement) -{ - assert(ragel); - - if (ragel->mem.written < nmemb) - ragel_throw_error(ragel, "parse error: received escape conversion with mem.written of %zu, expected >= %zu", ragel->mem.written, nmemb); - - ragel->mem.data[(ragel->mem.written -= nmemb)] = replacement; - ragel->mem.data[++ragel->mem.written] = 0; -} - -static inline void -ragel_convert_escape(struct ragel *ragel) -{ - assert(ragel); - - if (ragel->mem.written < 2) - ragel_throw_error(ragel, "parse error: received escape conversion with mem.written of %zu, expected >= 2", ragel->mem.written); - - const struct { - const char *e; - const char v, b; - } map[] = { - { .e = "\\a", .v = '\a' }, - { .e = "\\b", .v = '\b' }, - { .e = "\\f", .v = '\f' }, - { .e = "\\n", .v = '\n' }, - { .e = "\\r", .v = '\r' }, - { .e = "\\t", .v = '\t' }, - { .e = "\\v", .v = '\v' }, - { .e = "\\\\", .v = '\\' }, - { .e = "\\'", .v = '\'' }, - { .e = "\\\"", .v = '"' }, - { .e = "\\e", .v = '\e' }, - { .e = "\\x", .b = 16 }, - { .e = "\\", .b = 8 }, - }; - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) - const char *cur = (char*)ragel->mem.cur; - const size_t cur_sz = strlen(cur); - for (size_t i = 0; i < ARRAY_SIZE(map); ++i) { - if (!strncmp(cur, map[i].e, strlen(map[i].e))) { - const char v = (!map[i].b ? map[i].v : strtol(cur + strlen(map[i].e), NULL, map[i].b)); - assert((map[i].b == 8 && cur_sz >= 2) || (map[i].b == 16 && cur_sz >= 2) || (map[i].b == 0 && cur_sz == 2)); - assert(map[i].b != 8 || isdigit(cur[1])); - ragel_replace_data(ragel, cur_sz, v); - return; - } - } -#undef ARRAY_SIZE - - ragel_throw_error(ragel, "parse error: received unknown escape conversion"); -} - -static inline void -ragel_dump_data(struct ragel *ragel, const size_t offset) -{ - const uint8_t *end = ragel->mem.data + ragel->mem.written; - for (const uint8_t *p = ragel->mem.data + offset; p && p < end; p = (uint8_t*)memchr(p, 0, end - p), p += !!p) - printf("%s\n", p); -} - -static inline const uint8_t* -ragel_search_data(const struct ragel *ragel, const size_t offset, const uint8_t *data, const size_t size) -{ - assert(ragel && data); - - const uint8_t *end = ragel->mem.data + ragel->mem.written; - for (const uint8_t *p = ragel->mem.data + offset; p && p < end && (size_t)(end - p) >= size; p = (uint8_t*)memchr(p, 0, end - p), p += !!p) { - if (!memcmp(data, p, size)) - return p; - } - - return NULL; -} - -static inline const uint8_t* -ragel_search_str(const struct ragel *ragel, const size_t offset, const char *str) -{ - return ragel_search_data(ragel, offset, (const uint8_t*)str, strlen(str) + 1); -} - -static inline void -ragel_remove_last_data(struct ragel *ragel) -{ - assert(ragel); - const uint8_t *end = ragel->mem.data + ragel->mem.written; - const size_t size = end - ragel->mem.cur + 1; - assert(ragel->mem.written >= size); - ragel->mem.written -= size; - ragel->mem.data[ragel->mem.written] = 0; -} - -static inline void -ragel_finish_data(struct ragel *ragel) -{ - assert(ragel); - - const uint8_t *end = ragel->mem.data + ragel->mem.written, *p; - if ((p = ragel_search_data(ragel, 0, ragel->mem.cur, end - ragel->mem.cur + 1))) { - ragel_remove_last_data(ragel); - ragel->mem.cur = p; - } -} - -static inline void -ragel_store_data(struct ragel *ragel) -{ - ragel_bounds_check_data(ragel, 1); - ragel->mem.data[ragel->mem.written++] = *ragel->p; - ragel->mem.data[ragel->mem.written] = 0; -} - -static inline void -ragel_begin_data(struct ragel *ragel) -{ - ragel_bounds_check_data(ragel, 1); - ragel->mem.written += (ragel->mem.written > 0); - ragel->mem.cur = ragel->mem.data + ragel->mem.written; -} - -static inline void -ragel_advance_line(struct ragel *ragel) -{ - assert(ragel); - ++ragel->lineno; -} +__attribute__((format(printf, 2, 3))) void +ragel_throw_error(struct ragel *ragel, const char *fmt, ...); -static inline bool -ragel_confirm_input(struct ragel *ragel, const size_t bytes) -{ - assert(ragel); +void +ragel_set_name(struct ragel *ragel, const char *name); - if (bytes > sizeof(ragel->buf)) - errx(EXIT_FAILURE, "%s: gave larger buffer than %zu", __func__, sizeof(ragel->buf)); +void +ragel_advance_line(struct ragel *ragel); - const bool in_eof = (bytes < sizeof(ragel->buf)); - ragel->p = ragel->buf; - ragel->pe = ragel->p + bytes; - ragel->eof = (in_eof ? ragel->pe : NULL); - return !in_eof; -} +void +ragel_feed_input(struct ragel *ragel, const bool eof, const struct ragel_mem *input); diff --git a/src/ragel/ragel.rl b/src/ragel/ragel.rl new file mode 100644 index 0000000..48c4229 --- /dev/null +++ b/src/ragel/ragel.rl @@ -0,0 +1,88 @@ +#include "ragel.h" +#include <inttypes.h> +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +%%{ + machine ragel; + write data noerror nofinal; + + action red { fputs("\x1b[31m", stderr); } + action reset { fputs("\x1b[0m", stderr); } + action end { fputs("\x1b[0m\n", stderr); } + action mark { fputc('^', stderr); } + action tail { fputc('~', stderr); } + action lead { fputc(' ', stderr); } + + word = alnum*; + token = ' ' | punct; + until_err = (any when { fpc != *error })*; + search_err := ((any | token %{ *error = fpc; }) when { fpc != ragel->p })*; + print_err := (until_err %red <: word %reset <: (any - '\n')*) ${ fputc(fc, stderr); } >lead %!end %/end; + print_mark := (until_err ${ fputc(' ', stderr); } %red %mark <: any word $tail) >lead %!end %/end; +}%% + +static void +ragel_exec_error(const struct ragel *ragel, const int start_cs, const char **error) +{ + (void)ragel_start; + assert(ragel && ragel->cl && error); + int cs = start_cs; + const char *p = ragel->cl, *pe = ragel->pe, *eof = ragel->eof; + %% write exec; +} + +void +ragel_throw_error(struct ragel *ragel, const char *fmt, ...) +{ + assert(ragel && fmt); + ragel->error = true; + + const char *error = ragel->p; + + if (!ragel->input.binary) + ragel_exec_error(ragel, ragel_en_search_err, &error); + + const char *name = (ragel->name ? ragel->name : ""); + uint64_t column = (error - ragel->cl); + fprintf(stderr, "\x1b[37m%s:%" PRIu64 ":%" PRIu64 " \x1b[31merror: \x1b[0m", name, ragel->lineno, column); + + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fputc('\n', stderr); + + if (!ragel->input.binary) { + ragel_exec_error(ragel, ragel_en_print_err, &error); + ragel_exec_error(ragel, ragel_en_print_mark, &error); + } +} + +void +ragel_set_name(struct ragel *ragel, const char *name) +{ + assert(ragel); + ragel->name = name; +} + +void +ragel_advance_line(struct ragel *ragel) +{ + assert(ragel); + ++ragel->lineno; + ragel->cl = ragel->p; +} + +void +ragel_feed_input(struct ragel *ragel, const bool eof, const struct ragel_mem *input) +{ + assert(ragel); + ragel->input = *input; + ragel->cl = ragel->p = ragel->input.data; + ragel->pe = ragel->input.end; + ragel->eof = (eof ? ragel->pe : NULL); +} |