]> git.saurik.com Git - bison.git/blobdiff - src/scan-gram.l
Warn about character literals not of length one.
[bison.git] / src / scan-gram.l
index 5e595cf6f8b370a994b53638b96e20c463a44479..5c0418c630479b65c62445cacc0164aa6a76dfdc 100644 (file)
@@ -1,12 +1,13 @@
 /* Bison Grammar Scanner                             -*- C -*-
 
-   Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+   Free Software Foundation, Inc.
 
    This file is part of Bison, the GNU Compiler Compiler.
 
-   This program is free software; you can redistribute it and/or modify
+   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 2 of the License, or
+   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,
    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, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301  USA
-*/
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 %option debug nodefault nounput noyywrap never-interactive
 %option prefix="gram_" outfile="lex.yy.c"
 #define gram_wrap() 1
 
 #define FLEX_PREFIX(Id) gram_ ## Id
-#include "flex-scanner.h"
+#include <src/flex-scanner.h>
 
-#include "complain.h"
-#include "files.h"
-#include "gram.h"
-#include "quotearg.h"
-#include "reader.h"
-#include "uniqstr.h"
+#include <src/complain.h>
+#include <src/files.h>
+#include <src/gram.h>
+#include <quotearg.h>
+#include <src/reader.h>
+#include <src/uniqstr.h>
 
 #include <mbswidth.h>
 #include <quote.h>
 
-#include "scan-gram.h"
+#include <src/scan-gram.h>
 
 #define YY_DECL GRAM_LEX_DECL
 
@@ -57,8 +55,26 @@ static boundary scanner_cursor;
 static size_t no_cr_read (FILE *, char *, size_t);
 #define YY_INPUT(buf, result, size) ((result) = no_cr_read (yyin, buf, size))
 
+#define RETURN_PERCENT_FLAG(Value)              \
+  do {                                          \
+    val->uniqstr = uniqstr_new (Value);         \
+    return PERCENT_FLAG;                        \
+  } while (0)
+
+#define ROLLBACK_CURRENT_TOKEN                                  \
+  do {                                                          \
+    scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0);    \
+    yyless (0);                                                 \
+  } while (0)
+
 /* A string representing the most recently saved token.  */
-char *last_string;
+static char *last_string;
+
+/* Bracketed identifier. */
+static uniqstr bracketed_id_str = 0;
+static location bracketed_id_loc;
+static boundary bracketed_id_start;
+static int bracketed_id_context_state = 0;
 
 void
 gram_scanner_last_string_free (void)
@@ -80,6 +96,8 @@ static void unexpected_newline (boundary, char const *);
  /* A identifier was just read in directives/rules.  Special state
     to capture the sequence `identifier :'. */
 %x SC_AFTER_IDENTIFIER
+ /* A complex tag, with nested angles brackets. */
+%x SC_TAG
 
  /* Three types of user code:
     - prologue (code between `%{' `%}' in the first section, before %%);
@@ -90,16 +108,20 @@ static void unexpected_newline (boundary, char const *);
 %x SC_COMMENT SC_LINE_COMMENT
  /* Strings and characters in code. */
 %x SC_STRING SC_CHARACTER
+ /* Bracketed identifiers support. */
+%x SC_BRACKETED_ID SC_RETURN_BRACKETED_ID
 
