xref: /freebsd/contrib/bc/src/dc_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 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 
117*12e0d316SStefan Eßer #if !BC_ENABLE_OSSFUZZ
118d101cdd6SStefan Eßer 		assert(l->mode != BC_MODE_STDIN || l->buf == vm->buffer.v);
119*12e0d316SStefan Eßer #endif // !BC_ENABLE_OSSFUZZ
12044d4804dSStefan Eßer 
12144d4804dSStefan Eßer 		// This is the meat. As long as we don't run into the NUL byte, and we
12244d4804dSStefan Eßer 		// have "depth", which means we haven't completely balanced brackets
12344d4804dSStefan Eßer 		// yet, we continue eating the string.
12478bc019dSStefan Eßer 		for (i = l->i; (c = l->buf[i]) && depth; ++i)
12578bc019dSStefan Eßer 		{
12644d4804dSStefan Eßer 			// Check for escaped brackets and set the depths as appropriate.
12778bc019dSStefan Eßer 			if (c == '\\')
12878bc019dSStefan Eßer 			{
12950696a6eSStefan Eßer 				c = l->buf[++i];
13050696a6eSStefan Eßer 				if (!c) break;
13150696a6eSStefan Eßer 			}
13278bc019dSStefan Eßer 			else
13378bc019dSStefan Eßer 			{
13450696a6eSStefan Eßer 				depth += (c == '[');
13550696a6eSStefan Eßer 				depth -= (c == ']');
13650696a6eSStefan Eßer 			}
13750696a6eSStefan Eßer 
13844d4804dSStefan Eßer 			// We want to adjust the line in the lexer as necessary.
13950696a6eSStefan Eßer 			nls += (c == '\n');
14050696a6eSStefan Eßer 
14150696a6eSStefan Eßer 			if (depth) bc_vec_push(&l->str, &c);
14250696a6eSStefan Eßer 		}
14350696a6eSStefan Eßer 
14478bc019dSStefan Eßer 		if (BC_ERR(c == '\0' && depth))
14578bc019dSStefan Eßer 		{
146d101cdd6SStefan Eßer 			if (!vm->eof && l->mode != BC_MODE_FILE)
14778bc019dSStefan Eßer 			{
14823210c9fSStefan Eßer 				got_more = bc_lex_readLine(l);
14978bc019dSStefan Eßer 			}
150d101cdd6SStefan Eßer 
151d101cdd6SStefan Eßer 			if (got_more)
152d101cdd6SStefan Eßer 			{
153d101cdd6SStefan Eßer 				bc_vec_popAll(&l->str);
154d101cdd6SStefan Eßer 			}
15544d4804dSStefan Eßer 		}
15678bc019dSStefan Eßer 	}
15778bc019dSStefan Eßer 	while (got_more && depth);
15844d4804dSStefan Eßer 
15944d4804dSStefan Eßer 	// Obviously, if we didn't balance, that's an error.
16078bc019dSStefan Eßer 	if (BC_ERR(c == '\0' && depth))
16178bc019dSStefan Eßer 	{
16250696a6eSStefan Eßer 		l->i = i;
16350696a6eSStefan Eßer 		bc_lex_err(l, BC_ERR_PARSE_STRING);
16450696a6eSStefan Eßer 	}
16550696a6eSStefan Eßer 
16650696a6eSStefan Eßer 	bc_vec_pushByte(&l->str, '\0');
16750696a6eSStefan Eßer 
16850696a6eSStefan Eßer 	l->i = i;
16950696a6eSStefan Eßer 	l->line += nls;
17050696a6eSStefan Eßer }
17150696a6eSStefan Eßer 
17244d4804dSStefan Eßer /**
17344d4804dSStefan Eßer  * Lexes a dc token. This is the dc implementation of BcLexNext.
17444d4804dSStefan Eßer  * @param l  The lexer.
17544d4804dSStefan Eßer  */
17678bc019dSStefan Eßer void
dc_lex_token(BcLex * l)17778bc019dSStefan Eßer dc_lex_token(BcLex* l)
17878bc019dSStefan Eßer {
17950696a6eSStefan Eßer 	char c = l->buf[l->i++], c2;
18050696a6eSStefan Eßer 	size_t i;
18150696a6eSStefan Eßer 
18210041e99SStefan Eßer 	BC_SIG_ASSERT_LOCKED;
18310041e99SStefan Eßer 
18444d4804dSStefan Eßer 	// If the last token was a command that needs a register, we need to parse a
18544d4804dSStefan Eßer 	// register, so do so.
18678bc019dSStefan Eßer 	for (i = 0; i < dc_lex_regs_len; ++i)
18778bc019dSStefan Eßer 	{
18844d4804dSStefan Eßer 		// If the token is a register token, take care of it and return.
18978bc019dSStefan Eßer 		if (l->last == dc_lex_regs[i])
19078bc019dSStefan Eßer 		{
19150696a6eSStefan Eßer 			dc_lex_register(l);
19250696a6eSStefan Eßer 			return;
19350696a6eSStefan Eßer 		}
19450696a6eSStefan Eßer 	}
19550696a6eSStefan Eßer 
19644d4804dSStefan Eßer 	// These lines are for tokens that easily correspond to one character. We
19744d4804dSStefan Eßer 	// just set the token.
19850696a6eSStefan Eßer 	if (c >= '"' && c <= '~' &&
19950696a6eSStefan Eßer 	    (l->t = dc_lex_tokens[(c - '"')]) != BC_LEX_INVALID)
20050696a6eSStefan Eßer 	{
20150696a6eSStefan Eßer 		return;
20250696a6eSStefan Eßer 	}
20350696a6eSStefan Eßer 
20444d4804dSStefan Eßer 	// This is the workhorse of the lexer when more complicated things are
20544d4804dSStefan Eßer 	// needed.
20678bc019dSStefan Eßer 	switch (c)
20778bc019dSStefan Eßer 	{
20850696a6eSStefan Eßer 		case '\0':
20950696a6eSStefan Eßer 		case '\n':
21050696a6eSStefan Eßer 		case '\t':
21150696a6eSStefan Eßer 		case '\v':
21250696a6eSStefan Eßer 		case '\f':
21350696a6eSStefan Eßer 		case '\r':
21450696a6eSStefan Eßer 		case ' ':
21550696a6eSStefan Eßer 		{
21650696a6eSStefan Eßer 			bc_lex_commonTokens(l, c);
21750696a6eSStefan Eßer 			break;
21850696a6eSStefan Eßer 		}
21950696a6eSStefan Eßer 
22044d4804dSStefan Eßer 		// We don't have the ! command, so we always expect certain things
22144d4804dSStefan Eßer 		// after the exclamation point.
22250696a6eSStefan Eßer 		case '!':
22350696a6eSStefan Eßer 		{
22450696a6eSStefan Eßer 			c2 = l->buf[l->i];
22550696a6eSStefan Eßer 
22650696a6eSStefan Eßer 			if (c2 == '=') l->t = BC_LEX_OP_REL_NE;
22750696a6eSStefan Eßer 			else if (c2 == '<') l->t = BC_LEX_OP_REL_LE;
22850696a6eSStefan Eßer 			else if (c2 == '>') l->t = BC_LEX_OP_REL_GE;
22950696a6eSStefan Eßer 			else bc_lex_invalidChar(l, c);
23050696a6eSStefan Eßer 
23150696a6eSStefan Eßer 			l->i += 1;
23244d4804dSStefan Eßer 
23350696a6eSStefan Eßer 			break;
23450696a6eSStefan Eßer 		}
23550696a6eSStefan Eßer 
23650696a6eSStefan Eßer 		case '#':
23750696a6eSStefan Eßer 		{
23850696a6eSStefan Eßer 			bc_lex_lineComment(l);
23950696a6eSStefan Eßer 			break;
24050696a6eSStefan Eßer 		}
24150696a6eSStefan Eßer 
24250696a6eSStefan Eßer 		case '.':
24350696a6eSStefan Eßer 		{
24450696a6eSStefan Eßer 			c2 = l->buf[l->i];
24544d4804dSStefan Eßer 
24644d4804dSStefan Eßer 			// If the character after is a number, this dot is part of a number.
24744d4804dSStefan Eßer 			// Otherwise, it's the BSD dot (equivalent to last).
24850696a6eSStefan Eßer 			if (BC_NO_ERR(BC_LEX_NUM_CHAR(c2, true, false)))
24978bc019dSStefan Eßer 			{
25050696a6eSStefan Eßer 				bc_lex_number(l, c);
25178bc019dSStefan Eßer 			}
25250696a6eSStefan Eßer 			else bc_lex_invalidChar(l, c);
25344d4804dSStefan Eßer 
25450696a6eSStefan Eßer 			break;
25550696a6eSStefan Eßer 		}
25650696a6eSStefan Eßer 
25750696a6eSStefan Eßer 		case '0':
25850696a6eSStefan Eßer 		case '1':
25950696a6eSStefan Eßer 		case '2':
26050696a6eSStefan Eßer 		case '3':
26150696a6eSStefan Eßer 		case '4':
26250696a6eSStefan Eßer 		case '5':
26350696a6eSStefan Eßer 		case '6':
26450696a6eSStefan Eßer 		case '7':
26550696a6eSStefan Eßer 		case '8':
26650696a6eSStefan Eßer 		case '9':
26750696a6eSStefan Eßer 		case 'A':
26850696a6eSStefan Eßer 		case 'B':
26950696a6eSStefan Eßer 		case 'C':
27050696a6eSStefan Eßer 		case 'D':
27150696a6eSStefan Eßer 		case 'E':
27250696a6eSStefan Eßer 		case 'F':
27350696a6eSStefan Eßer 		{
27450696a6eSStefan Eßer 			bc_lex_number(l, c);
27550696a6eSStefan Eßer 			break;
27650696a6eSStefan Eßer 		}
27750696a6eSStefan Eßer 
278d43fa8efSStefan Eßer 		case 'g':
279d43fa8efSStefan Eßer 		{
280d43fa8efSStefan Eßer 			c2 = l->buf[l->i];
281d43fa8efSStefan Eßer 
282d43fa8efSStefan Eßer 			if (c2 == 'l') l->t = BC_LEX_KW_LINE_LENGTH;
283103d7cdfSStefan Eßer 			else if (c2 == 'x') l->t = BC_LEX_EXTENDED_REGISTERS;
284d43fa8efSStefan Eßer 			else if (c2 == 'z') l->t = BC_LEX_KW_LEADING_ZERO;
285d43fa8efSStefan Eßer 			else bc_lex_invalidChar(l, c2);
286d43fa8efSStefan Eßer 
287d43fa8efSStefan Eßer 			l->i += 1;
288d43fa8efSStefan Eßer 
289d43fa8efSStefan Eßer 			break;
290d43fa8efSStefan Eßer 		}
291d43fa8efSStefan Eßer 
29250696a6eSStefan Eßer 		case '[':
29350696a6eSStefan Eßer 		{
29450696a6eSStefan Eßer 			dc_lex_string(l);
29550696a6eSStefan Eßer 			break;
29650696a6eSStefan Eßer 		}
29750696a6eSStefan Eßer 
29850696a6eSStefan Eßer 		default:
29950696a6eSStefan Eßer 		{
30050696a6eSStefan Eßer 			bc_lex_invalidChar(l, c);
30150696a6eSStefan Eßer 		}
30250696a6eSStefan Eßer 	}
30350696a6eSStefan Eßer }
30450696a6eSStefan Eßer #endif // DC_ENABLED
305