Adds a Doxyfile in the shape libakgl uses -- fifteen deliberate lines, including WARN_IF_UNDOCUMENTED and WARN_AS_ERROR=FAIL_ON_WARNINGS -- and fills in the 70 public declarations that had no doc block, bringing include/akbasic to 114 of 114. libakgl is the model rather than libakstdlib. Measured before starting: libakgl documents 122 of 122 public declarations and libakstdlib 3 of 25, and libakstdlib's Doxyfile is the unedited doxygen default, PROJECT_NAME = "My Project" and an empty INPUT. So the house standard is libakgl's, down to the boilerplate phrasing for the recurring parameters -- "Object to initialize, inspect, or modify", "Output destination populated by the function", "`NULL` on success, otherwise an error context owned by the caller". Worth knowing what the gate actually gates. EXTRACT_ALL=YES suppresses doxygen's undocumented-entity warnings, so the rule it enforces is that a *partial* block is an error: document one @param and you must document them all. Verified by deleting a @param and confirming a non-zero exit, then restoring it. Full coverage is therefore a convention this commit adopts rather than something the tool made me do. Where a contract is non-obvious the block says so rather than restating the signature: math_plus explains why it alone mutates its left operand, new_unary notes that hanging the operand on .right is what makes the parser miscount a negative literal argument, leaf_to_string warns that an assignment renders with an empty operator, and stop_waiting records that a verb nobody is waiting for is tolerated. Each cross-references its TODO.md section 6 item. Also records in TODO.md that this repository has no CI, which libakgl and libakstdlib both have -- so ctest, the sanitizer build, coverage and this new doxygen gate are all run by hand today. ctest 61/61; doxygen Doxyfile exits 0; no warnings under -Wall -Wextra. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
272 lines
14 KiB
C
272 lines
14 KiB
C
/**
|
|
* @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 <akerror.h>
|
|
|
|
#include <akbasic/types.h>
|
|
|
|
/*
|
|
* 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_
|