/** * @file variable.h * @brief Declares a named, strongly-typed, optionally multi-dimensional slot. * * Ported from basicvariable.go. Type comes from the identifier's last character: * `$` string, `#` integer, `%` float. Note that `%` meaning *float* inverts the * Commodore convention where `%` is integer; the reference does it that way and * its README documents it, so it is kept. */ #ifndef _AKBASIC_VARIABLE_H_ #define _AKBASIC_VARIABLE_H_ #include #include #include #include typedef struct { /* * Sized to AKBASIC_SYMTAB_MAX_KEY, not AKBASIC_MAX_STRING_LENGTH: this name * only ever gets here by surviving akbasic_symtab_set() first * (akbasic_environment_create() calls it right after this field is * populated), and that call refuses anything AKBASIC_SYMTAB_MAX_KEY * characters or longer with AKBASIC_ERR_BOUNDS. A variable whose name did * not fit could never exist, so the wider buffer was 232 bytes of headroom * nothing could ever put a byte into. */ char name[AKBASIC_SYMTAB_MAX_KEY]; akbasic_Type valuetype; akbasic_Value *values; /** The pool, or `inlinevalue` for a scalar */ int valuecount; /* * A scalar's storage, so that creating one costs the value pool nothing. * * The pool is a bump allocator with no free, and a scope exit hands the * variable *slot* back while its storage stays counted against the pool -- * so a name first created inside a GOSUB used to spend slots on every call * and 4096 creations ended the run. Keeping the one value here instead * means a local, a FOR counter and a DEF parameter are all free. * * akbasic_variable_init() decides which of the two `values` points at, and * a `@` name is the one exclusion: see the comment there. */ akbasic_Value inlinevalue; int64_t dimensions[AKBASIC_MAX_ARRAY_DEPTH]; int dimensioncount; bool mutable_; bool used; /** Pool bookkeeping */ /* * What DIM ... AS recorded. A structure variable's `values` run is one * instance laid out by the type's offsets, exactly as an array's run is its * elements -- so these two are what tell the difference between the two * kinds of run. */ int structtype; /** type index, -1 when not a structure */ bool ispointer; /** declared PTR TO rather than a value */ void *hostbase; /** the host's struct, for a binding */ } akbasic_Variable; /** * @brief Give a variable storage and a type. * @param obj Object to initialize, inspect, or modify. * @param pool Backing store the elements are drawn from. * @param sizes Dimension sizes; every one must be positive. * @param sizecount Number of dimensions. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKERR_NULLPOINTER When `obj` or `pool` is NULL. * @throws AKBASIC_ERR_VALUE When the name is empty or a dimension is not positive. * @throws AKBASIC_ERR_BOUNDS When the array is larger than the pool can serve. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_variable_init(akbasic_Variable *obj, akbasic_ValuePool *pool, int64_t *sizes, int sizecount); /** * @brief Mark the variable undefined and mutable without touching its storage. * @param obj Object to initialize, inspect, or modify. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKERR_NULLPOINTER When `obj` is NULL. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_variable_zero(akbasic_Variable *obj); /** * @brief Resolve a subscript list to the value it addresses. * @param obj Object to initialize, inspect, or modify. * @param subscripts Index per dimension. * @param subscriptcount Number of indices supplied; must equal the dimension count. * @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 count is wrong or an index is out of range. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_variable_get_subscript(akbasic_Variable *obj, int64_t *subscripts, int subscriptcount, akbasic_Value **dest); /** * @brief Copy a value into the slot a subscript list addresses. * @param obj Object to initialize, inspect, or modify. * @param value Source value; cloned into the slot, not aliased. * @param subscripts Index per dimension. * @param subscriptcount Number of indices supplied; must equal the dimension count. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKERR_NULLPOINTER When `value` is NULL. * @throws AKBASIC_ERR_BOUNDS When the count is wrong or an index is out of range. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_variable_set_subscript(akbasic_Variable *obj, akbasic_Value *value, int64_t *subscripts, int subscriptcount); /** * @brief Store an integer into the slot a subscript list addresses. * @param obj Object to initialize, inspect, or modify. * @param value Value to store. * @param subscripts Index per dimension. * @param subscriptcount Number of indices supplied; must equal the dimension count. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKBASIC_ERR_BOUNDS When the count is wrong or an index is out of range. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_variable_set_integer(akbasic_Variable *obj, int64_t value, int64_t *subscripts, int subscriptcount); /** * @brief Store a float into the slot a subscript list addresses. * @param obj Object to initialize, inspect, or modify. * @param value Value to store. * @param subscripts Index per dimension. * @param subscriptcount Number of indices supplied; must equal the dimension count. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKBASIC_ERR_BOUNDS When the count is wrong or an index is out of range. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_variable_set_float(akbasic_Variable *obj, double value, int64_t *subscripts, int subscriptcount); /** * @brief Store a string into the slot a subscript list addresses. * @param obj Object to initialize, inspect, or modify. * @param value Value to store. * @param subscripts Index per dimension. * @param subscriptcount Number of indices supplied; must equal the dimension count. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKERR_NULLPOINTER When `value` is NULL. * @throws AKBASIC_ERR_VALUE When the string exceeds the length limit. * @throws AKBASIC_ERR_BOUNDS When the count is wrong or an index is out of range. */ akerr_ErrorContext AKERR_NOIGNORE *akbasic_variable_set_string(akbasic_Variable *obj, const char *value, int64_t *subscripts, int subscriptcount); #endif // _AKBASIC_VARIABLE_H_