Add new function nmem_strsplitx.
[yaz-moved-to-github.git] / src / ccltoken.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2011 Index Data
3  * See the file LICENSE for details.
4  */
5 /** 
6  * \file ccltoken.c
7  * \brief Implements CCL lexical analyzer (scanner)
8  */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <string.h>
14 #include <stdlib.h>
15 #include <yaz/yaz-iconv.h>
16 #include "cclp.h"
17
18 /*
19  * token_cmp: Compare token with keyword(s)
20  * kw:     Keyword list. Each keyword is separated by space.
21  * token:  CCL token.
22  * return: 1 if token string matches one of the keywords in list;
23  *         0 otherwise.
24  */
25 static int token_cmp(CCL_parser cclp, const char **kw, struct ccl_token *token)
26 {
27     const char **aliases;
28     int case_sensitive = cclp->ccl_case_sensitive;
29     int i;
30
31     aliases = ccl_qual_search_special(cclp->bibset, "case");
32     if (aliases)
33         case_sensitive = atoi(aliases[0]);
34
35     for (i = 0; kw[i]; i++)
36     {
37         if (token->len == strlen(kw[i]))
38         {
39             if (case_sensitive)
40             {
41                 if (!memcmp(kw[i], token->name, token->len))
42                     return 1;
43             }
44             else
45             {
46                 if (!ccl_memicmp(kw[i], token->name, token->len))
47                     return 1;
48             }
49         }
50     }
51     return 0;
52 }
53
54 /*
55  * ccl_tokenize: tokenize CCL command string.
56  * return: CCL token list.
57  */
58 struct ccl_token *ccl_parser_tokenize(CCL_parser cclp, const char *command)
59 {
60     const char **aliases;
61     const unsigned char *cp = (const unsigned char *) command;
62     struct ccl_token *first = NULL;
63     struct ccl_token *last = NULL;
64     cclp->start_pos = command;
65
66     while (1)
67     {
68         const unsigned char *cp0 = cp;
69         while (*cp && strchr(" \t\r\n", *cp))
70             cp++;
71         if (!first)
72         {
73             first = last = (struct ccl_token *)xmalloc(sizeof(*first));
74             ccl_assert(first);
75             last->prev = NULL;
76         }
77         else
78         {
79             last->next = (struct ccl_token *)xmalloc(sizeof(*first));
80             ccl_assert(last->next);
81             last->next->prev = last;
82             last = last->next;
83         }
84         last->left_trunc = last->right_trunc = 0;
85         last->ws_prefix_buf = (const char *) cp0;
86         last->ws_prefix_len = cp - cp0;
87         last->next = NULL;
88         last->name = (const char *) cp;
89         last->len = 1;
90         switch (*cp++)
91         {
92         case '\0':
93             last->kind = CCL_TOK_EOL;
94             return first;
95         case '(':
96             last->kind = CCL_TOK_LP;
97             break;
98         case ')':
99             last->kind = CCL_TOK_RP;
100             break;
101         case ',':
102             last->kind = CCL_TOK_COMMA;
103             break;
104         case '%':
105         case '!':
106             last->kind = CCL_TOK_PROX;
107             while (yaz_isdigit(*cp))
108             {
109                 ++ last->len;
110                 cp++;
111             }
112             break;
113         case '>':
114         case '<':
115         case '=':
116             if (*cp == '=' || *cp == '<' || *cp == '>')
117             {
118                 cp++;
119                 last->kind = CCL_TOK_REL;
120                 ++ last->len;
121             }
122             else if (cp[-1] == '=')
123                 last->kind = CCL_TOK_EQ;
124             else
125                 last->kind = CCL_TOK_REL;
126             break;
127         default:
128             --cp;
129             --last->len;
130             if (*cp == '?')
131             {
132                 last->left_trunc = 1;
133                 cp++;
134             }
135             if (*cp == '"')
136             {
137                 cp++;
138                 last->kind = CCL_TOK_TERM;
139                 last->name = (const char *) cp;
140                 while (*cp && *cp != '"')
141                 {
142                     cp++;
143                     ++ last->len;
144                 }
145                 if (*cp)
146                     cp++;
147             }
148             else
149             {
150                 last->kind = CCL_TOK_TERM;
151                 last->name = (const char *) cp;
152                 while (*cp && !strchr("(),%!><=? \t\n\r", *cp))
153                 {
154                     ++ last->len;
155                     cp++;
156                 }
157                 aliases = ccl_qual_search_special(cclp->bibset, "and");
158                 if (!aliases)
159                     aliases = cclp->ccl_token_and;
160                 if (token_cmp(cclp, aliases, last))
161                     last->kind = CCL_TOK_AND;
162                 
163                 aliases = ccl_qual_search_special(cclp->bibset, "or");
164                 if (!aliases)
165                     aliases = cclp->ccl_token_or;
166                 if (token_cmp(cclp, aliases, last))
167                     last->kind = CCL_TOK_OR;
168                 
169                 aliases = ccl_qual_search_special(cclp->bibset, "not");
170                 if (!aliases)
171                     aliases = cclp->ccl_token_not;
172                 if (token_cmp(cclp, aliases, last))
173                     last->kind = CCL_TOK_NOT;
174                 
175                 aliases = ccl_qual_search_special(cclp->bibset, "set");
176                 if (!aliases)
177                     aliases = cclp->ccl_token_set;
178                 
179                 if (token_cmp(cclp, aliases, last))
180                     last->kind = CCL_TOK_SET;
181             }
182             if (*cp == '?')
183             {
184                 last->right_trunc = 1;
185                 cp++;
186             }
187         }
188     }
189     return first;
190 }
191
192 struct ccl_token *ccl_token_add(struct ccl_token *at)
193 {
194     struct ccl_token *n = (struct ccl_token *)xmalloc(sizeof(*n));
195     ccl_assert(n);
196     n->next = at->next;
197     n->prev = at;
198     at->next = n;
199     if (n->next)
200         n->next->prev = n;
201
202     n->kind = CCL_TOK_TERM;
203     n->left_trunc = n->right_trunc = 0;
204     n->name = 0;
205     n->len = 0;
206     n->ws_prefix_buf = 0;
207     n->ws_prefix_len = 0;
208     return n;
209 }
210     
211 /*
212  * ccl_token_del: delete CCL tokens
213  */
214 void ccl_token_del(struct ccl_token *list)
215 {
216     struct ccl_token *list1;
217
218     while (list) 
219     {
220         list1 = list->next;
221         xfree(list);
222         list = list1;
223     }
224 }
225
226 static const char **create_ar(const char *v1, const char *v2)
227 {
228     const char **a = (const char **) xmalloc(3 * sizeof(*a));
229     a[0] = xstrdup(v1);
230     if (v2)
231     {
232         a[1] = xstrdup(v2);
233         a[2] = 0;
234     }
235     else
236         a[1] = 0;
237     return a;
238 }
239
240 static void destroy_ar(const char **a)
241 {
242     if (a)
243     {
244         int i;
245         for (i = 0; a[i]; i++)
246             xfree((char *) a[i]);
247         xfree((char **)a);
248     }
249 }
250
251 CCL_parser ccl_parser_create(CCL_bibset bibset)
252 {
253     CCL_parser p = (CCL_parser)xmalloc(sizeof(*p));
254     if (!p)
255         return p;
256     p->look_token = NULL;
257     p->error_code = 0;
258     p->error_pos = NULL;
259     p->bibset = bibset;
260
261     p->ccl_token_and = create_ar("and", 0);
262     p->ccl_token_or = create_ar("or", 0);
263     p->ccl_token_not = create_ar("not", "andnot");
264     p->ccl_token_set = create_ar("set", 0);
265     p->ccl_case_sensitive = 1;
266
267     return p;
268 }
269
270 void ccl_parser_destroy(CCL_parser p)
271 {
272     if (!p)
273         return;
274     destroy_ar(p->ccl_token_and);
275     destroy_ar(p->ccl_token_or);
276     destroy_ar(p->ccl_token_not);
277     destroy_ar(p->ccl_token_set);
278     xfree(p);
279 }
280
281 void ccl_parser_set_case(CCL_parser p, int case_sensitivity_flag)
282 {
283     if (p)
284         p->ccl_case_sensitive = case_sensitivity_flag;
285 }
286
287 int ccl_parser_get_error(CCL_parser cclp, int *pos)
288 {
289     if (pos && cclp->error_code)
290         *pos = cclp->error_pos - cclp->start_pos;
291     return cclp->error_code;
292 }
293
294 /*
295  * Local variables:
296  * c-basic-offset: 4
297  * c-file-style: "Stroustrup"
298  * indent-tabs-mode: nil
299  * End:
300  * vim: shiftwidth=4 tabstop=8 expandtab
301  */
302