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