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 dc.
3350696a6eSStefan Eßer *
3450696a6eSStefan Eßer */
3550696a6eSStefan Eßer
3650696a6eSStefan Eßer #if DC_ENABLED
3750696a6eSStefan Eßer
3850696a6eSStefan Eßer #include <ctype.h>
3950696a6eSStefan Eßer
4050696a6eSStefan Eßer #include <dc.h>
4150696a6eSStefan Eßer #include <vm.h>
4250696a6eSStefan Eßer
4378bc019dSStefan Eßer bool
dc_lex_negCommand(BcLex * l)4478bc019dSStefan Eßer dc_lex_negCommand(BcLex* l)
4578bc019dSStefan Eßer {
4650696a6eSStefan Eßer char c = l->buf[l->i];
4750696a6eSStefan Eßer return !BC_LEX_NUM_CHAR(c, false, false);
4850696a6eSStefan Eßer }
4950696a6eSStefan Eßer
5044d4804dSStefan Eßer /**
5144d4804dSStefan Eßer * Processes a dc command that needs a register. This is where the
5244d4804dSStefan Eßer * extended-register extension is implemented.
5344d4804dSStefan Eßer * @param l The lexer.
5444d4804dSStefan Eßer */
5578bc019dSStefan Eßer static void
dc_lex_register(BcLex * l)5678bc019dSStefan Eßer dc_lex_register(BcLex* l)
5778bc019dSStefan Eßer {
5844d4804dSStefan Eßer // If extended register is enabled and the character is whitespace...
5978bc019dSStefan Eßer if (DC_X && isspace(l->buf[l->i - 1]))
6078bc019dSStefan Eßer {
6150696a6eSStefan Eßer char c;
6250696a6eSStefan Eßer
6344d4804dSStefan Eßer // Eat the whitespace.
6450696a6eSStefan Eßer bc_lex_whitespace(l);
6550696a6eSStefan Eßer c = l->buf[l->i];
6650696a6eSStefan Eßer
6744d4804dSStefan Eßer // Check for a letter or underscore.
6844d4804dSStefan Eßer if (BC_ERR(!isalpha(c) && c != '_'))
6978bc019dSStefan Eßer {
7050696a6eSStefan Eßer bc_lex_verr(l, BC_ERR_PARSE_CHAR, c);
7178bc019dSStefan Eßer }
7250696a6eSStefan Eßer
7344d4804dSStefan Eßer // Parse a normal identifier.
7450696a6eSStefan Eßer l->i += 1;
7550696a6eSStefan Eßer bc_lex_name(l);
7650696a6eSStefan Eßer }
7778bc019dSStefan Eßer else
7878bc019dSStefan Eßer {
7944d4804dSStefan Eßer // I don't allow newlines because newlines are used for controlling when
8044d4804dSStefan Eßer // execution happens, and allowing newlines would just be complex.
8144d4804dSStefan Eßer if (BC_ERR(l->buf[l->i - 1] == '\n'))
8278bc019dSStefan Eßer {
8344d4804dSStefan Eßer bc_lex_verr(l, BC_ERR_PARSE_CHAR, l->buf[l->i - 1]);
8478bc019dSStefan Eßer }
8544d4804dSStefan Eßer
8644d4804dSStefan Eßer // Set the lexer string and token.
8710328f8bSStefan Eßer bc_vec_popAll(&l->str);
8850696a6eSStefan Eßer bc_vec_pushByte(&l->str, (uchar) l->buf[l->i - 1]);
8950696a6eSStefan Eßer bc_vec_pushByte(&l->str, '\0');
9050696a6eSStefan Eßer l->t = BC_LEX_NAME;
9150696a6eSStefan Eßer }
9250696a6eSStefan Eßer }
9350696a6eSStefan Eßer
9444d4804dSStefan Eßer /**
9544d4804dSStefan Eßer * Parses a dc string. Since dc's strings need to check for balanced brackets,
9644d4804dSStefan Eßer * we can't just parse bc and dc strings with different start and end
9744d4804dSStefan Eßer * characters. Oh, and dc strings need to check for escaped brackets.
9844d4804dSStefan Eßer * @param l The lexer.
9944d4804dSStefan Eßer */
10078bc019dSStefan Eßer static void
dc_lex_string(BcLex * l)10178bc019dSStefan Eßer dc_lex_string(BcLex* l)
10278bc019dSStefan Eßer {
10344d4804dSStefan Eßer size_t depth, nls, i;
10450696a6eSStefan Eßer char c;
10544d4804dSStefan Eßer bool got_more;
10650696a6eSStefan Eßer
10744d4804dSStefan Eßer // Set the token and clear the string.
10850696a6eSStefan Eßer l->t = BC_LEX_STR;
10910328f8bSStefan Eßer bc_vec_popAll(&l->str);
11050696a6eSStefan Eßer
11178bc019dSStefan Eßer do
11278bc019dSStefan Eßer {
11344d4804dSStefan Eßer depth = 1;
11444d4804dSStefan Eßer nls = 0;
11544d4804dSStefan Eßer got_more = false;
11644d4804dSStefan Eßer
117d101cdd6SStefan Eßer assert(l->mode != BC_MODE_STDIN || l->buf == vm->buffer.v);
11844d4804dSStefan Eßer
11944d4804dSStefan Eßer // This is the meat. As long as we don't run into the NUL byte, and we
12044d4804dSStefan Eßer // have "depth", which means we haven't completely balanced brackets
12144d4804dSStefan Eßer // yet, we continue eating the string.
12278bc019dSStefan Eßer for (i = l->i; (c = l->buf[i]) && depth; ++i)
12378bc019dSStefan Eßer {
12444d4804dSStefan Eßer // Check for escaped brackets and set the depths as appropriate.
12578bc019dSStefan Eßer if (c == '\\')
12678bc019dSStefan Eßer {
12750696a6eSStefan Eßer c = l->buf[++i];
12850696a6eSStefan Eßer if (!c) break;
12950696a6eSStefan Eßer }
13078bc019dSStefan Eßer else
13178bc019dSStefan Eßer {
13250696a6eSStefan Eßer depth += (c == '[');
13350696a6eSStefan Eßer depth -= (c == ']');
13450696a6eSStefan Eßer }
13550696a6eSStefan Eßer
13644d4804dSStefan Eßer // We want to adjust the line in the lexer as necessary.
13750696a6eSStefan Eßer nls += (c == '\n');
13850696a6eSStefan Eßer
13950696a6eSStefan Eßer if (depth) bc_vec_push(&l->str, &c);
14050696a6eSStefan Eßer }
14150696a6eSStefan Eßer
14278bc019dSStefan Eßer if (BC_ERR(c == '\0' && depth))
14378bc019dSStefan Eßer {
144d101cdd6SStefan Eßer if (!vm->eof && l->mode != BC_MODE_FILE)
14578bc019dSStefan Eßer {
14623210c9fSStefan Eßer got_more = bc_lex_readLine(l);
14778bc019dSStefan Eßer }
148d101cdd6SStefan Eßer
149d101cdd6SStefan Eßer if (got_more)
150d101cdd6SStefan Eßer {
151d101cdd6SStefan Eßer bc_vec_popAll(&l->str);
152d101cdd6SStefan Eßer }
15344d4804dSStefan Eßer }
15478bc019dSStefan Eßer }
15578bc019dSStefan Eßer while (got_more && depth);
15644d4804dSStefan Eßer
15744d4804dSStefan Eßer // Obviously, if we didn't balance, that's an error.
15878bc019dSStefan Eßer if (BC_ERR(c == '\0' && depth))
15978bc019dSStefan Eßer {
16050696a6eSStefan Eßer l->i = i;
16150696a6eSStefan Eßer bc_lex_err(l, BC_ERR_PARSE_STRING);
16250696a6eSStefan Eßer }
16350696a6eSStefan Eßer
16450696a6eSStefan Eßer bc_vec_pushByte(&l->str, '\0');
16550696a6eSStefan Eßer
16650696a6eSStefan Eßer l->i = i;
16750696a6eSStefan Eßer l->line += nls;
16850696a6eSStefan Eßer }
16950696a6eSStefan Eßer
17044d4804dSStefan Eßer /**
17144d4804dSStefan Eßer * Lexes a dc token. This is the dc implementation of BcLexNext.
17244d4804dSStefan Eßer * @param l The lexer.
17344d4804dSStefan Eßer */
17478bc019dSStefan Eßer void
dc_lex_token(BcLex * l)17578bc019dSStefan Eßer dc_lex_token(BcLex* l)
17678bc019dSStefan Eßer {
17750696a6eSStefan Eßer char c = l->buf[l->i++], c2;
17850696a6eSStefan Eßer size_t i;
17950696a6eSStefan Eßer
18010041e99SStefan Eßer BC_SIG_ASSERT_LOCKED;
18110041e99SStefan Eßer
18244d4804dSStefan Eßer // If the last token was a command that needs a register, we need to parse a
18344d4804dSStefan Eßer // register, so do so.
18478bc019dSStefan Eßer for (i = 0; i < dc_lex_regs_len; ++i)
18578bc019dSStefan Eßer {
18644d4804dSStefan Eßer // If the token is a register token, take care of it and return.
18778bc019dSStefan Eßer if (l->last == dc_lex_regs[i])
18878bc019dSStefan Eßer {
18950696a6eSStefan Eßer dc_lex_register(l);
19050696a6eSStefan Eßer return;
19150696a6eSStefan Eßer }
19250696a6eSStefan Eßer }
19350696a6eSStefan Eßer
19444d4804dSStefan Eßer // These lines are for tokens that easily correspond to one character. We
19544d4804dSStefan Eßer // just set the token.
19650696a6eSStefan Eßer if (c >= '"' && c <= '~' &&
19750696a6eSStefan Eßer (l->t = dc_lex_tokens[(c - '"')]) != BC_LEX_INVALID)
19850696a6eSStefan Eßer {
19950696a6eSStefan Eßer return;
20050696a6eSStefan Eßer }
20150696a6eSStefan Eßer
20244d4804dSStefan Eßer // This is the workhorse of the lexer when more complicated things are
20344d4804dSStefan Eßer // needed.
20478bc019dSStefan Eßer switch (c)
20578bc019dSStefan Eßer {
20650696a6eSStefan Eßer case '\0':
20750696a6eSStefan Eßer case '\n':
20850696a6eSStefan Eßer case '\t':
20950696a6eSStefan Eßer case '\v':
21050696a6eSStefan Eßer case '\f':
21150696a6eSStefan Eßer case '\r':
21250696a6eSStefan Eßer case ' ':
21350696a6eSStefan Eßer {
21450696a6eSStefan Eßer bc_lex_commonTokens(l, c);
21550696a6eSStefan Eßer break;
21650696a6eSStefan Eßer }
21750696a6eSStefan Eßer
21844d4804dSStefan Eßer // We don't have the ! command, so we always expect certain things
21944d4804dSStefan Eßer // after the exclamation point.
22050696a6eSStefan Eßer case '!':
22150696a6eSStefan Eßer {
22250696a6eSStefan Eßer c2 = l->buf[l->i];
22350696a6eSStefan Eßer
22450696a6eSStefan Eßer if (c2 == '=') l->t = BC_LEX_OP_REL_NE;
22550696a6eSStefan Eßer else if (c2 == '<') l->t = BC_LEX_OP_REL_LE;
22650696a6eSStefan Eßer else if (c2 == '>') l->t = BC_LEX_OP_REL_GE;
22750696a6eSStefan Eßer else bc_lex_invalidChar(l, c);
22850696a6eSStefan Eßer
22950696a6eSStefan Eßer l->i += 1;
23044d4804dSStefan Eßer
23150696a6eSStefan Eßer break;
23250696a6eSStefan Eßer }
23350696a6eSStefan Eßer
23450696a6eSStefan Eßer case '#':
23550696a6eSStefan Eßer {
23650696a6eSStefan Eßer bc_lex_lineComment(l);
23750696a6eSStefan Eßer break;
23850696a6eSStefan Eßer }
23950696a6eSStefan Eßer
24050696a6eSStefan Eßer case '.':
24150696a6eSStefan Eßer {
24250696a6eSStefan Eßer c2 = l->buf[l->i];
24344d4804dSStefan Eßer
24444d4804dSStefan Eßer // If the character after is a number, this dot is part of a number.
24544d4804dSStefan Eßer // Otherwise, it's the BSD dot (equivalent to last).
24650696a6eSStefan Eßer if (BC_NO_ERR(BC_LEX_NUM_CHAR(c2, true, false)))
24778bc019dSStefan Eßer {
24850696a6eSStefan Eßer bc_lex_number(l, c);
24978bc019dSStefan Eßer }
25050696a6eSStefan Eßer else bc_lex_invalidChar(l, c);
25144d4804dSStefan Eßer
25250696a6eSStefan Eßer break;
25350696a6eSStefan Eßer }
25450696a6eSStefan Eßer
25550696a6eSStefan Eßer case '0':
25650696a6eSStefan Eßer case '1':
25750696a6eSStefan Eßer case '2':
25850696a6eSStefan Eßer case '3':
25950696a6eSStefan Eßer case '4':
26050696a6eSStefan Eßer case '5':
26150696a6eSStefan Eßer case '6':
26250696a6eSStefan Eßer case '7':
26350696a6eSStefan Eßer case '8':
26450696a6eSStefan Eßer case '9':
26550696a6eSStefan Eßer case 'A':
26650696a6eSStefan Eßer case 'B':
26750696a6eSStefan Eßer case 'C':
26850696a6eSStefan Eßer case 'D':
26950696a6eSStefan Eßer case 'E':
27050696a6eSStefan Eßer case 'F':
27150696a6eSStefan Eßer {
27250696a6eSStefan Eßer bc_lex_number(l, c);
27350696a6eSStefan Eßer break;
27450696a6eSStefan Eßer }
27550696a6eSStefan Eßer
276d43fa8efSStefan Eßer case 'g':
277d43fa8efSStefan Eßer {
278d43fa8efSStefan Eßer c2 = l->buf[l->i];
279d43fa8efSStefan Eßer
280d43fa8efSStefan Eßer if (c2 == 'l') l->t = BC_LEX_KW_LINE_LENGTH;
281103d7cdfSStefan Eßer else if (c2 == 'x') l->t = BC_LEX_EXTENDED_REGISTERS;
282d43fa8efSStefan Eßer else if (c2 == 'z') l->t = BC_LEX_KW_LEADING_ZERO;
283d43fa8efSStefan Eßer else bc_lex_invalidChar(l, c2);
284d43fa8efSStefan Eßer
285d43fa8efSStefan Eßer l->i += 1;
286d43fa8efSStefan Eßer
287d43fa8efSStefan Eßer break;
288d43fa8efSStefan Eßer }
289d43fa8efSStefan Eßer
29050696a6eSStefan Eßer case '[':
29150696a6eSStefan Eßer {
29250696a6eSStefan Eßer dc_lex_string(l);
29350696a6eSStefan Eßer break;
29450696a6eSStefan Eßer }
29550696a6eSStefan Eßer
29650696a6eSStefan Eßer default:
29750696a6eSStefan Eßer {
29850696a6eSStefan Eßer bc_lex_invalidChar(l, c);
29950696a6eSStefan Eßer }
30050696a6eSStefan Eßer }
30150696a6eSStefan Eßer }
30250696a6eSStefan Eßer #endif // DC_ENABLED
303