]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bc/src/bc_lex.c
MFV: xz 5.4.4.
[FreeBSD/FreeBSD.git] / contrib / bc / src / bc_lex.c
1 /*
2  * *****************************************************************************
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  *
6  * Copyright (c) 2018-2023 Gavin D. Howard and contributors.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * * Redistributions of source code must retain the above copyright notice, this
12  *   list of conditions and the following disclaimer.
13  *
14  * * Redistributions in binary form must reproduce the above copyright notice,
15  *   this list of conditions and the following disclaimer in the documentation
16  *   and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * *****************************************************************************
31  *
32  * The lexer for bc.
33  *
34  */
35
36 #if BC_ENABLED
37
38 #include <assert.h>
39 #include <ctype.h>
40 #include <string.h>
41
42 #include <bc.h>
43 #include <vm.h>
44
45 /**
46  * Lexes an identifier, which may be a keyword.
47  * @param l  The lexer.
48  */
49 static void
50 bc_lex_identifier(BcLex* l)
51 {
52         // We already passed the first character, so we need to be sure to include
53         // it.
54         const char* buf = l->buf + l->i - 1;
55         size_t i;
56
57         // This loop is simply checking for keywords.
58         for (i = 0; i < bc_lex_kws_len; ++i)
59         {
60                 const BcLexKeyword* kw = bc_lex_kws + i;
61                 size_t n = BC_LEX_KW_LEN(kw);
62
63                 if (!strncmp(buf, kw->name, n) && !isalnum(buf[n]) && buf[n] != '_')
64                 {
65                         // If the keyword has been redefined, and redefinition is allowed
66                         // (it is not allowed for builtin libraries), break out of the loop
67                         // and use it as a name. This depends on the argument parser to
68                         // ensure that only non-POSIX keywords get redefined.
69                         if (!vm->no_redefine && vm->redefined_kws[i]) break;
70
71                         l->t = BC_LEX_KW_AUTO + (BcLexType) i;
72
73                         // Warn or error, as appropriate for the mode, if the keyword is not
74                         // in the POSIX standard.
75                         if (!BC_LEX_KW_POSIX(kw)) bc_lex_verr(l, BC_ERR_POSIX_KW, kw->name);
76
77                         // We minus 1 because the index has already been incremented.
78                         l->i += n - 1;
79
80                         // Already have the token; bail.
81                         return;
82                 }
83         }
84
85         // If not a keyword, parse the name.
86         bc_lex_name(l);
87
88         // POSIX doesn't allow identifiers that are more than one character, so we
89         // might have to warn or error here too.
90         if (BC_ERR(l->str.len - 1 > 1))
91         {
92                 bc_lex_verr(l, BC_ERR_POSIX_NAME_LEN, l->str.v);
93         }
94 }
95
96 /**
97  * Parses a bc string. This is separate from dc strings because dc strings need
98  * to be balanced.
99  * @param l  The lexer.
100  */
101 static void
102 bc_lex_string(BcLex* l)
103 {
104         // We need to keep track of newlines to increment them properly.
105         size_t len, nlines, i;
106         const char* buf;
107         char c;
108         bool got_more;
109
110         l->t = BC_LEX_STR;
111
112         do
113         {
114                 nlines = 0;
115                 buf = l->buf;
116                 got_more = false;
117
118                 assert(vm->mode != BC_MODE_STDIN || buf == vm->buffer.v);
119
120                 // Fortunately for us, bc doesn't escape quotes. Instead, the equivalent
121                 // is '\q', which makes this loop simpler.
122                 for (i = l->i; (c = buf[i]) && c != '"'; ++i)
123                 {
124                         nlines += (c == '\n');
125                 }
126
127                 if (BC_ERR(c == '\0') && !vm->eof && l->mode != BC_MODE_FILE)
128                 {
129                         got_more = bc_lex_readLine(l);
130                 }
131         }
132         while (got_more && c != '"');
133
134         // If the string did not end properly, barf.
135         if (c != '"')
136         {
137                 l->i = i;
138                 bc_lex_err(l, BC_ERR_PARSE_STRING);
139         }
140
141         // Set the temp string to the parsed string.
142         len = i - l->i;
143         bc_vec_string(&l->str, len, l->buf + l->i);
144
145         l->i = i + 1;
146         l->line += nlines;
147 }
148
149 /**
150  * This function takes a lexed operator and checks to see if it's the assignment
151  * version, setting the token appropriately.
152  * @param l        The lexer.
153  * @param with     The token to assign if it is an assignment operator.
154  * @param without  The token to assign if it is not an assignment operator.
155  */
156 static void
157 bc_lex_assign(BcLex* l, BcLexType with, BcLexType without)
158 {
159         if (l->buf[l->i] == '=')
160         {
161                 l->i += 1;
162                 l->t = with;
163         }
164         else l->t = without;
165 }
166
167 void
168 bc_lex_token(BcLex* l)
169 {
170         // We increment here. This means that all lexing needs to take that into
171         // account, such as when parsing an identifier. If we don't, the first
172         // character of every identifier would be missing.
173         char c = l->buf[l->i++], c2;
174
175         BC_SIG_ASSERT_LOCKED;
176
177         // This is the workhorse of the lexer.
178         switch (c)
179         {
180                 case '\0':
181                 case '\n':
182                 case '\t':
183                 case '\v':
184                 case '\f':
185                 case '\r':
186                 case ' ':
187                 {
188                         bc_lex_commonTokens(l, c);
189                         break;
190                 }
191
192                 case '!':
193                 {
194                         // Even though it's not an assignment, we can use this.
195                         bc_lex_assign(l, BC_LEX_OP_REL_NE, BC_LEX_OP_BOOL_NOT);
196
197                         // POSIX doesn't allow boolean not.
198                         if (l->t == BC_LEX_OP_BOOL_NOT)
199                         {
200                                 bc_lex_verr(l, BC_ERR_POSIX_BOOL, "!");
201                         }
202
203                         break;
204                 }
205
206                 case '"':
207                 {
208                         bc_lex_string(l);
209                         break;
210                 }
211
212                 case '#':
213                 {
214                         // POSIX does not allow line comments.
215                         bc_lex_err(l, BC_ERR_POSIX_COMMENT);
216                         bc_lex_lineComment(l);
217                         break;
218                 }
219
220                 case '%':
221                 {
222                         bc_lex_assign(l, BC_LEX_OP_ASSIGN_MODULUS, BC_LEX_OP_MODULUS);
223                         break;
224                 }
225
226                 case '&':
227                 {
228                         c2 = l->buf[l->i];
229
230                         // Either we have boolean and or an error. And boolean and is not
231                         // allowed by POSIX.
232                         if (BC_NO_ERR(c2 == '&'))
233                         {
234                                 bc_lex_verr(l, BC_ERR_POSIX_BOOL, "&&");
235
236                                 l->i += 1;
237                                 l->t = BC_LEX_OP_BOOL_AND;
238                         }
239                         else bc_lex_invalidChar(l, c);
240
241                         break;
242                 }
243 #if BC_ENABLE_EXTRA_MATH
244                 case '$':
245                 {
246                         l->t = BC_LEX_OP_TRUNC;
247                         break;
248                 }
249
250                 case '@':
251                 {
252                         bc_lex_assign(l, BC_LEX_OP_ASSIGN_PLACES, BC_LEX_OP_PLACES);
253                         break;
254                 }
255 #endif // BC_ENABLE_EXTRA_MATH
256                 case '(':
257                 case ')':
258                 {
259                         l->t = (BcLexType) (c - '(' + BC_LEX_LPAREN);
260                         break;
261                 }
262
263                 case '*':
264                 {
265                         bc_lex_assign(l, BC_LEX_OP_ASSIGN_MULTIPLY, BC_LEX_OP_MULTIPLY);
266                         break;
267                 }
268
269                 case '+':
270                 {
271                         c2 = l->buf[l->i];
272
273                         // Have to check for increment first.
274                         if (c2 == '+')
275                         {
276                                 l->i += 1;
277                                 l->t = BC_LEX_OP_INC;
278                         }
279                         else bc_lex_assign(l, BC_LEX_OP_ASSIGN_PLUS, BC_LEX_OP_PLUS);
280                         break;
281                 }
282
283                 case ',':
284                 {
285                         l->t = BC_LEX_COMMA;
286                         break;
287                 }
288
289                 case '-':
290                 {
291                         c2 = l->buf[l->i];
292
293                         // Have to check for decrement first.
294                         if (c2 == '-')
295                         {
296                                 l->i += 1;
297                                 l->t = BC_LEX_OP_DEC;
298                         }
299                         else bc_lex_assign(l, BC_LEX_OP_ASSIGN_MINUS, BC_LEX_OP_MINUS);
300                         break;
301                 }
302
303                 case '.':
304                 {
305                         c2 = l->buf[l->i];
306
307                         // If it's alone, it's an alias for last.
308                         if (BC_LEX_NUM_CHAR(c2, true, false)) bc_lex_number(l, c);
309                         else
310                         {
311                                 l->t = BC_LEX_KW_LAST;
312                                 bc_lex_err(l, BC_ERR_POSIX_DOT);
313                         }
314
315                         break;
316                 }
317
318                 case '/':
319                 {
320                         c2 = l->buf[l->i];
321                         if (c2 == '*') bc_lex_comment(l);
322                         else bc_lex_assign(l, BC_LEX_OP_ASSIGN_DIVIDE, BC_LEX_OP_DIVIDE);
323                         break;
324                 }
325
326                 case '0':
327                 case '1':
328                 case '2':
329                 case '3':
330                 case '4':
331                 case '5':
332                 case '6':
333                 case '7':
334                 case '8':
335                 case '9':
336                 case 'A':
337                 case 'B':
338                 case 'C':
339                 case 'D':
340                 case 'E':
341                 case 'F':
342                 // Apparently, GNU bc (and maybe others) allows any uppercase letter as
343                 // a number. When single digits, they act like the ones above. When
344                 // multi-digit, any letter above the input base is automatically set to
345                 // the biggest allowable digit in the input base.
346                 case 'G':
347                 case 'H':
348                 case 'I':
349                 case 'J':
350                 case 'K':
351                 case 'L':
352                 case 'M':
353                 case 'N':
354                 case 'O':
355                 case 'P':
356                 case 'Q':
357                 case 'R':
358                 case 'S':
359                 case 'T':
360                 case 'U':
361                 case 'V':
362                 case 'W':
363                 case 'X':
364                 case 'Y':
365                 case 'Z':
366                 {
367                         bc_lex_number(l, c);
368                         break;
369                 }
370
371                 case ';':
372                 {
373                         l->t = BC_LEX_SCOLON;
374                         break;
375                 }
376
377                 case '<':
378                 {
379 #if BC_ENABLE_EXTRA_MATH
380                         c2 = l->buf[l->i];
381
382                         // Check for shift.
383                         if (c2 == '<')
384                         {
385                                 l->i += 1;
386                                 bc_lex_assign(l, BC_LEX_OP_ASSIGN_LSHIFT, BC_LEX_OP_LSHIFT);
387                                 break;
388                         }
389 #endif // BC_ENABLE_EXTRA_MATH
390                         bc_lex_assign(l, BC_LEX_OP_REL_LE, BC_LEX_OP_REL_LT);
391                         break;
392                 }
393
394                 case '=':
395                 {
396                         bc_lex_assign(l, BC_LEX_OP_REL_EQ, BC_LEX_OP_ASSIGN);
397                         break;
398                 }
399
400                 case '>':
401                 {
402 #if BC_ENABLE_EXTRA_MATH
403                         c2 = l->buf[l->i];
404
405                         // Check for shift.
406                         if (c2 == '>')
407                         {
408                                 l->i += 1;
409                                 bc_lex_assign(l, BC_LEX_OP_ASSIGN_RSHIFT, BC_LEX_OP_RSHIFT);
410                                 break;
411                         }
412 #endif // BC_ENABLE_EXTRA_MATH
413                         bc_lex_assign(l, BC_LEX_OP_REL_GE, BC_LEX_OP_REL_GT);
414                         break;
415                 }
416
417                 case '[':
418                 case ']':
419                 {
420                         l->t = (BcLexType) (c - '[' + BC_LEX_LBRACKET);
421                         break;
422                 }
423
424                 case '\\':
425                 {
426                         // In bc, a backslash+newline is whitespace.
427                         if (BC_NO_ERR(l->buf[l->i] == '\n'))
428                         {
429                                 l->i += 1;
430                                 l->t = BC_LEX_WHITESPACE;
431                         }
432                         else bc_lex_invalidChar(l, c);
433                         break;
434                 }
435
436                 case '^':
437                 {
438                         bc_lex_assign(l, BC_LEX_OP_ASSIGN_POWER, BC_LEX_OP_POWER);
439                         break;
440                 }
441
442                 case 'a':
443                 case 'b':
444                 case 'c':
445                 case 'd':
446                 case 'e':
447                 case 'f':
448                 case 'g':
449                 case 'h':
450                 case 'i':
451                 case 'j':
452                 case 'k':
453                 case 'l':
454                 case 'm':
455                 case 'n':
456                 case 'o':
457                 case 'p':
458                 case 'q':
459                 case 'r':
460                 case 's':
461                 case 't':
462                 case 'u':
463                 case 'v':
464                 case 'w':
465                 case 'x':
466                 case 'y':
467                 case 'z':
468                 {
469                         bc_lex_identifier(l);
470                         break;
471                 }
472
473                 case '{':
474                 case '}':
475                 {
476                         l->t = (BcLexType) (c - '{' + BC_LEX_LBRACE);
477                         break;
478                 }
479
480                 case '|':
481                 {
482                         c2 = l->buf[l->i];
483
484                         // Once again, boolean or is not allowed by POSIX.
485                         if (BC_NO_ERR(c2 == '|'))
486                         {
487                                 bc_lex_verr(l, BC_ERR_POSIX_BOOL, "||");
488
489                                 l->i += 1;
490                                 l->t = BC_LEX_OP_BOOL_OR;
491                         }
492                         else bc_lex_invalidChar(l, c);
493
494                         break;
495                 }
496
497                 default:
498                 {
499                         bc_lex_invalidChar(l, c);
500                 }
501         }
502 }
503 #endif // BC_ENABLED