a95a157d78cd0f0c2dcfe1844c152ec88782f4e5
[pspp-builds.git] / src / data / identifier.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000, 2005 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17    02110-1301, USA. */
18
19 /*
20    This file is concerned with the definition of the PSPP syntax, NOT the
21    action of scanning/parsing code .
22 */
23
24 #include <config.h>
25 #include "identifier.h"
26
27
28 #include <assert.h>
29 #include <string.h>
30 #include <libpspp/assertion.h>
31
32 /* Recognizing identifiers. */
33
34 /* Returns true if C may be the first character in an
35    identifier in the current locale. */
36 bool
37 lex_is_id1 (char c_)
38 {
39   unsigned char c = c_;
40   return isalpha (c) || c == '@' || c == '#' || c == '$';
41 }
42
43
44 /* Returns true if C may be a character in an identifier other
45    than the first. */
46 bool
47 lex_is_idn (char c_)
48 {
49   unsigned char c = c_;
50   return lex_is_id1 (c) || isdigit (c) || c == '.' || c == '_';
51 }
52
53 /* Returns the length of the longest prefix of STRING that forms
54    a valid identifier.  Returns zero if STRING does not begin
55    with a valid identifier.  */
56 size_t
57 lex_id_get_length (struct substring string)
58 {
59   size_t length = 0;
60   if (!ss_is_empty (string) && lex_is_id1 (ss_first (string)))
61     {
62       length = 1;
63       while (length < ss_length (string)
64              && lex_is_idn (ss_at (string, length)))
65         length++;
66     }
67   return length;
68 }
69 \f
70 /* Comparing identifiers. */
71
72 /* Returns true if TOKEN is a case-insensitive match for KEYWORD.
73
74    Keywords match if one of the following is true: KEYWORD and
75    TOKEN are identical, or TOKEN is at least 3 characters long
76    and those characters are identical to KEYWORD. */
77 bool
78 lex_id_match (struct substring keyword, struct substring token)
79 {
80   size_t token_len = ss_length (token);
81   size_t keyword_len = ss_length (keyword);
82
83   if (token_len >= 3 && token_len < keyword_len)
84     return ss_equals_case (ss_head (keyword, token_len), token);
85   else
86     return ss_equals_case (keyword, token);
87 }
88 \f
89 /* Table of keywords. */
90 struct keyword
91   {
92     int token;
93     const struct substring identifier;
94   };
95
96 static const struct keyword keywords[] =
97   {
98     { T_AND,  SS_LITERAL_INITIALIZER ("AND") },
99     { T_OR,   SS_LITERAL_INITIALIZER ("OR") },
100     { T_NOT,  SS_LITERAL_INITIALIZER ("NOT") },
101     { T_EQ,   SS_LITERAL_INITIALIZER ("EQ") },
102     { T_GE,   SS_LITERAL_INITIALIZER ("GE") },
103     { T_GT,   SS_LITERAL_INITIALIZER ("GT") },
104     { T_LE,   SS_LITERAL_INITIALIZER ("LE") },
105     { T_LT,   SS_LITERAL_INITIALIZER ("LT") },
106     { T_NE,   SS_LITERAL_INITIALIZER ("NE") },
107     { T_ALL,  SS_LITERAL_INITIALIZER ("ALL") },
108     { T_BY,   SS_LITERAL_INITIALIZER ("BY") },
109     { T_TO,   SS_LITERAL_INITIALIZER ("TO") },
110     { T_WITH, SS_LITERAL_INITIALIZER ("WITH") },
111   };
112 static const size_t keyword_cnt = sizeof keywords / sizeof *keywords;
113
114 /* Returns true if TOKEN is representable as a keyword. */
115 bool
116 lex_is_keyword (int token)
117 {
118   const struct keyword *kw;
119   for (kw = keywords; kw < &keywords[keyword_cnt]; kw++)
120     if (kw->token == token)
121       return true;
122   return false;
123 }
124
125 /* Returns the proper token type, either T_ID or a reserved
126    keyword enum, for ID. */
127 int
128 lex_id_to_token (struct substring id)
129 {
130   if (ss_length (id) >= 2 && ss_length (id) <= 4)
131     {
132       const struct keyword *kw;
133       for (kw = keywords; kw < &keywords[keyword_cnt]; kw++)
134         if (ss_equals_case (kw->identifier, id))
135           return kw->token;
136     }
137
138   return T_ID;
139 }
140
141 /* Returns the name for the given keyword token type. */
142 const char *
143 lex_id_name (int token)
144 {
145   const struct keyword *kw;
146
147   for (kw = keywords; kw < &keywords[keyword_cnt]; kw++)
148     if (kw->token == token)
149       {
150         /* A "struct substring" is not guaranteed to be
151            null-terminated, as our caller expects, but in this
152            case it always will be. */
153         return ss_data (kw->identifier);
154       }
155   NOT_REACHED ();
156 }