xref: /freebsd/contrib/bc/include/parse.h (revision 103d7cdf)
1252884aeSStefan Eßer /*
2252884aeSStefan Eßer  * *****************************************************************************
3252884aeSStefan Eßer  *
43aa99676SStefan Eßer  * SPDX-License-Identifier: BSD-2-Clause
5252884aeSStefan Eßer  *
6d101cdd6SStefan Eßer  * Copyright (c) 2018-2023 Gavin D. Howard and contributors.
7252884aeSStefan Eßer  *
8252884aeSStefan Eßer  * Redistribution and use in source and binary forms, with or without
9252884aeSStefan Eßer  * modification, are permitted provided that the following conditions are met:
10252884aeSStefan Eßer  *
11252884aeSStefan Eßer  * * Redistributions of source code must retain the above copyright notice, this
12252884aeSStefan Eßer  *   list of conditions and the following disclaimer.
13252884aeSStefan Eßer  *
14252884aeSStefan Eßer  * * Redistributions in binary form must reproduce the above copyright notice,
15252884aeSStefan Eßer  *   this list of conditions and the following disclaimer in the documentation
16252884aeSStefan Eßer  *   and/or other materials provided with the distribution.
17252884aeSStefan Eßer  *
18252884aeSStefan Eßer  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19252884aeSStefan Eßer  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20252884aeSStefan Eßer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21252884aeSStefan Eßer  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22252884aeSStefan Eßer  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23252884aeSStefan Eßer  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24252884aeSStefan Eßer  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25252884aeSStefan Eßer  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26252884aeSStefan Eßer  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27252884aeSStefan Eßer  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28252884aeSStefan Eßer  * POSSIBILITY OF SUCH DAMAGE.
29252884aeSStefan Eßer  *
30252884aeSStefan Eßer  * *****************************************************************************
31252884aeSStefan Eßer  *
32252884aeSStefan Eßer  * Definitions for bc's parser.
33252884aeSStefan Eßer  *
34252884aeSStefan Eßer  */
35252884aeSStefan Eßer 
36252884aeSStefan Eßer #ifndef BC_PARSE_H
37252884aeSStefan Eßer #define BC_PARSE_H
38252884aeSStefan Eßer 
39252884aeSStefan Eßer #include <limits.h>
40252884aeSStefan Eßer #include <stdbool.h>
41252884aeSStefan Eßer #include <stdint.h>
42252884aeSStefan Eßer 
43252884aeSStefan Eßer #include <status.h>
44252884aeSStefan Eßer #include <vector.h>
45252884aeSStefan Eßer #include <lex.h>
46252884aeSStefan Eßer #include <lang.h>
47252884aeSStefan Eßer 
4844d4804dSStefan Eßer // The following are flags that can be passed to @a BcParseExpr functions. They
4944d4804dSStefan Eßer // define the requirements that the parsed expression must meet to not have an
5044d4804dSStefan Eßer // error thrown.
5144d4804dSStefan Eßer 
5244d4804dSStefan Eßer /// A flag that requires that the expression is valid for conditionals in for
5344d4804dSStefan Eßer /// loops, while loops, and if statements. This is because POSIX requires that
5444d4804dSStefan Eßer /// certain operators are *only* used in those cases. It's whacked, but that's
5544d4804dSStefan Eßer /// how it is.
56252884aeSStefan Eßer #define BC_PARSE_REL (UINTMAX_C(1) << 0)
5744d4804dSStefan Eßer 
5844d4804dSStefan Eßer /// A flag that requires that the expression is valid for a print statement.
59252884aeSStefan Eßer #define BC_PARSE_PRINT (UINTMAX_C(1) << 1)
6044d4804dSStefan Eßer 
6144d4804dSStefan Eßer /// A flag that requires that the expression does *not* have any function call.
62252884aeSStefan Eßer #define BC_PARSE_NOCALL (UINTMAX_C(1) << 2)
6344d4804dSStefan Eßer 
6478bc019dSStefan Eßer /// A flag that requires that the expression does *not* have a read()
6578bc019dSStefan Eßer /// expression.
66252884aeSStefan Eßer #define BC_PARSE_NOREAD (UINTMAX_C(1) << 3)
6744d4804dSStefan Eßer 
6844d4804dSStefan Eßer /// A flag that *allows* (rather than requires) that an array appear in the
6944d4804dSStefan Eßer /// expression. This is mostly used as parameters in bc.
70252884aeSStefan Eßer #define BC_PARSE_ARRAY (UINTMAX_C(1) << 4)
7144d4804dSStefan Eßer 
7244d4804dSStefan Eßer /// A flag that requires that the expression is not empty and returns a value.
73252884aeSStefan Eßer #define BC_PARSE_NEEDVAL (UINTMAX_C(1) << 5)
74252884aeSStefan Eßer 
7544d4804dSStefan Eßer /**
7644d4804dSStefan Eßer  * Returns true if the parser has been initialized.
7744d4804dSStefan Eßer  * @param p    The parser.
7844d4804dSStefan Eßer  * @param prg  The program.
7944d4804dSStefan Eßer  * @return     True if @a p has been initialized, false otherwise.
8044d4804dSStefan Eßer  */
8144d4804dSStefan Eßer #define BC_PARSE_IS_INITED(p, prg) ((p)->prog == (prg))
8244d4804dSStefan Eßer 
8344d4804dSStefan Eßer /**
8444d4804dSStefan Eßer  * Returns true if the current parser state allows parsing, false otherwise.
8544d4804dSStefan Eßer  * @param p  The parser.
8644d4804dSStefan Eßer  * @return   True if parsing can proceed, false otherwise.
8744d4804dSStefan Eßer  */
88252884aeSStefan Eßer #define BC_PARSE_CAN_PARSE(p) ((p).l.t != BC_LEX_EOF)
8944d4804dSStefan Eßer 
9044d4804dSStefan Eßer /**
9144d4804dSStefan Eßer  * Pushes the instruction @a i onto the bytecode vector for the current
9244d4804dSStefan Eßer  * function.
9344d4804dSStefan Eßer  * @param p  The parser.
9444d4804dSStefan Eßer  * @param i  The instruction to push onto the bytecode vector.
9544d4804dSStefan Eßer  */
96252884aeSStefan Eßer #define bc_parse_push(p, i) (bc_vec_pushByte(&(p)->func->code, (uchar) (i)))
9744d4804dSStefan Eßer 
9844d4804dSStefan Eßer /**
9944d4804dSStefan Eßer  * Pushes an index onto the bytecode vector. For more information, see
10044d4804dSStefan Eßer  * @a bc_vec_pushIndex() in src/vector.c and @a bc_program_index() in
10144d4804dSStefan Eßer  * src/program.c.
10244d4804dSStefan Eßer  * @param p    The parser.
10344d4804dSStefan Eßer  * @param idx  The index to push onto the bytecode vector.
10444d4804dSStefan Eßer  */
105252884aeSStefan Eßer #define bc_parse_pushIndex(p, idx) (bc_vec_pushIndex(&(p)->func->code, (idx)))
106252884aeSStefan Eßer 
10744d4804dSStefan Eßer /**
108d101cdd6SStefan Eßer  * A convenience macro for throwing errors in parse code. This takes care of
10944d4804dSStefan Eßer  * plumbing like passing in the current line the lexer is on.
11044d4804dSStefan Eßer  * @param p  The parser.
11144d4804dSStefan Eßer  * @param e  The error.
11244d4804dSStefan Eßer  */
113103d7cdfSStefan Eßer #if BC_DEBUG
114d101cdd6SStefan Eßer #define bc_parse_err(p, e) \
115d101cdd6SStefan Eßer 	(bc_vm_handleError((e), __FILE__, __LINE__, (p)->l.line))
116103d7cdfSStefan Eßer #else // BC_DEBUG
11750696a6eSStefan Eßer #define bc_parse_err(p, e) (bc_vm_handleError((e), (p)->l.line))
118103d7cdfSStefan Eßer #endif // BC_DEBUG
11944d4804dSStefan Eßer 
12044d4804dSStefan Eßer /**
121d101cdd6SStefan Eßer  * A convenience macro for throwing errors in parse code. This takes care of
12244d4804dSStefan Eßer  * plumbing like passing in the current line the lexer is on.
12344d4804dSStefan Eßer  * @param p    The parser.
12444d4804dSStefan Eßer  * @param e    The error.
12544d4804dSStefan Eßer  * @param ...  The varags that are needed.
12644d4804dSStefan Eßer  */
127103d7cdfSStefan Eßer #if BC_DEBUG
128d101cdd6SStefan Eßer #define bc_parse_verr(p, e, ...) \
129d101cdd6SStefan Eßer 	(bc_vm_handleError((e), __FILE__, __LINE__, (p)->l.line, __VA_ARGS__))
130103d7cdfSStefan Eßer #else // BC_DEBUG
13150696a6eSStefan Eßer #define bc_parse_verr(p, e, ...) \
13250696a6eSStefan Eßer 	(bc_vm_handleError((e), (p)->l.line, __VA_ARGS__))
133103d7cdfSStefan Eßer #endif // BC_DEBUG
134252884aeSStefan Eßer 
13544d4804dSStefan Eßer // Forward declarations.
136252884aeSStefan Eßer struct BcParse;
137252884aeSStefan Eßer struct BcProgram;
138252884aeSStefan Eßer 
13944d4804dSStefan Eßer /**
14044d4804dSStefan Eßer  * A function pointer to call when more parsing is needed.
14144d4804dSStefan Eßer  * @param p  The parser.
14244d4804dSStefan Eßer  */
14344d4804dSStefan Eßer typedef void (*BcParseParse)(struct BcParse* p);
144252884aeSStefan Eßer 
14544d4804dSStefan Eßer /**
14644d4804dSStefan Eßer  * A function pointer to call when an expression needs to be parsed. This can
14744d4804dSStefan Eßer  * happen for read() expressions or dc strings.
14844d4804dSStefan Eßer  * @param p      The parser.
14944d4804dSStefan Eßer  * @param flags  The flags for what is allowed or required. (See flags above.)
15044d4804dSStefan Eßer  */
15144d4804dSStefan Eßer typedef void (*BcParseExpr)(struct BcParse* p, uint8_t flags);
15244d4804dSStefan Eßer 
15344d4804dSStefan Eßer /// The parser struct.
15478bc019dSStefan Eßer typedef struct BcParse
15578bc019dSStefan Eßer {
15644d4804dSStefan Eßer 	/// The lexer.
157252884aeSStefan Eßer 	BcLex l;
158252884aeSStefan Eßer 
159252884aeSStefan Eßer #if BC_ENABLED
16044d4804dSStefan Eßer 	/// The stack of flags for bc. (See comments in include/bc.h.) This stack is
16144d4804dSStefan Eßer 	/// *required* to have one item at all times. Not maintaining that invariant
16244d4804dSStefan Eßer 	/// will cause problems.
163252884aeSStefan Eßer 	BcVec flags;
16444d4804dSStefan Eßer 
16544d4804dSStefan Eßer 	/// The stack of exits. These are indices into the bytecode vector where
16644d4804dSStefan Eßer 	/// blocks for loops and if statements end. Basically, these are the places
16744d4804dSStefan Eßer 	/// to jump to when skipping code.
168252884aeSStefan Eßer 	BcVec exits;
16944d4804dSStefan Eßer 
17044d4804dSStefan Eßer 	/// The stack of conditionals. Unlike exits, which are indices to jump
17144d4804dSStefan Eßer 	/// *forward* to, this is a vector of indices to jump *backward* to, usually
17244d4804dSStefan Eßer 	/// to the conditional of a loop, hence the name.
173252884aeSStefan Eßer 	BcVec conds;
17444d4804dSStefan Eßer 
17544d4804dSStefan Eßer 	/// A stack of operators. When parsing expressions, the bc parser uses the
17644d4804dSStefan Eßer 	/// Shunting-Yard algorithm, which requires a stack of operators. This can
17744d4804dSStefan Eßer 	/// hold the stack for multiple expressions at once because the expressions
17844d4804dSStefan Eßer 	/// stack as well. For more information, see the Expression Parsing section
17944d4804dSStefan Eßer 	/// of the Development manual (manuals/development.md).
180252884aeSStefan Eßer 	BcVec ops;
18144d4804dSStefan Eßer 
18244d4804dSStefan Eßer 	/// A buffer to temporarily store a string in. This is because the lexer
18344d4804dSStefan Eßer 	/// might generate a string as part of its work, and the parser needs that
18444d4804dSStefan Eßer 	/// string, but it also needs the lexer to continue lexing, which might
18544d4804dSStefan Eßer 	/// overwrite the string stored in the lexer. This buffer is for copying
18644d4804dSStefan Eßer 	/// that string from the lexer to keep it safe.
187252884aeSStefan Eßer 	BcVec buf;
188252884aeSStefan Eßer #endif // BC_ENABLED
189252884aeSStefan Eßer 
19044d4804dSStefan Eßer 	/// A reference to the program to grab the current function when necessary.
191252884aeSStefan Eßer 	struct BcProgram* prog;
19244d4804dSStefan Eßer 
19344d4804dSStefan Eßer 	/// A reference to the current function. The function is what holds the
19444d4804dSStefan Eßer 	/// bytecode vector that the parser is filling.
195252884aeSStefan Eßer 	BcFunc* func;
19644d4804dSStefan Eßer 
19744d4804dSStefan Eßer 	/// The index of the function.
198252884aeSStefan Eßer 	size_t fidx;
199252884aeSStefan Eßer 
20044d4804dSStefan Eßer #if BC_ENABLED
20144d4804dSStefan Eßer 	/// True if the bc parser just entered a function and an auto statement
20244d4804dSStefan Eßer 	/// would be valid.
203252884aeSStefan Eßer 	bool auto_part;
20444d4804dSStefan Eßer #endif // BC_ENABLED
205252884aeSStefan Eßer 
206252884aeSStefan Eßer } BcParse;
207252884aeSStefan Eßer 
20844d4804dSStefan Eßer /**
20944d4804dSStefan Eßer  * Initializes a parser.
21044d4804dSStefan Eßer  * @param p     The parser to initialize.
21144d4804dSStefan Eßer  * @param prog  A referenc to the program.
21244d4804dSStefan Eßer  * @param func  The index of the current function.
21344d4804dSStefan Eßer  */
21478bc019dSStefan Eßer void
21578bc019dSStefan Eßer bc_parse_init(BcParse* p, struct BcProgram* prog, size_t func);
21644d4804dSStefan Eßer 
21744d4804dSStefan Eßer /**
218103d7cdfSStefan Eßer  * Frees a parser. This is not guarded by #if BC_DEBUG because a separate
21944d4804dSStefan Eßer  * parser is created at runtime to parse read() expressions and dc strings.
22044d4804dSStefan Eßer  * @param p  The parser to free.
22144d4804dSStefan Eßer  */
22278bc019dSStefan Eßer void
22378bc019dSStefan Eßer bc_parse_free(BcParse* p);
22444d4804dSStefan Eßer 
22544d4804dSStefan Eßer /**
22644d4804dSStefan Eßer  * Resets the parser. Resetting means erasing all state to the point that the
22744d4804dSStefan Eßer  * parser would think it was just initialized.
22844d4804dSStefan Eßer  * @param p  The parser to reset.
22944d4804dSStefan Eßer  */
23078bc019dSStefan Eßer void
23178bc019dSStefan Eßer bc_parse_reset(BcParse* p);
232252884aeSStefan Eßer 
23344d4804dSStefan Eßer /**
23444d4804dSStefan Eßer  * Adds a string. See @a BcProgram in include/program.h for more details.
23544d4804dSStefan Eßer  * @param p  The parser that parsed the string.
23644d4804dSStefan Eßer  */
23778bc019dSStefan Eßer void
23878bc019dSStefan Eßer bc_parse_addString(BcParse* p);
239252884aeSStefan Eßer 
24044d4804dSStefan Eßer /**
24144d4804dSStefan Eßer  * Adds a number. See @a BcProgram in include/program.h for more details.
24244d4804dSStefan Eßer  * @param p  The parser that parsed the number.
24344d4804dSStefan Eßer  */
24478bc019dSStefan Eßer void
24578bc019dSStefan Eßer bc_parse_number(BcParse* p);
24644d4804dSStefan Eßer 
24744d4804dSStefan Eßer /**
24844d4804dSStefan Eßer  * Update the current function in the parser.
24944d4804dSStefan Eßer  * @param p     The parser.
25044d4804dSStefan Eßer  * @param fidx  The index of the new function.
25144d4804dSStefan Eßer  */
25278bc019dSStefan Eßer void
25378bc019dSStefan Eßer bc_parse_updateFunc(BcParse* p, size_t fidx);
25444d4804dSStefan Eßer 
25544d4804dSStefan Eßer /**
25644d4804dSStefan Eßer  * Adds a new variable or array. See @a BcProgram in include/program.h for more
25744d4804dSStefan Eßer  * details.
25844d4804dSStefan Eßer  * @param p     The parser that parsed the variable or array name.
25944d4804dSStefan Eßer  * @param name  The name of the variable or array to add.
26044d4804dSStefan Eßer  * @param var   True if the name is for a variable, false if it's for an array.
26144d4804dSStefan Eßer  */
26278bc019dSStefan Eßer void
26378bc019dSStefan Eßer bc_parse_pushName(const BcParse* p, char* name, bool var);
26444d4804dSStefan Eßer 
26544d4804dSStefan Eßer /**
26644d4804dSStefan Eßer  * Sets the text that the parser will parse.
26744d4804dSStefan Eßer  * @param p     The parser.
26844d4804dSStefan Eßer  * @param text  The text to lex.
269d101cdd6SStefan Eßer  * @param mode  The mode to parse in.
27044d4804dSStefan Eßer  */
27178bc019dSStefan Eßer void
272d101cdd6SStefan Eßer bc_parse_text(BcParse* p, const char* text, BcMode mode);
27344d4804dSStefan Eßer 
27444d4804dSStefan Eßer // References to const 0 and 1 strings for special cases. bc and dc have
27544d4804dSStefan Eßer // specific instructions for 0 and 1 because they pop up so often and (in the
27644d4804dSStefan Eßer // case of 1), increment/decrement operators.
27750696a6eSStefan Eßer extern const char bc_parse_zero[2];
27850696a6eSStefan Eßer extern const char bc_parse_one[2];
279252884aeSStefan Eßer 
280252884aeSStefan Eßer #endif // BC_PARSE_H
281