xref: /freebsd/contrib/bc/src/dc_lex.c (revision 103d7cdf)
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