/*
 * *****************************************************************************
 *
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * *****************************************************************************
 *
 * Definitions for bc only.
 *
 */

#ifndef BC_BC_H
#define BC_BC_H

#if BC_ENABLED

#include <limits.h>
#include <stdbool.h>

#include <status.h>
#include <lex.h>
#include <parse.h>

/**
 * The main function for bc. It just sets variables and passes its arguments
 * through to @a bc_vm_boot().
 * @return  A status.
 */
BcStatus
bc_main(int argc, const char* argv[]);

// These are references to the help text, the library text, and the "filename"
// for the library.
extern const char bc_help[];
extern const char bc_lib[];
extern const char* bc_lib_name;

// These are references to the second math library and its "filename."
#if BC_ENABLE_EXTRA_MATH
extern const char bc_lib2[];
extern const char* bc_lib2_name;
#endif // BC_ENABLE_EXTRA_MATH

/**
 * A struct containing information about a bc keyword.
 */
typedef struct BcLexKeyword
{
	/// Holds the length of the keyword along with a bit that, if set, means the
	/// keyword is used in POSIX bc.
	uchar data;

	/// The keyword text.
	const char name[14];
} BcLexKeyword;

/// Sets the most significant bit. Used for setting the POSIX bit in
/// BcLexKeyword's data field.
#define BC_LEX_CHAR_MSB(bit) ((bit) << (CHAR_BIT - 1))

/// Returns non-zero if the keyword is POSIX, zero otherwise.
#define BC_LEX_KW_POSIX(kw) ((kw)->data & (BC_LEX_CHAR_MSB(1)))

/// Returns the length of the keyword.
#define BC_LEX_KW_LEN(kw) ((size_t) ((kw)->data & ~(BC_LEX_CHAR_MSB(1))))

/// A macro to easily build a keyword entry. See bc_lex_kws in src/data.c.
#define BC_LEX_KW_ENTRY(a, b, c) \
	{ .data = ((b) & ~(BC_LEX_CHAR_MSB(1))) | BC_LEX_CHAR_MSB(c), .name = a }

#if BC_ENABLE_EXTRA_MATH

/// A macro for the number of keywords bc has. This has to be updated if any are
/// added. This is for the redefined_kws field of the BcVm struct.
#define BC_LEX_NKWS (37)

#else // BC_ENABLE_EXTRA_MATH

/// A macro for the number of keywords bc has. This has to be updated if any are
/// added. This is for the redefined_kws field of the BcVm struct.
#define BC_LEX_NKWS (33)

#endif // BC_ENABLE_EXTRA_MATH

// The array of keywords and its length.
extern const BcLexKeyword bc_lex_kws[];
extern const size_t bc_lex_kws_len;

/**
 * The @a BcLexNext function for bc. (See include/lex.h for a definition of
 * @a BcLexNext.)
 * @param l  The lexer.
 */
void
bc_lex_token(BcLex* l);

// The following section is for flags needed when parsing bc code. These flags
// are complicated, but necessary. Why you ask? Because bc's standard is awful.
//
// If you don't believe me, go read the bc Parsing section of the Development
// manual (manuals/development.md). Then come back.
//
// In other words, these flags are the sign declaring, "Here be dragons."

/**
 * This returns a pointer to the set of flags at the top of the flag stack.
 * @a p is expected to be a BcParse pointer.
 * @param p  The parser.
 * @return   A pointer to the top flag set.
 */
#define BC_PARSE_TOP_FLAG_PTR(p) ((uint16_t*) bc_vec_top(&(p)->flags))

/**
 * This returns the flag set at the top of the flag stack. @a p is expected to
 * be a BcParse pointer.
 * @param p  The parser.
 * @return   The top flag set.
 */
#define BC_PARSE_TOP_FLAG(p) (*(BC_PARSE_TOP_FLAG_PTR(p)))

// After this point, all flag #defines are in sets of 2: one to define the flag,
// and one to define a way to grab the flag from the flag set at the top of the
// flag stack. All `p` arguments are pointers to a BcParse.

// This flag is set if the parser has seen a left brace.
#define BC_PARSE_FLAG_BRACE (UINTMAX_C(1) << 0)
#define BC_PARSE_BRACE(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_BRACE)

// This flag is set if the parser is parsing inside of the braces of a function
// body.
#define BC_PARSE_FLAG_FUNC_INNER (UINTMAX_C(1) << 1)
#define BC_PARSE_FUNC_INNER(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_FUNC_INNER)

