181 lines
7.8 KiB
C
181 lines
7.8 KiB
C
|
|
/**
|
||
|
|
* @file structtype.h
|
||
|
|
* @brief Declares the table of `TYPE` declarations and how a field is found in one.
|
||
|
|
*
|
||
|
|
* BASIC 7.0 has no records at all, so none of this is a port -- see TODO.md
|
||
|
|
* section 5 for what was invented and why.
|
||
|
|
*
|
||
|
|
* **Declaring the type is what buys the storage model.** Because a `TYPE` states
|
||
|
|
* its fields up front, an instance has a known slot count and is laid out
|
||
|
|
* exactly as an array is: one contiguous run taken from the runtime's value pool
|
||
|
|
* by akbasic_valuepool_take(). A field access is then offset arithmetic against
|
||
|
|
* an offset this table already knows, and no new pool is needed for the data --
|
||
|
|
* only this table of descriptors, which holds names and offsets rather than
|
||
|
|
* values.
|
||
|
|
*
|
||
|
|
* A field nested **by value** flattens into its parent's run at an offset. A
|
||
|
|
* field declared `PTR TO` occupies one slot holding a reference. That is the
|
||
|
|
* whole difference, and it is why a `TYPE` may refer to itself only through a
|
||
|
|
* pointer: by value it would have no finite size, which is refused at
|
||
|
|
* declaration rather than discovered when the pool runs out.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#ifndef _AKBASIC_STRUCTTYPE_H_
|
||
|
|
#define _AKBASIC_STRUCTTYPE_H_
|
||
|
|
|
||
|
|
#include <akerror.h>
|
||
|
|
|
||
|
|
#include <akbasic/grammar.h>
|
||
|
|
#include <akbasic/types.h>
|
||
|
|
|
||
|
|
struct akbasic_Runtime;
|
||
|
|
|
||
|
|
/** @brief What a field holds, which is what says how much room it needs. */
|
||
|
|
typedef enum
|
||
|
|
{
|
||
|
|
AKBASIC_FIELD_PRIMITIVE = 0, /* one slot, type from the name's suffix */
|
||
|
|
AKBASIC_FIELD_STRUCT, /* nested by value; flattened into the parent */
|
||
|
|
AKBASIC_FIELD_POINTER /* one slot holding a reference */
|
||
|
|
} akbasic_FieldKind;
|
||
|
|
|
||
|
|
/** @brief One declared field: what it is called, what it holds, and where it sits. */
|
||
|
|
typedef struct
|
||
|
|
{
|
||
|
|
char name[AKBASIC_MAX_STRUCT_NAME]; /** as written, suffix included */
|
||
|
|
akbasic_FieldKind kind;
|
||
|
|
akbasic_Type valuetype; /** for a primitive, from the suffix */
|
||
|
|
int typeindex; /** for STRUCT and POINTER */
|
||
|
|
int offset; /** slots from the start of the instance */
|
||
|
|
int slotcount; /** slots this field occupies */
|
||
|
|
} akbasic_StructField;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief One `TYPE` declaration.
|
||
|
|
*
|
||
|
|
* `firstline` and `lastline` are the `TYPE` and `END TYPE` source lines. They
|
||
|
|
* are kept because the block has to be *skipped* at run time: its field lines
|
||
|
|
* are declarations, and letting them execute would evaluate each field name as a
|
||
|
|
* bare identifier and quietly create a global of that name.
|
||
|
|
*/
|
||
|
|
typedef struct
|
||
|
|
{
|
||
|
|
char name[AKBASIC_MAX_STRUCT_NAME];
|
||
|
|
akbasic_StructField fields[AKBASIC_MAX_STRUCT_FIELDS];
|
||
|
|
int fieldcount;
|
||
|
|
int slotcount; /** total slots for one instance */
|
||
|
|
int64_t firstline;
|
||
|
|
int64_t lastline;
|
||
|
|
bool used;
|
||
|
|
} akbasic_StructType;
|
||
|
|
|
||
|
|
/** @brief Every `TYPE` a program declared. Lives on the runtime. */
|
||
|
|
typedef struct
|
||
|
|
{
|
||
|
|
akbasic_StructType types[AKBASIC_MAX_STRUCT_TYPES];
|
||
|
|
int count;
|
||
|
|
} akbasic_StructTypeTable;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Empty the table.
|
||
|
|
* @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_structtype_table_init(akbasic_StructTypeTable *obj);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Find a declared type by name.
|
||
|
|
* @param obj Table to search.
|
||
|
|
* @param name Type name, without a suffix.
|
||
|
|
* @param dest Output destination populated by the function; the index, or -1.
|
||
|
|
* @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_structtype_find(akbasic_StructTypeTable *obj, const char *name, int *dest);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Resolve a field of a declared type, refusing one it does not declare.
|
||
|
|
*
|
||
|
|
* The refusal lists the fields the type *does* have, because the whole reason a
|
||
|
|
* misspelled field can be caught at all is that the set is closed and written
|
||
|
|
* down -- so the diagnostic may as well show it.
|
||
|
|
*
|
||
|
|
* @param obj Table the type lives in.
|
||
|
|
* @param typeindex Which type.
|
||
|
|
* @param name Field name, suffix included.
|
||
|
|
* @param dest Output destination populated by the function.
|
||
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||
|
|
* @throws AKERR_NULLPOINTER When `obj`, `name` or `dest` is NULL.
|
||
|
|
* @throws AKBASIC_ERR_BOUNDS When `typeindex` names no type.
|
||
|
|
* @throws AKBASIC_ERR_UNDEFINED When the type does not declare that field.
|
||
|
|
*/
|
||
|
|
akerr_ErrorContext AKERR_NOIGNORE *akbasic_structtype_field(akbasic_StructTypeTable *obj, int typeindex, const char *name, akbasic_StructField **dest);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Read every `TYPE` block out of the program before it runs.
|
||
|
|
*
|
||
|
|
* Called from akbasic_runtime_set_mode() beside the label and `DATA` prescans,
|
||
|
|
* and for the same reason all three are prescans: a declaration has to be in
|
||
|
|
* effect wherever control happens to go, including when a branch skips over the
|
||
|
|
* lines that made it. It also means `DIM P@ AS RECT` can never run before the
|
||
|
|
* type it names exists.
|
||
|
|
*
|
||
|
|
* @param obj The runtime, whose source is read and whose table is filled.
|
||
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||
|
|
* @throws AKERR_NULLPOINTER When `obj` is NULL.
|
||
|
|
* @throws AKBASIC_ERR_SYNTAX When a block is malformed or unterminated.
|
||
|
|
* @throws AKBASIC_ERR_VALUE When a type contains itself by value, or redeclares a name.
|
||
|
|
* @throws AKBASIC_ERR_BOUNDS When the table or a field list is full.
|
||
|
|
*/
|
||
|
|
akerr_ErrorContext AKERR_NOIGNORE *akbasic_structtype_scan(struct akbasic_Runtime *obj);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Which `TYPE` block a source line opens, or -1.
|
||
|
|
*
|
||
|
|
* How `TYPE` skips its own body at run time: the verb asks what block it is
|
||
|
|
* looking at and jumps past its `END TYPE`.
|
||
|
|
*
|
||
|
|
* @param obj Table to search.
|
||
|
|
* @param lineno The line the interpreter is on.
|
||
|
|
* @param dest Output destination populated by the function; the index, or -1.
|
||
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||
|
|
* @throws AKERR_NULLPOINTER When `obj` or `dest` is NULL.
|
||
|
|
*/
|
||
|
|
akerr_ErrorContext AKERR_NOIGNORE *akbasic_structtype_block_at(akbasic_StructTypeTable *obj, int64_t lineno, int *dest);
|
||
|
|
|
||
|
|
/* --------------------------------------------------- the runtime half --- */
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Fill @p dest with a description of an instance: which type, and where.
|
||
|
|
*
|
||
|
|
* What a structure-valued *expression* evaluates to. Not the data -- an instance
|
||
|
|
* is a run of slots and a value is one slot -- so this is the reference that
|
||
|
|
* lets assignment find the slots to copy, or lets `.` step into them.
|
||
|
|
*/
|
||
|
|
akerr_ErrorContext AKERR_NOIGNORE *akbasic_struct_describe(struct akbasic_Runtime *obj, int typeindex, akbasic_Value *base, bool pointer, akbasic_Value *dest);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Copy one instance's slots onto another's.
|
||
|
|
*
|
||
|
|
* Deep through nested values, and stops at pointers -- as it does for a C struct
|
||
|
|
* holding a pointer. A `TYPE` cannot contain itself by value, so the depth is
|
||
|
|
* fixed by the type graph before the program runs and this cannot recurse away.
|
||
|
|
*/
|
||
|
|
akerr_ErrorContext AKERR_NOIGNORE *akbasic_struct_copy(struct akbasic_Runtime *obj, int typeindex, akbasic_Value *src, akbasic_Value *dest);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Resolve a field-access leaf to the slot it addresses.
|
||
|
|
*
|
||
|
|
* Enforces the strict-pointer rule on the way: `.` requires a structure on its
|
||
|
|
* left and `->` requires a pointer, and neither stands in for the other.
|
||
|
|
*/
|
||
|
|
akerr_ErrorContext AKERR_NOIGNORE *akbasic_struct_resolve(struct akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_StructField **fielddest, akbasic_Value **slotdest);
|
||
|
|
|
||
|
|
/** @brief Evaluate a field access to the value it yields. */
|
||
|
|
akerr_ErrorContext AKERR_NOIGNORE *akbasic_struct_evaluate_field(struct akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value **dest);
|
||
|
|
|
||
|
|
/** @brief Render an instance the way PRINT does, bounded by depth. */
|
||
|
|
akerr_ErrorContext AKERR_NOIGNORE *akbasic_struct_to_string(struct akbasic_Runtime *obj, int typeindex, akbasic_Value *base, int depth, char *dest, size_t len);
|
||
|
|
|
||
|
|
#endif // _AKBASIC_STRUCTTYPE_H_
|