xref: /freebsd/contrib/bc/src/bc_lex.c (revision d101cdd6)
150696a6eSStefan Eßer /*
250696a6eSStefan Eßer  * *****************************************************************************
350696a6eSStefan Eßer  *
450696a6eSStefan Eßer  * SPDX-License-Identifier: BSD-2-Clause
550696a6eSStefan Eßer  *
6d101cdd6SStefan Eßer  * Copyright (c) 2018-2023 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
bc_lex_identifier(BcLex * l)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
bc_lex_string(BcLex * l)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 
118d101cdd6SStefan Eßer 		assert(vm->mode != BC_MODE_STDIN || buf == vm->buffer.v);
11944d4804dSStefan Eßer 
12044d4804dSStefan Eßer 		// Fortunately for us, bc doesn't escape quotes. Instead, the equivalent
12144d4804dSStefan Eßer 		// is '\q', which makes this loop simpler.
12278bc019dSStefan Eßer 		for (i = l->i; (c = buf[i]) && c != '"'; ++i)
12378bc019dSStefan Eßer 		{
12478bc019dSStefan Eßer 			nlines += (c == '\n');
12578bc019dSStefan Eßer 		}
12644d4804dSStefan Eßer 
127d101cdd6SStefan Eßer 		if (BC_ERR(c == '\0') && !vm->eof && l->mode != BC_MODE_FILE)
12878bc019dSStefan Eßer 		{
12944d4804dSStefan Eßer 			got_more = bc_lex_readLine(l);
13078bc019dSStefan Eßer 		}
13178bc019dSStefan Eßer 	}
13278bc019dSStefan Eßer 	while (got_more && c != '"');
13344d4804dSStefan Eßer 
13444d4804dSStefan Eßer 	// If the string did not end properly, barf.
13578bc019dSStefan Eßer 	if (c != '"')
13678bc019dSStefan Eßer 	{
13750696a6eSStefan Eßer 		l->i = i;
13850696a6eSStefan Eßer 		bc_lex_err(l, BC_ERR_PARSE_STRING);
13950696a6eSStefan Eßer 	}
14050696a6eSStefan Eßer 
14144d4804dSStefan Eßer 	// Set the temp string to the parsed string.
14250696a6eSStefan Eßer 	len = i - l->i;
14350696a6eSStefan Eßer 	bc_vec_string(&l->str, len, l->buf + l->i);
14450696a6eSStefan Eßer 
14550696a6eSStefan Eßer 	l->i = i + 1;
14650696a6eSStefan Eßer 	l->line += nlines;
14750696a6eSStefan Eßer }
14850696a6eSStefan Eßer 
14944d4804dSStefan Eßer /**
15044d4804dSStefan Eßer  * This function takes a lexed operator and checks to see if it's the assignment
15144d4804dSStefan Eßer  * version, setting the token appropriately.
15244d4804dSStefan Eßer  * @param l        The lexer.
15344d4804dSStefan Eßer  * @param with     The token to assign if it is an assignment operator.
15444d4804dSStefan Eßer  * @param without  The token to assign if it is not an assignment operator.
15544d4804dSStefan Eßer  */
15678bc019dSStefan Eßer static void
bc_lex_assign(BcLex * l,BcLexType with,BcLexType without)15778bc019dSStefan Eßer bc_lex_assign(BcLex* l, BcLexType with, BcLexType without)
15878bc019dSStefan Eßer {
15978bc019dSStefan Eßer 	if (l->buf[l->i] == '=')
16078bc019dSStefan Eßer 	{
16150696a6eSStefan Eßer 		l->i += 1;
16250696a6eSStefan Eßer 		l->t = with;
16350696a6eSStefan Eßer 	}
16450696a6eSStefan Eßer 	else l->t = without;
16550696a6eSStefan Eßer }
16650696a6eSStefan Eßer 
16778bc019dSStefan Eßer void
bc_lex_token(BcLex * l)16878bc019dSStefan Eßer bc_lex_token(BcLex* l)
16978bc019dSStefan Eßer {
17044d4804dSStefan Eßer 	// We increment here. This means that all lexing needs to take that into
17144d4804dSStefan Eßer 	// account, such as when parsing an identifier. If we don't, the first
17244d4804dSStefan Eßer 	// character of every identifier would be missing.
17350696a6eSStefan Eßer 	char c = l->buf[l->i++], c2;
17450696a6eSStefan Eßer 
17510041e99SStefan Eßer 	BC_SIG_ASSERT_LOCKED;
17610041e99SStefan Eßer 
17750696a6eSStefan Eßer 	// This is the workhorse of the lexer.
17878bc019dSStefan Eßer 	switch (c)
17978bc019dSStefan Eßer 	{
18050696a6eSStefan Eßer 		case '\0':
18150696a6eSStefan Eßer 		case '\n':
18250696a6eSStefan Eßer 		case '\t':
18350696a6eSStefan Eßer 		case '\v':
18450696a6eSStefan Eßer 		case '\f':
18550696a6eSStefan Eßer 		case '\r':
18650696a6eSStefan Eßer 		case ' ':
18750696a6eSStefan Eßer 		{
18850696a6eSStefan Eßer 			bc_lex_commonTokens(l, c);
18950696a6eSStefan Eßer 			break;
19050696a6eSStefan Eßer 		}
19150696a6eSStefan Eßer 
19250696a6eSStefan Eßer 		case '!':
19350696a6eSStefan Eßer 		{
19444d4804dSStefan Eßer 			// Even though it's not an assignment, we can use this.
19550696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_REL_NE, BC_LEX_OP_BOOL_NOT);
19650696a6eSStefan Eßer 
19744d4804dSStefan Eßer 			// POSIX doesn't allow boolean not.
19850696a6eSStefan Eßer 			if (l->t == BC_LEX_OP_BOOL_NOT)
19978bc019dSStefan Eßer 			{
20050696a6eSStefan Eßer 				bc_lex_verr(l, BC_ERR_POSIX_BOOL, "!");
20178bc019dSStefan Eßer 			}
20250696a6eSStefan Eßer 
20350696a6eSStefan Eßer 			break;
20450696a6eSStefan Eßer 		}
20550696a6eSStefan Eßer 
20650696a6eSStefan Eßer 		case '"':
20750696a6eSStefan Eßer 		{
20850696a6eSStefan Eßer 			bc_lex_string(l);
20950696a6eSStefan Eßer 			break;
21050696a6eSStefan Eßer 		}
21150696a6eSStefan Eßer 
21250696a6eSStefan Eßer 		case '#':
21350696a6eSStefan Eßer 		{
21444d4804dSStefan Eßer 			// POSIX does not allow line comments.
21550696a6eSStefan Eßer 			bc_lex_err(l, BC_ERR_POSIX_COMMENT);
21650696a6eSStefan Eßer 			bc_lex_lineComment(l);
21750696a6eSStefan Eßer 			break;
21850696a6eSStefan Eßer 		}
21950696a6eSStefan Eßer 
22050696a6eSStefan Eßer 		case '%':
22150696a6eSStefan Eßer 		{
22250696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_ASSIGN_MODULUS, BC_LEX_OP_MODULUS);
22350696a6eSStefan Eßer 			break;
22450696a6eSStefan Eßer 		}
22550696a6eSStefan Eßer 
22650696a6eSStefan Eßer 		case '&':
22750696a6eSStefan Eßer 		{
22850696a6eSStefan Eßer 			c2 = l->buf[l->i];
22944d4804dSStefan Eßer 
23044d4804dSStefan Eßer 			// Either we have boolean and or an error. And boolean and is not
23144d4804dSStefan Eßer 			// allowed by POSIX.
23278bc019dSStefan Eßer 			if (BC_NO_ERR(c2 == '&'))
23378bc019dSStefan Eßer 			{
23450696a6eSStefan Eßer 				bc_lex_verr(l, BC_ERR_POSIX_BOOL, "&&");
23550696a6eSStefan Eßer 
23650696a6eSStefan Eßer 				l->i += 1;
23750696a6eSStefan Eßer 				l->t = BC_LEX_OP_BOOL_AND;
23850696a6eSStefan Eßer 			}
23950696a6eSStefan Eßer 			else bc_lex_invalidChar(l, c);
24050696a6eSStefan Eßer 
24150696a6eSStefan Eßer 			break;
24250696a6eSStefan Eßer 		}
24350696a6eSStefan Eßer #if BC_ENABLE_EXTRA_MATH
24450696a6eSStefan Eßer 		case '$':
24550696a6eSStefan Eßer 		{
24650696a6eSStefan Eßer 			l->t = BC_LEX_OP_TRUNC;
24750696a6eSStefan Eßer 			break;
24850696a6eSStefan Eßer 		}
24950696a6eSStefan Eßer 
25050696a6eSStefan Eßer 		case '@':
25150696a6eSStefan Eßer 		{
25250696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_ASSIGN_PLACES, BC_LEX_OP_PLACES);
25350696a6eSStefan Eßer 			break;
25450696a6eSStefan Eßer 		}
25550696a6eSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
25650696a6eSStefan Eßer 		case '(':
25750696a6eSStefan Eßer 		case ')':
25850696a6eSStefan Eßer 		{
25950696a6eSStefan Eßer 			l->t = (BcLexType) (c - '(' + BC_LEX_LPAREN);
26050696a6eSStefan Eßer 			break;
26150696a6eSStefan Eßer 		}
26250696a6eSStefan Eßer 
26350696a6eSStefan Eßer 		case '*':
26450696a6eSStefan Eßer 		{
26550696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_ASSIGN_MULTIPLY, BC_LEX_OP_MULTIPLY);
26650696a6eSStefan Eßer 			break;
26750696a6eSStefan Eßer 		}
26850696a6eSStefan Eßer 
26950696a6eSStefan Eßer 		case '+':
27050696a6eSStefan Eßer 		{
27150696a6eSStefan Eßer 			c2 = l->buf[l->i];
27244d4804dSStefan Eßer 
27344d4804dSStefan Eßer 			// Have to check for increment first.
27478bc019dSStefan Eßer 			if (c2 == '+')
27578bc019dSStefan Eßer 			{
27650696a6eSStefan Eßer 				l->i += 1;
27750696a6eSStefan Eßer 				l->t = BC_LEX_OP_INC;
27850696a6eSStefan Eßer 			}
27950696a6eSStefan Eßer 			else bc_lex_assign(l, BC_LEX_OP_ASSIGN_PLUS, BC_LEX_OP_PLUS);
28050696a6eSStefan Eßer 			break;
28150696a6eSStefan Eßer 		}
28250696a6eSStefan Eßer 
28350696a6eSStefan Eßer 		case ',':
28450696a6eSStefan Eßer 		{
28550696a6eSStefan Eßer 			l->t = BC_LEX_COMMA;
28650696a6eSStefan Eßer 			break;
28750696a6eSStefan Eßer 		}
28850696a6eSStefan Eßer 
28950696a6eSStefan Eßer 		case '-':
29050696a6eSStefan Eßer 		{
29150696a6eSStefan Eßer 			c2 = l->buf[l->i];
29244d4804dSStefan Eßer 
29344d4804dSStefan Eßer 			// Have to check for decrement first.
29478bc019dSStefan Eßer 			if (c2 == '-')
29578bc019dSStefan Eßer 			{
29650696a6eSStefan Eßer 				l->i += 1;
29750696a6eSStefan Eßer 				l->t = BC_LEX_OP_DEC;
29850696a6eSStefan Eßer 			}
29950696a6eSStefan Eßer 			else bc_lex_assign(l, BC_LEX_OP_ASSIGN_MINUS, BC_LEX_OP_MINUS);
30050696a6eSStefan Eßer 			break;
30150696a6eSStefan Eßer 		}
30250696a6eSStefan Eßer 
30350696a6eSStefan Eßer 		case '.':
30450696a6eSStefan Eßer 		{
30550696a6eSStefan Eßer 			c2 = l->buf[l->i];
30644d4804dSStefan Eßer 
30744d4804dSStefan Eßer 			// If it's alone, it's an alias for last.
30850696a6eSStefan Eßer 			if (BC_LEX_NUM_CHAR(c2, true, false)) bc_lex_number(l, c);
30978bc019dSStefan Eßer 			else
31078bc019dSStefan Eßer 			{
31150696a6eSStefan Eßer 				l->t = BC_LEX_KW_LAST;
31250696a6eSStefan Eßer 				bc_lex_err(l, BC_ERR_POSIX_DOT);
31350696a6eSStefan Eßer 			}
31444d4804dSStefan Eßer 
31550696a6eSStefan Eßer 			break;
31650696a6eSStefan Eßer 		}
31750696a6eSStefan Eßer 
31850696a6eSStefan Eßer 		case '/':
31950696a6eSStefan Eßer 		{
32050696a6eSStefan Eßer 			c2 = l->buf[l->i];
32150696a6eSStefan Eßer 			if (c2 == '*') bc_lex_comment(l);
32250696a6eSStefan Eßer 			else bc_lex_assign(l, BC_LEX_OP_ASSIGN_DIVIDE, BC_LEX_OP_DIVIDE);
32350696a6eSStefan Eßer 			break;
32450696a6eSStefan Eßer 		}
32550696a6eSStefan Eßer 
32650696a6eSStefan Eßer 		case '0':
32750696a6eSStefan Eßer 		case '1':
32850696a6eSStefan Eßer 		case '2':
32950696a6eSStefan Eßer 		case '3':
33050696a6eSStefan Eßer 		case '4':
33150696a6eSStefan Eßer 		case '5':
33250696a6eSStefan Eßer 		case '6':
33350696a6eSStefan Eßer 		case '7':
33450696a6eSStefan Eßer 		case '8':
33550696a6eSStefan Eßer 		case '9':
33650696a6eSStefan Eßer 		case 'A':
33750696a6eSStefan Eßer 		case 'B':
33850696a6eSStefan Eßer 		case 'C':
33950696a6eSStefan Eßer 		case 'D':
34050696a6eSStefan Eßer 		case 'E':
34150696a6eSStefan Eßer 		case 'F':
34250696a6eSStefan Eßer 		// Apparently, GNU bc (and maybe others) allows any uppercase letter as
34350696a6eSStefan Eßer 		// a number. When single digits, they act like the ones above. When
34450696a6eSStefan Eßer 		// multi-digit, any letter above the input base is automatically set to
34550696a6eSStefan Eßer 		// the biggest allowable digit in the input base.
34650696a6eSStefan Eßer 		case 'G':
34750696a6eSStefan Eßer 		case 'H':
34850696a6eSStefan Eßer 		case 'I':
34950696a6eSStefan Eßer 		case 'J':
35050696a6eSStefan Eßer 		case 'K':
35150696a6eSStefan Eßer 		case 'L':
35250696a6eSStefan Eßer 		case 'M':
35350696a6eSStefan Eßer 		case 'N':
35450696a6eSStefan Eßer 		case 'O':
35550696a6eSStefan Eßer 		case 'P':
35650696a6eSStefan Eßer 		case 'Q':
35750696a6eSStefan Eßer 		case 'R':
35850696a6eSStefan Eßer 		case 'S':
35950696a6eSStefan Eßer 		case 'T':
36050696a6eSStefan Eßer 		case 'U':
36150696a6eSStefan Eßer 		case 'V':
36250696a6eSStefan Eßer 		case 'W':
36350696a6eSStefan Eßer 		case 'X':
36450696a6eSStefan Eßer 		case 'Y':
36550696a6eSStefan Eßer 		case 'Z':
36650696a6eSStefan Eßer 		{
36750696a6eSStefan Eßer 			bc_lex_number(l, c);
36850696a6eSStefan Eßer 			break;
36950696a6eSStefan Eßer 		}
37050696a6eSStefan Eßer 
37150696a6eSStefan Eßer 		case ';':
37250696a6eSStefan Eßer 		{
37350696a6eSStefan Eßer 			l->t = BC_LEX_SCOLON;
37450696a6eSStefan Eßer 			break;
37550696a6eSStefan Eßer 		}
37650696a6eSStefan Eßer 
37750696a6eSStefan Eßer 		case '<':
37850696a6eSStefan Eßer 		{
37950696a6eSStefan Eßer #if BC_ENABLE_EXTRA_MATH
38050696a6eSStefan Eßer 			c2 = l->buf[l->i];
38150696a6eSStefan Eßer 
38244d4804dSStefan Eßer 			// Check for shift.
38378bc019dSStefan Eßer 			if (c2 == '<')
38478bc019dSStefan Eßer 			{
38550696a6eSStefan Eßer 				l->i += 1;
38650696a6eSStefan Eßer 				bc_lex_assign(l, BC_LEX_OP_ASSIGN_LSHIFT, BC_LEX_OP_LSHIFT);
38750696a6eSStefan Eßer 				break;
38850696a6eSStefan Eßer 			}
38950696a6eSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
39050696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_REL_LE, BC_LEX_OP_REL_LT);
39150696a6eSStefan Eßer 			break;
39250696a6eSStefan Eßer 		}
39350696a6eSStefan Eßer 
39450696a6eSStefan Eßer 		case '=':
39550696a6eSStefan Eßer 		{
39650696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_REL_EQ, BC_LEX_OP_ASSIGN);
39750696a6eSStefan Eßer 			break;
39850696a6eSStefan Eßer 		}
39950696a6eSStefan Eßer 
40050696a6eSStefan Eßer 		case '>':
40150696a6eSStefan Eßer 		{
40250696a6eSStefan Eßer #if BC_ENABLE_EXTRA_MATH
40350696a6eSStefan Eßer 			c2 = l->buf[l->i];
40450696a6eSStefan Eßer 
40544d4804dSStefan Eßer 			// Check for shift.
40678bc019dSStefan Eßer 			if (c2 == '>')
40778bc019dSStefan Eßer 			{
40850696a6eSStefan Eßer 				l->i += 1;
40950696a6eSStefan Eßer 				bc_lex_assign(l, BC_LEX_OP_ASSIGN_RSHIFT, BC_LEX_OP_RSHIFT);
41050696a6eSStefan Eßer 				break;
41150696a6eSStefan Eßer 			}
41250696a6eSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
41350696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_REL_GE, BC_LEX_OP_REL_GT);
41450696a6eSStefan Eßer 			break;
41550696a6eSStefan Eßer 		}
41650696a6eSStefan Eßer 
41750696a6eSStefan Eßer 		case '[':
41850696a6eSStefan Eßer 		case ']':
41950696a6eSStefan Eßer 		{
42050696a6eSStefan Eßer 			l->t = (BcLexType) (c - '[' + BC_LEX_LBRACKET);
42150696a6eSStefan Eßer 			break;
42250696a6eSStefan Eßer 		}
42350696a6eSStefan Eßer 
42450696a6eSStefan Eßer 		case '\\':
42550696a6eSStefan Eßer 		{
42644d4804dSStefan Eßer 			// In bc, a backslash+newline is whitespace.
42778bc019dSStefan Eßer 			if (BC_NO_ERR(l->buf[l->i] == '\n'))
42878bc019dSStefan Eßer 			{
42950696a6eSStefan Eßer 				l->i += 1;
43050696a6eSStefan Eßer 				l->t = BC_LEX_WHITESPACE;
43150696a6eSStefan Eßer 			}
43250696a6eSStefan Eßer 			else bc_lex_invalidChar(l, c);
43350696a6eSStefan Eßer 			break;
43450696a6eSStefan Eßer 		}
43550696a6eSStefan Eßer 
43650696a6eSStefan Eßer 		case '^':
43750696a6eSStefan Eßer 		{
43850696a6eSStefan Eßer 			bc_lex_assign(l, BC_LEX_OP_ASSIGN_POWER, BC_LEX_OP_POWER);
43950696a6eSStefan Eßer 			break;
44050696a6eSStefan Eßer 		}
44150696a6eSStefan Eßer 
44250696a6eSStefan Eßer 		case 'a':
44350696a6eSStefan Eßer 		case 'b':
44450696a6eSStefan Eßer 		case 'c':
44550696a6eSStefan Eßer 		case 'd':
44650696a6eSStefan Eßer 		case 'e':
44750696a6eSStefan Eßer 		case 'f':
44850696a6eSStefan Eßer 		case 'g':
44950696a6eSStefan Eßer 		case 'h':
45050696a6eSStefan Eßer 		case 'i':
45150696a6eSStefan Eßer 		case 'j':
45250696a6eSStefan Eßer 		case 'k':
45350696a6eSStefan Eßer 		case 'l':
45450696a6eSStefan Eßer 		case 'm':
45550696a6eSStefan Eßer 		case 'n':
45650696a6eSStefan Eßer 		case 'o':
45750696a6eSStefan Eßer 		case 'p':
45850696a6eSStefan Eßer 		case 'q':
45950696a6eSStefan Eßer 		case 'r':
46050696a6eSStefan Eßer 		case 's':
46150696a6eSStefan Eßer 		case 't':
46250696a6eSStefan Eßer 		case 'u':
46350696a6eSStefan Eßer 		case 'v':
46450696a6eSStefan Eßer 		case 'w':
46550696a6eSStefan Eßer 		case 'x':
46650696a6eSStefan Eßer 		case 'y':
46750696a6eSStefan Eßer 		case 'z':
46850696a6eSStefan Eßer 		{
46950696a6eSStefan Eßer 			bc_lex_identifier(l);
47050696a6eSStefan Eßer 			break;
47150696a6eSStefan Eßer 		}
47250696a6eSStefan Eßer 
47350696a6eSStefan Eßer 		case '{':
47450696a6eSStefan Eßer 		case '}':
47550696a6eSStefan Eßer 		{
47650696a6eSStefan Eßer 			l->t = (BcLexType) (c - '{' + BC_LEX_LBRACE);
47750696a6eSStefan Eßer 			break;
47850696a6eSStefan Eßer 		}
47950696a6eSStefan Eßer 
48050696a6eSStefan Eßer 		case '|':
48150696a6eSStefan Eßer 		{
48250696a6eSStefan Eßer 			c2 = l->buf[l->i];
48350696a6eSStefan Eßer 
48444d4804dSStefan Eßer 			// Once again, boolean or is not allowed by POSIX.
48578bc019dSStefan Eßer 			if (BC_NO_ERR(c2 == '|'))
48678bc019dSStefan Eßer 			{
48750696a6eSStefan Eßer 				bc_lex_verr(l, BC_ERR_POSIX_BOOL, "||");
48850696a6eSStefan Eßer 
48950696a6eSStefan Eßer 				l->i += 1;
49050696a6eSStefan Eßer 				l->t = BC_LEX_OP_BOOL_OR;
49150696a6eSStefan Eßer 			}
49250696a6eSStefan Eßer 			else bc_lex_invalidChar(l, c);
49350696a6eSStefan Eßer 
49450696a6eSStefan Eßer 			break;
49550696a6eSStefan Eßer 		}
49650696a6eSStefan Eßer 
49750696a6eSStefan Eßer 		default:
49850696a6eSStefan Eßer 		{
49950696a6eSStefan Eßer 			bc_lex_invalidChar(l, c);
50050696a6eSStefan Eßer 		}
50150696a6eSStefan Eßer 	}
50250696a6eSStefan Eßer }
50350696a6eSStefan Eßer #endif // BC_ENABLED
504