X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flanguage%2Fcontrol%2Fdefine.c;h=c439c64571b75d2637c3e79df5980584e71ba9a7;hb=f41e87501f3a3c600f4443033799b1689f84270a;hp=48dda4c7afb0331afa63113d9cc0fc5e36ae7a08;hpb=7dd414a543ed29246aa23a9cc4d6ba56043cebcb;p=pspp diff --git a/src/language/control/define.c b/src/language/control/define.c index 48dda4c7af..c439c64571 100644 --- a/src/language/control/define.c +++ b/src/language/control/define.c @@ -23,6 +23,7 @@ #include "language/lexer/macro.h" #include "language/lexer/scan.h" #include "language/lexer/token.h" +#include "libpspp/intern.h" #include "libpspp/message.h" #include "gl/xalloc.h" @@ -30,12 +31,6 @@ #include "gettext.h" #define _(msgid) gettext (msgid) -static bool -force_macro_id (struct lexer *lexer) -{ - return lex_token (lexer) == T_MACRO_ID || lex_force_id (lexer); -} - static bool match_macro_id (struct lexer *lexer, const char *keyword) { @@ -82,8 +77,9 @@ dup_arg_type (struct lexer *lexer, bool *saw_arg_type) { if (*saw_arg_type) { - lex_error (lexer, _("Only one of !TOKENS, !CHAREND, !ENCLOSE, or " - "!CMDEND is allowed.")); + lex_next_error (lexer, -1, -1, + _("Only one of !TOKENS, !CHAREND, !ENCLOSE, or " + "!CMDEND is allowed.")); return false; } else @@ -93,28 +89,110 @@ dup_arg_type (struct lexer *lexer, bool *saw_arg_type) } } +static bool +parse_macro_body (struct lexer *lexer, struct macro_tokens *mts) +{ + *mts = (struct macro_tokens) { .n = 0 }; + struct string body = DS_EMPTY_INITIALIZER; + struct msg_point start = lex_ofs_start_point (lexer, lex_ofs (lexer)); + while (!match_macro_id (lexer, "!ENDDEFINE")) + { + if (lex_token (lexer) != T_STRING) + { + lex_error (lexer, + _("Syntax error expecting macro body or !ENDDEFINE.")); + ds_destroy (&body); + return false; + } + + ds_put_substring (&body, lex_tokss (lexer)); + ds_put_byte (&body, '\n'); + lex_get (lexer); + } + + struct segmenter segmenter = segmenter_init (lex_get_syntax_mode (lexer), + true); + struct substring p = body.ss; + bool ok = true; + while (p.length > 0) + { + enum segment_type type; + int seg_len = segmenter_push (&segmenter, p.string, + p.length, true, &type); + assert (seg_len >= 0); + + struct macro_token mt = { + .token = { .type = T_STOP }, + .syntax = ss_head (p, seg_len), + }; + enum tokenize_result result + = token_from_segment (type, mt.syntax, &mt.token); + ss_advance (&p, seg_len); + + switch (result) + { + case TOKENIZE_EMPTY: + break; + + case TOKENIZE_TOKEN: + macro_tokens_add (mts, &mt); + break; + + case TOKENIZE_ERROR: + { + size_t start_offset = mt.syntax.string - body.ss.string; + size_t end_offset = start_offset + (mt.syntax.length ? mt.syntax.length - 1 : 0); + + const struct msg_location loc = { + .file_name = intern_new_if_nonnull (lex_get_file_name (lexer)), + .start = msg_point_advance (start, ss_buffer (body.ss.string, start_offset)), + .end = msg_point_advance (start, ss_buffer (body.ss.string, end_offset)), + .src = CONST_CAST (struct lex_source *, lex_source (lexer)), + }; + msg_at (SE, &loc, "%s", mt.token.string.string); + intern_unref (loc.file_name); + + ok = false; + } + break; + } + + token_uninit (&mt.token); + } + ds_destroy (&body); + return ok; +} + int cmd_define (struct lexer *lexer, struct dataset *ds UNUSED) { - if (!force_macro_id (lexer)) - return CMD_FAILURE; + /* Parse macro name. + + The macro name is a T_STRING token, even though it's an identifier, + because that's the way that the segmenter prevents it from getting + macro-expanded. */ + if (lex_token (lexer) != T_STRING) + { + lex_error (lexer, _("Syntax error expecting identifier.")); + return CMD_FAILURE; + } + const char *name = lex_tokcstr (lexer); + if (!id_is_plausible (name + (name[0] == '!'))) + { + lex_error (lexer, _("Syntax error expecting identifier.")); + return CMD_FAILURE; + } - /* Parse macro name. */ struct macro *m = xmalloc (sizeof *m); - *m = (struct macro) { - .name = ss_xstrdup (lex_tokss (lexer)), - .location = xmalloc (sizeof *m->location), - }; - *m->location = (struct msg_location) { - .file_name = xstrdup_if_nonnull (lex_get_file_name (lexer)), - .first_line = lex_get_first_line_number (lexer, 0), - }; + *m = (struct macro) { .name = xstrdup (name) }; + struct msg_point macro_start = lex_ofs_start_point (lexer, lex_ofs (lexer)); lex_get (lexer); if (!lex_force_match (lexer, T_LPAREN)) goto error; size_t allocated_params = 0; + int keyword_ofs = 0; while (!lex_match (lexer, T_RPAREN)) { if (m->n_params >= allocated_params) @@ -130,8 +208,11 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED) { if (param_index > 0 && !m->params[param_index - 1].positional) { - lex_error (lexer, _("Positional parameters must precede " - "keyword parameters.")); + lex_next_error (lexer, -1, -1, + _("Positional parameters must precede " + "keyword parameters.")); + lex_ofs_msg (lexer, SN, keyword_ofs, keyword_ofs, + _("Here is a previous keyword parameter.")); goto error; } @@ -140,6 +221,8 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED) } else { + if (keyword_ofs == 0) + keyword_ofs = lex_ofs (lexer); if (lex_token (lexer) == T_MACRO_ID) { lex_error (lexer, _("Keyword macro parameter must be named in " @@ -171,8 +254,9 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED) { if (saw_default) { - lex_error (lexer, - _("!DEFAULT is allowed only once per argument.")); + lex_next_error ( + lexer, -1, -1, + _("!DEFAULT is allowed only once per argument.")); goto error; } saw_default = true; @@ -219,10 +303,9 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED) goto error; p->arg_type = ARG_CHAREND; - p->charend = (struct token) { .type = T_STOP }; if (!lex_force_match (lexer, T_LPAREN) - || !parse_quoted_token (lexer, &p->charend) + || !parse_quoted_token (lexer, &p->end) || !lex_force_match (lexer, T_RPAREN)) goto error; } @@ -232,12 +315,11 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED) goto error; p->arg_type = ARG_ENCLOSE; - p->enclose[0] = p->enclose[1] = (struct token) { .type = T_STOP }; if (!lex_force_match (lexer, T_LPAREN) - || !parse_quoted_token (lexer, &p->enclose[0]) + || !parse_quoted_token (lexer, &p->start) || !lex_force_match (lexer, T_COMMA) - || !parse_quoted_token (lexer, &p->enclose[1]) + || !parse_quoted_token (lexer, &p->end) || !lex_force_match (lexer, T_RPAREN)) goto error; } @@ -262,24 +344,16 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED) goto error; } - struct string body = DS_EMPTY_INITIALIZER; - while (!match_macro_id (lexer, "!ENDDEFINE")) - { - if (lex_token (lexer) != T_STRING) - { - lex_error (lexer, _("Expecting macro body or !ENDDEFINE")); - ds_destroy (&body); - goto error; - } - - ds_put_substring (&body, lex_tokss (lexer)); - ds_put_byte (&body, '\n'); - lex_get (lexer); - } - m->location->last_line = lex_get_last_line_number (lexer, 0); + if (!parse_macro_body (lexer, &m->body)) + goto error; - macro_tokens_from_string (&m->body, body.ss, lex_get_syntax_mode (lexer)); - ds_destroy (&body); + struct msg_point macro_end = lex_ofs_end_point (lexer, lex_ofs (lexer) - 1); + m->location = xmalloc (sizeof *m->location); + *m->location = (struct msg_location) { + .file_name = intern_new_if_nonnull (lex_get_file_name (lexer)), + .start = { .line = macro_start.line }, + .end = { .line = macro_end.line }, + }; lex_define_macro (lexer, m);