]> mj.ucw.cz Git - leo.git/blob - css-lex.c
More clean OSM XML parsing
[leo.git] / css-lex.c
1 /*
2  *      Experimenta lMai Renderer -- MapCSS Lexer
3  *
4  *      (c) 2014 Martin Mares <mj@ucw.cz>
5  */
6
7 #include <ucw/lib.h>
8 #include <ucw/chartype.h>
9 #include <ucw/fastbuf.h>
10 #include <ucw/mempool.h>
11
12 #include <fcntl.h>
13
14 #include "leo.h"
15 #include "style.h"
16 #include "css.h"
17 #include "css-parse.h"
18
19 static struct fastbuf *fb;
20 static int lino;
21
22 void css_error(char *err, ...)
23 {
24   va_list args;
25   va_start(args, err);
26   char *msg = mp_vprintf(css_this->pool, err, args);
27   die("Error in %s, line %d: %s", css_this->filename, lino, msg);
28 }
29
30 void css_lex_open(void)
31 {
32   fb = bopen_file(css_this->filename, O_RDONLY, NULL);
33   lino = 1;
34 }
35
36 void css_lex_close(void)
37 {
38   bclose(fb);
39 }
40
41 int css_lex(void)
42 {
43   struct mempool *mp = css_this->pool;
44   char *p;
45   int c, next, len;
46
47   for (;;)
48     {
49       c = bgetc(fb);
50       if (c < 0)
51         return 0;
52       if (c == '\n')
53         {
54           lino++;
55           continue;
56         }
57       if (c == ' ' || c == '\t')
58         continue;
59
60       next = bpeekc(fb);
61
62       if (c >= '0' && c <= '9' || c == '-' && next >= '0' && next <= '9')
63         {
64           // Number
65           p = mp_start(mp, 1);
66           if (c == '-')
67             {
68               p = mp_append_char(mp, p, c);
69               c = bgetc(fb);
70             }
71           while (c >= '0' && c <= '9')
72             {
73               p = mp_append_char(mp, p, c);
74               c = bgetc(fb);
75             }
76           if (c == '.')
77             {
78               p = mp_append_char(mp, p, c);
79               c = bgetc(fb);
80               while (c >= '0' && c <= '9')
81                 {
82                   p = mp_append_char(mp, p, c);
83                   c = bgetc(fb);
84                 }
85             }
86           p = mp_end_string(mp, p);
87           if (c >= 0)
88             bungetc(fb);
89           css_lval.s = p;
90           return NUMBER;
91         }
92
93       if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_')
94         {
95           // Alphabetical identifier
96           // FIXME: Identifiers starting with "-" are not supported
97           // FIXME: Unquoted identifiers should be always case-insensitive
98           p = mp_start(mp, 1);
99           while (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '-' || c == '_')
100             {
101               p = mp_append_char(mp, p, c);
102               c = bgetc(fb);
103             }
104           p = mp_end_string(mp, p);
105           if (c >= 0)
106             bungetc(fb);
107           css_lval.s = p;
108           return IDENT;
109         }
110
111       switch (c)
112         {
113         case '"':
114           // Quoted string
115           p = mp_start(mp, 1);
116           c = bgetc(fb);
117           while (c != '"')
118             {
119               if (c < 0)
120                 css_error("Unterminated string literal");
121               if (c == '\\')
122                 {
123                   c = bgetc(fb);
124                   if (c == '\\' || c == '"')
125                     ;
126                   else if (c == 'n')
127                     c = '\n';
128                   else
129                     css_error("Unknown backslash sequence \"\\%c\" in string literal", c);
130                 }
131               p = mp_append_char(mp, p, c);
132               c = bgetc(fb);
133             }
134           css_lval.s = mp_end_string(mp, p);
135           return QUOTED;
136
137         case '/':
138           if (next == '*')
139             {
140               // C comment
141               bgetc(fb);
142               c = bgetc(fb);
143               for (;;)
144                 {
145                   if (c < 0)
146                     css_error("Unterminated comment");
147                   if (c == '\n')
148                     {
149                       lino++;
150                       c = bgetc(fb);
151                     }
152                   else if (c == '*')
153                     {
154                       c = bgetc(fb);
155                       if (c == '/')
156                         break;
157                     }
158                   else
159                     c = bgetc(fb);
160                 }
161               continue;
162              }
163           else if (next == '/')
164             {
165               // C++ comment
166               do
167                 c = bgetc(fb);
168               while (c >= 0 && c != '\n');
169               lino++;
170               continue;
171             }
172           return '/';
173
174         case '#':
175           p = mp_start(mp, 1);
176           while (next >= '0' && next <= '9' || next >= 'a' && next <= 'f' || next >= 'A' && next <= 'F')
177             {
178               p = mp_append_char(mp, p, bgetc(fb));
179               next = bpeekc(fb);
180             }
181           p = mp_end_string(mp, p);
182           len = strlen(p);
183           if (len != 3 && len != 6)
184             css_error("Invalid RGB literal");
185           css_lval.s = p;
186           return RGB;
187
188           // One-character operators
189         case '{':
190         case '}':
191         case '[':
192         case ']':
193         case ';':
194         case ',':
195         case '*':
196         case '.':
197         case '=':
198         case '?':
199           return c;
200
201           // Two-character operators
202         case '!':
203           if (next == '=')
204             {
205               bgetc(fb);
206               return NE;
207             }
208           return '!';
209         case '<':
210           if (next == '=')
211             {
212               bgetc(fb);
213               return LE;
214             }
215           return '<';
216         case '>':
217           if (next == '=')
218             {
219               bgetc(fb);
220               return GE;
221             }
222           return '>';
223         case ':':
224           if (next == ':')
225             {
226               bgetc(fb);
227               return CC;
228             }
229           return ':';
230
231         default:
232           css_error("Invalid character \"%c\"", c);
233         }
234     }
235 }
236
237 color_t css_rgb_to_color(const char *rgb)
238 {
239   uns n = strlen(rgb);
240   ASSERT(n == 3 || n == 6);
241   color_t color = 0;
242   for (uns i=0; i<n; i++)
243     {
244       uns j = Cxvalue(rgb[i]);
245       if (n == 6)
246         color = (color << 4) | j;
247       else
248         color = (color << 8) | (j * 17);
249     }
250   return color;
251 }