-letter   [.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
+letter   [-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_]
 id       {letter}({letter}|[0-9])*
-directive %{letter}({letter}|[0-9]|-)*
+directive %{id}
 int      [0-9]+
 
 /* POSIX says that a tag must be both an id and a C union member, but
    historically almost any character is allowed in a tag.  We disallow
-   NUL and newline, as this simplifies our implementation.  */
-tag     [^\0\n>]+
+   NUL, as this simplifies our implementation.  We disallow angle
+   bracket to match them in nested pairs: several languages use them
+   for generics/template types.  */
+tag     [^\0<>]+
 
 /* Zero or more instances of backslash-newline.  Following GCC, allow
    white space between the backslash and the newline.  */
@@ -107,8 +129,9 @@ splice       (\\[ \f\t\v]*\n)*
 
 %%
 %{
-  /* Nesting level of the current code in braces.  */
-  int braces_level IF_LINT (= 0);
+  /* Nesting level.  Either for nested braces, or nested angle brackets
+     (but not mixed).  */
+  int nesting IF_LINT (= 0);
 
   /* Parent context state, when applicable.  */
   int context_state IF_LINT (= 0);
@@ -131,7 +154,7 @@ splice       (\\[ \f\t\v]*\n)*
   | Scanning white space.  |
   `-----------------------*/
 
-<INITIAL,SC_AFTER_IDENTIFIER>
+<INITIAL,SC_AFTER_IDENTIFIER,SC_BRACKETED_ID,SC_RETURN_BRACKETED_ID>
 {
   /* Comments and white space.  */
   ","         warn_at (*loc, _("stray `,' treated as white space"));
@@ -154,53 +177,58 @@ splice     (\\[ \f\t\v]*\n)*
   /*----------------------------.
   | Scanning Bison directives.  |
   `----------------------------*/
+
+  /* For directives that are also command line options, the regex must be
+       "%..."
+     after "[-_]"s are removed, and the directive must match the --long
+     option name, with a single string argument.  Otherwise, add exceptions
+     to ../build-aux/cross-options.pl.  */
+
 <INITIAL>
 {
-  "%binary"                        return PERCENT_NONASSOC;
+  "%binary"                         return PERCENT_NONASSOC;
   "%code"                           return PERCENT_CODE;
-  "%code-top"                       return PERCENT_CODE_TOP;
-  "%debug"                         return PERCENT_DEBUG;
-  "%default"[-_]"prec"             return PERCENT_DEFAULT_PREC;
-  "%define"                        return PERCENT_DEFINE;
-  "%defines"                       return PERCENT_DEFINES;
-  "%destructor"                            return PERCENT_DESTRUCTOR;
-  "%dprec"                         return PERCENT_DPREC;
-  "%error"[-_]"verbose"            return PERCENT_ERROR_VERBOSE;
-  "%expect"                        return PERCENT_EXPECT;
-  "%expect"[-_]"rr"                return PERCENT_EXPECT_RR;
-  "%file-prefix"                   return PERCENT_FILE_PREFIX;
+  "%debug"                          RETURN_PERCENT_FLAG("parse.trace");
+  "%default"[-_]"prec"              return PERCENT_DEFAULT_PREC;
+  "%define"                         return PERCENT_DEFINE;
+  "%defines"                        return PERCENT_DEFINES;
+  "%destructor"                     return PERCENT_DESTRUCTOR;
+  "%dprec"                          return PERCENT_DPREC;
+  "%error"[-_]"verbose"             return PERCENT_ERROR_VERBOSE;
+  "%expect"                         return PERCENT_EXPECT;
+  "%expect"[-_]"rr"                 return PERCENT_EXPECT_RR;
+  "%file-prefix"                    return PERCENT_FILE_PREFIX;
   "%fixed"[-_]"output"[-_]"files"   return PERCENT_YACC;
-  "%initial-action"                return PERCENT_INITIAL_ACTION;
-  "%glr-parser"                    return PERCENT_GLR_PARSER;
-  "%left"                          return PERCENT_LEFT;
-  "%lex-param"                     return PERCENT_LEX_PARAM;
-  "%locations"                     return PERCENT_LOCATIONS;
-  "%merge"                         return PERCENT_MERGE;
-  "%name"[-_]"prefix"              return PERCENT_NAME_PREFIX;
-  "%no"[-_]"default"[-_]"prec"     return PERCENT_NO_DEFAULT_PREC;
-  "%no"[-_]"lines"                 return PERCENT_NO_LINES;
-  "%nonassoc"                      return PERCENT_NONASSOC;
-  "%nondeterministic-parser"               return PERCENT_NONDETERMINISTIC_PARSER;
-  "%nterm"                         return PERCENT_NTERM;
-  "%output"                        return PERCENT_OUTPUT;
-  "%parse-param"                   return PERCENT_PARSE_PARAM;
-  "%prec"                          return PERCENT_PREC;
-  "%printer"                       return PERCENT_PRINTER;
-  "%provides"                       return PERCENT_PROVIDES;
-  "%pure"[-_]"parser"              return PERCENT_PURE_PARSER;
-  "%push"[-_]"parser"              return PERCENT_PUSH_PARSER;
-  "%require"                       return PERCENT_REQUIRE;
-  "%requires"                       return PERCENT_REQUIRES;
-  "%right"                         return PERCENT_RIGHT;
-  "%skeleton"                      return PERCENT_SKELETON;
-  "%start"                         return PERCENT_START;
-  "%term"                          return PERCENT_TOKEN;
-  "%token"                         return PERCENT_TOKEN;
-  "%token"[-_]"table"              return PERCENT_TOKEN_TABLE;
-  "%type"                          return PERCENT_TYPE;
-  "%union"                         return PERCENT_UNION;
-  "%verbose"                       return PERCENT_VERBOSE;
-  "%yacc"                          return PERCENT_YACC;
+  "%initial-action"                 return PERCENT_INITIAL_ACTION;
+  "%glr-parser"                     return PERCENT_GLR_PARSER;
+  "%language"                       return PERCENT_LANGUAGE;
+  "%left"                           return PERCENT_LEFT;
+  "%lex-param"                      return PERCENT_LEX_PARAM;
+  "%locations"                      RETURN_PERCENT_FLAG("locations");
+  "%merge"                          return PERCENT_MERGE;
+  "%name"[-_]"prefix"               return PERCENT_NAME_PREFIX;
+  "%no"[-_]"default"[-_]"prec"      return PERCENT_NO_DEFAULT_PREC;
+  "%no"[-_]"lines"                  return PERCENT_NO_LINES;
+  "%nonassoc"                       return PERCENT_NONASSOC;
+  "%nondeterministic-parser"        return PERCENT_NONDETERMINISTIC_PARSER;
+  "%nterm"                          return PERCENT_NTERM;
+  "%output"                         return PERCENT_OUTPUT;
+  "%parse-param"                    return PERCENT_PARSE_PARAM;
+  "%prec"                           return PERCENT_PREC;
+  "%precedence"                     return PERCENT_PRECEDENCE;
+  "%printer"                        return PERCENT_PRINTER;
+  "%pure"[-_]"parser"               RETURN_PERCENT_FLAG("api.pure");
+  "%require"                        return PERCENT_REQUIRE;
+  "%right"                          return PERCENT_RIGHT;
+  "%skeleton"                       return PERCENT_SKELETON;
+  "%start"                          return PERCENT_START;
+  "%term"                           return PERCENT_TOKEN;
+  "%token"                          return PERCENT_TOKEN;
+  "%token"[-_]"table"               return PERCENT_TOKEN_TABLE;
+  "%type"                           return PERCENT_TYPE;
+  "%union"                          return PERCENT_UNION;
+  "%verbose"                        return PERCENT_VERBOSE;
+  "%yacc"                           return PERCENT_YACC;
 
   {directive} {
     complain_at (*loc, _("invalid directive: %s"), quote (yytext));
@@ -209,12 +237,11 @@ splice     (\\[ \f\t\v]*\n)*
   "="                     return EQUAL;
   "|"                     return PIPE;
   ";"                     return SEMICOLON;
-  "<*>"                   return TYPE_TAG_ANY;
-  "<!>"                   return TYPE_TAG_NONE;
 
   {id} {
     val->uniqstr = uniqstr_new (yytext);
     id_loc = *loc;
+    bracketed_id_str = NULL;
     BEGIN SC_AFTER_IDENTIFIER;
   }
 
@@ -227,7 +254,13 @@ splice      (\\[ \f\t\v]*\n)*
     return INT;
   }
 
-  /* Characters.  We don't check there is only one.  */
+  /* Identifiers may not start with a digit.  Yet, don't silently
+     accept "1FOO" as "1 FOO".  */
+  {int}{id} {
+    complain_at (*loc, _("invalid identifier: %s"), quote (yytext));
+  }
+
+  /* Characters.  */
   "'"        STRING_GROW; token_start = loc->start; BEGIN SC_ESCAPED_CHARACTER;
 
   /* Strings. */
@@ -239,18 +272,25 @@ splice     (\\[ \f\t\v]*\n)*
   /* Code in between braces.  */
   "{" {
     STRING_GROW;
-    braces_level = 0;
+    nesting = 0;
     code_start = loc->start;
     BEGIN SC_BRACED_CODE;
   }
 
   /* A type. */
+  "<*>"       return TAG_ANY;
+  "<>"        return TAG_NONE;
   "<"{tag}">" {
     obstack_grow (&obstack_for_string, yytext + 1, yyleng - 2);
     STRING_FINISH;
     val->uniqstr = uniqstr_new (last_string);
     STRING_FREE;
-    return TYPE;
+    return TAG;
+  }
+  "<"         {
+    nesting = 0;
+    token_start = loc->start;
+    BEGIN SC_TAG;
   }
 
   "%%" {
@@ -260,6 +300,13 @@ splice      (\\[ \f\t\v]*\n)*
     return PERCENT_PERCENT;
   }
 
+  "[" {
+    bracketed_id_str = NULL;
+    bracketed_id_start = loc->start;
+    bracketed_id_context_state = YY_START;
+    BEGIN SC_BRACKETED_ID;
+  }
+
   . {
     complain_at (*loc, _("invalid character: %s"), quote (yytext));
   }
@@ -271,31 +318,111 @@ splice    (\\[ \f\t\v]*\n)*
 }
 
 
+  /*--------------------------------------------------------------.
+  | Supporting \0 complexifies our implementation for no expected |
+  | added value.                                                  |
+  `--------------------------------------------------------------*/
+
+<SC_ESCAPED_CHARACTER,SC_ESCAPED_STRING,SC_TAG>
+{
+  \0       complain_at (*loc, _("invalid null character"));
+}
+
+
   /*-----------------------------------------------------------------.
   | Scanning after an identifier, checking whether a colon is next.  |
   `-----------------------------------------------------------------*/
 
 <SC_AFTER_IDENTIFIER>
 {
+  "[" {
+    if (bracketed_id_str)
+      {
+       ROLLBACK_CURRENT_TOKEN;
+       BEGIN SC_RETURN_BRACKETED_ID;
+       *loc = id_loc;
+       return ID;
+      }
+    else
+      {
+       bracketed_id_start = loc->start;
+       bracketed_id_context_state = YY_START;
+       BEGIN SC_BRACKETED_ID;
+      }
+  }
   ":" {
+    BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL);
     *loc = id_loc;
-    BEGIN INITIAL;
     return ID_COLON;
   }
   . {
-    scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0);
-    yyless (0);
+    ROLLBACK_CURRENT_TOKEN;
+    BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL);
     *loc = id_loc;
-    BEGIN INITIAL;
     return ID;
   }
   <<EOF>> {
+    BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL);
     *loc = id_loc;
