X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=src%2Flanguage%2Flexer%2Flexer.c;h=5044731348ecce0c6a7573dfaabd28b9b3709d67;hb=40fb94bbe38b3e444df7952631ec5fe83f7f1086;hp=a3642f8a6c6f7e7f5fc5b9376ee31db8308e9efa;hpb=b6d66ec3f328d0e8bf35b71f29332695121f7173;p=pspp diff --git a/src/language/lexer/lexer.c b/src/language/lexer/lexer.c index a3642f8a6c..5044731348 100644 --- a/src/language/lexer/lexer.c +++ b/src/language/lexer/lexer.c @@ -44,7 +44,7 @@ #include "libpspp/str.h" #include "libpspp/u8-istream.h" #include "output/journal.h" -#include "output/text-item.h" +#include "output/output-item.h" #include "gl/c-ctype.h" #include "gl/minmax.h" @@ -132,6 +132,7 @@ lex_reader_init (struct lex_reader *reader, reader->file_name = NULL; reader->encoding = NULL; reader->line_number = 0; + reader->eof = false; } /* Frees any file name already in READER and replaces it by a copy of @@ -140,7 +141,7 @@ void lex_reader_set_file_name (struct lex_reader *reader, const char *file_name) { free (reader->file_name); - reader->file_name = file_name != NULL ? xstrdup (file_name) : NULL; + reader->file_name = xstrdup_if_nonnull (file_name); } /* Creates and returns a new lexer. */ @@ -268,23 +269,40 @@ lex_next_error (struct lexer *lexer, int n0, int n1, const char *format, ...) va_end (args); } -/* Prints a syntax error message saying that OPTION0 or one of the other - strings following it, up to the first NULL, is expected. */ +/* Prints a syntax error message saying that one of the strings provided as + varargs, up to the first NULL, is expected. */ void -lex_error_expecting (struct lexer *lexer, const char *option0, ...) +(lex_error_expecting) (struct lexer *lexer, ...) { - enum { MAX_OPTIONS = 8 }; - const char *options[MAX_OPTIONS + 1]; va_list args; - int n; - va_start (args, option0); - options[0] = option0; - n = 0; - while (n + 1 < MAX_OPTIONS && options[n] != NULL) - options[++n] = va_arg (args, const char *); + va_start (args, lexer); + lex_error_expecting_valist (lexer, args); va_end (args); +} + +/* Prints a syntax error message saying that one of the options provided in + ARGS, up to the first NULL, is expected. */ +void +lex_error_expecting_valist (struct lexer *lexer, va_list args) +{ + enum { MAX_OPTIONS = 9 }; + const char *options[MAX_OPTIONS]; + int n = 0; + while (n < MAX_OPTIONS) + { + const char *option = va_arg (args, const char *); + if (!option) + break; + options[n++] = option; + } + lex_error_expecting_array (lexer, options, n); +} + +void +lex_error_expecting_array (struct lexer *lexer, const char **options, size_t n) +{ switch (n) { case 0: @@ -333,7 +351,7 @@ lex_error_expecting (struct lexer *lexer, const char *option0, ...) break; default: - NOT_REACHED (); + lex_error (lexer, NULL); } } @@ -427,14 +445,14 @@ lex_end_of_command (struct lexer *lexer) /* Returns true if the current token is a number. */ bool -lex_is_number (struct lexer *lexer) +lex_is_number (const struct lexer *lexer) { return lex_next_is_number (lexer, 0); } /* Returns true if the current token is a string. */ bool -lex_is_string (struct lexer *lexer) +lex_is_string (const struct lexer *lexer) { return lex_next_is_string (lexer, 0); } @@ -442,14 +460,14 @@ lex_is_string (struct lexer *lexer) /* Returns the value of the current token, which must be a floating point number. */ double -lex_number (struct lexer *lexer) +lex_number (const struct lexer *lexer) { return lex_next_number (lexer, 0); } /* Returns true iff the current token is an integer. */ bool -lex_is_integer (struct lexer *lexer) +lex_is_integer (const struct lexer *lexer) { return lex_next_is_integer (lexer, 0); } @@ -457,7 +475,7 @@ lex_is_integer (struct lexer *lexer) /* Returns the value of the current token, which must be an integer. */ long -lex_integer (struct lexer *lexer) +lex_integer (const struct lexer *lexer) { return lex_next_integer (lexer, 0); } @@ -471,7 +489,7 @@ lex_integer (struct lexer *lexer) /* Returns true if the token N ahead of the current token is a number. */ bool -lex_next_is_number (struct lexer *lexer, int n) +lex_next_is_number (const struct lexer *lexer, int n) { enum token_type next_token = lex_next_token (lexer, n); return next_token == T_POS_NUM || next_token == T_NEG_NUM; @@ -479,7 +497,7 @@ lex_next_is_number (struct lexer *lexer, int n) /* Returns true if the token N ahead of the current token is a string. */ bool -lex_next_is_string (struct lexer *lexer, int n) +lex_next_is_string (const struct lexer *lexer, int n) { return lex_next_token (lexer, n) == T_STRING; } @@ -487,7 +505,7 @@ lex_next_is_string (struct lexer *lexer, int n) /* Returns the value of the token N ahead of the current token, which must be a floating point number. */ double -lex_next_number (struct lexer *lexer, int n) +lex_next_number (const struct lexer *lexer, int n) { assert (lex_next_is_number (lexer, n)); return lex_next_tokval (lexer, n); @@ -495,7 +513,7 @@ lex_next_number (struct lexer *lexer, int n) /* Returns true if the token N ahead of the current token is an integer. */ bool -lex_next_is_integer (struct lexer *lexer, int n) +lex_next_is_integer (const struct lexer *lexer, int n) { double value; @@ -509,7 +527,7 @@ lex_next_is_integer (struct lexer *lexer, int n) /* Returns the value of the token N ahead of the current token, which must be an integer. */ long -lex_next_integer (struct lexer *lexer, int n) +lex_next_integer (const struct lexer *lexer, int n) { assert (lex_next_is_integer (lexer, n)); return lex_next_tokval (lexer, n); @@ -587,7 +605,7 @@ lex_force_match_id (struct lexer *lexer, const char *identifier) return true; else { - lex_error_expecting (lexer, identifier, NULL_SENTINEL); + lex_error_expecting (lexer, identifier); return false; } } @@ -608,11 +626,11 @@ lex_force_match (struct lexer *lexer, enum token_type type) if (type_string) { char *s = xasprintf ("`%s'", type_string); - lex_error_expecting (lexer, s, NULL_SENTINEL); + lex_error_expecting (lexer, s); free (s); } else - lex_error_expecting (lexer, token_type_to_name (type), NULL_SENTINEL); + lex_error_expecting (lexer, token_type_to_name (type)); return false; } @@ -661,6 +679,96 @@ lex_force_int (struct lexer *lexer) } } +/* If the current token is an integer in the range MIN...MAX (inclusive), does + nothing and returns true. Otherwise, reports an error and returns false. + If NAME is nonnull, then it is used in the error message. */ +bool +lex_force_int_range (struct lexer *lexer, const char *name, long min, long max) +{ + bool is_integer = lex_is_integer (lexer); + bool too_small = is_integer && lex_integer (lexer) < min; + bool too_big = is_integer && lex_integer (lexer) > max; + if (is_integer && !too_small && !too_big) + return true; + + if (min > max) + { + /* Weird, maybe a bug in the caller. Just report that we needed an + integer. */ + if (name) + lex_error (lexer, _("Integer expected for %s."), name); + else + lex_error (lexer, _("Integer expected.")); + } + else if (min == max) + { + if (name) + lex_error (lexer, _("Expected %ld for %s."), min, name); + else + lex_error (lexer, _("Expected %ld."), min); + } + else if (min + 1 == max) + { + if (name) + lex_error (lexer, _("Expected %ld or %ld for %s."), min, min + 1, name); + else + lex_error (lexer, _("Expected %ld or %ld."), min, min + 1); + } + else + { + bool report_lower_bound = (min > INT_MIN / 2) || too_small; + bool report_upper_bound = (max < INT_MAX / 2) || too_big; + + if (report_lower_bound && report_upper_bound) + { + if (name) + lex_error (lexer, + _("Expected integer between %ld and %ld for %s."), + min, max, name); + else + lex_error (lexer, _("Expected integer between %ld and %ld."), + min, max); + } + else if (report_lower_bound) + { + if (min == 0) + { + if (name) + lex_error (lexer, _("Expected non-negative integer for %s."), + name); + else + lex_error (lexer, _("Expected non-negative integer.")); + } + else if (min == 1) + { + if (name) + lex_error (lexer, _("Expected positive integer for %s."), + name); + else + lex_error (lexer, _("Expected positive integer.")); + } + } + else if (report_upper_bound) + { + if (name) + lex_error (lexer, + _("Expected integer less than or equal to %ld for %s."), + max, name); + else + lex_error (lexer, _("Expected integer less than or equal to %ld."), + max); + } + else + { + if (name) + lex_error (lexer, _("Integer expected for %s."), name); + else + lex_error (lexer, _("Integer expected.")); + } + } + return false; +} + /* If the current token is a number, does nothing and returns true. Otherwise, reports an error and returns false. */ bool @@ -825,8 +933,8 @@ lex_next_tokcstr (const struct lexer *lexer, int n) The string is null-terminated (but the null terminator is not included in the returned substring's 'length'). - Only T_ID and T_STRING tokens have meaningful strings. For other tokens - this functions this function will always return NULL. + Only T_ID, T_MACRO_ID, T_STRING tokens have meaningful strings. For other + tokens this functions this function will always return NULL. The UTF-8 encoding of the returned string is correct for variable names and other identifiers. Use filename_to_utf8() to use it as a filename. Use @@ -876,7 +984,7 @@ lex_match_phrase (struct lexer *lexer, const char *s) int i; i = 0; - string_lexer_init (&slex, s, SEG_MODE_INTERACTIVE); + string_lexer_init (&slex, s, strlen (s), SEG_MODE_INTERACTIVE); while (string_lexer_next (&slex, &token)) if (token.type != SCAN_SKIP) { @@ -1190,38 +1298,11 @@ lex_source_read__ (struct lex_source *src) space, prompt); assert (n <= space); - for (char *p = &src->buffer[head_ofs]; p < &src->buffer[head_ofs + n]; - p++) - if (*p == '\0') - { - struct msg m; - m.category = MSG_C_SYNTAX; - m.severity = MSG_S_ERROR; - m.file_name = src->reader->file_name; - m.first_line = 0; - m.last_line = 0; - m.first_column = 0; - m.last_column = 0; - m.text = xstrdup ("Bad character U+0000 in input."); - msg_emit (&m); - - *p = ' '; - } - if (n == 0) { - /* End of input. - - Ensure that the input always ends in a new-line followed by a null - byte, as required by the segmenter library. */ - - if (src->head == src->tail - || src->buffer[src->head - src->tail - 1] != '\n') - src->buffer[src->head++ - src->tail] = '\n'; - + /* End of input. */ + src->reader->eof = true; lex_source_expand__ (src); - src->buffer[src->head++ - src->tail] = '\0'; - return; } @@ -1257,10 +1338,14 @@ lex_ellipsize__ (struct substring in, char *out, size_t out_size) int mblen; assert (out_size >= 16); - out_maxlen = out_size - (in.length >= out_size ? 3 : 0) - 1; + out_maxlen = out_size - 1; + if (in.length > out_maxlen - 3) + out_maxlen -= 3; + for (out_len = 0; out_len < in.length; out_len += mblen) { if (in.string[out_len] == '\n' + || in.string[out_len] == '\0' || (in.string[out_len] == '\r' && out_len + 1 < in.length && in.string[out_len + 1] == '\n')) @@ -1268,6 +1353,10 @@ lex_ellipsize__ (struct substring in, char *out, size_t out_size) mblen = u8_mblen (CHAR_CAST (const uint8_t *, in.string + out_len), in.length - out_len); + + if (mblen < 0) + break; + if (out_len + mblen > out_maxlen) break; } @@ -1282,7 +1371,6 @@ lex_source_error_valist (struct lex_source *src, int n0, int n1, { const struct lex_token *token; struct string s; - struct msg m; ds_init_empty (&s); @@ -1308,16 +1396,19 @@ lex_source_error_valist (struct lex_source *src, int n0, int n1, ds_put_cstr (&s, ": "); ds_put_vformat (&s, format, args); } - ds_put_byte (&s, '.'); - - m.category = MSG_C_SYNTAX; - m.severity = MSG_S_ERROR; - m.file_name = src->reader->file_name; - m.first_line = lex_source_get_first_line_number (src, n0); - m.last_line = lex_source_get_last_line_number (src, n1); - m.first_column = lex_source_get_first_column (src, n0); - m.last_column = lex_source_get_last_column (src, n1); - m.text = ds_steal_cstr (&s); + if (ds_last (&s) != '.') + ds_put_byte (&s, '.'); + + struct msg m = { + .category = MSG_C_SYNTAX, + .severity = MSG_S_ERROR, + .file_name = src->reader->file_name, + .first_line = lex_source_get_first_line_number (src, n0), + .last_line = lex_source_get_last_line_number (src, n1), + .first_column = lex_source_get_first_column (src, n0), + .last_column = lex_source_get_last_column (src, n1), + .text = ds_steal_cstr (&s), + }; msg_emit (&m); } @@ -1391,10 +1482,11 @@ lex_source_get__ (const struct lex_source *src_) size_t seg_maxlen = src->head - state.seg_pos; enum segment_type type; int seg_len = segmenter_push (&state.segmenter, segment, seg_maxlen, - &type); + src->reader->eof, &type); if (seg_len < 0) { /* The segmenter needs more input to produce a segment. */ + assert (!src->reader->eof); lex_source_read__ (src); continue; } @@ -1438,20 +1530,32 @@ lex_source_get__ (const struct lex_source *src_) } for (int i = 0; i < n_lines; i++) { + /* Beginning of line. */ const char *line = &src->buffer[src->journal_pos - src->tail]; - const char *newline = rawmemchr (line, '\n'); - size_t line_len = newline - line; - if (line_len > 0 && line[line_len - 1] == '\r') - line_len--; - - char *syntax = malloc (line_len + 2); - memcpy (syntax, line, line_len); - syntax[line_len] = '\n'; - syntax[line_len + 1] = '\0'; - - text_item_submit (text_item_create_nocopy (TEXT_ITEM_SYNTAX, syntax)); - src->journal_pos += newline - line + 1; + /* Calculate line length, including \n or \r\n end-of-line if present. + + We use src->head even though that may be beyond what we've actually + converted to tokens (which is only through state.line_pos). That's + because, if we're emitting the line due to SEG_END_COMMAND, we want to + take the whole line through the newline, not just through the '.'. */ + size_t max_len = src->head - src->journal_pos; + const char *newline = memchr (line, '\n', max_len); + size_t line_len = newline ? newline - line + 1 : max_len; + + /* Calculate line length excluding end-of-line. */ + size_t copy_len = line_len; + if (copy_len > 0 && line[copy_len - 1] == '\n') + copy_len--; + if (copy_len > 0 && line[copy_len - 1] == '\r') + copy_len--; + + /* Submit the line as syntax. */ + output_item_submit (text_item_create_nocopy (TEXT_ITEM_SYNTAX, + xmemdup0 (line, copy_len), + NULL)); + + src->journal_pos += line_len; } token->token_len = state.seg_pos - src->seg_pos; @@ -1614,7 +1718,7 @@ lex_reader_for_file (const char *file_name, const char *encoding, r->reader.syntax = syntax; r->reader.error = error; r->reader.file_name = xstrdup (file_name); - r->reader.encoding = encoding ? xstrdup (encoding) : NULL; + r->reader.encoding = xstrdup_if_nonnull (encoding); r->reader.line_number = 1; r->istream = istream; @@ -1683,7 +1787,7 @@ lex_reader_for_substring_nocopy (struct substring s, const char *encoding) r = xmalloc (sizeof *r); lex_reader_init (&r->reader, &lex_string_reader_class); r->reader.syntax = LEX_SYNTAX_AUTO; - r->reader.encoding = encoding ? xstrdup (encoding) : NULL; + r->reader.encoding = xstrdup_if_nonnull (encoding); r->s = s; r->offset = 0;