1252884aeSStefan Eßer /*
2252884aeSStefan Eßer * *****************************************************************************
3252884aeSStefan Eßer *
43aa99676SStefan Eßer * SPDX-License-Identifier: BSD-2-Clause
5252884aeSStefan Eßer *
6a970610aSStefan Eßer * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
7252884aeSStefan Eßer *
8252884aeSStefan Eßer * Redistribution and use in source and binary forms, with or without
9252884aeSStefan Eßer * modification, are permitted provided that the following conditions are met:
10252884aeSStefan Eßer *
11252884aeSStefan Eßer * * Redistributions of source code must retain the above copyright notice, this
12252884aeSStefan Eßer * list of conditions and the following disclaimer.
13252884aeSStefan Eßer *
14252884aeSStefan Eßer * * Redistributions in binary form must reproduce the above copyright notice,
15252884aeSStefan Eßer * this list of conditions and the following disclaimer in the documentation
16252884aeSStefan Eßer * and/or other materials provided with the distribution.
17252884aeSStefan Eßer *
18252884aeSStefan Eßer * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19252884aeSStefan Eßer * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20252884aeSStefan Eßer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21252884aeSStefan Eßer * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22252884aeSStefan Eßer * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23252884aeSStefan Eßer * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24252884aeSStefan Eßer * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25252884aeSStefan Eßer * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26252884aeSStefan Eßer * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27252884aeSStefan Eßer * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28252884aeSStefan Eßer * POSSIBILITY OF SUCH DAMAGE.
29252884aeSStefan Eßer *
30252884aeSStefan Eßer * *****************************************************************************
31252884aeSStefan Eßer *
32252884aeSStefan Eßer * Common code for the lexers.
33252884aeSStefan Eßer *
34252884aeSStefan Eßer */
35252884aeSStefan Eßer
36252884aeSStefan Eßer #include <assert.h>
37252884aeSStefan Eßer #include <ctype.h>
38252884aeSStefan Eßer #include <stdbool.h>
39252884aeSStefan Eßer #include <string.h>
40252884aeSStefan Eßer
41252884aeSStefan Eßer #include <lex.h>
42252884aeSStefan Eßer #include <vm.h>
43252884aeSStefan Eßer #include <bc.h>
44252884aeSStefan Eßer
4578bc019dSStefan Eßer void
bc_lex_invalidChar(BcLex * l,char c)4678bc019dSStefan Eßer bc_lex_invalidChar(BcLex* l, char c)
4778bc019dSStefan Eßer {
48252884aeSStefan Eßer l->t = BC_LEX_INVALID;
4950696a6eSStefan Eßer bc_lex_verr(l, BC_ERR_PARSE_CHAR, c);
50252884aeSStefan Eßer }
51252884aeSStefan Eßer
5278bc019dSStefan Eßer void
bc_lex_lineComment(BcLex * l)5378bc019dSStefan Eßer bc_lex_lineComment(BcLex* l)
5478bc019dSStefan Eßer {
55252884aeSStefan Eßer l->t = BC_LEX_WHITESPACE;
5678bc019dSStefan Eßer while (l->i < l->len && l->buf[l->i] != '\n')
5778bc019dSStefan Eßer {
5878bc019dSStefan Eßer l->i += 1;
5978bc019dSStefan Eßer }
60252884aeSStefan Eßer }
61252884aeSStefan Eßer
6278bc019dSStefan Eßer void
bc_lex_comment(BcLex * l)6378bc019dSStefan Eßer bc_lex_comment(BcLex* l)
6478bc019dSStefan Eßer {
65252884aeSStefan Eßer size_t i, nlines = 0;
6644d4804dSStefan Eßer const char* buf;
6744d4804dSStefan Eßer bool end = false, got_more;
68252884aeSStefan Eßer char c;
69252884aeSStefan Eßer
70252884aeSStefan Eßer l->i += 1;
71252884aeSStefan Eßer l->t = BC_LEX_WHITESPACE;
72252884aeSStefan Eßer
7344d4804dSStefan Eßer // This loop is complex because it might need to request more data from
7444d4804dSStefan Eßer // stdin if the comment is not ended. This loop is taken until the comment
7544d4804dSStefan Eßer // is finished or we have EOF.
7678bc019dSStefan Eßer do
7778bc019dSStefan Eßer {
7844d4804dSStefan Eßer buf = l->buf;
7944d4804dSStefan Eßer got_more = false;
8044d4804dSStefan Eßer
8144d4804dSStefan Eßer // If we are in stdin mode, the buffer must be the one used for stdin.
82*12e0d316SStefan Eßer #if !BC_ENABLE_OSSFUZZ
83d101cdd6SStefan Eßer assert(vm->mode != BC_MODE_STDIN || buf == vm->buffer.v);
84*12e0d316SStefan Eßer #endif // !BC_ENABLE_OSSFUZZ
8544d4804dSStefan Eßer
8644d4804dSStefan Eßer // Find the end of the comment.
8778bc019dSStefan Eßer for (i = l->i; !end; i += !end)
8878bc019dSStefan Eßer {
8944d4804dSStefan Eßer // While we don't have an asterisk, eat, but increment nlines.
9078bc019dSStefan Eßer for (; (c = buf[i]) && c != '*'; ++i)
9178bc019dSStefan Eßer {
9278bc019dSStefan Eßer nlines += (c == '\n');
9378bc019dSStefan Eßer }
94252884aeSStefan Eßer
9544d4804dSStefan Eßer // If this is true, we need to request more data.
9678bc019dSStefan Eßer if (BC_ERR(!c || buf[i + 1] == '\0'))
9778bc019dSStefan Eßer {
98*12e0d316SStefan Eßer #if !BC_ENABLE_OSSFUZZ
9923210c9fSStefan Eßer // Read more, if possible.
100d101cdd6SStefan Eßer if (!vm->eof && l->mode != BC_MODE_FILE)
10178bc019dSStefan Eßer {
10223210c9fSStefan Eßer got_more = bc_lex_readLine(l);
10378bc019dSStefan Eßer }
104*12e0d316SStefan Eßer #endif // !BC_ENABLE_OSSFUZZ
10544d4804dSStefan Eßer
10644d4804dSStefan Eßer break;
107252884aeSStefan Eßer }
108252884aeSStefan Eßer
10944d4804dSStefan Eßer // If this turns true, we found the end. Yay!
11044d4804dSStefan Eßer end = (buf[i + 1] == '/');
11144d4804dSStefan Eßer }
11278bc019dSStefan Eßer }
11378bc019dSStefan Eßer while (got_more && !end);
11444d4804dSStefan Eßer
11544d4804dSStefan Eßer // If we didn't find the end, barf.
11678bc019dSStefan Eßer if (!end)
11778bc019dSStefan Eßer {
11844d4804dSStefan Eßer l->i = i;
11944d4804dSStefan Eßer bc_lex_err(l, BC_ERR_PARSE_COMMENT);
120252884aeSStefan Eßer }
121252884aeSStefan Eßer
122252884aeSStefan Eßer l->i = i + 2;
123252884aeSStefan Eßer l->line += nlines;
124252884aeSStefan Eßer }
125252884aeSStefan Eßer
12678bc019dSStefan Eßer void
bc_lex_whitespace(BcLex * l)12778bc019dSStefan Eßer bc_lex_whitespace(BcLex* l)
12878bc019dSStefan Eßer {
129252884aeSStefan Eßer char c;
13044d4804dSStefan Eßer
131252884aeSStefan Eßer l->t = BC_LEX_WHITESPACE;
13244d4804dSStefan Eßer
13344d4804dSStefan Eßer // Eat. We don't eat newlines because they can be special.
13478bc019dSStefan Eßer for (c = l->buf[l->i]; c != '\n' && isspace(c); c = l->buf[++l->i])
13578bc019dSStefan Eßer {
13678bc019dSStefan Eßer continue;
13778bc019dSStefan Eßer }
138252884aeSStefan Eßer }
139252884aeSStefan Eßer
14078bc019dSStefan Eßer void
bc_lex_commonTokens(BcLex * l,char c)14178bc019dSStefan Eßer bc_lex_commonTokens(BcLex* l, char c)
14278bc019dSStefan Eßer {
143252884aeSStefan Eßer if (!c) l->t = BC_LEX_EOF;
144252884aeSStefan Eßer else if (c == '\n') l->t = BC_LEX_NLINE;
145252884aeSStefan Eßer else bc_lex_whitespace(l);
146252884aeSStefan Eßer }
147252884aeSStefan Eßer
14844d4804dSStefan Eßer /**
14944d4804dSStefan Eßer * Parses a number.
15044d4804dSStefan Eßer * @param l The lexer.
15144d4804dSStefan Eßer * @param start The start character.
15244d4804dSStefan Eßer * @param int_only Whether this function should only look for an integer. This
15344d4804dSStefan Eßer * is used to implement the exponent of scientific notation.
15444d4804dSStefan Eßer */
15578bc019dSStefan Eßer static size_t
bc_lex_num(BcLex * l,char start,bool int_only)15678bc019dSStefan Eßer bc_lex_num(BcLex* l, char start, bool int_only)
15778bc019dSStefan Eßer {
158252884aeSStefan Eßer const char* buf = l->buf + l->i;
159252884aeSStefan Eßer size_t i;
160252884aeSStefan Eßer char c;
161252884aeSStefan Eßer bool last_pt, pt = (start == '.');
162252884aeSStefan Eßer
16344d4804dSStefan Eßer // This loop looks complex. It is not. It is asking if the character is not
16444d4804dSStefan Eßer // a nul byte and it if it a valid num character based on what we have found
16544d4804dSStefan Eßer // thus far, or whether it is a backslash followed by a newline. I can do
16644d4804dSStefan Eßer // i+1 on the buffer because the buffer must have a nul byte.
167252884aeSStefan Eßer for (i = 0; (c = buf[i]) && (BC_LEX_NUM_CHAR(c, pt, int_only) ||
16878bc019dSStefan Eßer (c == '\\' && buf[i + 1] == '\n'));
16978bc019dSStefan Eßer ++i)
170252884aeSStefan Eßer {
17144d4804dSStefan Eßer // I don't need to test that the next character is a newline because
17244d4804dSStefan Eßer // the loop condition above ensures that.
17378bc019dSStefan Eßer if (c == '\\')
17478bc019dSStefan Eßer {
175252884aeSStefan Eßer i += 2;
176252884aeSStefan Eßer
177252884aeSStefan Eßer // Make sure to eat whitespace at the beginning of the line.
17878bc019dSStefan Eßer while (isspace(buf[i]) && buf[i] != '\n')
17978bc019dSStefan Eßer {
18078bc019dSStefan Eßer i += 1;
18178bc019dSStefan Eßer }
182252884aeSStefan Eßer
183252884aeSStefan Eßer c = buf[i];
184252884aeSStefan Eßer
18544d4804dSStefan Eßer // If the next character is not a number character, bail.
186252884aeSStefan Eßer if (!BC_LEX_NUM_CHAR(c, pt, int_only)) break;
187252884aeSStefan Eßer }
188252884aeSStefan Eßer
18944d4804dSStefan Eßer // Did we find the radix point?
190252884aeSStefan Eßer last_pt = (c == '.');
19144d4804dSStefan Eßer
19244d4804dSStefan Eßer // If we did, and we already have one, then break because it's not part
19344d4804dSStefan Eßer // of this number.
194252884aeSStefan Eßer if (pt && last_pt) break;
19544d4804dSStefan Eßer
19644d4804dSStefan Eßer // Set whether we have found a radix point.
197252884aeSStefan Eßer pt = pt || last_pt;
198252884aeSStefan Eßer
199252884aeSStefan Eßer bc_vec_push(&l->str, &c);
200252884aeSStefan Eßer }
201252884aeSStefan Eßer
202252884aeSStefan Eßer return i;
203252884aeSStefan Eßer }
204252884aeSStefan Eßer
20578bc019dSStefan Eßer void
bc_lex_number(BcLex * l,char start)20678bc019dSStefan Eßer bc_lex_number(BcLex* l, char start)
20778bc019dSStefan Eßer {
208252884aeSStefan Eßer l->t = BC_LEX_NUMBER;
209252884aeSStefan Eßer
21044d4804dSStefan Eßer // Make sure the string is clear.
21110328f8bSStefan Eßer bc_vec_popAll(&l->str);
212252884aeSStefan Eßer bc_vec_push(&l->str, &start);
213252884aeSStefan Eßer
21444d4804dSStefan Eßer // Parse the number.
215252884aeSStefan Eßer l->i += bc_lex_num(l, start, false);
216252884aeSStefan Eßer
217252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH
218252884aeSStefan Eßer {
219252884aeSStefan Eßer char c = l->buf[l->i];
220252884aeSStefan Eßer
22144d4804dSStefan Eßer // Do we have a number in scientific notation?
22278bc019dSStefan Eßer if (c == 'e')
22378bc019dSStefan Eßer {
224252884aeSStefan Eßer #if BC_ENABLED
22544d4804dSStefan Eßer // Barf for POSIX.
22650696a6eSStefan Eßer if (BC_IS_POSIX) bc_lex_err(l, BC_ERR_POSIX_EXP_NUM);
227252884aeSStefan Eßer #endif // BC_ENABLED
228252884aeSStefan Eßer
22944d4804dSStefan Eßer // Push the e.
230252884aeSStefan Eßer bc_vec_push(&l->str, &c);
231252884aeSStefan Eßer l->i += 1;
232252884aeSStefan Eßer c = l->buf[l->i];
233252884aeSStefan Eßer
23444d4804dSStefan Eßer // Check for negative specifically because bc_lex_num() does not.
23578bc019dSStefan Eßer if (c == BC_LEX_NEG_CHAR)
23678bc019dSStefan Eßer {
237252884aeSStefan Eßer bc_vec_push(&l->str, &c);
238252884aeSStefan Eßer l->i += 1;
239252884aeSStefan Eßer c = l->buf[l->i];
240252884aeSStefan Eßer }
241252884aeSStefan Eßer
24244d4804dSStefan Eßer // We must have a number character, so barf if not.
243252884aeSStefan Eßer if (BC_ERR(!BC_LEX_NUM_CHAR(c, false, true)))
24478bc019dSStefan Eßer {
24550696a6eSStefan Eßer bc_lex_verr(l, BC_ERR_PARSE_CHAR, c);
24678bc019dSStefan Eßer }
247252884aeSStefan Eßer
24844d4804dSStefan Eßer // Parse the exponent.
249252884aeSStefan Eßer l->i += bc_lex_num(l, 0, true);
250252884aeSStefan Eßer }
251252884aeSStefan Eßer }
252252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
253252884aeSStefan Eßer
254252884aeSStefan Eßer bc_vec_pushByte(&l->str, '\0');
255252884aeSStefan Eßer }
256252884aeSStefan Eßer
25778bc019dSStefan Eßer void
bc_lex_name(BcLex * l)25878bc019dSStefan Eßer bc_lex_name(BcLex* l)
25978bc019dSStefan Eßer {
260252884aeSStefan Eßer size_t i = 0;
261252884aeSStefan Eßer const char* buf = l->buf + l->i - 1;
262252884aeSStefan Eßer char c = buf[i];
263252884aeSStefan Eßer
264252884aeSStefan Eßer l->t = BC_LEX_NAME;
265252884aeSStefan Eßer
26644d4804dSStefan Eßer // Should be obvious. It's looking for valid characters.
26778bc019dSStefan Eßer while ((c >= 'a' && c <= 'z') || isdigit(c) || c == '_')
26878bc019dSStefan Eßer {
26978bc019dSStefan Eßer c = buf[++i];
27078bc019dSStefan Eßer }
271252884aeSStefan Eßer
27244d4804dSStefan Eßer // Set the string to the identifier.
273252884aeSStefan Eßer bc_vec_string(&l->str, i, buf);
274252884aeSStefan Eßer
275252884aeSStefan Eßer // Increment the index. We minus 1 because it has already been incremented.
276252884aeSStefan Eßer l->i += i - 1;
277252884aeSStefan Eßer }
278252884aeSStefan Eßer
27978bc019dSStefan Eßer void
bc_lex_init(BcLex * l)28078bc019dSStefan Eßer bc_lex_init(BcLex* l)
28178bc019dSStefan Eßer {
282252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED;
283252884aeSStefan Eßer assert(l != NULL);
28444d4804dSStefan Eßer bc_vec_init(&l->str, sizeof(char), BC_DTOR_NONE);
285252884aeSStefan Eßer }
286252884aeSStefan Eßer
28778bc019dSStefan Eßer void
bc_lex_free(BcLex * l)28878bc019dSStefan Eßer bc_lex_free(BcLex* l)
28978bc019dSStefan Eßer {
290252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED;
291252884aeSStefan Eßer assert(l != NULL);
292252884aeSStefan Eßer bc_vec_free(&l->str);
293252884aeSStefan Eßer }
294252884aeSStefan Eßer
29578bc019dSStefan Eßer void
bc_lex_file(BcLex * l,const char * file)29678bc019dSStefan Eßer bc_lex_file(BcLex* l, const char* file)
29778bc019dSStefan Eßer {
298252884aeSStefan Eßer assert(l != NULL && file != NULL);
299252884aeSStefan Eßer l->line = 1;
300d101cdd6SStefan Eßer vm->file = file;
301252884aeSStefan Eßer }
302252884aeSStefan Eßer
30378bc019dSStefan Eßer void
bc_lex_next(BcLex * l)30478bc019dSStefan Eßer bc_lex_next(BcLex* l)
30578bc019dSStefan Eßer {
30610041e99SStefan Eßer BC_SIG_ASSERT_LOCKED;
30710041e99SStefan Eßer
308252884aeSStefan Eßer assert(l != NULL);
309252884aeSStefan Eßer
310252884aeSStefan Eßer l->last = l->t;
31144d4804dSStefan Eßer
31244d4804dSStefan Eßer // If this wasn't here, the line number would be off.
313252884aeSStefan Eßer l->line += (l->i != 0 && l->buf[l->i - 1] == '\n');
314252884aeSStefan Eßer
31544d4804dSStefan Eßer // If the last token was EOF, someone called this one too many times.
31650696a6eSStefan Eßer if (BC_ERR(l->last == BC_LEX_EOF)) bc_lex_err(l, BC_ERR_PARSE_EOF);
317252884aeSStefan Eßer
318252884aeSStefan Eßer l->t = BC_LEX_EOF;
319252884aeSStefan Eßer
32044d4804dSStefan Eßer // We are done if this is true.
321252884aeSStefan Eßer if (l->i == l->len) return;
322252884aeSStefan Eßer
323252884aeSStefan Eßer // Loop until failure or we don't have whitespace. This
324252884aeSStefan Eßer // is so the parser doesn't get inundated with whitespace.
32578bc019dSStefan Eßer do
32678bc019dSStefan Eßer {
327d101cdd6SStefan Eßer vm->next(l);
32878bc019dSStefan Eßer }
32978bc019dSStefan Eßer while (l->t == BC_LEX_WHITESPACE);
330252884aeSStefan Eßer }
331252884aeSStefan Eßer
33244d4804dSStefan Eßer /**
33344d4804dSStefan Eßer * Updates the buffer and len so that they are not invalidated when the stdin
33444d4804dSStefan Eßer * buffer grows.
33544d4804dSStefan Eßer * @param l The lexer.
33644d4804dSStefan Eßer * @param text The text.
33744d4804dSStefan Eßer * @param len The length of the text.
33844d4804dSStefan Eßer */
33978bc019dSStefan Eßer static void
bc_lex_fixText(BcLex * l,const char * text,size_t len)34078bc019dSStefan Eßer bc_lex_fixText(BcLex* l, const char* text, size_t len)
34178bc019dSStefan Eßer {
342252884aeSStefan Eßer l->buf = text;
34344d4804dSStefan Eßer l->len = len;
34444d4804dSStefan Eßer }
34544d4804dSStefan Eßer
34678bc019dSStefan Eßer bool
bc_lex_readLine(BcLex * l)34778bc019dSStefan Eßer bc_lex_readLine(BcLex* l)
34878bc019dSStefan Eßer {
34910041e99SStefan Eßer bool good;
35010041e99SStefan Eßer
35110041e99SStefan Eßer // These are reversed because they should be already locked, but
35210041e99SStefan Eßer // bc_vm_readLine() needs them to be unlocked.
35310041e99SStefan Eßer BC_SIG_UNLOCK;
35410041e99SStefan Eßer
35523210c9fSStefan Eßer // Make sure we read from the appropriate place.
356d101cdd6SStefan Eßer switch (l->mode)
35778bc019dSStefan Eßer {
358d101cdd6SStefan Eßer case BC_MODE_EXPRS:
359d101cdd6SStefan Eßer {
36023210c9fSStefan Eßer good = bc_vm_readBuf(false);
361d101cdd6SStefan Eßer break;
362d101cdd6SStefan Eßer }
363d101cdd6SStefan Eßer
364d101cdd6SStefan Eßer case BC_MODE_FILE:
365d101cdd6SStefan Eßer {
366d101cdd6SStefan Eßer good = false;
367d101cdd6SStefan Eßer break;
368d101cdd6SStefan Eßer }
369d101cdd6SStefan Eßer
370*12e0d316SStefan Eßer #if !BC_ENABLE_OSSFUZZ
371*12e0d316SStefan Eßer
372d101cdd6SStefan Eßer case BC_MODE_STDIN:
373d101cdd6SStefan Eßer {
374d101cdd6SStefan Eßer good = bc_vm_readLine(false);
375d101cdd6SStefan Eßer break;
376d101cdd6SStefan Eßer }
377d101cdd6SStefan Eßer
378*12e0d316SStefan Eßer #endif // !BC_ENABLE_OSSFUZZ
379*12e0d316SStefan Eßer
380d101cdd6SStefan Eßer #ifdef __GNUC__
381d101cdd6SStefan Eßer #ifndef __clang__
382d101cdd6SStefan Eßer default:
383d101cdd6SStefan Eßer {
384d101cdd6SStefan Eßer // We should never get here.
385d101cdd6SStefan Eßer abort();
386d101cdd6SStefan Eßer }
387d101cdd6SStefan Eßer #endif // __clang__
388d101cdd6SStefan Eßer #endif // __GNUC__
38923210c9fSStefan Eßer }
39010041e99SStefan Eßer
39110041e99SStefan Eßer BC_SIG_LOCK;
39244d4804dSStefan Eßer
393d101cdd6SStefan Eßer bc_lex_fixText(l, vm->buffer.v, vm->buffer.len - 1);
39444d4804dSStefan Eßer
39544d4804dSStefan Eßer return good;
39644d4804dSStefan Eßer }
39744d4804dSStefan Eßer
39878bc019dSStefan Eßer void
bc_lex_text(BcLex * l,const char * text,BcMode mode)399d101cdd6SStefan Eßer bc_lex_text(BcLex* l, const char* text, BcMode mode)
40078bc019dSStefan Eßer {
40110041e99SStefan Eßer BC_SIG_ASSERT_LOCKED;
40210041e99SStefan Eßer
40344d4804dSStefan Eßer assert(l != NULL && text != NULL);
40410041e99SStefan Eßer
40544d4804dSStefan Eßer bc_lex_fixText(l, text, strlen(text));
406252884aeSStefan Eßer l->i = 0;
407252884aeSStefan Eßer l->t = l->last = BC_LEX_INVALID;
408d101cdd6SStefan Eßer l->mode = mode;
40910041e99SStefan Eßer
410252884aeSStefan Eßer bc_lex_next(l);
411252884aeSStefan Eßer }
412