-    BEGIN INITIAL;
     return ID;
   }
 }
 
+  /*--------------------------------.
+  | Scanning bracketed identifiers. |
+  `--------------------------------*/
+
+<SC_BRACKETED_ID>
+{
+  {id} {
+    if (bracketed_id_str)
+      {
+       complain_at (*loc, _("unexpected identifier in bracketed name: %s"),
+                    quote (yytext));
+      }
+    else
+      {
+       bracketed_id_str = uniqstr_new (yytext);
+       bracketed_id_loc = *loc;
+      }
+  }
+  "]" {
+    BEGIN bracketed_id_context_state;
+    if (bracketed_id_str)
+      {
+       if (INITIAL == bracketed_id_context_state)
+         {
+           val->uniqstr = bracketed_id_str;
+           bracketed_id_str = 0;
+           *loc = bracketed_id_loc;
+           return BRACKETED_ID;
+         }
+      }
+    else
+      complain_at (*loc, _("an identifier expected"));
+  }
+  . {
+    complain_at (*loc, _("invalid character in bracketed name: %s"),
+                quote (yytext));
+  }
+  <<EOF>> {
+    BEGIN bracketed_id_context_state;
+    unexpected_eof (bracketed_id_start, "]");
+  }
+}
+
+<SC_RETURN_BRACKETED_ID>
+{
+  . {
+    ROLLBACK_CURRENT_TOKEN;
+    val->uniqstr = bracketed_id_str;
+    bracketed_id_str = 0;
+    *loc = bracketed_id_loc;
+    BEGIN INITIAL;
+    return BRACKETED_ID;
+  }
+}
+
 
   /*---------------------------------------------------------------.
   | Scanning a Yacc comment.  The initial `/ *' is already eaten.  |
@@ -366,35 +493,80 @@ splice     (\\[ \f\t\v]*\n)*
 <SC_ESCAPED_CHARACTER>
 {
   "'"|"\n" {
-    if (yytext[0] == '\n')
-      unexpected_newline (token_start, "'");
     STRING_GROW;
     STRING_FINISH;
     loc->start = token_start;
     val->character = last_string[1];
+    {
+      /* FIXME: Eventually, make these errors.  */
+      size_t length = strlen (last_string);
+      if (strlen (last_string) < 3)
+        warn_at (*loc, _("empty character literal"));
+      else if (strlen (last_string) > 3)
+        warn_at (*loc, _("extra characters in character literal"));
+    }
+    if (yytext[0] == '\n')
+      unexpected_newline (token_start, "'");
     STRING_FREE;
     BEGIN INITIAL;
     return CHAR;
   }
   <<EOF>> {
-    unexpected_eof (token_start, "'");
     STRING_FINISH;
     loc->start = token_start;
-    if (strlen(last_string) > 1)
-      val->character = last_string[1];
-    else
-      val->character = last_string[0];
+    {
+      size_t length = strlen (last_string);
+      /* FIXME: Eventually, make these errors.  */
+      if (length < 2)
+        warn_at (*loc, _("empty character literal"));
+      else if (length > 2)
+        warn_at (*loc, _("extra characters in character literal"));
+      if (length > 1)
+        val->character = last_string[1];
+      else
+        val->character = last_string[0];
+    }
+    unexpected_eof (token_start, "'");
     STRING_FREE;
     BEGIN INITIAL;
     return CHAR;
   }
 }
 
