update.
[chise/concord.git] / read.c
1 /* Copyright (C) 2013 MORIOKA Tomohiko
2    This file is part of the CONCORD Library.
3
4    The CONCORD Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The CONCORD Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the CONCORD Library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
18
19 #include <stdlib.h>
20 #include <ctype.h>
21 #include "cos-read.h"
22
23 static int
24 is_delimiter (int ch)
25 {
26   return isspace (ch)
27     || ( ch == '(' )
28     || ( ch == ')' )
29     || ( ch == '[' )
30     || ( ch == ']' )
31     || ( ch == '"' )
32     || ( ch == '#' )
33     || ( ch == ';' );
34 }
35
36 static int
37 cos_skip_space (unsigned char *str, size_t len, size_t start, size_t* endp)
38 {
39   int i = start;
40
41   while ( ( i < len ) && isspace (str[i]) )
42     {
43       i++;
44     }
45   *endp = i;
46   return i;
47 }
48
49 COS_object
50 cos_read_int (unsigned char *str, size_t len, size_t start, size_t* endp)
51 {
52   size_t i = start;
53   int c;
54   int negative_flag;
55   int dest;
56
57   if ( i < len )
58     {
59       switch ( str[i] )
60         {
61         case '+':
62           negative_flag = 0;
63           i++;
64           break;
65         case '-':
66           negative_flag = 1;
67           i++;
68           break;
69         default:
70           negative_flag = 0;
71         }
72
73       if ( (i < len) && (c = str[i++])
74            && ('0' <= c) && (c <= '9') )
75         {
76           dest = c - '0';
77
78           while ( i < len )
79             {
80               c = str[i];
81               if ( ('0' <= c) && (c <= '9') )
82                 {
83                   dest = dest * 10 + c - '0';
84                   i++;
85                 }
86               else if ( is_delimiter (c) )
87                 {
88                   *endp = i;
89                   return cos_make_int ( negative_flag ? - dest : dest );
90                 }
91               else
92                 return NULL;
93             }
94           *endp = i;
95           return cos_make_int ( negative_flag ? - dest : dest );
96         }
97     }
98   return NULL;
99 }
100
101
102 int
103 cos_read_utf8 (unsigned char *str, size_t len, size_t start, size_t* endp)
104 {
105   size_t i = start;
106
107   if ( len >= i + 1 )
108     {
109       unsigned char c = str[i++];
110       int counter;
111       int cid;
112
113       if ( c < 0xC0 )
114         {
115           cid = c;
116           counter = 0;
117         }
118       else if ( c < 0xE0 )
119         {
120           cid = c & 0x1f;
121           counter = 1;
122         }
123       else if ( c < 0xF0 )
124         {
125           cid = c & 0x0f;
126           counter = 2;
127         }
128       else if ( c < 0xF8 )
129         {
130           cid = c & 0x07;
131           counter = 3;
132         }
133       else if ( c < 0xFC )
134         {
135           cid = c & 0x03;
136           counter = 4;
137         }
138       else
139         {
140           cid = c & 0x01;
141           counter = 5;
142         }
143
144       if (counter + i <= len)
145         {
146           int j;
147
148           for (j = 0; j < counter; j++)
149             cid = (cid << 6) | (str[i++] & 0x3F);
150           *endp = i;
151           return cid;
152         }
153     }
154   return -1;
155 }
156
157 int
158 cos_read_char (unsigned char *str, size_t len, size_t start, size_t* endp)
159 {
160   size_t i = start;
161
162   if ( (len >= start + 2) && (str[i++] == '?') )
163     {
164       if ( (len >= start + 3) && (str[i] == '\\') )
165         {
166           i++;
167           return cos_read_utf8 (str, len, i, endp);
168         }
169       else
170         return cos_read_utf8 (str, len, i, endp);
171     }
172
173   return -1;
174 }
175
176
177 COS_String
178 cos_read_string (unsigned char *str, size_t len, size_t start, size_t* endp)
179 {
180   size_t i = start;
181   int c;
182
183   if ( (len >= start + 2) && (str[i++] == '"') )
184     {
185       while ( ( i < len )
186               && ( (c = cos_read_utf8 (str, len, i, &i)) >= 0 )
187               )
188         {
189           if ( c == '"' )
190             {
191               *endp = i;
192               return cos_make_string ((char*)&str[start + 1], i - 2 - start);
193             }
194           else if ( c == '\\' )
195             {
196               i++;
197               if ( cos_read_utf8 (str, len, i, &i) < 0 )
198                 return NULL;
199             }
200         }
201     }
202   return NULL;
203 }
204
205
206 COS_Symbol
207 cos_read_symbol (unsigned char *str, size_t len, size_t start, size_t* endp)
208 {
209   size_t i = start;
210   int c;
211
212   if ( i < len )
213     {
214       while ( ( i < len )
215               && ( (c = cos_read_utf8 (str, len, i, &i)) >= 0 )
216               )
217         {
218           if ( is_delimiter (c) )
219             {
220               i--;
221               if ( i == start )
222                 return NULL;
223               else
224                 {
225                   *endp = i;
226                   return
227                     cos_intern (cos_make_string (&str[start], i - start));
228                 }
229             }
230           else if ( c == '\\' )
231             {
232               i++;
233               if ( cos_read_utf8 (str, len, i, &i) < 0 )
234                 return NULL;
235             }
236         }
237     }
238   if ( i == start )
239     return NULL;
240   else
241     {
242       *endp = i;
243       return cos_intern (cos_make_string (&str[start], i - start));
244     }
245 }
246
247
248 static COS_Cons
249 cos_read_list0 (unsigned char *str, size_t len, size_t start, size_t* endp)
250 {
251   size_t i = start;
252
253   i = cos_skip_space (str, len, i, endp);
254   if ( len >= start + 1 )
255     {
256       COS_object car = cos_read_object (str, len, i, endp);
257
258       if ( car == NULL )
259         return NULL;
260       i = *endp;
261
262       i = cos_skip_space (str, len, i, endp);
263       if ( str[i] == ')' )
264         {
265           *endp = i + 1;
266           return cos_cons (car, cos_Qnil);
267         }
268       else if ( str[i] == '.' )
269         {
270           COS_object cdr;
271
272           i++;
273           if ( isspace (str[i])
274                || ( str[i] == '"' )
275                || ( str[i] == '[' )
276                || ( str[i] == '(' ) )
277             {
278               cdr = cos_read_object (str, len, i, endp);
279               if ( cdr == NULL )
280                 return NULL;
281               i = *endp;
282               i = cos_skip_space (str, len, i, endp);
283               if ( str[i] == ')' )
284                 {
285                   *endp = i + 1;
286                   return cos_cons (car, cdr);
287                 }
288               cos_release_object (car);
289               cos_release_object (cdr);
290               return NULL;
291             }
292           else
293             {
294               cos_release_object (car);
295               return NULL;
296             }
297         }
298       else
299         {
300           COS_object rest;
301
302           rest = cos_read_list0 (str, len, i, endp);
303           if ( rest == NULL )
304             return NULL;
305           return cos_cons (car, rest);
306         }
307     }
308   return NULL;
309 }
310
311 COS_Cons
312 cos_read_list (unsigned char *str, size_t len, size_t start, size_t* endp)
313 {
314   size_t i = start;
315
316   i = cos_skip_space (str, len, i, endp);
317   if ( (len >= start + 2) && (str[i++] == '(') )
318     {
319       return cos_read_list0 (str, len, i, endp);
320     }
321   return NULL;
322 }
323
324
325 COS_object
326 cos_read_object (unsigned char *str, size_t len, size_t start, size_t* endp)
327 {
328   COS_object val_obj;
329   int val_cid;
330   COS_String val_str;
331
332   start = cos_skip_space (str, len, start, endp);
333
334   val_obj = cos_read_list (str, len, start, endp);
335   if ( val_obj != NULL )
336     return val_obj;
337
338   val_obj = cos_read_int (str, len, start, endp);
339   if ( val_obj != NULL )
340     return val_obj;
341
342   val_cid = cos_read_char (str, len, start, endp);
343   if ( val_cid >= 0 )
344     return cos_make_char (val_cid);
345
346   val_str = cos_read_string (str, len, start, endp);
347   if ( val_str != NULL )
348     return val_str;
349
350   val_obj = cos_read_symbol (str, len, start, endp);
351   if ( val_obj != NULL )
352     return val_obj;
353
354   return NULL;
355 }