X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flanguage%2Flexer%2Flexer.c;h=a3642f8a6c6f7e7f5fc5b9376ee31db8308e9efa;hb=b6d66ec3f328d0e8bf35b71f29332695121f7173;hp=010875f5c29f654f6cbaea6b4cf98ee81a613e56;hpb=ff5e81803b409939e921211f1ffd46cb24df33e9;p=pspp diff --git a/src/language/lexer/lexer.c b/src/language/lexer/lexer.c index 010875f5c2..a3642f8a6c 100644 --- a/src/language/lexer/lexer.c +++ b/src/language/lexer/lexer.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2013 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2013, 2016 Free Software Foundation, Inc. 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 @@ -30,7 +30,6 @@ #include #include -#include "data/file-name.h" #include "language/command.h" #include "language/lexer/scan.h" #include "language/lexer/segment.h" @@ -129,8 +128,9 @@ lex_reader_init (struct lex_reader *reader, { reader->class = class; reader->syntax = LEX_SYNTAX_AUTO; - reader->error = LEX_ERROR_INTERACTIVE; + reader->error = LEX_ERROR_CONTINUE; reader->file_name = NULL; + reader->encoding = NULL; reader->line_number = 0; } @@ -184,7 +184,7 @@ lex_append (struct lexer *lexer, struct lex_reader *reader) ll_push_tail (&lexer->sources, &lex_source_create (reader)->ll); } -/* Advacning. */ +/* Advancing. */ static struct lex_token * lex_push_token__ (struct lex_source *src) @@ -604,9 +604,16 @@ lex_force_match (struct lexer *lexer, enum token_type type) } else { - char *s = xasprintf ("`%s'", token_type_to_string (type)); - lex_error_expecting (lexer, s, NULL_SENTINEL); - free (s); + const char *type_string = token_type_to_string (type); + if (type_string) + { + char *s = xasprintf ("`%s'", type_string); + lex_error_expecting (lexer, s, NULL_SENTINEL); + free (s); + } + else + lex_error_expecting (lexer, token_type_to_name (type), NULL_SENTINEL); + return false; } } @@ -637,7 +644,7 @@ lex_force_string (struct lexer *lexer) bool lex_force_string_or_id (struct lexer *lexer) { - return lex_is_integer (lexer) || lex_force_string (lexer); + return lex_token (lexer) == T_ID || lex_force_string (lexer); } /* If the current token is an integer, does nothing and returns true. @@ -1038,6 +1045,14 @@ lex_get_file_name (const struct lexer *lexer) return src == NULL ? NULL : src->reader->file_name; } +const char * +lex_get_encoding (const struct lexer *lexer) +{ + struct lex_source *src = lex_source__ (lexer); + return src == NULL ? NULL : src->reader->encoding; +} + + /* Returns the syntax mode for the syntax file from which the current drawn is drawn. Returns LEX_SYNTAX_AUTO for a T_STOP token or if the command's source does not have line numbers. @@ -1053,7 +1068,7 @@ lex_get_syntax_mode (const struct lexer *lexer) } /* Returns the error mode for the syntax file from which the current drawn is - drawn. Returns LEX_ERROR_INTERACTIVE for a T_STOP token or if the command's + drawn. Returns LEX_ERROR_TERMINAL for a T_STOP token or if the command's source does not have line numbers. There is no version of this function that takes an N argument because @@ -1063,13 +1078,12 @@ enum lex_error_mode lex_get_error_mode (const struct lexer *lexer) { struct lex_source *src = lex_source__ (lexer); - return src == NULL ? LEX_ERROR_INTERACTIVE : src->reader->error; + return src == NULL ? LEX_ERROR_TERMINAL : src->reader->error; } /* If the source that LEXER is currently reading has error mode - LEX_ERROR_INTERACTIVE, discards all buffered input and tokens, so that the - next token to be read comes directly from whatever is next read from the - stream. + LEX_ERROR_TERMINAL, discards all buffered input and tokens, so that the next + token to be read comes directly from whatever is next read from the stream. It makes sense to call this function after encountering an error in a command entered on the console, because usually the user would prefer not to @@ -1078,7 +1092,7 @@ void lex_interactive_reset (struct lexer *lexer) { struct lex_source *src = lex_source__ (lexer); - if (src != NULL && src->reader->error == LEX_ERROR_INTERACTIVE) + if (src != NULL && src->reader->error == LEX_ERROR_TERMINAL) { src->head = src->tail = 0; src->journal_pos = src->seg_pos = src->line_pos = 0; @@ -1100,7 +1114,7 @@ lex_discard_rest_of_command (struct lexer *lexer) } /* Discards all lookahead tokens in LEXER, then discards all input sources - until it encounters one with error mode LEX_ERROR_INTERACTIVE or until it + until it encounters one with error mode LEX_ERROR_TERMINAL or until it runs out of input sources. */ void lex_discard_noninteractive (struct lexer *lexer) @@ -1112,7 +1126,7 @@ lex_discard_noninteractive (struct lexer *lexer) while (!deque_is_empty (&src->deque)) lex_source_pop__ (src); - for (; src != NULL && src->reader->error != LEX_ERROR_INTERACTIVE; + for (; src != NULL && src->reader->error != LEX_ERROR_TERMINAL; src = lex_source__ (lexer)) lex_source_destroy (src); } @@ -1167,19 +1181,33 @@ lex_source_read__ (struct lex_source *src) { do { - size_t head_ofs; - size_t space; - size_t n; - lex_source_expand__ (src); - head_ofs = src->head - src->tail; - space = src->allocated - head_ofs; - n = src->reader->class->read (src->reader, &src->buffer[head_ofs], - space, - segmenter_get_prompt (&src->segmenter)); + size_t head_ofs = src->head - src->tail; + size_t space = src->allocated - head_ofs; + enum prompt_style prompt = segmenter_get_prompt (&src->segmenter); + size_t n = src->reader->class->read (src->reader, &src->buffer[head_ofs], + 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. @@ -1308,37 +1336,44 @@ lex_get_error (struct lex_source *src, const char *format, ...) va_end (args); } +/* Attempts to append an additional token into SRC's deque, reading more from + the underlying lex_reader if necessary.. Returns true if successful, false + if the deque already represents (a suffix of) the whole lex_reader's + contents, */ static bool lex_source_get__ (const struct lex_source *src_) { struct lex_source *src = CONST_CAST (struct lex_source *, src_); + if (src->eof) + return false; + /* State maintained while scanning tokens. Usually we only need a single + state, but scanner_push() can return SCAN_SAVE to indicate that the state + needs to be saved and possibly restored later with SCAN_BACK. */ struct state { struct segmenter segmenter; enum segment_type last_segment; - int newlines; + int newlines; /* Number of newlines encountered so far. */ + /* Maintained here so we can update lex_source's similar members when we + finish. */ size_t line_pos; size_t seg_pos; }; - struct state state, saved; - enum scan_result result; - struct scanner scanner; - struct lex_token *token; - int n_lines; - int i; - - if (src->eof) - return false; - - state.segmenter = src->segmenter; - state.newlines = 0; - state.seg_pos = src->seg_pos; - state.line_pos = src->line_pos; - saved = state; + /* Initialize state. */ + struct state state = + { + .segmenter = src->segmenter, + .newlines = 0, + .seg_pos = src->seg_pos, + .line_pos = src->line_pos, + }; + struct state saved = state; - token = lex_push_token__ (src); + /* Append a new token to SRC and initialize it. */ + struct lex_token *token = lex_push_token__ (src); + struct scanner scanner; scanner_init (&scanner, &token->token); token->line_pos = src->line_pos; token->token_pos = src->seg_pos; @@ -1347,22 +1382,24 @@ lex_source_get__ (const struct lex_source *src_) else token->first_line = 0; + /* Extract segments and pass them through the scanner until we obtain a + token. */ for (;;) { + /* Extract a segment. */ + const char *segment = &src->buffer[state.seg_pos - src->tail]; + size_t seg_maxlen = src->head - state.seg_pos; enum segment_type type; - const char *segment; - size_t seg_maxlen; - int seg_len; - - segment = &src->buffer[state.seg_pos - src->tail]; - seg_maxlen = src->head - state.seg_pos; - seg_len = segmenter_push (&state.segmenter, segment, seg_maxlen, &type); + int seg_len = segmenter_push (&state.segmenter, segment, seg_maxlen, + &type); if (seg_len < 0) { + /* The segmenter needs more input to produce a segment. */ lex_source_read__ (src); continue; } + /* Update state based on the segment. */ state.last_segment = type; state.seg_pos += seg_len; if (type == SEG_NEWLINE) @@ -1371,8 +1408,10 @@ lex_source_get__ (const struct lex_source *src_) state.line_pos = state.seg_pos; } - result = scanner_push (&scanner, type, ss_buffer (segment, seg_len), - &token->token); + /* Pass the segment into the scanner and try to get a token out. */ + enum scan_result result = scanner_push (&scanner, type, + ss_buffer (segment, seg_len), + &token->token); if (result == SCAN_SAVE) saved = state; else if (result == SCAN_BACK) @@ -1384,7 +1423,9 @@ lex_source_get__ (const struct lex_source *src_) break; } - n_lines = state.newlines; + /* If we've reached the end of a line, or the end of a command, then pass + the line to the output engine as a syntax text item. */ + int n_lines = state.newlines; if (state.last_segment == SEG_END_COMMAND && !src->suppress_next_newline) { n_lines++; @@ -1395,20 +1436,15 @@ lex_source_get__ (const struct lex_source *src_) n_lines--; src->suppress_next_newline = false; } - for (i = 0; i < n_lines; i++) + for (int i = 0; i < n_lines; i++) { - const char *newline; - const char *line; - size_t line_len; - char *syntax; - - line = &src->buffer[src->journal_pos - src->tail]; - newline = rawmemchr (line, '\n'); - line_len = newline - 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--; - syntax = malloc (line_len + 2); + char *syntax = malloc (line_len + 2); memcpy (syntax, line, line_len); syntax[line_len] = '\n'; syntax[line_len + 1] = '\0'; @@ -1528,9 +1564,11 @@ static void lex_source_destroy (struct lex_source *src) { char *file_name = src->reader->file_name; + char *encoding = src->reader->encoding; if (src->reader->class->destroy != NULL) src->reader->class->destroy (src->reader); free (file_name); + free (encoding); free (src->buffer); while (!deque_is_empty (&src->deque)) lex_source_pop__ (src); @@ -1543,7 +1581,6 @@ struct lex_file_reader { struct lex_reader reader; struct u8_istream *istream; - char *file_name; }; static struct lex_reader_class lex_file_reader_class; @@ -1577,9 +1614,9 @@ 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.line_number = 1; r->istream = istream; - r->file_name = xstrdup (file_name); return &r->reader; } @@ -1598,7 +1635,7 @@ lex_file_read (struct lex_reader *r_, char *buf, size_t n, ssize_t n_read = u8_istream_read (r->istream, buf, n); if (n_read < 0) { - msg (ME, _("Error reading `%s': %s."), r->file_name, strerror (errno)); + msg (ME, _("Error reading `%s': %s."), r_->file_name, strerror (errno)); return 0; } return n_read; @@ -1612,12 +1649,11 @@ lex_file_close (struct lex_reader *r_) if (u8_istream_fileno (r->istream) != STDIN_FILENO) { if (u8_istream_close (r->istream) != 0) - msg (ME, _("Error closing `%s': %s."), r->file_name, strerror (errno)); + msg (ME, _("Error closing `%s': %s."), r_->file_name, strerror (errno)); } else u8_istream_free (r->istream); - free (r->file_name); free (r); } @@ -1637,16 +1673,17 @@ struct lex_string_reader static struct lex_reader_class lex_string_reader_class; /* Creates and returns a new lex_reader for the contents of S, which must be - encoded in UTF-8. The new reader takes ownership of S and will free it + encoded in the given ENCODING. The new reader takes ownership of S and will free it with ss_dealloc() when it is closed. */ struct lex_reader * -lex_reader_for_substring_nocopy (struct substring s) +lex_reader_for_substring_nocopy (struct substring s, const char *encoding) { struct lex_string_reader *r; r = xmalloc (sizeof *r); lex_reader_init (&r->reader, &lex_string_reader_class); - r->reader.syntax = LEX_SYNTAX_INTERACTIVE; + r->reader.syntax = LEX_SYNTAX_AUTO; + r->reader.encoding = encoding ? xstrdup (encoding) : NULL; r->s = s; r->offset = 0; @@ -1654,25 +1691,25 @@ lex_reader_for_substring_nocopy (struct substring s) } /* Creates and returns a new lex_reader for a copy of null-terminated string S, - which must be encoded in UTF-8. The caller retains ownership of S. */ + which must be encoded in ENCODING. The caller retains ownership of S. */ struct lex_reader * -lex_reader_for_string (const char *s) +lex_reader_for_string (const char *s, const char *encoding) { struct substring ss; ss_alloc_substring (&ss, ss_cstr (s)); - return lex_reader_for_substring_nocopy (ss); + return lex_reader_for_substring_nocopy (ss, encoding); } /* Formats FORMAT as a printf()-like format string and creates and returns a new lex_reader for the formatted result. */ struct lex_reader * -lex_reader_for_format (const char *format, ...) +lex_reader_for_format (const char *format, const char *encoding, ...) { struct lex_reader *r; va_list args; - va_start (args, format); - r = lex_reader_for_substring_nocopy (ss_cstr (xvasprintf (format, args))); + va_start (args, encoding); + r = lex_reader_for_substring_nocopy (ss_cstr (xvasprintf (format, args)), encoding); va_end (args); return r;