Document the public API in libakgl's Doxygen style

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>
Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
This commit is contained in:
2026-07-31 07:00:16 -04:00
parent 09b39a5f4b
commit fa52b2beca
9 changed files with 783 additions and 15 deletions

View File

@@ -104,32 +104,120 @@ typedef struct akbasic_Runtime
*/
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_init(akbasic_Runtime *obj, akbasic_TextSink *sink);
/** @brief Reset the per-line state without disturbing the program or variables. */
/**
* @brief Reset the per-line state without disturbing the program or variables.
* @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_runtime_zero(akbasic_Runtime *obj);
/**
* @brief Push a new scope, taking one from the pool.
* @param obj Object to initialize, inspect, or modify.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKBASIC_ERR_ENVIRONMENT When the environment pool is exhausted.
*/
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_new_environment(akbasic_Runtime *obj);
/**
* @brief Pop the active scope and release it back to the pool.
*
* The reference never releases, which its garbage collector papers over. Here
* the pool is finite, so an unreleased scope shows up as exhaustion later.
*
* @param obj Object to initialize, inspect, or modify.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKBASIC_ERR_ENVIRONMENT When the active scope is the root.
*/
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_prev_environment(akbasic_Runtime *obj);
/** @brief Report a BASIC error on the current line, in the reference's format. */
/**
* @brief Report a BASIC error on the current line, in the reference's format.
*
* Writes `? <line> : <CLASS> <message>` through the sink. The message is expected
* to end in a newline and the sink adds another, which is why an error line in
* the golden corpus is followed by a blank one -- that is the contract, not an
* accident.
*
* @param obj Object to initialize, inspect, or modify.
* @param errclass Which error class to name in the line.
* @param message Text to append; conventionally ends in a newline.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER When `obj` or `message` is NULL.
*/
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_error(akbasic_Runtime *obj, akbasic_ErrorClass errclass, const char *message);
/** @brief Write text, mirroring the reference's Write(). */
/**
* @brief Write text through the sink, mirroring the reference's Write().
* @param obj Object to initialize, inspect, or modify.
* @param text Text to emit.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER When `obj` or `text` is NULL.
*/
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_write(akbasic_Runtime *obj, const char *text);
/** @brief Write text and a newline, mirroring the reference's Println(). */
/**
* @brief Write text and a newline, mirroring the reference's Println().
* @param obj Object to initialize, inspect, or modify.
* @param text Text to emit.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER When `obj` or `text` is NULL.
*/
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_println(akbasic_Runtime *obj, const char *text);
/**
* @brief Change the execution mode, announcing READY when entering the REPL.
* @param obj Object to initialize, inspect, or modify.
* @param mode One of the AKBASIC_MODE_* values.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER When `obj` is NULL.
*/
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_set_mode(akbasic_Runtime *obj, int mode);
/** @brief Evaluate one AST leaf, drawing scratch values from the environment. */
/**
* @brief Evaluate one AST leaf, drawing scratch values from the environment.
* @param obj Object to initialize, inspect, or modify.
* @param expr Leaf to evaluate.
* @param dest Output destination populated by the function; points into the per-line value pool.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER When any argument is NULL.
* @throws AKBASIC_ERR_BOUNDS When the per-line value pool is exhausted.
* @throws AKBASIC_ERR_UNDEFINED When the leaf names a verb, function or label that does not exist.
*/
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_evaluate(akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value **dest);
/** @brief Evaluate a leaf only when it is a command a REPL may run immediately. */
/**
* @brief Evaluate a leaf only when it is a verb a REPL may run without a line number.
* @param obj Object to initialize, inspect, or modify.
* @param expr Leaf to consider.
* @param dest Output destination populated by the function; NULL when the leaf was not immediate.
* @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_runtime_interpret_immediate(akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value **dest);
/** @brief Evaluate a leaf unless the environment is skipping forward to a verb. */
/**
* @brief Evaluate a leaf unless the environment is skipping forward to a verb.
* @param obj Object to initialize, inspect, or modify.
* @param expr Leaf to evaluate.
* @param dest Output destination populated by the function.
* @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_runtime_interpret(akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value **dest);
/** @brief Call a user-defined function or subroutine. */
/**
* @brief Call a user-defined function or subroutine.
*
* A single-expression DEF evaluates and returns. A multi-line one hands control
* to its own scope and runs until RETURN pops back out, which is why this can
* execute an arbitrary number of source lines.
*
* @param obj Object to initialize, inspect, or modify.
* @param expr The call leaf, carrying the function name and its arguments.
* @param dest Output destination populated by the function.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_KEY When no such function is defined.
*/
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_user_function(akbasic_Runtime *obj, akbasic_ASTLeaf *expr, akbasic_Value **dest);
/**
@@ -137,16 +225,38 @@ akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_user_function(akbasic_Runtime
*
* One call reads or runs at most one source line. It never blocks beyond a
* single readline on the sink, and it always returns.
*
* @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_runtime_step(akbasic_Runtime *obj);
/**
* @brief Step until the runtime quits or `maxsteps` steps have elapsed.
*
* This is the entry point an embedding host calls once per frame. A bounded
* budget is what keeps a script with an infinite loop from taking the host's
* frame rate with it.
*
* @param obj Object to initialize, inspect, or modify.
* @param maxsteps Step ceiling; zero or negative means unbounded.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER When `obj` is NULL.
*/
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_run(akbasic_Runtime *obj, int maxsteps);
/** @brief Point the runtime at an input stream and choose a starting mode. */
/**
* @brief Choose a starting mode and decide what happens when a program ends.
*
* Starting in AKBASIC_MODE_REPL returns to the REPL when a program finishes;
* anything else quits.
*
* @param obj Object to initialize, inspect, or modify.
* @param mode One of the AKBASIC_MODE_* values.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER When `obj` is NULL.
*/
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_start(akbasic_Runtime *obj, int mode);
/**
@@ -173,12 +283,60 @@ akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_load(akbasic_Runtime *obj, co
/* --- Internal API: exposed for the scanner, parser and verb handlers only. --- */
/**
* @brief Take an unused variable from the runtime's pool.
* @param obj Object to initialize, inspect, or modify.
* @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 every variable slot is in use.
*/
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_new_variable(akbasic_Runtime *obj, akbasic_Variable **dest);
/**
* @brief Take an unused function definition from the runtime's pool.
* @param obj Object to initialize, inspect, or modify.
* @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 every function slot is in use.
*/
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_new_function(akbasic_Runtime *obj, akbasic_FunctionDef **dest);
/**
* @brief File one already-scanned source line under its line number.
*
* Hosts should prefer akbasic_runtime_load(), which scans as well.
*
* @param obj Object to initialize, inspect, or modify.
* @param lineno Line number to file it under.
* @param code Source text, with any line number already stripped.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKBASIC_ERR_BOUNDS When the number or the line length is out of range.
*/
akerr_ErrorContext AKERR_NOIGNORE *akbasic_runtime_store_line(akbasic_Runtime *obj, int64_t lineno, const char *code);
/**
* @brief Execute the next line of the stored program.
* @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_runtime_process_line_run(akbasic_Runtime *obj);
/**
* @brief Read one line from the sink and file it, without executing it.
* @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_runtime_process_line_runstream(akbasic_Runtime *obj);
/**
* @brief Read one line from the sink and either run it or file it.
* @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_runtime_process_line_repl(akbasic_Runtime *obj);
/**
* @brief The nearest non-empty line before the current one.
* @param obj Runtime to inspect.
* @return That line's number, or the current one when there is nothing before it.
*/
int64_t akbasic_runtime_find_previous_lineno(akbasic_Runtime *obj);
#endif // _AKBASIC_RUNTIME_H_