]> mj.ucw.cz Git - leo.git/blob - css-lex.c
ae6727168b5315161f8df805c770bb544e63059c
[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 }
47
48 int css_lex(void)
49 {
50   struct mempool *mp = css_this->pool;
51   char *p;
52   int c, next, len, tok;
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           // One-character operators
202         case '{':
203         case '}':
204         case '[':
205         case ']':
206         case ';':
207         case ',':
208         case '*':
209         case '.':
210         case '=':
211         case '?':
212           return c;
213
214           // Two-character operators
215         case '!':
216           if (next == '=')
217             {
218               bgetc(fb);
219               return NE;
220             }
221           return '!';
222         case '<':
223           if (next == '=')
224             {
225               bgetc(fb);
226               return LE;
227             }
228           return '<';
229         case '>':
230           if (next == '=')
231             {
232               bgetc(fb);
233               return GE;
234             }
235           return '>';
236         case ':':
237           if (next == ':')
238             {
239               bgetc(fb);
240               return CC;
241             }
242           return ':';
243
244         default:
245           css_error("Invalid character \"%c\"", c);
246         }
247     }
248 }
249
250 color_t css_rgb_to_color(const char *rgb)
251 {
252   uns n = strlen(rgb);
253   ASSERT(n == 3 || n == 6);
254   color_t color = 0;
255   for (uns i=0; i<n; i++)
256     {
257       uns j = Cxvalue(rgb[i]);
258       if (n == 6)
259         color = (color << 4) | j;
260       else
261         color = (color << 8) | (j * 17);
262     }
263   return color;
264 }