diff options
author | Mattias Andrée <maandree@operamail.com> | 2014-11-09 20:48:31 +0100 |
---|---|---|
committer | Mattias Andrée <maandree@operamail.com> | 2014-11-09 20:48:31 +0100 |
commit | 6afd09f080f3f4501f04d3f9fbfc55768be3b675 (patch) | |
tree | 9c56068b45839cf3a80c05e45251e12876a93735 | |
parent | whitespace (diff) | |
download | mds-6afd09f080f3f4501f04d3f9fbfc55768be3b675.tar.gz mds-6afd09f080f3f4501f04d3f9fbfc55768be3b675.tar.bz2 mds-6afd09f080f3f4501f04d3f9fbfc55768be3b675.tar.xz |
working on tree parsing
Signed-off-by: Mattias Andrée <maandree@operamail.com>
Diffstat (limited to '')
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | src/mds-kbdc/make-tree.c | 164 | ||||
-rw-r--r-- | src/mds-kbdc/make-tree.h | 39 | ||||
-rw-r--r-- | src/mds-kbdc/mds-kbdc.c | 154 | ||||
-rw-r--r-- | src/mds-kbdc/parse-error.c | 160 | ||||
-rw-r--r-- | src/mds-kbdc/parse-error.h | 143 | ||||
-rw-r--r-- | src/mds-kbdc/raw-data.c | 2 | ||||
-rw-r--r-- | src/mds-kbdc/raw-data.h | 2 |
8 files changed, 523 insertions, 144 deletions
@@ -30,7 +30,8 @@ OBJ_mds-server_ = mds-server interception-condition client multicast \ OBJ_mds-registry_ = mds-registry util globals reexec registry signals \ slave -OBJ_mds-kbdc_ = mds-kbdc globals raw-data functions string tree +OBJ_mds-kbdc_ = mds-kbdc globals raw-data functions string tree \ + make-tree parse-error OBJ_mds-server = $(foreach O,$(OBJ_mds-server_),obj/mds-server/$(O).o) OBJ_mds-registry = $(foreach O,$(OBJ_mds-registry_),obj/mds-registry/$(O).o) diff --git a/src/mds-kbdc/make-tree.c b/src/mds-kbdc/make-tree.c new file mode 100644 index 0000000..80faf5f --- /dev/null +++ b/src/mds-kbdc/make-tree.c @@ -0,0 +1,164 @@ +/** + * mds — A micro-display server + * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include "make-tree.h" + +#include "raw-data.h" + +#include <libmdsserver/macros.h> + +#include <limits.h> +#include <stdlib.h> +#include <libgen.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> + + + +/** + * Parse a file into a syntex tree + * + * @param filename The filename of the file to parse + * @param result Output parameter for the root of the tree, `NULL` if -1 is returned + * @param errors `NULL`-terminated list of found error, `NULL` if no errors were found or if -1 is returned + * @return -1 if an error occursed that cannot be stored in `*errors`, zero otherwise + */ +int parse_to_tree(const char* restrict filename, mds_kbdc_tree_t** restrict result, + mds_kbdc_parse_error_t*** restrict errors) +{ + mds_kbdc_parse_error_t** old_errors = NULL; + char* pathname; + source_code_t source_code; + size_t errors_size = 0; + size_t errors_ptr = 0; + size_t line_i, line_n; + int saved_errno; + + *errors = NULL; + source_code_initialise(&source_code); + + /* Get a non-relative pathname for the file, relative filenames + * can be misleading as the program can have changed working + * directroy to be able to resolve filenames. */ + pathname = realpath(filename, NULL); + fail_if (pathname == NULL); + + /* Check that the file exists and can be read. */ + if (access(pathname, R_OK) < 0) + { + saved_errno = errno; + fail_if (xmalloc(*errors, 2, mds_kbdc_parse_error_t*)); + fail_if (xmalloc(**errors, 1, mds_kbdc_parse_error_t)); + (*errors)[1] = NULL; + + (**errors)->severity = MDS_KBDC_PARSE_ERROR_ERROR; + (**errors)->error_is_in_file = 0; + (**errors)->pathname = pathname, pathname = NULL; + (**errors)->line = 0; + (**errors)->start = 0; + (**errors)->end = 0; + (**errors)->code = NULL; + (**errors)->description = strdup(strerror(saved_errno)); + fail_if ((**errors)->description == NULL); + return 0; + } + + /* Read the file and simplify it a bit. */ + fail_if (read_source_lines(pathname, &source_code) < 0); + /* TODO '\t':s should be expanded into ' ':s. */ + + for (line_i = 0, line_n = source_code.line_count; line_i < line_n; line_i++) + { + char* line = source_code.lines[line_i]; + char* end; + char prev_end_char; + + while (*line && (*line == ' ')) + line++; + end = strchrnul(line, ' '); + if (end == line) + continue; + prev_end_char = *end; + *end = '\0'; + + if (!strcmp(line, "information")) ; + else if (!strcmp(line, "language")) ; + else if (!strcmp(line, "country")) ; + else if (!strcmp(line, "variant")) ; + else if (!strcmp(line, "include")) ; + else if (!strcmp(line, "function")) ; + else if (!strcmp(line, "macro")) ; + else if (!strcmp(line, "assumption")) ; + else if (!strcmp(line, "if")) ; + else if (!strcmp(line, "else")) ; + else if (!strcmp(line, "for")) ; + else if (!strcmp(line, "end")) ; + else if (!strcmp(line, "return")) ; + else if (!strcmp(line, "continue")) ; + else if (!strcmp(line, "break")) ; + else if (!strcmp(line, "let")) ; + else if (!strcmp(line, "have")) ; + else if (!strcmp(line, "have_chars")) ; + else if (!strcmp(line, "have_range")) ; + else if (strchr("\"<([", *line) || strchr(line, '(')) ; + else + { + mds_kbdc_parse_error_t* error; + + if (errors_ptr + 1 >= errors_size) + { + errors_size = errors_size ? (errors_size << 1) : 2; + fail_if (xxrealloc(old_errors, *errors, errors_size, mds_kbdc_parse_error_t*)); + } + + fail_if (xcalloc(error, 1, mds_kbdc_parse_error_t)); + (*errors)[errors_ptr + 0] = error; + (*errors)[errors_ptr + 1] = NULL; + errors_ptr++; + + error->severity = MDS_KBDC_PARSE_ERROR_ERROR; + error->error_is_in_file = 1; + fail_if ((error->pathname = strdup(pathname)) == NULL); + error->line = line_i; + error->start = (size_t)(line - source_code.lines[line_i]); + error->end = (size_t)(end - source_code.lines[line_i]); + fail_if ((error->code = strdup(source_code.real_lines[line_i])) == NULL); + if (asprintf(&(error->description), "invalid keyword ‘%s’", line) < 0) + { + error->description = NULL; + goto pfail; + } + } + + *end = prev_end_char; + } + + free(pathname); + source_code_destroy(&source_code); + return 0; + + pfail: + saved_errno = errno; + free(pathname); + source_code_destroy(&source_code); + mds_kbdc_parse_error_free_all(old_errors); + mds_kbdc_parse_error_free_all(*errors), *errors = NULL; + return errno = saved_errno, -1; +} + diff --git a/src/mds-kbdc/make-tree.h b/src/mds-kbdc/make-tree.h new file mode 100644 index 0000000..bcd4773 --- /dev/null +++ b/src/mds-kbdc/make-tree.h @@ -0,0 +1,39 @@ +/** + * mds — A micro-display server + * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef MDS_MDS_KBDC_MAKE_TREE_H +#define MDS_MDS_KBDC_MAKE_TREE_H + + +#include "tree.h" +#include "parse-error.h" + + +/** + * Parse a file into a syntex tree + * + * @param filename The filename of the file to parse + * @param result Output parameter for the root of the tree, `NULL` if -1 is returned + * @param errors `NULL`-terminated list of found error, `NULL` if no errors were found or if -1 is returned + * @return -1 if an error occursed that cannot be stored in `*errors`, zero otherwise + */ +int parse_to_tree(const char* restrict filename, mds_kbdc_tree_t** restrict result, + mds_kbdc_parse_error_t*** restrict errors); + + +#endif + diff --git a/src/mds-kbdc/mds-kbdc.c b/src/mds-kbdc/mds-kbdc.c index d9484f6..d0d3aa7 100644 --- a/src/mds-kbdc/mds-kbdc.c +++ b/src/mds-kbdc/mds-kbdc.c @@ -18,7 +18,7 @@ #include "mds-kbdc.h" #include "globals.h" -#include "raw-data.h" +#include "make-tree.h" #include <libmdsserver/macros.h> @@ -38,159 +38,31 @@ */ int main(int argc_, char** argv_) { - const char* pathname = argv_[1]; - source_code_t source_code; - size_t i, n; + mds_kbdc_parse_error_t** parse_errors; argc = argc_; argv = argv_; - source_code_initialise(&source_code); - fail_if (read_source_lines(pathname, &source_code) < 0); - /* TODO '\t':s should be expanded into ' ':s. */ - - for (i = 0, n = source_code.line_count; i < n; i++) + fail_if (parse_to_tree(argv[1], NULL, &parse_errors) < 0); + if (parse_errors != NULL) { - char* line = source_code.lines[i]; - char* end; - char prev_end_char; - - while (*line && (*line == ' ')) - line++; - end = strchrnul(line, ' '); - if (end == line) - continue; - prev_end_char = *end; - *end = '\0'; - - if (!strcmp(line, "information")) - ; - else if (!strcmp(line, "language")) - ; - else if (!strcmp(line, "country")) - ; - else if (!strcmp(line, "variant")) - ; - else if (!strcmp(line, "include")) - ; - else if (!strcmp(line, "function")) - ; - else if (!strcmp(line, "macro")) - ; - else if (!strcmp(line, "assumption")) - ; - else if (!strcmp(line, "if")) - ; - else if (!strcmp(line, "else")) - ; - else if (!strcmp(line, "for")) - ; - else if (!strcmp(line, "end")) - ; - else if (!strcmp(line, "return")) - ; - else if (!strcmp(line, "continue")) - ; - else if (!strcmp(line, "break")) - ; - else if (!strcmp(line, "let")) - ; - else if (!strcmp(line, "have")) - ; - else if (!strcmp(line, "have_chars")) - ; - else if (!strcmp(line, "have_range")) - ; - else if ((*line != '"') && (*line != '<') && (strchr(line, '(') == NULL)) + mds_kbdc_parse_error_t** errors = parse_errors; + int fatal = 0; + while (*errors) { - size_t j, m1, m2; - m1 = (size_t)(line - source_code.lines[i]); - m2 = (size_t)(end - source_code.lines[i]); - fprintf(stderr, "\033[1m%s:%zu:%zu-%zu: \033[31merror:\033[0m invalid keyword \033[1m‘%s’\033[0m\n", - pathname, i + 1, m1 + 1, m2 + 1 /* TODO measure chars, not bytes */, line); - fprintf(stderr, " %s\n \033[1;32m", source_code.real_lines[i]); - for (j = 0; j < m1; j++) - fputc(' ', stderr); - for (; j < m2; j++) - if ((source_code.lines[i][j] & 0xC0) != 0x80) - fputc('^', stderr); - fprintf(stderr, "\033[0m\n"); + if ((*errors)->severity >= MDS_KBDC_PARSE_ERROR_ERROR) + fatal = 1; + mds_kbdc_parse_error_print(*errors++, stderr); } - - *end = prev_end_char; + mds_kbdc_parse_error_free_all(parse_errors); + if (fatal) + return 1; } - /* - - information - language "LANGUAGE" # multiple is allowed - country "COUNTRY" # multiple is allowed - variant "VARIANT" - end information - - include "some file" - - function add/3 - \add(\add(\1 \2) \3) - end function - function add/4 - \add(\add(\1 \2 \3) \4) - end function - - macro caps_affected/2 - <letter \1> : "\1" - <shift letter \1> : "\2" - <caps letter \1> : "\2" - <caps shift letter \1> : "\1" - end macro - - <altgr letter \\> : <dead letter ´> - <altgr shift letter \\> : "¬" - <altgr shift letter j> : <dead letter \u031B> # horn - - assumption - have_range "a" "z" - have_range "A" "Z" - have_range "0" "9" - have_chars " !\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~" - have <dead compose> - have <shift> - have <altgr> - have <alt> - have <ctrl> - end assumption - - for "A" to "Z" as \1 - <dead compose> "(" "\1" ")" : "\add(\u24B6 \sub(\1 "A"))" - end for - - let \6 : { \and(\4 16) \and(\4 32) \and(\4 64) \and(\4 128) } - - for 0 to 3 as \8 - \set(\6 \8 \add(\rsh(\get(\6 \8) \8) \mul(2 \rsh(\get(\7 \8))))) - end for - - if \or(\equals(\1 0) \equals(\1 "0")) - "0" - else if \or(\equals(\1 1) \equals(\1 "1")) - "1" - else - "9" - end if - - if \and(\1 128) ## a number is true iff it is not zero - let \2 : \or(\2 64) - end if - - <altgr menu> : <-altgr ultra> - - */ - source_code_destroy(&source_code); return 0; pfail: xperror(*argv); - source_code_destroy(&source_code); return 1; } diff --git a/src/mds-kbdc/parse-error.c b/src/mds-kbdc/parse-error.c new file mode 100644 index 0000000..c16b9f4 --- /dev/null +++ b/src/mds-kbdc/parse-error.c @@ -0,0 +1,160 @@ +/** + * mds — A micro-display server + * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include "parse-error.h" + +#include <stdlib.h> +#include <alloca.h> +#include <string.h> + + + +/** + * Print information about a parsing error + * + * @param this The error structure + * @param output The output file + */ +void mds_kbdc_parse_error_print(const mds_kbdc_parse_error_t* restrict this, FILE* restrict output) +{ + size_t i, n, start = 0, end = 0; + ssize_t m; + const char* restrict code = this->code; + char* desc; + char* dend = this->description + strlen(this->description); + char* dstart; + char* dptr; + char* p; + char* q; + + /* Count the number points in the description we should modify to format it. */ + for (p = this->description, n = 0;;) + { + if (q = strstr(p, "‘"), q == NULL) q = dend; + if (p = strstr(p, "’"), p == NULL) p = dend; + if (q < p) p = q; + if (*p++) n++; + else break; + } + + /* Allocate string for the formatted description. */ + n = 1 + strlen(this->description) + strlen("\033[xxm’") * n; + dptr = desc = alloca(n * sizeof(char)); + + /* Format description. */ + for (p = this->description;;) + { + dstart = p; + if (q = strstr(p, "‘"), q == NULL) q = dend; + if (p = strstr(p, "’"), p == NULL) p = dend; + if (q < p) p = q; + if ((n = (size_t)(p - dstart))) + memcpy(dptr, dstart, n), dptr += n; + if (p == dend) + break; + if (strstr(p, "‘") == p) sprintf(dptr, "\033[01m‘%zn", &m), dptr += (size_t)m, p += strlen("‘"); + else sprintf(dptr, "’\033[21m%zn", &m), dptr += (size_t)m, p += strlen("’"); + } + *dptr = '\0'; + + /* Convert bytes count to character count for the code position */ + for (i = 0, n = this->start; i < n; i++) + start++; + for (n = this->end; i < n; i++) + if ((code[i] & 0xC0) != 0x80) + end++; + end += start; + + /* Print error information. */ + fprintf(output, "\033[01m%s\033[21m:", this->pathname); /* TODO should be relative to the current dir */ + if (this->error_is_in_file) + fprintf(output, "%zu:%zu–%zu:", this->line + 1, start + 1, end + 1); + switch (this->severity) + { + case MDS_KBDC_PARSE_ERROR_NOTE: fprintf(output, " \033[01;36mnote:\033[00m "); break; + case MDS_KBDC_PARSE_ERROR_WARNING: fprintf(output, " \033[01;35mwarning:\033[00m "); break; + case MDS_KBDC_PARSE_ERROR_ERROR: fprintf(output, " \033[01;31merror:\033[00m "); break; + case MDS_KBDC_PARSE_ERROR_INTERNAL_ERROR: fprintf(output, " \033[01;31minternal error:\033[00m "); break; + default: + abort(); + break; + } + fprintf(output, "%s\n %s\n \033[01;32m", desc, code); + i = 0; + for (n = start; i < n; i++) fputc(' ', output); + for (n = end; i < n; i++) fputc('^', output); + fprintf(output, "\033[00m\n"); +} + + + +/** + * Release all resources allocated in a `mds_kbdc_parse_error_t*` + * + * @param this The error structure + */ +void mds_kbdc_parse_error_destroy(mds_kbdc_parse_error_t* restrict this) +{ + if (this == NULL) + return; + free(this->pathname), this->pathname = NULL; + free(this->code), this->code = NULL; + free(this->description), this->description = NULL; +} + + +/** + * Release all resources allocated in a `mds_kbdc_parse_error_t*` + * and release the allocation of the structure itself + * + * @param this The error structure + */ +void mds_kbdc_parse_error_free(mds_kbdc_parse_error_t* restrict this) +{ + mds_kbdc_parse_error_destroy(this); + free(this); +} + + +/** + * Release all resources allocated in a `NULL`-terminated group + * of `mds_kbdc_parse_error_t*`:s + * + * @param this The group of error structures + */ +void mds_kbdc_parse_error_destroy_all(mds_kbdc_parse_error_t** restrict these) +{ + mds_kbdc_parse_error_t* restrict that; + if (these == NULL) + return; + while (that = *these, that != NULL) + mds_kbdc_parse_error_free(that), *these++ = NULL; +} + + +/** + * Release all resources allocated in a `NULL`-terminated group of + * `mds_kbdc_parse_error_t*`:s and release the allocation of the group itself + * + * @param this The group of error structures + */ +void mds_kbdc_parse_error_free_all(mds_kbdc_parse_error_t** restrict these) +{ + mds_kbdc_parse_error_destroy_all(these); + free(these); +} + diff --git a/src/mds-kbdc/parse-error.h b/src/mds-kbdc/parse-error.h new file mode 100644 index 0000000..2c4aca8 --- /dev/null +++ b/src/mds-kbdc/parse-error.h @@ -0,0 +1,143 @@ +/** + * mds — A micro-display server + * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef MDS_MDS_KBDC_PARSE_ERROR_H +#define MDS_MDS_KBDC_PARSE_ERROR_H + + +#include <stddef.h> +#include <stdio.h> + + +/** + * Not an error, simply a note about the previous error or warning + */ +#define MDS_KBDC_PARSE_ERROR_NOTE 1 + +/** + * A warning, must likely an error that is not fatal to the compilation + */ +#define MDS_KBDC_PARSE_ERROR_WARNING 2 + +/** + * An error, the compilation will halt + */ +#define MDS_KBDC_PARSE_ERROR_ERROR 3 + +/** + * Internal compiler error or system error, compilation halts + */ +#define MDS_KBDC_PARSE_ERROR_INTERNAL_ERROR 4 + + + +/** + * Description of an parsing error + */ +typedef struct mds_kbdc_parse_error +{ + /** + * Either of: + * - `MDS_KBDC_PARSE_ERROR_NOTE` + * - `MDS_KBDC_PARSE_ERROR_WARNING` + * - `MDS_KBDC_PARSE_ERROR_ERROR` + * - `MDS_KBDC_PARSE_ERROR_INTERNAL_ERROR` + */ + int severity; + + /** + * If zero, disregard `.line`, `.start`, `.end` and `.code` + */ + int error_is_in_file; + + /** + * The pathname of the file with the error + */ + char* pathname; + + /** + * The line where the error occurred, zero-based + */ + size_t line; + + /** + * The byte where the error started, inclusive, zero-based + */ + size_t start; + + /** + * The byte where the error ended, exclusive, zero-based + */ + size_t end; + + /** + * The code on the line where the error occurred + */ + char* code; + + /** + * Description of the error + */ + char* description; + +} mds_kbdc_parse_error_t; + + + +/** + * Print information about a parsing error + * + * @param this The error structure + * @param output The output file + */ +void mds_kbdc_parse_error_print(const mds_kbdc_parse_error_t* restrict this, FILE* restrict output); + + +/** + * Release all resources allocated in a `mds_kbdc_parse_error_t*` + * + * @param this The error structure + */ +void mds_kbdc_parse_error_destroy(mds_kbdc_parse_error_t* restrict this); + +/** + * Release all resources allocated in a `mds_kbdc_parse_error_t*` + * and release the allocation of the structure itself + * + * @param this The error structure + */ +void mds_kbdc_parse_error_free(mds_kbdc_parse_error_t* restrict this); + +/** + * Release all resources allocated in a `NULL`-terminated group + * of `mds_kbdc_parse_error_t*`:s + * + * @param this The group of error structures + */ +void mds_kbdc_parse_error_destroy_all(mds_kbdc_parse_error_t** restrict these); + +/** + * Release all resources allocated in a `NULL`-terminated group of + * `mds_kbdc_parse_error_t*`:s and release the allocation of the group itself + * + * @param this The group of error structures + */ +void mds_kbdc_parse_error_free_all(mds_kbdc_parse_error_t** restrict these); + + +#endif + diff --git a/src/mds-kbdc/raw-data.c b/src/mds-kbdc/raw-data.c index 1d81d5e..7c1f5fd 100644 --- a/src/mds-kbdc/raw-data.c +++ b/src/mds-kbdc/raw-data.c @@ -313,7 +313,7 @@ static char** line_split(char* content, size_t length) * Read lines of a source file * * @param pathname The pathname of the source file - * @parma source_code Output parameter for read data + * @param source_code Output parameter for read data * @return Zero on success, -1 on error */ int read_source_lines(const char* restrict pathname, source_code_t* restrict source_code) diff --git a/src/mds-kbdc/raw-data.h b/src/mds-kbdc/raw-data.h index 54a9740..a0217d6 100644 --- a/src/mds-kbdc/raw-data.h +++ b/src/mds-kbdc/raw-data.h @@ -84,7 +84,7 @@ void source_code_free(source_code_t* restrict this); * Read lines of a source file * * @param pathname The pathname of the source file - * @parma source_code Output parameter for read data + * @param source_code Output parameter for read data * @return Zero on success, -1 on error */ int read_source_lines(const char* restrict pathname, source_code_t* restrict source_code); |