// This flag is set if the parser is parsing a function. It is different from
// the one above because it is set if it is parsing a function body *or* header,
// not just if it's parsing a function body.
#define BC_PARSE_FLAG_FUNC (UINTMAX_C(1) << 2)
#define BC_PARSE_FUNC(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_FUNC)

// This flag is set if the parser is expecting to parse a body, whether of a
// function, an if statement, or a loop.
#define BC_PARSE_FLAG_BODY (UINTMAX_C(1) << 3)
#define BC_PARSE_BODY(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_BODY)

// This flag is set if bc is parsing a loop. This is important because the break
// and continue keywords are only valid inside of a loop.
#define BC_PARSE_FLAG_LOOP (UINTMAX_C(1) << 4)
#define BC_PARSE_LOOP(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_LOOP)

// This flag is set if bc is parsing the body of a loop. It is different from
// the one above the same way @a BC_PARSE_FLAG_FUNC_INNER is different from
// @a BC_PARSE_FLAG_FUNC.
#define BC_PARSE_FLAG_LOOP_INNER (UINTMAX_C(1) << 5)
#define BC_PARSE_LOOP_INNER(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_LOOP_INNER)

// This flag is set if bc is parsing an if statement.
#define BC_PARSE_FLAG_IF (UINTMAX_C(1) << 6)
#define BC_PARSE_IF(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_IF)

// This flag is set if bc is parsing an else statement. This is important
// because of "else if" constructions, among other things.
#define BC_PARSE_FLAG_ELSE (UINTMAX_C(1) << 7)
#define BC_PARSE_ELSE(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_ELSE)

// This flag is set if bc just finished parsing an if statement and its body.
// It tells the parser that it can probably expect an else statement next. This
// flag is, thus, one of the most subtle.
#define BC_PARSE_FLAG_IF_END (UINTMAX_C(1) << 8)
#define BC_PARSE_IF_END(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_IF_END)

/**
 * This returns true if bc is in a state where it should not execute any code
 * at all.
 * @param p  The parser.
 * @return   True if execution cannot proceed, false otherwise.
 */
#define BC_PARSE_NO_EXEC(p) ((p)->flags.len != 1 || BC_PARSE_TOP_FLAG(p) != 0)

/**
 * This returns true if the token @a t is a statement delimiter, which is
 * either a newline or a semicolon.
 * @param t  The token to check.
 * @return   True if t is a statement delimiter token; false otherwise.
 */
#define BC_PARSE_DELIMITER(t) \
	((t) == BC_LEX_SCOLON || (t) == BC_LEX_NLINE || (t) == BC_LEX_EOF)

/**
 * This is poorly named, but it basically returns whether or not the current
 * state is valid for the end of an else statement.
 * @param f  The flag set to be checked.
 * @return   True if the state is valid for the end of an else statement.
 */
#define BC_PARSE_BLOCK_STMT(f) \
	((f) & (BC_PARSE_FLAG_ELSE | BC_PARSE_FLAG_LOOP_INNER))

/**
 * This returns the value of the data for an operator with precedence @a p and
 * associativity @a l (true if left associative, false otherwise). This is used
 * to construct an array of operators, bc_parse_ops, in src/data.c.
 * @param p  The precedence.
 * @param l  True if the operator is left associative, false otherwise.
 * @return   The data for the operator.
 */
#define BC_PARSE_OP(p, l) (((p) & ~(BC_LEX_CHAR_MSB(1))) | (BC_LEX_CHAR_MSB(l)))

/**
 * Returns the operator data for the lex token @a t.
 * @param t  The token to return operator data for.
 * @return   The operator data for @a t.
 */
#define BC_PARSE_OP_DATA(t) bc_parse_ops[((t) - BC_LEX_OP_INC)]

/**
 * Returns non-zero if operator @a op is left associative, zero otherwise.
 * @param op  The operator to test for associativity.
 * @return    Non-zero if the operator is left associative, zero otherwise.
 */
#define BC_PARSE_OP_LEFT(op) (BC_PARSE_OP_DATA(op) & BC_LEX_CHAR_MSB(1))

/**
 * Returns the precedence of operator @a op. Lower number means higher
 * precedence.
 * @param op  The operator to return the precedence of.
 * @return    The precedence of @a op.
 */
#define BC_PARSE_OP_PREC(op) (BC_PARSE_OP_DATA(op) & ~(BC_LEX_CHAR_MSB(1)))

