/** * @file value.h * @brief Declares the strongly-typed BASIC value and its operators. * * Ported from the reference's basicvalue.go. The arithmetic is reproduced * exactly, including the parts that look wrong -- see TODO.md section 12 for the * catalogue and the reason they are not fixed yet. * * Every binary operator takes a `scratch` value the caller has drawn from an * environment's pool and an out-parameter `dest`. Most operators set `*dest` to * `scratch` and fill it; akbasic_value_math_plus sets `*dest` to `self` when * `self` is mutable, because the reference mutates in place there and * CommandNEXT's increment relies on it. */ #ifndef _AKBASIC_VALUE_H_ #define _AKBASIC_VALUE_H_ #include #include /* * Backing store for array variables. Scalars and arrays alike draw their storage * from here rather than from malloc, and a variable holds a pointer into it. The * runtime owns exactly one; it is passed explicitly rather than kept at file * scope, because the interpreter must be embeddable and may not own global * mutable state. * * The allocator is a bump: releasing is not supported, because nothing in BASIC * destroys a variable. Re-DIMming a variable to the same size or smaller reuses * its existing slice; growing it takes fresh slots and abandons the old ones. A * program that re-DIMs the same array larger in a loop will exhaust the pool and * get AKBASIC_ERR_BOUNDS -- bounded and diagnosable, which is the point. */ typedef struct { int next; akbasic_Value values[AKBASIC_MAX_ARRAY_VALUES]; } akbasic_ValuePool; /** @brief Reset the pool to empty. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_valuepool_init(akbasic_ValuePool *obj); /** * @brief Take `count` contiguous zeroed values from the pool. * @param obj Object to initialize, inspect, or modify. * @param count Number of elements required. * @param dest Output destination populated by the function. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKBASIC_ERR_BOUNDS When the pool has too few slots left. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_valuepool_take(akbasic_ValuePool *obj, int count, akbasic_Value **dest); /** @brief Reset a value to a usable empty state. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_init(akbasic_Value *obj); /** @brief Clear a value to undefined, zero, immutable and unnamed. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_zero(akbasic_Value *obj); /** * @brief Copy the payload of one value onto another. * * Copies name, type, string, integer, float and boolean payloads. It does *not* * copy `mutable_`: the destination keeps its own, matching the reference, where * clone() writes every field except that one. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_clone(akbasic_Value *self, akbasic_Value *dest); /** @brief Render a value the way PRINT does. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_to_string(akbasic_Value *self, char *dest, size_t len); /** @brief Set a value to a BASIC boolean (-1 true, 0 false). */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_set_bool(akbasic_Value *obj, bool result); /** @brief True when the value is a boolean holding AKBASIC_TRUE. */ bool akbasic_value_is_true(akbasic_Value *self); /** * @brief Negate a numeric value. * * Negates both the integer and float payloads, so it is correct whichever one * the value is carrying. * @param self Operand. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKBASIC_ERR_TYPE When `self` is a string. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_invert(akbasic_Value *self, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief One's complement of an integer value. * @param self Operand. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKBASIC_ERR_TYPE When `self` is not an integer. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_bitwise_not(akbasic_Value *self, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief Shift an integer value left. * * Refuses a count outside 0..63. Go defines the result for any count; in C it * would be undefined behaviour, so this is a deliberate deviation. * @param self Operand; must be an integer. * @param bits Number of bit positions to shift by; must be 0..63. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKBASIC_ERR_TYPE When `self` is not an integer. * @throws AKBASIC_ERR_VALUE When `bits` is outside 0..63. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_shift_left(akbasic_Value *self, int64_t bits, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief Shift an integer value right. * @param self Operand; must be an integer. * @param bits Number of bit positions to shift by; must be 0..63. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKBASIC_ERR_TYPE When `self` is not an integer. * @throws AKBASIC_ERR_VALUE When `bits` is outside 0..63. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_shift_right(akbasic_Value *self, int64_t bits, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief Bitwise AND of two integer values. * @param self Left operand, and the value whose type selects the operation. * @param rval Right operand. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKBASIC_ERR_TYPE When `self` is not an integer. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_bitwise_and(akbasic_Value *self, akbasic_Value *rval, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief Bitwise OR of two integer values. * @param self Left operand, and the value whose type selects the operation. * @param rval Right operand. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKBASIC_ERR_TYPE When `self` is not an integer. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_bitwise_or(akbasic_Value *self, akbasic_Value *rval, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief Bitwise exclusive OR of two integer values. * @param self Left operand, and the value whose type selects the operation. * @param rval Right operand. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKBASIC_ERR_TYPE When either operand is not an integer. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_bitwise_xor(akbasic_Value *self, akbasic_Value *rval, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief Add two values, or concatenate when the left operand is a string. * * The one operator that does not always clone: when `self` is mutable the result * is written into `self` and `*dest` is set to it rather than to `scratch`. * CommandNEXT's loop increment depends on that, so it is not an oversight -- * see TODO.md section 6 item 4. * @param self Left operand, and the value whose type selects the operation. * @param rval Right operand. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKBASIC_ERR_TYPE When the operand types have no defined addition. * @throws AKBASIC_ERR_VALUE When a concatenated string exceeds the length limit. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_math_plus(akbasic_Value *self, akbasic_Value *rval, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief Subtract one numeric value from another. * @param self Left operand, and the value whose type selects the operation. * @param rval Right operand. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKBASIC_ERR_TYPE When either operand is a string. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_math_minus(akbasic_Value *self, akbasic_Value *rval, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief Multiply two numeric values, or repeat a string. * * A string left operand is repeated `rval->intval` times, which is what makes * `" " * 5` work. * @param self Left operand, and the value whose type selects the operation. * @param rval Right operand. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKBASIC_ERR_TYPE When both operands are strings. * @throws AKBASIC_ERR_VALUE When the multiplier is negative or the result would overflow the length limit. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_math_multiply(akbasic_Value *self, akbasic_Value *rval, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief Divide one numeric value by another. * @param self Left operand, and the value whose type selects the operation. * @param rval Right operand. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKBASIC_ERR_TYPE When either operand is a string. * @throws AKBASIC_ERR_VALUE On integer division by zero. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_math_divide(akbasic_Value *self, akbasic_Value *rval, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief Compare two values for less-than, yielding a BASIC boolean. * @param self Left operand, and the value whose type selects the operation. * @param rval Right operand. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKERR_NULLPOINTER When any argument is NULL. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_less_than(akbasic_Value *self, akbasic_Value *rval, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief Compare two values for less-than-or-equal, yielding a BASIC boolean. * @param self Left operand, and the value whose type selects the operation. * @param rval Right operand. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKERR_NULLPOINTER When any argument is NULL. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_less_than_equal(akbasic_Value *self, akbasic_Value *rval, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief Compare two values for greater-than, yielding a BASIC boolean. * @param self Left operand, and the value whose type selects the operation. * @param rval Right operand. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKERR_NULLPOINTER When any argument is NULL. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_greater_than(akbasic_Value *self, akbasic_Value *rval, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief Compare two values for greater-than-or-equal, yielding a BASIC boolean. * @param self Left operand, and the value whose type selects the operation. * @param rval Right operand. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKERR_NULLPOINTER When any argument is NULL. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_greater_than_equal(akbasic_Value *self, akbasic_Value *rval, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief Compare two values for equality, yielding a BASIC boolean. * @param self Left operand, and the value whose type selects the operation. * @param rval Right operand. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKERR_NULLPOINTER When any argument is NULL. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_is_equal(akbasic_Value *self, akbasic_Value *rval, akbasic_Value *scratch, akbasic_Value **dest); /** * @brief Compare two values for inequality, yielding a BASIC boolean. * @param self Left operand, and the value whose type selects the operation. * @param rval Right operand. * @param scratch A value drawn from the caller's pool for the result to land in. * @param dest Output destination populated by the function; set to `scratch`. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKERR_NULLPOINTER When any argument is NULL. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_value_is_not_equal(akbasic_Value *self, akbasic_Value *rval, akbasic_Value *scratch, akbasic_Value **dest); #endif // _AKBASIC_VALUE_H_