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