-<SC_ESCAPED_CHARACTER,SC_ESCAPED_STRING>
+  /*-----------------------------------------------------------.
+  | Scanning a Bison nested tag.  The initial angle bracket is |
+  | already eaten.                                             |
+  `-----------------------------------------------------------*/
+
+<SC_TAG>
 {
-  \0       complain_at (*loc, _("invalid null character"));
-}
+  ">" {
+    --nesting;
+    if (nesting < 0)
+      {
+        STRING_FINISH;
+        loc->start = token_start;
+        val->uniqstr = uniqstr_new (last_string);
+        STRING_FREE;
+        BEGIN INITIAL;
+        return TAG;
+      }
+    STRING_GROW;
+  }
 
+  [^<>]+ STRING_GROW;
+  "<"+   STRING_GROW; nesting += yyleng;
+
+  <<EOF>> {
+    unexpected_eof (token_start, ">");
+    STRING_FINISH;
+    loc->start = token_start;
+    val->uniqstr = uniqstr_new (last_string);
+    STRING_FREE;
+    BEGIN INITIAL;
+    return TAG;
+  }
+}
 
   /*----------------------------.
   | Decode escaped characters.  |
@@ -513,17 +685,17 @@ splice     (\\[ \f\t\v]*\n)*
 
 <SC_BRACED_CODE>
 {
-  "{"|"<"{splice}"%"  STRING_GROW; braces_level++;
-  "%"{splice}">"      STRING_GROW; braces_level--;
+  "{"|"<"{splice}"%"  STRING_GROW; nesting++;
+  "%"{splice}">"      STRING_GROW; nesting--;
   "}" {
     obstack_1grow (&obstack_for_string, '}');
 
-    --braces_level;
-    if (braces_level < 0)
+    --nesting;
+    if (nesting < 0)
       {
        STRING_FINISH;
        loc->start = code_start;
-       val->chars = last_string;
+       val->code = last_string;
        BEGIN INITIAL;
        return BRACED_CODE;
       }
@@ -537,7 +709,7 @@ splice       (\\[ \f\t\v]*\n)*
     unexpected_eof (code_start, "}");
     STRING_FINISH;
     loc->start = code_start;
-    val->chars = last_string;
+    val->code = last_string;
     BEGIN INITIAL;
     return BRACED_CODE;
   }
@@ -800,5 +972,5 @@ gram_scanner_free (void)
 {
   obstack_free (&obstack_for_string, 0);
   /* Reclaim Flex's buffers.  */
-  yy_delete_buffer (YY_CURRENT_BUFFER);
+  yylex_destroy ();
 }