|
1 | | -#pragma once |
2 | | -#include <kernel.h> |
3 | | - |
4 | 1 | /** |
5 | 2 | * @file unified_expression.h |
6 | 3 | * @brief Typed expression and conditional entry points for the unified parser. |
|
11 | 8 | * integer, real, and string expressions. |
12 | 9 | * |
13 | 10 | * ### Tokeniser interaction |
14 | | - * - **Parentheses**: content inside `(` … `)` is parsed as a relation-aware |
15 | | - * expression so forms such as `(in<128)` are valid in IF/WHILE. |
16 | | - * - **String literals**: obtained via `tokenizer_string(...)`. No subsequent |
17 | | - * `accept(STRING)` is performed; the tokeniser already advances to lookahead. |
| 11 | + * - **Parentheses**: content inside `(` .. `)` is parsed as a relation-aware |
| 12 | + * expression, so forms such as `(in<128)` are valid in IF/WHILE. |
| 13 | + * - **String literals**: obtained via `tokenizer_string(...)` and then |
| 14 | + * `accept(STRING)`; the tokeniser produces the literal and we consume it. |
18 | 15 | * - **Variables and built-ins**: |
19 | | - * - Names ending with `$` → STRING. |
20 | | - * - Names ending with `#` → REAL. |
21 | | - * - Unsuffixed names → INT, *except* for a temporary whitelist of known |
22 | | - * real-returning built-ins (implemented in the `.c` file): |
23 | | - * `ACS, ASN, ATAN, ATAN2, CEIL, COS, DEG, EXP, FMOD, GETVARR, LOG, POW, |
24 | | - * RAD, REALVAL, ROUND, SIN, SQRT, TAN`. |
25 | | - * |
26 | | - * ### Operators |
| 16 | + * - Names ending with `$` - STRING. |
| 17 | + * - Names ending with `#` - REAL. |
| 18 | + * - Unsuffixed names default to INT *unless* the core marks them as real |
| 19 | + * built-ins (queried via the interpreter’s registry, e.g. |
| 20 | + * `is_builtin_double_fn(name)`). |
| 21 | + * |
| 22 | + * ### Operators and precedence |
| 23 | + * - **Unary**: leading `+` / `-` bind **tighter** than `* / MOD`. |
| 24 | + * Applies to numeric values only (e.g. `A--3`, `-(2+3)`, `-SIN(ANGLE#)`). |
27 | 25 | * - **Arithmetic**: `+ - * / MOD` |
28 | | - * - Numeric arithmetic; `+` concatenates when both operands are strings. |
29 | | - * - Mixed string/numeric on `+` or `-` is an error. `MOD` requires integers. |
30 | | - * - Division by zero emits a kernel-style error. INT+REAL promotes to REAL. |
31 | | - * - **Bitwise**: `AND OR EOR` |
32 | | - * - Numeric only; REAL operands are cast to `int64_t`; strings are an error. |
| 26 | + * - Numeric arithmetic; `+` concatenates when **both** operands are strings. |
| 27 | + * - Mixing string and numeric with `+`/`-` is an error. `MOD` requires INTs. |
| 28 | + * - INT↔REAL promotes to REAL; division by zero reports a runtime error. |
33 | 29 | * - **Relational**: `< > = <= >= <>` |
34 | 30 | * - Numeric comparisons promote INT↔REAL. String comparisons use `strcmp`. |
35 | 31 | * - Result is INT 0/1. |
36 | | - * - **Boolean precedence (conditionals)**: `NOT` > `AND` > `OR`, left-associative. |
| 32 | + * - **Boolean (conditionals)**: `NOT` > `AND` > `OR`, all left-associative. |
37 | 33 | * |
38 | 34 | * ### Consumption and stop conditions |
39 | | - * - `up_conditional()` stops before `THEN` / `NEWLINE` / `EOF` / `')'`. `THEN` |
40 | | - * is not consumed; statement parsers should continue to `accept(THEN, ctx)`. |
| 35 | + * - `up_conditional()` stops before `THEN` / `NEWLINE` / `EOF` / `')'`. |
| 36 | + * The caller should `accept(THEN, ctx)` if required. |
| 37 | + * - `up_eval_value()` parses one value expression and **leaves** list |
| 38 | + * separators (`,` / `;`), `THEN`, `NEWLINE`, and `EOF` untouched for the |
| 39 | + * enclosing statement (e.g. `PRINT`, `INPUT`) to handle. |
41 | 40 | * |
42 | 41 | * ### String lifetime |
43 | | - * - String results are duplicated with the GC (`gc_strdup`) to match existing |
44 | | - * `str_expr` behaviour. |
| 42 | + * - String results returned by expression evaluation are duplicated via the |
| 43 | + * GC (`gc_strdup`) to match historic `str_expr` behaviour. |
| 44 | + */ |
| 45 | + |
| 46 | +#pragma once |
| 47 | +#include <kernel.h> |
| 48 | + |
| 49 | +/** |
| 50 | + * @enum up_kind |
| 51 | + * @brief Discriminant for a typed value produced by the unified evaluator. |
45 | 52 | * |
46 | | - * @note The temporary real-builtin whitelist can be replaced by a descriptor |
47 | | - * table (name → return type) without API changes. |
| 53 | + * Enumerates the runtime kind carried by an ::up_value. |
48 | 54 | */ |
| 55 | +typedef enum { |
| 56 | + /** 64-bit signed integer value. */ |
| 57 | + UP_INT, |
| 58 | + /** IEEE-754 double-precision real value. */ |
| 59 | + UP_REAL, |
| 60 | + /** NUL-terminated string pointer. */ |
| 61 | + UP_STR |
| 62 | +} up_kind; |
| 63 | + |
| 64 | +/** |
| 65 | + * @struct up_value |
| 66 | + * @brief Discriminated union carrying a typed expression result. |
| 67 | + * |
| 68 | + * Represents one value returned by the unified expression evaluator. |
| 69 | + * The active member of the union is indicated by ::up_value::kind. |
| 70 | + * |
| 71 | + * @var up_value::kind |
| 72 | + * Tag identifying the active member in ::up_value::v. |
| 73 | + * |
| 74 | + * @var up_value::v |
| 75 | + * Anonymous union holding the underlying value. |
| 76 | + * |
| 77 | + * @var up_value::v::i |
| 78 | + * 64-bit signed integer; valid when ::up_value::kind is ::UP_INT. |
| 79 | + * |
| 80 | + * @var up_value::v::r |
| 81 | + * Double-precision real; valid when ::up_value::kind is ::UP_REAL. |
| 82 | + * |
| 83 | + * @var up_value::v::s |
| 84 | + * Pointer to a NUL-terminated string; valid when ::up_value::kind is ::UP_STR. |
| 85 | + * Ownership/lifetime is external to this struct (typically GC-managed). |
| 86 | + */ |
| 87 | +typedef struct { |
| 88 | + up_kind kind; |
| 89 | + union { |
| 90 | + int64_t i; |
| 91 | + double r; |
| 92 | + const char *s; |
| 93 | + } v; |
| 94 | +} up_value; |
| 95 | + |
49 | 96 |
|
50 | 97 | /** |
51 | 98 | * @brief Evaluate a BASIC conditional (boolean) expression. |
@@ -75,7 +122,7 @@ bool up_conditional(struct basic_ctx *ctx); |
75 | 122 | int64_t up_relation_i(struct basic_ctx *ctx); |
76 | 123 |
|
77 | 124 | /* ------------------------------------------------------------------------- */ |
78 | | -/* Strict shims — drop-in replacements for legacy int/real/string expr APIs. */ |
| 125 | +/* Strict shims - drop-in replacements for legacy int/real/string expr APIs. */ |
79 | 126 | /* ------------------------------------------------------------------------- */ |
80 | 127 |
|
81 | 128 | /** |
@@ -110,3 +157,55 @@ void up_double_expr_strict(struct basic_ctx *ctx, double *out); |
110 | 157 | * @return Non-NULL GC-managed string pointer. |
111 | 158 | */ |
112 | 159 | const char* up_str_expr_strict(struct basic_ctx *ctx); |
| 160 | + |
| 161 | +/** |
| 162 | + * @brief Construct a typed value holding an integer. |
| 163 | + * |
| 164 | + * @param x Signed 64-bit integer to store. |
| 165 | + * @return up_value tagged as UP_INT with @p x in the integer field. |
| 166 | + * |
| 167 | + * @note No allocation is performed. |
| 168 | + */ |
| 169 | +up_value up_make_int(int64_t x); |
| 170 | + |
| 171 | +/** |
| 172 | + * @brief Construct a typed value holding a real (double). |
| 173 | + * |
| 174 | + * @param x Double to store. |
| 175 | + * @return up_value tagged as UP_REAL with @p x in the real field. |
| 176 | + * |
| 177 | + * @note No allocation is performed. |
| 178 | + */ |
| 179 | +up_value up_make_real(double x); |
| 180 | + |
| 181 | +/** |
| 182 | + * @brief Construct a typed value holding a string pointer. |
| 183 | + * |
| 184 | + * @param s Pointer to NUL-terminated string to store (may be NULL). |
| 185 | + * @return up_value tagged as UP_STR with @p s in the string field. |
| 186 | + * |
| 187 | + * @note The pointer is stored as-is; lifetime/ownership of @p s must be |
| 188 | + * managed by the caller (typically GC-allocated in this interpreter). |
| 189 | + * No copy is made here. |
| 190 | + */ |
| 191 | +up_value up_make_str(const char *s); |
| 192 | + |
| 193 | +/** |
| 194 | + * @brief Evaluate a single value expression and return its typed result. |
| 195 | + * |
| 196 | + * Parses one expression using the unified evaluator and writes the result to |
| 197 | + * @p out without coercion (the kind will be UP_INT, UP_REAL, or UP_STR). |
| 198 | + * |
| 199 | + * @param ctx BASIC interpreter context; @c ctx->ptr must point at the start |
| 200 | + * of the expression to evaluate. |
| 201 | + * @param[out] out Receives the typed result of the evaluation. |
| 202 | + * |
| 203 | + * @post On return, @c ctx->ptr is positioned at the first token not consumed |
| 204 | + * by the expression. In particular, list separators (','/';'), THEN, |
| 205 | + * NEWLINE, and ENDOFINPUT are left untouched for the caller to handle. |
| 206 | + * |
| 207 | + * @note Errors encountered during evaluation are reported via the existing |
| 208 | + * error machinery (e.g. @c tokenizer_error_print) and reflected in |
| 209 | + * @c ctx->errored; this function does not throw or longjmp. |
| 210 | + */ |
| 211 | +void up_eval_value(struct basic_ctx *ctx, up_value *out); |
0 commit comments