Add records: TYPE, DIM ... AS, field access, copy on assign
BASIC 7.0 has no records at all, so none of this is a port. The `@` suffix was not invented either: the Go reference reserved IDENTIFIER_STRUCT and never used it, and src/grammar.c rendered such a leaf as "NOT IMPLEMENTED" until now. Declaring the type is what buys the storage model. A TYPE states its fields, so an instance has a known slot count and is laid out exactly as an array is -- one contiguous run from the same value pool DIM already draws from, with field access as offset arithmetic. No new pool holds data; the only new table holds descriptors. A nested value flattens into its container's run, which is why LINE with two POINTs and a string is five slots rather than three. Each field takes its type from its own suffix, the same rule every other name here follows, so a field list needs no type column. An `@` field is the exception and has to name its type, because three primitive types fit in three suffix characters and N declared types do not fit in one. The declaration is prescanned before the program runs, like labels and DATA and for the same reason: it has to be in effect wherever control goes. Three passes, each for a case the one before cannot do -- names first so a field can refer to a type declared later, then field lists, then sizes by repeated resolution. What never resolves is a cycle of by-value containment, so "a TYPE cannot contain itself by value" is a diagnosis rather than an assumption, and the message says to use PTR TO instead. Assignment copies. That interception is the whole feature and it cannot live in akbasic_value_clone(), which copies one slot -- and one slot holds a *reference* to an instance rather than the instance, so going through it would alias. A structure is intercepted before that path and its slots are copied one at a time, walking the descriptor rather than memcpy-ing the run, because a pointer field must copy its reference where a value field must copy its slots. Two smaller things the work required. All three prescans now sit inside one ATTEMPT: a malformed declaration is the program's mistake, and it was printing a stack trace and taking the driver with it, which is the boundary goal 3 exists to draw. And a fresh variable's structtype is -1 rather than the 0 a memset leaves, because 0 is a valid type index and every new variable was claiming to be the first type declared. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
14
tests/language/structures/missing_field.bas
Normal file
14
tests/language/structures/missing_field.bas
Normal file
@@ -0,0 +1,14 @@
|
||||
10 REM A field name is checked against a closed set the program declared, which
|
||||
20 REM is the one thing in this language whose valid spellings are written down.
|
||||
30 REM A misspelled *variable* is still silent -- see the last two lines.
|
||||
40 TYPE RECT
|
||||
50 W#
|
||||
60 H#
|
||||
70 END TYPE
|
||||
80 DIM R@ AS RECT
|
||||
90 R@.W# = 3
|
||||
100 PRINT R@.W#
|
||||
110 REM A variable nobody assigned is zero, with no complaint at all.
|
||||
120 PRINT TOTLA#
|
||||
130 REM A field the type does not declare is refused, and lists the real ones.
|
||||
140 PRINT R@.NOPE#
|
||||
4
tests/language/structures/missing_field.txt
Normal file
4
tests/language/structures/missing_field.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
3
|
||||
0
|
||||
? 140 : RUNTIME ERROR RECT has no field NOPE# (W#, H#)
|
||||
|
||||
29
tests/language/structures/records.bas
Normal file
29
tests/language/structures/records.bas
Normal file
@@ -0,0 +1,29 @@
|
||||
10 REM A TYPE declares its fields; each takes its own type from its own suffix,
|
||||
20 REM which is the same rule every other name in this language follows.
|
||||
30 TYPE POINT
|
||||
40 X#
|
||||
50 Y#
|
||||
60 END TYPE
|
||||
70 TYPE SHAPE
|
||||
80 NAME$
|
||||
90 ORIGIN@ AS POINT
|
||||
100 AREA%
|
||||
110 END TYPE
|
||||
120 DIM A@ AS SHAPE
|
||||
130 DIM B@ AS SHAPE
|
||||
140 A@.NAME$ = "FIRST"
|
||||
150 A@.ORIGIN@.X# = 10
|
||||
160 A@.ORIGIN@.Y# = 20
|
||||
170 A@.AREA% = 2.5
|
||||
180 REM Assignment COPIES. B@ is its own record from here on.
|
||||
190 B@ = A@
|
||||
200 A@.NAME$ = "CHANGED"
|
||||
210 A@.ORIGIN@.X# = 99
|
||||
220 PRINT A@
|
||||
230 PRINT B@
|
||||
240 REM A whole nested record copies as a unit too.
|
||||
250 DIM P@ AS POINT
|
||||
260 P@ = B@.ORIGIN@
|
||||
270 PRINT P@
|
||||
280 REM And a structure renders with its type name and its fields.
|
||||
290 PRINT B@.ORIGIN@.X# + B@.ORIGIN@.Y#
|
||||
4
tests/language/structures/records.txt
Normal file
4
tests/language/structures/records.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
SHAPE(NAME$=CHANGED, ORIGIN@=POINT(X#=99, Y#=20), AREA%=2.500000)
|
||||
SHAPE(NAME$=FIRST, ORIGIN@=POINT(X#=10, Y#=20), AREA%=2.500000)
|
||||
POINT(X#=10, Y#=20)
|
||||
30
|
||||
Reference in New Issue
Block a user