/**
 * A macro to easily define a series of bits for whether a lex token is an
 * expression token or not. It takes 8 expression bits, corresponding to the 8
 * bits in a uint8_t. You can see this in use for bc_parse_exprs in src/data.c.
 * @param e1  The first bit.
 * @param e2  The second bit.
 * @param e3  The third bit.
 * @param e4  The fourth bit.
 * @param e5  The fifth bit.
 * @param e6  The sixth bit.
 * @param e7  The seventh bit.
 * @param e8  The eighth bit.
 * @return    An expression entry for bc_parse_exprs[].
 */
#define BC_PARSE_EXPR_ENTRY(e1, e2, e3, e4, e5, e6, e7, e8)               \
	((UINTMAX_C(e1) << 7) | (UINTMAX_C(e2) << 6) | (UINTMAX_C(e3) << 5) | \
	 (UINTMAX_C(e4) << 4) | (UINTMAX_C(e5) << 3) | (UINTMAX_C(e6) << 2) | \
	 (UINTMAX_C(e7) << 1) | (UINTMAX_C(e8) << 0))

/**
 * Returns true if token @a i is a token that belongs in an expression.
 * @param i  The token to test.
 * @return   True if i is an expression token, false otherwise.
 */
#define BC_PARSE_EXPR(i) \
	(bc_parse_exprs[(((i) & (uchar) ~(0x07)) >> 3)] & (1 << (7 - ((i) & 0x07))))

/**
 * Returns the operator (by lex token) that is at the top of the operator
 * stack.
 * @param p  The parser.
 * @return   The operator that is at the top of the operator stack, as a lex
 *           token.
 */
#define BC_PARSE_TOP_OP(p) (*((BcLexType*) bc_vec_top(&(p)->ops)))

/**
 * Returns true if bc has a "leaf" token. A "leaf" token is one that can stand
 * alone in an expression. For example, a number by itself can be an expression,
 * but a binary operator, while valid for an expression, cannot be alone in the
 * expression. It must have an expression to the left and right of itself. See
 * the documentation for @a bc_parse_expr_err() in src/bc_parse.c.
 * @param prev      The previous token as an instruction.
 * @param bin_last  True if that last operator was a binary operator, false
 *                  otherwise.
 * @param rparen    True if the last operator was a right paren.
 * return           True if the last token was a leaf token, false otherwise.
 */
#define BC_PARSE_LEAF(prev, bin_last, rparen) \
	(!(bin_last) && ((rparen) || bc_parse_inst_isLeaf(prev)))

/**
 * This returns true if the token @a t should be treated as though it's a
 * variable. This goes for actual variables, array elements, and globals.
 * @param t  The token to test.
 * @return   True if @a t should be treated as though it's a variable, false
 *           otherwise.
 */
#if BC_ENABLE_EXTRA_MATH
#define BC_PARSE_INST_VAR(t) \
	((t) >= BC_INST_VAR && (t) <= BC_INST_SEED && (t) != BC_INST_ARRAY)
#else // BC_ENABLE_EXTRA_MATH
#define BC_PARSE_INST_VAR(t) \
	((t) >= BC_INST_VAR && (t) <= BC_INST_SCALE && (t) != BC_INST_ARRAY)
#endif // BC_ENABLE_EXTRA_MATH

/**
 * Returns true if the previous token @a p (in the form of a bytecode
 * instruction) is a prefix operator. The fact that it is for bytecode
 * instructions is what makes it different from @a BC_PARSE_OP_PREFIX below.
 * @param p  The previous token.
 * @return   True if @a p is a prefix operator.
 */
#define BC_PARSE_PREV_PREFIX(p) ((p) >= BC_INST_NEG && (p) <= BC_INST_BOOL_NOT)

/**
 * Returns true if token @a t is a prefix operator.
 * @param t  The token to test.
 * @return   True if @a t is a prefix operator, false otherwise.
 */
#define BC_PARSE_OP_PREFIX(t) ((t) == BC_LEX_OP_BOOL_NOT || (t) == BC_LEX_NEG)

/**
 * We can calculate the conversion between tokens and bytecode instructions by
 * subtracting the position of the first operator in the lex enum and adding the
 * position of the first in the instruction enum. Note: This only works for
 * binary operators.
 * @param t  The token to turn into an instruction.
 * @return   The token as an instruction.
 */
#define BC_PARSE_TOKEN_INST(t) ((uchar) ((t) - BC_LEX_NEG + BC_INST_NEG))

