]> git.mxchange.org Git - simgear.git/blob - simgear/nasal/lex.c
916356269c69e8795b8e452946b553dc314ee4b0
[simgear.git] / simgear / nasal / lex.c
1 #include "parse.h"
2
3 // Static table of recognized lexemes in the language
4 struct Lexeme {
5     char* str;
6     int   tok;
7 } LEXEMES[] = {
8     {"and", TOK_AND},
9     {"or",  TOK_OR},
10     {"!",   TOK_NOT},
11     {"(", TOK_LPAR},
12     {")", TOK_RPAR},
13     {"[", TOK_LBRA},
14     {"]", TOK_RBRA},
15     {"{", TOK_LCURL},
16     {"}", TOK_RCURL},
17     {"*", TOK_MUL},
18     {"+", TOK_PLUS},
19     {"-", TOK_MINUS},
20     {"/", TOK_DIV},
21     {"~", TOK_CAT},
22     {":", TOK_COLON},
23     {".", TOK_DOT},
24     {",", TOK_COMMA},
25     {";", TOK_SEMI},
26     {"=", TOK_ASSIGN},
27     {"<",  TOK_LT},
28     {"<=", TOK_LTE},
29     {"==", TOK_EQ},
30     {"!=", TOK_NEQ},
31     {">",  TOK_GT},
32     {">=", TOK_GTE},
33     {"nil", TOK_NIL},
34     {"if",    TOK_IF},
35     {"elsif", TOK_ELSIF},
36     {"else",  TOK_ELSE},
37     {"for",     TOK_FOR},
38     {"foreach", TOK_FOREACH},
39     {"while",   TOK_WHILE},
40     {"return",   TOK_RETURN},
41     {"break",    TOK_BREAK},
42     {"continue", TOK_CONTINUE},
43     {"func", TOK_FUNC},
44     {"...", TOK_ELLIPSIS},
45     {"?", TOK_QUESTION},
46     {"var", TOK_VAR},
47 };
48
49 // Build a table of where each line ending is
50 static int* findLines(struct Parser* p)
51 {
52     char* buf = p->buf;
53     int sz = p->len/10 + 16;
54     int* lines = naParseAlloc(p, (sizeof(int) * sz));
55     int i, j, n=0;
56
57     for(i=0; i<p->len; i++) {
58         // Not a line ending at all
59         if(buf[i] != '\n' && buf[i] != '\r')
60             continue;
61
62         // Skip over the \r of a \r\n pair.
63         if(buf[i] == '\r' && (i+1)<p->len && buf[i+1] == '\n') {
64             continue;
65         }
66         // Reallocate if necessary
67         if(n == sz) {
68             int* nl;
69             sz *= 2;
70             nl = naParseAlloc(p, sizeof(int) * sz);
71             for(j=0; j<n; j++) nl[j] = lines[j];
72             lines = nl;
73         }
74         lines[n++] = i;
75     }
76     p->lines = lines;
77     p->nLines = n;
78     return lines;
79 }
80
81 // What line number is the index on?
82 static int getLine(struct Parser* p, int index)
83 {
84     int i;
85     for(i=0; i<p->nLines; i++)
86         if(p->lines[i] > index)
87             return (p->firstLine-1) + i+1;
88     return (p->firstLine-1) + p->nLines+1;
89 }
90
91 static void error(struct Parser* p, char* msg, int index)
92 {
93     naParseError(p, msg, getLine(p, index));
94 }
95
96 // End index (the newline character) of the given line
97 static int lineEnd(struct Parser* p, int line)
98 {
99     if(line > p->nLines) return p->len;
100     return p->lines[line-1];
101 }
102
103 static void newToken(struct Parser* p, int pos, int type,
104                      char* str, int slen, double num)
105 {
106     struct Token* tok;
107
108     tok = naParseAlloc(p, sizeof(struct Token));
109     tok->type = type;
110     tok->line = getLine(p, pos);
111     tok->str = str;
112     tok->strlen = slen;
113     tok->num = num;
114     tok->parent = &p->tree;
115     tok->next = 0;
116     tok->prev = p->tree.lastChild;
117     tok->children = 0;
118     tok->lastChild = 0;
119
120     // Context sensitivity hack: a "-" following a binary operator of
121     // higher precedence (MUL and DIV, basically) must be a unary
122     // negation.  Needed to get precedence right in the parser for
123     // expressiong like "a * -2"
124     if(type == TOK_MINUS && tok->prev)
125         if(tok->prev->type == TOK_MUL || tok->prev->type == TOK_DIV)
126             tok->type = type = TOK_NEG;
127
128     if(!p->tree.children) p->tree.children = tok;
129     if(p->tree.lastChild) p->tree.lastChild->next = tok;
130     p->tree.lastChild = tok;
131 }
132
133 // Parse a hex nibble
134 static int hexc(char c, struct Parser* p, int index)
135 {
136     if(c >= '0' && c <= '9') return c - '0';
137     if(c >= 'A' && c <= 'F') return c - 'A' + 10;
138     if(c >= 'a' && c <= 'f') return c - 'a' + 10;
139     error(p, "bad hex constant", index);
140     return 0;
141 }
142
143 // Escape and returns a single backslashed expression in a single
144 // quoted string.  Trivial, just escape \' and leave everything else
145 // alone.
146 static void sqEscape(char* buf, int len, int index, struct Parser* p,
147                      char* cOut, int* eatenOut)
148 {
149     if(len < 2) error(p, "unterminated string", index);
150     if(buf[1] == '\'') {
151         *cOut = '\'';
152         *eatenOut = 2;
153     } else {
154         *cOut = '\\';
155         *eatenOut = 1;
156     }
157 }
158
159 // Ditto, but more complicated for double quotes.
160 static void dqEscape(char* buf, int len, int index, struct Parser* p,
161                      char* cOut, int* eatenOut)
162 {
163     if(len < 2) error(p, "unterminated string", index);
164     *eatenOut = 2;
165     switch(buf[1]) {
166     case '"': *cOut = '"'; break;
167     case 'r': *cOut = '\r'; break;
168     case 'n': *cOut = '\n'; break;
169     case 't': *cOut = '\t'; break;
170     case '\\': *cOut = '\\'; break;
171     case 'x':
172         if(len < 4) error(p, "unterminated string", index);
173         *cOut = (char)((hexc(buf[2], p, index)<<4) | hexc(buf[3], p, index));
174         *eatenOut = 4;
175         break;
176     default:
177         // Unhandled, put the backslash back
178         *cOut = '\\';
179         *eatenOut = 1;
180     }
181 }
182
183 // Read in a string literal
184 static int lexStringLiteral(struct Parser* p, int index, int singleQuote)
185 {
186     int i, j, len, iteration;
187     char* out = 0;
188     char* buf = p->buf;
189     char endMark = singleQuote ? '\'' : '"';
190
191     for(iteration = 0; iteration<2; iteration++) {
192         i = index+1;
193         j = len = 0;
194         while(i < p->len) {
195             char c = buf[i];
196             int eaten = 1;
197             if(c == endMark)
198                 break;
199             if(c == '\\') {
200                 if(singleQuote) sqEscape(buf+i, p->len-i, i, p, &c, &eaten);
201                 else            dqEscape(buf+i, p->len-i, i, p, &c, &eaten);
202             }
203             if(iteration == 1) out[j++] = c;
204             i += eaten;
205             len++;
206         }
207         // Finished stage one -- allocate the buffer for stage two
208         if(iteration == 0) out = naParseAlloc(p, len);
209     }
210     newToken(p, index, TOK_LITERAL, out, len, 0);
211     return i+1;
212 }
213
214 static int lexNumLiteral(struct Parser* p, int index)
215 {
216     int len = p->len, i = index;
217     unsigned char* buf = p->buf;
218     double d;
219
220     while(i<len && buf[i] >= '0' && buf[i] <= '9') i++;
221     if(i<len && buf[i] == '.') {
222         i++;
223         while(i<len && buf[i] >= '0' && buf[i] <= '9') i++;
224     }
225     if(i<len && (buf[i] == 'e' || buf[i] == 'E')) {
226         i++;
227         if(i<len
228            && (buf[i] == '-' || buf[i] == '+')
229            && (i+1<len && buf[i+1] >= '0' && buf[i+1] <= '9')) i++;
230         while(i<len && buf[i] >= '0' && buf[i] <= '9') i++;
231     }
232     naStr_parsenum(p->buf + index, i - index, &d);
233     newToken(p, index, TOK_LITERAL, 0, 0, d);
234     return i;
235 }
236
237 static int trySymbol(struct Parser* p, int start)
238 {
239     int i = start;
240     while((i < p->len) &&
241           ((p->buf[i] == '_') ||
242            (p->buf[i] >= 'A' && p->buf[i] <= 'Z') ||
243            (p->buf[i] >= 'a' && p->buf[i] <= 'z') ||
244            (p->buf[i] >= '0' && p->buf[i] <= '9')))
245     { i++; }
246     return i-start;
247 }
248
249 // Returns the length of lexeme l if the buffer prefix matches, or
250 // else zero.
251 static int matchLexeme(char* buf, int len, char* l)
252 {
253     int i;
254     for(i=0; i<len; i++) {
255         if(l[i] == 0)      return i;
256         if(l[i] != buf[i]) return 0;
257     }
258     // Ran out of buffer.  This is still OK if we're also at the end
259     // of the lexeme.
260     if(l[i] == 0) return i;
261     return 0;
262 }
263
264 // This is dumb and algorithmically slow.  It would be much more
265 // elegant to sort and binary search the lexeme list, but that's a lot
266 // more code and this really isn't very slow in practice; it checks
267 // every byte of every lexeme for each input byte.  There are less
268 // than 100 bytes of lexemes in the grammar.  Returns the number of
269 // bytes in the lexeme read (or zero if none was recognized)
270 static int tryLexemes(struct Parser* p, int index, int* lexemeOut)
271 {
272     int i, n, best, bestIndex=-1;
273     char* start = p->buf + index;
274     int len = p->len - index;
275
276     n = sizeof(LEXEMES) / sizeof(struct Lexeme);
277     best = 0;
278     for(i=0; i<n; i++) {
279         int l = matchLexeme(start, len, LEXEMES[i].str);
280         if(l > best) {
281             best = l;
282             bestIndex = i;
283         }
284     }
285     if(best > 0) *lexemeOut = bestIndex;
286     return best;
287 }
288
289 void naLex(struct Parser* p)
290 {
291     int i = 0;
292     findLines(p);
293     while(i<p->len) {
294         char c = p->buf[i];
295
296         // Whitespace, comments and string literals have obvious
297         // markers and can be handled by a switch:
298         int handled = 1;
299         switch(c) {
300         case ' ': case '\t': case '\n': case '\r': case '\f': case '\v':
301             i++;
302             break;
303         case '#':
304             i = lineEnd(p, getLine(p, i));
305             break;
306         case '\'': case '"':
307             i = lexStringLiteral(p, i, (c=='"' ? 0 : 1));
308             break;
309         default:
310             if(c >= '0' && c <= '9') i = lexNumLiteral(p, i);
311             else                     handled = 0;
312         }
313
314         // Lexemes and symbols are a little more complicated.  Pick
315         // the longest one that matches.  Since some lexemes look like
316         // symbols (e.g. "or") they need a higher precedence, but we
317         // don't want a lexeme match to clobber the beginning of a
318         // symbol (e.g. "orchid").  If neither match, we have a bad
319         // character in the mix.
320         if(!handled) {
321             int symlen=0, lexlen=0, lexeme;
322             lexlen = tryLexemes(p, i, &lexeme);
323             if((c>='A' && c<='Z') || (c>='a' && c<='z') || (c=='_'))
324                 symlen = trySymbol(p, i);
325             if(lexlen && lexlen >= symlen) {
326                 newToken(p, i, LEXEMES[lexeme].tok, 0, 0, 0);
327                 i += lexlen;
328             } else if(symlen) {
329                 newToken(p, i, TOK_SYMBOL, p->buf+i, symlen, 0);
330                 i += symlen;
331             } else {
332                 error(p, "illegal character", i);
333             }
334         }
335     }
336 }