(cos_skip_space): New function.
[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 int
24 cos_skip_space (unsigned char *str, size_t len, size_t start, size_t* endp)
25 {
26   int i = start;
27
28   while ( ( i < len ) && isspace (str[i]) )
29     {
30       i++;
31     }
32   *endp = i;
33   return i;
34 }
35
36 COS_object
37 cos_read_int (unsigned char *str, size_t len, size_t start, size_t* endp)
38 {
39   size_t i = start;
40   int c;
41   int negative_flag;
42   int dest;
43
44   if ( i < len )
45     {
46       switch ( str[i] )
47         {
48         case '+':
49           negative_flag = 0;
50           i++;
51           break;
52         case '-':
53           negative_flag = 1;
54           i++;
55           break;
56         default:
57           negative_flag = 0;
58         }
59
60       if ( (i < len) && (c = str[i++])
61            && ('0' <= c) && (c <= '9') )
62         {
63           dest = c - '0';
64
65           while ( i < len )
66             {
67               c = str[i];
68               if ( ('0' <= c) && (c <= '9') )
69                 {
70                   dest = dest * 10 + c - '0';
71                   i++;
72                 }
73               else if ( isspace (c)
74                         || ( c == '(' )
75                         || ( c == ')' )
76                         || ( c == '[' )
77                         || ( c == ']' )
78                         || ( c == '"' ) )
79                 {
80                   *endp = i;
81                   return cos_make_int ( negative_flag ? - dest : dest );
82                 }
83               else
84                 return NULL;
85             }
86           *endp = i;
87           return cos_make_int ( negative_flag ? - dest : dest );
88         }
89     }
90   return NULL;
91 }
92
93
94 int
95 cos_read_utf8 (unsigned char *str, size_t len, size_t start, size_t* endp)
96 {
97   size_t i = start;
98
99   if ( len >= i + 1 )
100     {
101       unsigned char c = str[i++];
102       int counter;
103       int cid;
104
105       if ( c < 0xC0 )
106         {
107           cid = c;
108           counter = 0;
109         }
110       else if ( c < 0xE0 )
111         {
112           cid = c & 0x1f;
113           counter = 1;
114         }
115       else if ( c < 0xF0 )
116         {
117           cid = c & 0x0f;
118           counter = 2;
119         }
120       else if ( c < 0xF8 )
121         {
122           cid = c & 0x07;
123           counter = 3;
124         }
125       else if ( c < 0xFC )
126         {
127           cid = c & 0x03;
128           counter = 4;
129         }
130       else
131         {
132           cid = c & 0x01;
133           counter = 5;
134         }
135
136       if (counter + i <= len)
137         {
138           int j;
139
140           for (j = 0; j < counter; j++)
141             cid = (cid << 6) | (str[i++] & 0x3F);
142           *endp = i;
143           return cid;
144         }
145     }
146   return -1;
147 }
148
149 int
150 cos_read_char (unsigned char *str, size_t len, size_t start, size_t* endp)
151 {
152   size_t i = start;
153
154   if ( (len >= start + 2) && (str[i++] == '?') )
155     {
156       if ( (len >= start + 3) && (str[i] == '\\') )
157         {
158           i++;
159           return cos_read_utf8 (str, len, i, endp);
160         }
161       else
162         return cos_read_utf8 (str, len, i, endp);
163     }
164
165   return -1;
166 }
167
168
169 COS_String
170 cos_read_string (unsigned char *str, size_t len, size_t start, size_t* endp)
171 {
172   size_t i = start;
173   int c;
174
175   if ( (len >= start + 2) && (str[i++] == '"') )
176     {
177       while ( ( i < len )
178               && ( (c = cos_read_utf8 (str, len, i, &i)) >= 0 )
179               )
180         {
181           if ( c == '"' )
182             {
183               return cos_make_string ((char*)&str[1], i - 2);
184             }
185           else if ( c == '\\' )
186             {
187               i++;
188               if ( cos_read_utf8 (str, len, i, &i) < 0 )
189                 return NULL;
190             }
191         }
192     }
193   return NULL;
194 }
195
196
197 static COS_Cons
198 cos_read_list0 (unsigned char *str, size_t len, size_t start, size_t* endp)
199 {
200   size_t i = start;
201
202   i = cos_skip_space (str, len, i, endp);
203   if ( len >= start + 1 )
204     {
205       COS_object car = cos_read_object (str, len, i, endp);
206
207       if ( car == NULL )
208         return NULL;
209       i = *endp;
210
211       i = cos_skip_space (str, len, i, endp);
212       if ( str[i] == ')' )
213         {
214           *endp = i + 1;
215           return cos_cons (car, cos_Qnil);
216         }
217       else if ( str[i] == '.' )
218         {
219           COS_object cdr;
220
221           i++;
222           if ( isspace (str[i])
223                || ( str[i] == '"' )
224                || ( str[i] == '[' )
225                || ( str[i] == '(' ) )
226             {
227               cdr = cos_read_object (str, len, i, endp);
228               if ( cdr == NULL )
229                 return NULL;
230               i = *endp;
231               i = cos_skip_space (str, len, i, endp);
232               if ( str[i] == ')' )
233                 {
234                   *endp = i + 1;
235                   return cos_cons (car, cdr);
236                 }
237               cos_release_object (car);
238               cos_release_object (cdr);
239               return NULL;
240             }
241           else
242             {
243               cos_release_object (car);
244               return NULL;
245             }
246         }
247       else
248         {
249           COS_object rest;
250
251           rest = cos_read_list0 (str, len, i, endp);
252           if ( rest == NULL )
253             return NULL;
254           return cos_cons (car, rest);
255         }
256     }
257   return NULL;
258 }
259
260 COS_Cons
261 cos_read_list (unsigned char *str, size_t len, size_t start, size_t* endp)
262 {
263   size_t i = start;
264
265   i = cos_skip_space (str, len, i, endp);
266   if ( (len >= start + 2) && (str[i++] == '(') )
267     {
268       return cos_read_list0 (str, len, i, endp);
269     }
270   return NULL;
271 }
272
273
274 COS_object
275 cos_read_object (unsigned char *str, size_t len, size_t start, size_t* endp)
276 {
277   COS_object val_obj;
278   int val_cid;
279   COS_String val_str;
280
281   start = cos_skip_space (str, len, start, endp);
282
283   val_obj = cos_read_int (str, len, start, endp);
284   if ( val_obj != NULL )
285     return val_obj;
286
287   val_cid = cos_read_char (str, len, start, endp);
288   if ( val_cid >= 0 )
289     return cos_make_char (val_cid);
290
291   val_str = cos_read_string (str, len, start, endp);
292   if ( val_str != NULL )
293     return val_str;
294
295   val_obj = cos_read_list (str, len, start, endp);
296   if ( val_obj != NULL )
297     return val_obj;
298
299   return NULL;
300 }