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