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