/**
 * Returns true if the token is a bc keyword.
 * @param t  The token to check.
 * @return   True if @a t is a bc keyword, false otherwise.
 */
#define BC_PARSE_IS_KEYWORD(t) ((t) >= BC_LEX_KW_AUTO && (t) <= BC_LEX_KW_ELSE)

/// A struct that holds data about what tokens should be expected next. There
/// are a few instances of these, all named because they are used in specific
/// cases. Basically, in certain situations, it's useful to use the same code,
/// but have a list of valid tokens.
///
/// Obviously, @a len is the number of tokens in the @a tokens array. If more
/// than 4 is needed in the future, @a tokens will have to be changed.
typedef struct BcParseNext
{
	/// The number of tokens in the tokens array.
	uchar len;

	/// The tokens that can be expected next.
	uchar tokens[4];

} BcParseNext;

/// A macro to construct an array literal of tokens from a parameter list.
#define BC_PARSE_NEXT_TOKENS(...) .tokens = { __VA_ARGS__ }

/// A macro to generate a BcParseNext literal from BcParseNext data. See
/// src/data.c for examples.
#define BC_PARSE_NEXT(a, ...) \
	{ .len = (uchar) (a), BC_PARSE_NEXT_TOKENS(__VA_ARGS__) }

/// A status returned by @a bc_parse_expr_err(). It can either return success or
/// an error indicating an empty expression.
typedef enum BcParseStatus
{
	BC_PARSE_STATUS_SUCCESS,
	BC_PARSE_STATUS_EMPTY_EXPR,

} BcParseStatus;

/**
 * The @a BcParseExpr function for bc. (See include/parse.h for a definition of
 * @a BcParseExpr.)
 * @param p      The parser.
 * @param flags  Flags that define the requirements that the parsed code must
 *               meet or an error will result. See @a BcParseExpr for more info.
 */
void
bc_parse_expr(BcParse* p, uint8_t flags);

/**
 * The @a BcParseParse function for bc. (See include/parse.h for a definition of
 * @a BcParseParse.)
 * @param p  The parser.
 */
void
bc_parse_parse(BcParse* p);

/**
 * Ends a series of if statements. This is to ensure that full parses happen
 * when a file finishes or before defining a function. Without this, bc thinks
 * that it cannot parse any further. But if we reach the end of a file or a
 * function definition, we know we can add an empty else clause.
 * @param p  The parser.
 */
void
bc_parse_endif(BcParse* p);

/// References to the signal message and its length.
extern const char bc_sig_msg[];
extern const uchar bc_sig_msg_len;

/// A reference to an array of bits that are set if the corresponding lex token
/// is valid in an expression.
extern const uint8_t bc_parse_exprs[];

/// A reference to an array of bc operators.
extern const uchar bc_parse_ops[];

// References to the various instances of BcParseNext's.

/// A reference to what tokens are valid as next tokens when parsing normal
/// expressions. More accurately. these are the tokens that are valid for
/// *ending* the expression.
extern const BcParseNext bc_parse_next_expr;

/// A reference to what tokens are valid as next tokens when parsing function
/// parameters (well, actually arguments).
extern const BcParseNext bc_parse_next_arg;

/// A reference to what tokens are valid as next tokens when parsing a print
/// statement.
extern const BcParseNext bc_parse_next_print;

/// A reference to what tokens are valid as next tokens when parsing things like
/// loop headers and builtin functions where the only thing expected is a right
/// paren.
///
/// The name is an artifact of history, and is related to @a BC_PARSE_REL (see
/// include/parse.h). It refers to how POSIX only allows some operators as part
/// of the conditional of for loops, while loops, and if statements.
extern const BcParseNext bc_parse_next_rel;

// What tokens are valid as next tokens when parsing an array element
// expression.
extern const BcParseNext bc_parse_next_elem;

/// A reference to what tokens are valid as next tokens when parsing the first
/// two parts of a for loop header.
extern const BcParseNext bc_parse_next_for;

/// A reference to what tokens are valid as next tokens when parsing a read
/// expression.
extern const BcParseNext bc_parse_next_read;

/// A reference to what tokens are valid as next tokens when parsing a builtin
/// function with multiple arguments.
extern const BcParseNext bc_parse_next_builtin;

#else // BC_ENABLED

// If bc is not enabled, execution is always possible because dc has strict
// rules that ensure execution can always proceed safely.
#define BC_PARSE_NO_EXEC(p) (0)

#endif // BC_ENABLED

#endif // BC_BC_H
