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