xref: /freebsd-src/contrib/bc/src/bc_lex.c (revision 12e0d316644a4f80f5f1f78cf07bd93def43b1ca)
150696a6eSStefan Eßer /*
250696a6eSStefan Eßer  * *****************************************************************************
350696a6eSStefan Eßer  *
450696a6eSStefan Eßer  * SPDX-License-Identifier: BSD-2-Clause
550696a6eSStefan Eßer  *
6a970610aSStefan Eßer  * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
750696a6eSStefan Eßer  *
850696a6eSStefan Eßer  * Redistribution and use in source and binary forms, with or without
950696a6eSStefan Eßer  * modification, are permitted provided that the following conditions are met:
1050696a6eSStefan Eßer  *
1150696a6eSStefan Eßer  * * Redistributions of source code must retain the above copyright notice, this
1250696a6eSStefan Eßer  *   list of conditions and the following disclaimer.
1350696a6eSStefan Eßer  *
1450696a6eSStefan Eßer  * * Redistributions in binary form must reproduce the above copyright notice,
1550696a6eSStefan Eßer  *   this list of conditions and the following disclaimer in the documentation
1650696a6eSStefan Eßer  *   and/or other materials provided with the distribution.
1750696a6eSStefan Eßer  *
1850696a6eSStefan Eßer  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1950696a6eSStefan Eßer  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2050696a6eSStefan Eßer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2150696a6eSStefan Eßer  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
2250696a6eSStefan Eßer  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2350696a6eSStefan Eßer  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2450696a6eSStefan Eßer  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2550696a6eSStefan Eßer  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2650696a6eSStefan Eßer  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2750696a6eSStefan Eßer  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2850696a6eSStefan Eßer  * POSSIBILITY OF SUCH DAMAGE.
2950696a6eSStefan Eßer  *
3050696a6eSStefan Eßer  * *****************************************************************************
3150696a6eSStefan Eßer  *
3250696a6eSStefan Eßer  * The lexer for bc.
3350696a6eSStefan Eßer  *
3450696a6eSStefan Eßer  */
3550696a6eSStefan Eßer 
3650696a6eSStefan Eßer #if BC_ENABLED
3750696a6eSStefan Eßer 
3850696a6eSStefan Eßer #include <assert.h>
3950696a6eSStefan Eßer #include <ctype.h>
4050696a6eSStefan Eßer #include <string.h>
4150696a6eSStefan Eßer 
4250696a6eSStefan Eßer #include <bc.h>
4350696a6eSStefan Eßer #include <vm.h>
4450696a6eSStefan Eßer 
4544d4804dSStefan Eßer /**
4644d4804dSStefan Eßer  * Lexes an identifier, which may be a keyword.
4744d4804dSStefan Eßer  * @param l  The lexer.
4844d4804dSStefan Eßer  */
4978bc019dSStefan Eßer static void
5078bc019dSStefan Eßer bc_lex_identifier(BcLex* l)
5178bc019dSStefan Eßer {
5244d4804dSStefan Eßer 	// We already passed the first character, so we need to be sure to include
5344d4804dSStefan Eßer 	// it.
5450696a6eSStefan Eßer 	const char* buf = l->buf + l->i - 1;
5544d4804dSStefan Eßer 	size_t i;
5650696a6eSStefan Eßer 
5744d4804dSStefan Eßer 	// This loop is simply checking for keywords.
5878bc019dSStefan Eßer 	for (i = 0; i < bc_lex_kws_len; ++i)
5978bc019dSStefan Eßer 	{
6050696a6eSStefan Eßer 		const BcLexKeyword* kw = bc_lex_kws + i;
6150696a6eSStefan Eßer 		size_t n = BC_LEX_KW_LEN(kw);
6250696a6eSStefan Eßer 
6378bc019dSStefan Eßer 		if (!strncmp(buf, kw->name, n) && !isalnum(buf[n]) && buf[n] != '_')
6478bc019dSStefan Eßer 		{
6544d4804dSStefan Eßer 			// If the keyword has been redefined, and redefinition is allowed
6644d4804dSStefan Eßer 			// (it is not allowed for builtin libraries), break out of the loop
6744d4804dSStefan Eßer 			// and use it as a name. This depends on the argument parser to
6844d4804dSStefan Eßer 			// ensure that only non-POSIX keywords get redefined.
69d101cdd6SStefan Eßer 			if (!vm->no_redefine && vm->redefined_kws[i]) break;
7044d4804dSStefan Eßer 
7150696a6eSStefan Eßer 			l->t = BC_LEX_KW_AUTO + (BcLexType) i;
7250696a6eSStefan Eßer 
7344d4804dSStefan Eßer 			// Warn or error, as appropriate for the mode, if the keyword is not
7444d4804dSStefan Eßer 			// in the POSIX standard.
7544d4804dSStefan Eßer 			if (!BC_LEX_KW_POSIX(kw)) bc_lex_verr(l, BC_ERR_POSIX_KW, kw->name);
7650696a6eSStefan Eßer 
7750696a6eSStefan Eßer 			// We minus 1 because the index has already been incremented.
7850696a6eSStefan Eßer 			l->i += n - 1;
7944d4804dSStefan Eßer 
8044d4804dSStefan Eßer 			// Already have the token; bail.
8150696a6eSStefan Eßer 			return;
8250696a6eSStefan Eßer 		}
8350696a6eSStefan Eßer 	}
8450696a6eSStefan Eßer 
8544d4804dSStefan Eßer 	// If not a keyword, parse the name.
8650696a6eSStefan Eßer 	bc_lex_name(l);
8750696a6eSStefan Eßer 
8844d4804dSStefan Eßer 	// POSIX doesn't allow identifiers that are more than one character, so we
8944d4804dSStefan Eßer 	// might have to warn or error here too.
9050696a6eSStefan Eßer 	if (BC_ERR(l->str.len - 1 > 1))
9178bc019dSStefan Eßer 	{
9250696a6eSStefan Eßer 		bc_lex_verr(l, BC_ERR_POSIX_NAME_LEN, l->str.v);
9350696a6eSStefan Eßer 	}
9478bc019dSStefan Eßer }
9550696a6eSStefan Eßer 
9644d4804dSStefan Eßer /**
9744d4804dSStefan Eßer  * Parses a bc string. This is separate from dc strings because dc strings need
9844d4804dSStefan Eßer  * to be balanced.
9944d4804dSStefan Eßer  * @param l  The lexer.
10044d4804dSStefan Eßer  */
10178bc019dSStefan Eßer static void
10278bc019dSStefan Eßer bc_lex_string(BcLex* l)
10378bc019dSStefan Eßer {
10444d4804dSStefan Eßer 	// We need to keep track of newlines to increment them properly.
10544d4804dSStefan Eßer 	size_t len, nlines, i;
10644d4804dSStefan Eßer 	const char* buf;
10750696a6eSStefan Eßer 	char c;
10844d4804dSStefan Eßer 	bool got_more;
10950696a6eSStefan Eßer 
11050696a6eSStefan Eßer 	l->t = BC_LEX_STR;
11150696a6eSStefan Eßer 
11278bc019dSStefan Eßer 	do
11378bc019dSStefan Eßer 	{
11444d4804dSStefan Eßer 		nlines = 0;
11544d4804dSStefan Eßer 		buf = l->buf;
11644d4804dSStefan Eßer 		got_more = false;
11744d4804dSStefan Eßer 
118*12e0d316SStefan Eßer #if !BC_ENABLE_OSSFUZZ
119d101cdd6SStefan Eßer 		assert(vm->mode != BC_MODE_STDIN || buf == vm->buffer.v);
120*12e0d316SStefan Eßer #endif // !BC_ENABLE_OSSFUZZ
12144d4804dSStefan Eßer 
12244d4804dSStefan Eßer 		// Fortunately for us, bc doesn't escape quotes. Instead, the equivalent
12344d4804dSStefan Eßer 		// is '\q', which makes this loop simpler.
12478bc019dSStefan Eßer 		for (i = l->i; (c = buf[i]) && c != '"'; ++i)
12578bc019dSStefan Eßer 		{
12678bc019dSStefan Eßer 			nlines += (c == '\n');
12778bc019dSStefan Eßer 		}
12844d4804dSStefan Eßer 
129d101cdd6SStefan Eßer 		if (BC_ERR(c == '\0') && !vm->eof && l->mode != BC_MODE_FILE)
13078bc019dSStefan Eßer 		{
13144d4804dSStefan Eßer 			got_more = bc_lex_readLine(l);
13278bc019dSStefan Eßer 		}
13378bc019dSStefan Eßer 	}
13478bc019dSStefan Eßer 	while (got_more && c != '"');
13544d4804dSStefan Eßer 
13644d4804dSStefan Eßer 	// If the string did not end properly, barf.
13778bc019dSStefan Eßer 	if (c != '"')
13878bc019dSStefan Eßer 	{
13950696a6eSStefan Eßer 		l->i = i;
14050696a6eSStefan Eßer 		bc_lex_err(l, BC_ERR_PARSE_STRING);
14150696a6eSStefan Eßer 	}
14250696a6eSStefan Eßer 
14344d4804dSStefan Eßer 	// Set the temp string to the parsed string.
14450696a6eSStefan Eßer 	len = i - l->i;
14550696a6eSStefan Eßer 	bc_vec_string(&l->str, len, l->buf + l->i);
14650696a6eSStefan Eßer 
14750696a6eSStefan Eßer 	l->i = i + 1;
14850696a6eSStefan Eßer 	l->line += nlines;
14950696a6eSStefan Eßer }
15050696a6eSStefan Eßer 
15144d4804dSStefan Eßer /**
15244d4804dSStefan Eßer  * This function takes a lexed operator and checks to see if it's the assignment
15344d4804dSStefan Eßer  * version, setting the token appropriately.
15444d4804dSStefan Eßer  * @param l        The lexer.
15544d4804dSStefan Eßer  * @param with     The token to assign if it is an assignment operator.
15644d4804dSStefan Eßer  * @param without  The token to assign if it is not an assignment operator.
15744d4804dSStefan Eßer  */
15878bc019dSStefan Eßer static void
15978bc019dSStefan Eßer bc_lex_assign(BcLex* l, BcLexType with, BcLexType without)
16078bc019dSStefan Eßer {
16178bc019dSStefan Eßer 	if (l->buf[l->i] == '=')
16278bc019dSStefan Eßer 	{
16350696a6eSStefan Eßer 		l->i += 1;
16450696a6eSStefan Eßer 		l->t = with;
16550696a6eSStefan Eßer 	}
16650696a6eSStefan Eßer 	else l->t = without;
16750696a6eSStefan Eßer }
16850696a6eSStefan Eßer 
16978bc019dSStefan Eßer void
17078bc019dSStefan Eßer bc_lex_token(BcLex* l)
17178bc019dSStefan Eßer {
17244d4804dSStefan Eßer 	// We increment here. This means that all lexing needs to take that into
17344d4804dSStefan Eßer 	// account, such as when parsing an identifier. If we don't, the first
17444d4804dSStefan Eßer 	// character of every identifier would be missing.
17550696a6eSStefan Eßer 	char c = l->buf[l->i++], c2;
17650696a6eSStefan Eßer 
17710041e99SStefan Eßer 	BC_SIG_ASSERT_LOCKED;
17810041e99SStefan Eßer 
17950696a6eSStefan Eßer 	// This is the workhorse of the lexer.
18078bc019dSStefan Eßer 	switch (c)
18178bc019dSStefan Eßer 	{
18250696a6eSStefan Eßer 		case '\0':
18350696a6eSStefan Eßer 		case '\n':
18450696a6eSStefan Eßer 		case '\t':
18550696a6eSStefan Eßer 		case '\v':
18650696a6eSStefan Eßer 		case '\f':
18750696a6eSStefan Eßer 		case '\r':
18850696a6eSStefan Eßer 		case ' ':
18950696a6eSStefan Eßer 		{
19050696a6eSStefan Eßer 			bc_lex_commonTokens(l, c);
19150696a6eSStefan Eßer 			break;
19250696a6eSStefan Eßer 		}
19350696a6eSStefan Eßer 
19450696a6eSStefan Eßer 		case '!':
19550696a6eSStefan Eßer 		{
19644d4804dSStefan Eßer 			// Even though it's not an assignment, we can use this.
19750696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_REL_NE, BC_LEX_OP_BOOL_NOT);
19850696a6eSStefan Eßer 
19944d4804dSStefan Eßer 			// POSIX doesn't allow boolean not.
20050696a6eSStefan Eßer 			if (l->t == BC_LEX_OP_BOOL_NOT)
20178bc019dSStefan Eßer 			{
20250696a6eSStefan Eßer 				bc_lex_verr(l, BC_ERR_POSIX_BOOL, "!");
20378bc019dSStefan Eßer 			}
20450696a6eSStefan Eßer 
20550696a6eSStefan Eßer 			break;
20650696a6eSStefan Eßer 		}
20750696a6eSStefan Eßer 
20850696a6eSStefan Eßer 		case '"':
20950696a6eSStefan Eßer 		{
21050696a6eSStefan Eßer 			bc_lex_string(l);
21150696a6eSStefan Eßer 			break;
21250696a6eSStefan Eßer 		}
21350696a6eSStefan Eßer 
21450696a6eSStefan Eßer 		case '#':
21550696a6eSStefan Eßer 		{
21644d4804dSStefan Eßer 			// POSIX does not allow line comments.
21750696a6eSStefan Eßer 			bc_lex_err(l, BC_ERR_POSIX_COMMENT);
21850696a6eSStefan Eßer 			bc_lex_lineComment(l);
21950696a6eSStefan Eßer 			break;
22050696a6eSStefan Eßer 		}
22150696a6eSStefan Eßer 
22250696a6eSStefan Eßer 		case '%':
22350696a6eSStefan Eßer 		{
22450696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_ASSIGN_MODULUS, BC_LEX_OP_MODULUS);
22550696a6eSStefan Eßer 			break;
22650696a6eSStefan Eßer 		}
22750696a6eSStefan Eßer 
22850696a6eSStefan Eßer 		case '&':
22950696a6eSStefan Eßer 		{
23050696a6eSStefan Eßer 			c2 = l->buf[l->i];
23144d4804dSStefan Eßer 
23244d4804dSStefan Eßer 			// Either we have boolean and or an error. And boolean and is not
23344d4804dSStefan Eßer 			// allowed by POSIX.
23478bc019dSStefan Eßer 			if (BC_NO_ERR(c2 == '&'))
23578bc019dSStefan Eßer 			{
23650696a6eSStefan Eßer 				bc_lex_verr(l, BC_ERR_POSIX_BOOL, "&&");
23750696a6eSStefan Eßer 
23850696a6eSStefan Eßer 				l->i += 1;
23950696a6eSStefan Eßer 				l->t = BC_LEX_OP_BOOL_AND;
24050696a6eSStefan Eßer 			}
24150696a6eSStefan Eßer 			else bc_lex_invalidChar(l, c);
24250696a6eSStefan Eßer 
24350696a6eSStefan Eßer 			break;
24450696a6eSStefan Eßer 		}
24550696a6eSStefan Eßer #if BC_ENABLE_EXTRA_MATH
24650696a6eSStefan Eßer 		case '$':
24750696a6eSStefan Eßer 		{
24850696a6eSStefan Eßer 			l->t = BC_LEX_OP_TRUNC;
24950696a6eSStefan Eßer 			break;
25050696a6eSStefan Eßer 		}
25150696a6eSStefan Eßer 
25250696a6eSStefan Eßer 		case '@':
25350696a6eSStefan Eßer 		{
25450696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_ASSIGN_PLACES, BC_LEX_OP_PLACES);
25550696a6eSStefan Eßer 			break;
25650696a6eSStefan Eßer 		}
25750696a6eSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
25850696a6eSStefan Eßer 		case '(':
25950696a6eSStefan Eßer 		case ')':
26050696a6eSStefan Eßer 		{
26150696a6eSStefan Eßer 			l->t = (BcLexType) (c - '(' + BC_LEX_LPAREN);
26250696a6eSStefan Eßer 			break;
26350696a6eSStefan Eßer 		}
26450696a6eSStefan Eßer 
26550696a6eSStefan Eßer 		case '*':
26650696a6eSStefan Eßer 		{
26750696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_ASSIGN_MULTIPLY, BC_LEX_OP_MULTIPLY);
26850696a6eSStefan Eßer 			break;
26950696a6eSStefan Eßer 		}
27050696a6eSStefan Eßer 
27150696a6eSStefan Eßer 		case '+':
27250696a6eSStefan Eßer 		{
27350696a6eSStefan Eßer 			c2 = l->buf[l->i];
27444d4804dSStefan Eßer 
27544d4804dSStefan Eßer 			// Have to check for increment first.
27678bc019dSStefan Eßer 			if (c2 == '+')
27778bc019dSStefan Eßer 			{
27850696a6eSStefan Eßer 				l->i += 1;
27950696a6eSStefan Eßer 				l->t = BC_LEX_OP_INC;
28050696a6eSStefan Eßer 			}
28150696a6eSStefan Eßer 			else bc_lex_assign(l, BC_LEX_OP_ASSIGN_PLUS, BC_LEX_OP_PLUS);
28250696a6eSStefan Eßer 			break;
28350696a6eSStefan Eßer 		}
28450696a6eSStefan Eßer 
28550696a6eSStefan Eßer 		case ',':
28650696a6eSStefan Eßer 		{
28750696a6eSStefan Eßer 			l->t = BC_LEX_COMMA;
28850696a6eSStefan Eßer 			break;
28950696a6eSStefan Eßer 		}
29050696a6eSStefan Eßer 
29150696a6eSStefan Eßer 		case '-':
29250696a6eSStefan Eßer 		{
29350696a6eSStefan Eßer 			c2 = l->buf[l->i];
29444d4804dSStefan Eßer 
29544d4804dSStefan Eßer 			// Have to check for decrement first.
29678bc019dSStefan Eßer 			if (c2 == '-')
29778bc019dSStefan Eßer 			{
29850696a6eSStefan Eßer 				l->i += 1;
29950696a6eSStefan Eßer 				l->t = BC_LEX_OP_DEC;
30050696a6eSStefan Eßer 			}
30150696a6eSStefan Eßer 			else bc_lex_assign(l, BC_LEX_OP_ASSIGN_MINUS, BC_LEX_OP_MINUS);
30250696a6eSStefan Eßer 			break;
30350696a6eSStefan Eßer 		}
30450696a6eSStefan Eßer 
30550696a6eSStefan Eßer 		case '.':
30650696a6eSStefan Eßer 		{
30750696a6eSStefan Eßer 			c2 = l->buf[l->i];
30844d4804dSStefan Eßer 
30944d4804dSStefan Eßer 			// If it's alone, it's an alias for last.
31050696a6eSStefan Eßer 			if (BC_LEX_NUM_CHAR(c2, true, false)) bc_lex_number(l, c);
31178bc019dSStefan Eßer 			else
31278bc019dSStefan Eßer 			{
31350696a6eSStefan Eßer 				l->t = BC_LEX_KW_LAST;
31450696a6eSStefan Eßer 				bc_lex_err(l, BC_ERR_POSIX_DOT);
31550696a6eSStefan Eßer 			}
31644d4804dSStefan Eßer 
31750696a6eSStefan Eßer 			break;
31850696a6eSStefan Eßer 		}
31950696a6eSStefan Eßer 
32050696a6eSStefan Eßer 		case '/':
32150696a6eSStefan Eßer 		{
32250696a6eSStefan Eßer 			c2 = l->buf[l->i];
32350696a6eSStefan Eßer 			if (c2 == '*') bc_lex_comment(l);
32450696a6eSStefan Eßer 			else bc_lex_assign(l, BC_LEX_OP_ASSIGN_DIVIDE, BC_LEX_OP_DIVIDE);
32550696a6eSStefan Eßer 			break;
32650696a6eSStefan Eßer 		}
32750696a6eSStefan Eßer 
32850696a6eSStefan Eßer 		case '0':
32950696a6eSStefan Eßer 		case '1':
33050696a6eSStefan Eßer 		case '2':
33150696a6eSStefan Eßer 		case '3':
33250696a6eSStefan Eßer 		case '4':
33350696a6eSStefan Eßer 		case '5':
33450696a6eSStefan Eßer 		case '6':
33550696a6eSStefan Eßer 		case '7':
33650696a6eSStefan Eßer 		case '8':
33750696a6eSStefan Eßer 		case '9':
33850696a6eSStefan Eßer 		case 'A':
33950696a6eSStefan Eßer 		case 'B':
34050696a6eSStefan Eßer 		case 'C':
34150696a6eSStefan Eßer 		case 'D':
34250696a6eSStefan Eßer 		case 'E':
34350696a6eSStefan Eßer 		case 'F':
34450696a6eSStefan Eßer 		// Apparently, GNU bc (and maybe others) allows any uppercase letter as
34550696a6eSStefan Eßer 		// a number. When single digits, they act like the ones above. When
34650696a6eSStefan Eßer 		// multi-digit, any letter above the input base is automatically set to
34750696a6eSStefan Eßer 		// the biggest allowable digit in the input base.
34850696a6eSStefan Eßer 		case 'G':
34950696a6eSStefan Eßer 		case 'H':
35050696a6eSStefan Eßer 		case 'I':
35150696a6eSStefan Eßer 		case 'J':
35250696a6eSStefan Eßer 		case 'K':
35350696a6eSStefan Eßer 		case 'L':
35450696a6eSStefan Eßer 		case 'M':
35550696a6eSStefan Eßer 		case 'N':
35650696a6eSStefan Eßer 		case 'O':
35750696a6eSStefan Eßer 		case 'P':
35850696a6eSStefan Eßer 		case 'Q':
35950696a6eSStefan Eßer 		case 'R':
36050696a6eSStefan Eßer 		case 'S':
36150696a6eSStefan Eßer 		case 'T':
36250696a6eSStefan Eßer 		case 'U':
36350696a6eSStefan Eßer 		case 'V':
36450696a6eSStefan Eßer 		case 'W':
36550696a6eSStefan Eßer 		case 'X':
36650696a6eSStefan Eßer 		case 'Y':
36750696a6eSStefan Eßer 		case 'Z':
36850696a6eSStefan Eßer 		{
36950696a6eSStefan Eßer 			bc_lex_number(l, c);
37050696a6eSStefan Eßer 			break;
37150696a6eSStefan Eßer 		}
37250696a6eSStefan Eßer 
37350696a6eSStefan Eßer 		case ';':
37450696a6eSStefan Eßer 		{
37550696a6eSStefan Eßer 			l->t = BC_LEX_SCOLON;
37650696a6eSStefan Eßer 			break;
37750696a6eSStefan Eßer 		}
37850696a6eSStefan Eßer 
37950696a6eSStefan Eßer 		case '<':
38050696a6eSStefan Eßer 		{
38150696a6eSStefan Eßer #if BC_ENABLE_EXTRA_MATH
38250696a6eSStefan Eßer 			c2 = l->buf[l->i];
38350696a6eSStefan Eßer 
38444d4804dSStefan Eßer 			// Check for shift.
38578bc019dSStefan Eßer 			if (c2 == '<')
38678bc019dSStefan Eßer 			{
38750696a6eSStefan Eßer 				l->i += 1;
38850696a6eSStefan Eßer 				bc_lex_assign(l, BC_LEX_OP_ASSIGN_LSHIFT, BC_LEX_OP_LSHIFT);
38950696a6eSStefan Eßer 				break;
39050696a6eSStefan Eßer 			}
39150696a6eSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
39250696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_REL_LE, BC_LEX_OP_REL_LT);
39350696a6eSStefan Eßer 			break;
39450696a6eSStefan Eßer 		}
39550696a6eSStefan Eßer 
39650696a6eSStefan Eßer 		case '=':
39750696a6eSStefan Eßer 		{
39850696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_REL_EQ, BC_LEX_OP_ASSIGN);
39950696a6eSStefan Eßer 			break;
40050696a6eSStefan Eßer 		}
40150696a6eSStefan Eßer 
40250696a6eSStefan Eßer 		case '>':
40350696a6eSStefan Eßer 		{
40450696a6eSStefan Eßer #if BC_ENABLE_EXTRA_MATH
40550696a6eSStefan Eßer 			c2 = l->buf[l->i];
40650696a6eSStefan Eßer 
40744d4804dSStefan Eßer 			// Check for shift.
40878bc019dSStefan Eßer 			if (c2 == '>')
40978bc019dSStefan Eßer 			{
41050696a6eSStefan Eßer 				l->i += 1;
41150696a6eSStefan Eßer 				bc_lex_assign(l, BC_LEX_OP_ASSIGN_RSHIFT, BC_LEX_OP_RSHIFT);
41250696a6eSStefan Eßer 				break;
41350696a6eSStefan Eßer 			}
41450696a6eSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
41550696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_REL_GE, BC_LEX_OP_REL_GT);
41650696a6eSStefan Eßer 			break;
41750696a6eSStefan Eßer 		}
41850696a6eSStefan Eßer 
41950696a6eSStefan Eßer 		case '[':
42050696a6eSStefan Eßer 		case ']':
42150696a6eSStefan Eßer 		{
42250696a6eSStefan Eßer 			l->t = (BcLexType) (c - '[' + BC_LEX_LBRACKET);
42350696a6eSStefan Eßer 			break;
42450696a6eSStefan Eßer 		}
42550696a6eSStefan Eßer 
42650696a6eSStefan Eßer 		case '\\':
42750696a6eSStefan Eßer 		{
42844d4804dSStefan Eßer 			// In bc, a backslash+newline is whitespace.
42978bc019dSStefan Eßer 			if (BC_NO_ERR(l->buf[l->i] == '\n'))
43078bc019dSStefan Eßer 			{
43150696a6eSStefan Eßer 				l->i += 1;
43250696a6eSStefan Eßer 				l->t = BC_LEX_WHITESPACE;
43350696a6eSStefan Eßer 			}
43450696a6eSStefan Eßer 			else bc_lex_invalidChar(l, c);
43550696a6eSStefan Eßer 			break;
43650696a6eSStefan Eßer 		}
43750696a6eSStefan Eßer 
43850696a6eSStefan Eßer 		case '^':
43950696a6eSStefan Eßer 		{
44050696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_ASSIGN_POWER, BC_LEX_OP_POWER);
44150696a6eSStefan Eßer 			break;
44250696a6eSStefan Eßer 		}
44350696a6eSStefan Eßer 
44450696a6eSStefan Eßer 		case 'a':
44550696a6eSStefan Eßer 		case 'b':
44650696a6eSStefan Eßer 		case 'c':
44750696a6eSStefan Eßer 		case 'd':
44850696a6eSStefan Eßer 		case 'e':
44950696a6eSStefan Eßer 		case 'f':
45050696a6eSStefan Eßer 		case 'g':
45150696a6eSStefan Eßer 		case 'h':
45250696a6eSStefan Eßer 		case 'i':
45350696a6eSStefan Eßer 		case 'j':
45450696a6eSStefan Eßer 		case 'k':
45550696a6eSStefan Eßer 		case 'l':
45650696a6eSStefan Eßer 		case 'm':
45750696a6eSStefan Eßer 		case 'n':
45850696a6eSStefan Eßer 		case 'o':
45950696a6eSStefan Eßer 		case 'p':
46050696a6eSStefan Eßer 		case 'q':
46150696a6eSStefan Eßer 		case 'r':
46250696a6eSStefan Eßer 		case 's':
46350696a6eSStefan Eßer 		case 't':
46450696a6eSStefan Eßer 		case 'u':
46550696a6eSStefan Eßer 		case 'v':
46650696a6eSStefan Eßer 		case 'w':
46750696a6eSStefan Eßer 		case 'x':
46850696a6eSStefan Eßer 		case 'y':
46950696a6eSStefan Eßer 		case 'z':
47050696a6eSStefan Eßer 		{
47150696a6eSStefan Eßer 			bc_lex_identifier(l);
47250696a6eSStefan Eßer 			break;
47350696a6eSStefan Eßer 		}
47450696a6eSStefan Eßer 
47550696a6eSStefan Eßer 		case '{':
47650696a6eSStefan Eßer 		case '}':
47750696a6eSStefan Eßer 		{
47850696a6eSStefan Eßer 			l->t = (BcLexType) (c - '{' + BC_LEX_LBRACE);
47950696a6eSStefan Eßer 			break;
48050696a6eSStefan Eßer 		}
48150696a6eSStefan Eßer 
48250696a6eSStefan Eßer 		case '|':
48350696a6eSStefan Eßer 		{
48450696a6eSStefan Eßer 			c2 = l->buf[l->i];
48550696a6eSStefan Eßer 
48644d4804dSStefan Eßer 			// Once again, boolean or is not allowed by POSIX.
48778bc019dSStefan Eßer 			if (BC_NO_ERR(c2 == '|'))
48878bc019dSStefan Eßer 			{
48950696a6eSStefan Eßer 				bc_lex_verr(l, BC_ERR_POSIX_BOOL, "||");
49050696a6eSStefan Eßer 
49150696a6eSStefan Eßer 				l->i += 1;
49250696a6eSStefan Eßer 				l->t = BC_LEX_OP_BOOL_OR;
49350696a6eSStefan Eßer 			}
49450696a6eSStefan Eßer 			else bc_lex_invalidChar(l, c);
49550696a6eSStefan Eßer 
49650696a6eSStefan Eßer 			break;
49750696a6eSStefan Eßer 		}
49850696a6eSStefan Eßer 
49950696a6eSStefan Eßer 		default:
50050696a6eSStefan Eßer 		{
50150696a6eSStefan Eßer 			bc_lex_invalidChar(l, c);
50250696a6eSStefan Eßer 		}
50350696a6eSStefan Eßer 	}
50450696a6eSStefan Eßer }
50550696a6eSStefan Eßer #endif // BC_ENABLED
506