Initial commit.
This commit is contained in:
commit
a0b6f078f8
23 changed files with 9630 additions and 0 deletions
92
.claude/settings.local.json
Normal file
92
.claude/settings.local.json
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(cc:*)",
|
||||
"Bash(./basic2c)",
|
||||
"Bash(./basic2c:*)",
|
||||
"Bash(./test_output)",
|
||||
"Bash(./test_classic:*)",
|
||||
"Bash(./test_redim:*)",
|
||||
"Bash(/tmp/t1)",
|
||||
"Bash(/tmp/t2)",
|
||||
"Bash(/tmp/t3)",
|
||||
"Bash(./test_big)",
|
||||
"Bash(/tmp/tbig:*)",
|
||||
"Bash(/tmp/ttypes:*)",
|
||||
"Bash(/tmp/ttest:*)",
|
||||
"Bash(/tmp/tclassic:*)",
|
||||
"Bash(/tmp/tredim:*)",
|
||||
"Bash(/tmp/tfileio:*)",
|
||||
"Bash(/tmp/tdata)",
|
||||
"Bash(/tmp/test_labels:*)",
|
||||
"Bash(for f in test.bas test_classic.bas test_redim.bas test_big.bas test_types.bas test_fileio.bas test_data.bas)",
|
||||
"Bash(do echo \"=== $f ===\")",
|
||||
"Bash(if [ $? -ne 0 ])",
|
||||
"Bash(then echo \"FAIL: $f\")",
|
||||
"Bash(else echo \"OK\")",
|
||||
"Bash(fi)",
|
||||
"Bash(done)",
|
||||
"Bash(/tmp/test_data)",
|
||||
"Bash(for f in test.bas test_classic.bas test_redim.bas test_big.bas test_types.bas test_fileio.bas test_labels.bas)",
|
||||
"Bash(do echo -n \"$f: \")",
|
||||
"Bash(for f in test.bas test_classic.bas test_redim.bas test_big.bas test_types.bas test_fileio.bas test_data.bas test_labels.bas)",
|
||||
"Bash(for f in test_classic.bas test_data.bas test_labels.bas test_fileio.bas)",
|
||||
"Bash(do echo \"===== $f =====\")",
|
||||
"Bash(/tmp/out)",
|
||||
"Bash(/tmp/test_multidim:*)",
|
||||
"Bash(for f in test.bas test_classic.bas test_redim.bas test_big.bas test_types.bas test_fileio.bas test_data.bas test_labels.bas test_multidim.bas)",
|
||||
"Bash(echo:*)",
|
||||
"Bash(/tmp/out_udt)",
|
||||
"Bash(for f in test.bas test_classic.bas test_redim.bas test_big.bas test_types.bas test_fileio.bas test_data.bas test_labels.bas test_multidim.bas test_udt.bas)",
|
||||
"Bash(failed=0)",
|
||||
"Bash(for bas in test.bas test_classic.bas test_redim.bas test_big.bas test_types.bas test_fileio.bas test_data.bas test_labels.bas test_multidim.bas test_udt.bas)",
|
||||
"Bash(do:*)",
|
||||
"Bash(then echo \"FAIL compile: $bas\")",
|
||||
"Bash(failed=1)",
|
||||
"Bash(else /tmp/test_out)",
|
||||
"Bash(then echo \"FAIL run: $bas\")",
|
||||
"Bash(else echo \"PASS \\(debug\\): $bas\")",
|
||||
"Bash(if [ $failed -eq 0 ])",
|
||||
"Bash(then echo \"All debug tests passed\")",
|
||||
"Bash(else echo \"PASS \\(release\\): $bas\")",
|
||||
"Bash(then echo \"All release tests passed\")",
|
||||
"Bash(then echo \"FAIL compile \\($mode\\): $bas\")",
|
||||
"Bash(then echo \"FAIL run \\($mode\\): $bas\")",
|
||||
"Bash(then echo \"All 20 tests passed \\(10 debug + 10 release\\)\")",
|
||||
"Bash(printf:*)",
|
||||
"Bash(/tmp/test_sincos:*)",
|
||||
"Bash(then echo \"FAIL: $bas\")",
|
||||
"Bash(else /tmp/t)",
|
||||
"Bash({ echo \"FAIL run: $bas\")",
|
||||
"Bash(})",
|
||||
"Bash([ $failed -eq 0 ])",
|
||||
"Bash(python3:*)",
|
||||
"Bash({ echo \"FAIL run \\($mode\\): $bas\")",
|
||||
"Bash(for bas in test.bas test_classic.bas test_redim.bas test_big.bas test_types.bas test_fileio.bas test_labels.bas test_data.bas test_multidim.bas test_udt.bas)",
|
||||
"Bash(for bas in test.bas test_big.bas)",
|
||||
"Bash(/tmp/test_prog:*)",
|
||||
"Bash(/tmp/test_nf)",
|
||||
"Bash(for bas in test.bas test_classic.bas test_redim.bas test_big.bas test_types.bas test_fileio.bas test_labels.bas test_data.bas test_multidim.bas test_udt.bas test_newfeatures.bas)",
|
||||
"Bash(/tmp/inc_test)",
|
||||
"Bash(for:*)",
|
||||
"Bash(/tmp/test_continue)",
|
||||
"Bash(/tmp/test_print:*)",
|
||||
"Bash(/tmp/test2)",
|
||||
"Bash(/tmp/test_tab)",
|
||||
"Bash(/tmp/test_tab2)",
|
||||
"Bash(/tmp/test_all)",
|
||||
"Bash(/tmp/test_rnd)",
|
||||
"Bash(/tmp/test_rnd2)",
|
||||
"Bash(/tmp/test_spc:*)",
|
||||
"Bash(/tmp/test_spc2:*)",
|
||||
"Bash(./test_extern:*)",
|
||||
"Bash(./test_timer:*)",
|
||||
"Bash(cat:*)",
|
||||
"Bash(./test_final)",
|
||||
"Bash(./test_using)",
|
||||
"Bash(./test_debug:*)",
|
||||
"Bash(./test_using2)",
|
||||
"Bash(/tmp/testproj/test:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
*~
|
||||
basic2c
|
||||
*.o
|
||||
951
README.md
Normal file
951
README.md
Normal file
|
|
@ -0,0 +1,951 @@
|
|||
# basic2c
|
||||
|
||||
A BASIC-to-C transpiler. Translates BASIC source code into equivalent C source
|
||||
code with an embedded runtime library.
|
||||
|
||||
## Build
|
||||
|
||||
```
|
||||
cc -Wall -o basic2c basic2c.c -lm
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
basic2c [--release|-r] input.bas [output.c]
|
||||
```
|
||||
|
||||
- If `output.c` is omitted, C code is written to stdout.
|
||||
- `--release` (or `-r`) selects the release runtime (see [Runtime Modes](#runtime-modes)).
|
||||
|
||||
Compile the generated C:
|
||||
|
||||
```
|
||||
cc -Wall -o program output.c -lm
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
The transpiler is a single-file C program with three phases:
|
||||
|
||||
1. **Lexer** — tokenizes BASIC source (case-insensitive keywords)
|
||||
2. **Parser** — recursive descent, builds an AST
|
||||
3. **Codegen** — walks the AST, emits C source with a small runtime library
|
||||
|
||||
## Data Types
|
||||
|
||||
| BASIC Type | C Type | Suffix | Notes |
|
||||
|----------------|------------|--------|------------------------------|
|
||||
| `BYTE` | `uint8_t` | | Unsigned 8-bit |
|
||||
| `INTEGER` | `int16_t` | `%` | Signed 16-bit |
|
||||
| `LONG` | `int32_t` | | Signed 32-bit |
|
||||
| `FLOAT` | `float` | `!` | Single precision |
|
||||
| `DOUBLE` | `double` | `#` | Double precision (default numeric) |
|
||||
| `STRING` | `char*` | `$` | Dynamic, heap-allocated |
|
||||
|
||||
Type suffixes on variable names are recognized: `name$` is STRING, `count%` is
|
||||
INTEGER, `total#` is DOUBLE, `rate!` is FLOAT. Variables without a suffix or
|
||||
explicit type declaration default to DOUBLE.
|
||||
|
||||
Numeric types follow a promotion hierarchy: BYTE < INTEGER < LONG < FLOAT <
|
||||
DOUBLE. Mixed-type expressions promote to the higher-ranked type.
|
||||
|
||||
## Variables and Arrays
|
||||
|
||||
### Declaration
|
||||
|
||||
```basic
|
||||
DIM x AS DOUBLE
|
||||
DIM name AS STRING
|
||||
DIM count AS INTEGER
|
||||
```
|
||||
|
||||
Variables can also be used without declaration — they are implicitly declared
|
||||
based on their type suffix or as DOUBLE by default.
|
||||
|
||||
### Arrays
|
||||
|
||||
```basic
|
||||
DIM arr(10) AS INTEGER ' 1D array, indices 0..10
|
||||
DIM matrix(3, 4) AS DOUBLE ' 2D array, indices 0..3 x 0..4
|
||||
DIM cube(2, 3, 4) AS INTEGER ' 3D array
|
||||
```
|
||||
|
||||
Arrays are zero-based. The dimension value is the upper bound (inclusive), so
|
||||
`DIM arr(10)` allocates 11 elements (0 through 10).
|
||||
|
||||
### REDIM
|
||||
|
||||
```basic
|
||||
REDIM arr(20) AS INTEGER ' Resize array (contents reset to zero)
|
||||
REDIM matrix(5, 5) AS DOUBLE ' Resize multidimensional array
|
||||
```
|
||||
|
||||
`REDIM` frees the previous allocation and creates a new zero-initialized array.
|
||||
|
||||
## Operators
|
||||
|
||||
### Arithmetic
|
||||
|
||||
| Operator | Description |
|
||||
|----------|----------------------|
|
||||
| `+` | Addition |
|
||||
| `-` | Subtraction / unary negation |
|
||||
| `*` | Multiplication |
|
||||
| `/` | Division |
|
||||
| `\` | Integer division |
|
||||
| `MOD` | Modulo |
|
||||
| `^` | Exponentiation |
|
||||
|
||||
### Comparison
|
||||
|
||||
| Operator | Description |
|
||||
|----------|----------------------|
|
||||
| `=` | Equal |
|
||||
| `<>` | Not equal |
|
||||
| `<` | Less than |
|
||||
| `>` | Greater than |
|
||||
| `<=` | Less than or equal |
|
||||
| `>=` | Greater than or equal|
|
||||
|
||||
### Bitwise / Logical
|
||||
|
||||
| Operator | Description |
|
||||
|----------|----------------------|
|
||||
| `AND` | Bitwise AND |
|
||||
| `OR` | Bitwise OR |
|
||||
| `NOT` | Bitwise NOT |
|
||||
| `XOR` | Bitwise XOR |
|
||||
|
||||
These operators work as both bitwise and logical operators. When used with
|
||||
comparisons (which return 0 or 1), they behave logically: `x > 5 AND y < 10`.
|
||||
When used with integers, they operate on individual bits: `15 AND 9` gives `9`.
|
||||
|
||||
### String
|
||||
|
||||
| Operator | Description |
|
||||
|----------|----------------------|
|
||||
| `+` | Concatenation (when operands are strings) |
|
||||
| `&` | Concatenation (explicit) |
|
||||
|
||||
## Control Flow
|
||||
|
||||
### IF / THEN / ELSE
|
||||
|
||||
Single-line:
|
||||
```basic
|
||||
IF x > 0 THEN PRINT "positive" ELSE PRINT "non-positive"
|
||||
```
|
||||
|
||||
Multi-line:
|
||||
```basic
|
||||
IF x > 0 THEN
|
||||
PRINT "positive"
|
||||
ELSEIF x = 0 THEN
|
||||
PRINT "zero"
|
||||
ELSE
|
||||
PRINT "negative"
|
||||
END IF
|
||||
```
|
||||
|
||||
### FOR / NEXT
|
||||
|
||||
```basic
|
||||
FOR i = 1 TO 10
|
||||
PRINT i
|
||||
NEXT i
|
||||
|
||||
FOR i = 10 TO 0 STEP -2
|
||||
PRINT i
|
||||
NEXT i
|
||||
```
|
||||
|
||||
### WHILE / WEND
|
||||
|
||||
```basic
|
||||
WHILE x > 0
|
||||
x = x - 1
|
||||
WEND
|
||||
```
|
||||
|
||||
### DO / LOOP
|
||||
|
||||
```basic
|
||||
DO
|
||||
x = x + 1
|
||||
LOOP UNTIL x >= 10
|
||||
|
||||
DO WHILE x < 100
|
||||
x = x * 2
|
||||
LOOP
|
||||
```
|
||||
|
||||
### SELECT CASE
|
||||
|
||||
```basic
|
||||
SELECT CASE grade
|
||||
CASE 90 TO 100
|
||||
PRINT "A"
|
||||
CASE 80 TO 89
|
||||
PRINT "B"
|
||||
CASE 70 TO 79
|
||||
PRINT "C"
|
||||
CASE IS < 60
|
||||
PRINT "F"
|
||||
CASE ELSE
|
||||
PRINT "D"
|
||||
END SELECT
|
||||
```
|
||||
|
||||
CASE values support single values (`CASE 1`), comma-separated values
|
||||
(`CASE 1, 2, 3`), ranges (`CASE 5 TO 10`), comparisons (`CASE IS > 100`),
|
||||
and a default (`CASE ELSE`). Works with both numeric and string expressions.
|
||||
|
||||
### EXIT
|
||||
|
||||
```basic
|
||||
EXIT FOR
|
||||
EXIT WHILE
|
||||
EXIT DO
|
||||
EXIT SUB
|
||||
EXIT FUNCTION
|
||||
```
|
||||
|
||||
### CONTINUE
|
||||
|
||||
```basic
|
||||
CONTINUE FOR
|
||||
CONTINUE WHILE
|
||||
CONTINUE DO
|
||||
```
|
||||
|
||||
Skips the rest of the current loop iteration and jumps to the next iteration.
|
||||
|
||||
### GOTO
|
||||
|
||||
```basic
|
||||
GOTO 100 ' Jump to line number
|
||||
GOTO myLabel ' Jump to named label
|
||||
```
|
||||
|
||||
### GOSUB / RETURN
|
||||
|
||||
```basic
|
||||
GOSUB 200
|
||||
GOSUB myRoutine
|
||||
' ...
|
||||
200 PRINT "in subroutine"
|
||||
RETURN
|
||||
|
||||
myRoutine:
|
||||
PRINT "named routine"
|
||||
RETURN
|
||||
```
|
||||
|
||||
GOSUB uses a compile-time dispatch mechanism — each GOSUB site gets a unique
|
||||
return-point ID, and RETURN uses a switch statement to jump back.
|
||||
|
||||
### ON GOTO / ON GOSUB
|
||||
|
||||
```basic
|
||||
ON choice GOTO label1, label2, label3
|
||||
ON choice GOSUB routine1, routine2, routine3
|
||||
```
|
||||
|
||||
Branches to the Nth label based on the expression value (1-based). If the
|
||||
value is out of range, execution continues at the next statement.
|
||||
|
||||
### Labels
|
||||
|
||||
Both classic line numbers and named labels are supported:
|
||||
|
||||
```basic
|
||||
10 PRINT "line 10"
|
||||
20 GOTO 10
|
||||
|
||||
myLabel:
|
||||
PRINT "named label"
|
||||
GOTO myLabel
|
||||
```
|
||||
|
||||
## Constants
|
||||
|
||||
```basic
|
||||
CONST PI = 3.14159
|
||||
CONST MAX_SIZE = 100
|
||||
CONST GREETING$ = "Hello"
|
||||
```
|
||||
|
||||
Constants are evaluated at compile time and substituted directly into
|
||||
expressions. They cannot be reassigned.
|
||||
|
||||
## SWAP
|
||||
|
||||
```basic
|
||||
SWAP a, b
|
||||
SWAP s1$, s2$
|
||||
```
|
||||
|
||||
Exchanges the values of two variables of the same type.
|
||||
|
||||
## Procedures
|
||||
|
||||
### SUB
|
||||
|
||||
```basic
|
||||
SUB greet(name AS STRING)
|
||||
PRINT "Hello, "; name
|
||||
END SUB
|
||||
|
||||
CALL greet("World")
|
||||
greet "World" ' CALL keyword is optional
|
||||
```
|
||||
|
||||
### FUNCTION
|
||||
|
||||
```basic
|
||||
FUNCTION square(x AS DOUBLE) AS DOUBLE
|
||||
square = x * x
|
||||
END FUNCTION
|
||||
|
||||
PRINT square(5)
|
||||
```
|
||||
|
||||
Functions return values by assigning to the function name or using `RETURN expr`.
|
||||
|
||||
### Parameter Passing
|
||||
|
||||
```basic
|
||||
SUB increment(BYREF x AS INTEGER)
|
||||
x = x + 1
|
||||
END SUB
|
||||
|
||||
SUB display(BYVAL x AS INTEGER)
|
||||
PRINT x
|
||||
END SUB
|
||||
```
|
||||
|
||||
- `BYREF` (default) — passes a pointer; changes affect the caller's variable
|
||||
- `BYVAL` — passes a copy; changes are local to the procedure
|
||||
|
||||
### LOCAL and STATIC
|
||||
|
||||
```basic
|
||||
SUB counter()
|
||||
STATIC count AS INTEGER
|
||||
LOCAL temp AS INTEGER
|
||||
count = count + 1
|
||||
temp = count
|
||||
PRINT temp
|
||||
END SUB
|
||||
```
|
||||
|
||||
- `LOCAL` — declares a variable scoped to the procedure
|
||||
- `STATIC` — declares a variable that persists across calls
|
||||
|
||||
## User-Defined Types
|
||||
|
||||
### TYPE / END TYPE
|
||||
|
||||
```basic
|
||||
TYPE PersonRecord
|
||||
firstName AS STRING * 20
|
||||
lastName AS STRING * 30
|
||||
age AS INTEGER
|
||||
salary AS DOUBLE
|
||||
END TYPE
|
||||
|
||||
DIM person AS PersonRecord
|
||||
person.firstName = "John"
|
||||
person.lastName = "Doe"
|
||||
person.age = 30
|
||||
person.salary = 55000.50
|
||||
```
|
||||
|
||||
String fields in TYPE definitions require a fixed length (`STRING * N`). Dynamic
|
||||
strings (`AS STRING` without a length) are not permitted in TYPE definitions
|
||||
because struct copy would produce dangling pointers.
|
||||
|
||||
Supported field types: `BYTE`, `INTEGER`, `LONG`, `FLOAT`, `DOUBLE`,
|
||||
`STRING * N`, and other user-defined types (nesting).
|
||||
|
||||
### Nested UDTs
|
||||
|
||||
```basic
|
||||
TYPE Vec2
|
||||
x AS DOUBLE
|
||||
y AS DOUBLE
|
||||
END TYPE
|
||||
|
||||
TYPE Circle
|
||||
center AS Vec2
|
||||
radius AS DOUBLE
|
||||
END TYPE
|
||||
|
||||
DIM c AS Circle
|
||||
c.center.x = 10.0
|
||||
c.center.y = 20.0
|
||||
c.radius = 5.0
|
||||
```
|
||||
|
||||
Nesting depth is unlimited. Chained dot-access works for both reads and writes.
|
||||
|
||||
### UDT Arrays
|
||||
|
||||
```basic
|
||||
DIM points(10) AS Vec2
|
||||
points(0).x = 1.5
|
||||
points(0).y = 2.5
|
||||
```
|
||||
|
||||
### UDT Assignment
|
||||
|
||||
Whole-struct copy via assignment:
|
||||
|
||||
```basic
|
||||
DIM a AS Vec2
|
||||
DIM b AS Vec2
|
||||
a.x = 1.0
|
||||
a.y = 2.0
|
||||
b = a ' Copies all fields
|
||||
```
|
||||
|
||||
Sub-struct copy also works:
|
||||
|
||||
```basic
|
||||
DIM saved AS Vec2
|
||||
saved = c.center ' Copy nested struct out
|
||||
c.center = saved ' Copy nested struct in
|
||||
```
|
||||
|
||||
Array element copy:
|
||||
|
||||
```basic
|
||||
circles(0) = circles(2)
|
||||
```
|
||||
|
||||
### SIZEOF
|
||||
|
||||
```basic
|
||||
DIM sz AS LONG
|
||||
sz = SIZEOF(PersonRecord)
|
||||
```
|
||||
|
||||
Returns the byte size of a user-defined type. Used primarily with random-access
|
||||
file I/O to specify record length.
|
||||
|
||||
## Built-in Functions
|
||||
|
||||
### String Functions
|
||||
|
||||
| Function | Description |
|
||||
|-----------------------|------------------------------------------------|
|
||||
| `LEN(s$)` | Length of string |
|
||||
| `MID$(s$, start, len)` | Substring (1-based start position) |
|
||||
| `LEFT$(s$, n)` | First n characters |
|
||||
| `RIGHT$(s$, n)` | Last n characters |
|
||||
| `CHR$(n)` | Character from ASCII code |
|
||||
| `ASC(s$)` | ASCII code of first character |
|
||||
| `STR$(n)` | Convert number to string |
|
||||
| `VAL(s$)` | Convert string to number |
|
||||
| `UCASE$(s$)` | Convert to uppercase |
|
||||
| `LCASE$(s$)` | Convert to lowercase |
|
||||
| `INSTR(haystack$, needle$)` | Find substring position (1-based, 0 if not found) |
|
||||
| `STRING$(n, char$)` | Repeat a character n times |
|
||||
| `LTRIM$(s$)` | Remove leading spaces |
|
||||
| `RTRIM$(s$)` | Remove trailing spaces |
|
||||
| `TRIM$(s$)` | Remove leading and trailing spaces |
|
||||
| `SPACE$(n)` | String of n spaces |
|
||||
| `HEX$(n)` | Hexadecimal string representation |
|
||||
| `OCT$(n)` | Octal string representation |
|
||||
|
||||
### MID$ Assignment
|
||||
|
||||
```basic
|
||||
DIM s AS STRING
|
||||
s = "Hello World"
|
||||
MID$(s, 7, 5) = "BASIC" ' s is now "Hello BASIC"
|
||||
```
|
||||
|
||||
Replaces characters in a string starting at a 1-based position. The length
|
||||
parameter limits how many characters are replaced.
|
||||
|
||||
### Math Functions
|
||||
|
||||
| Function | Description |
|
||||
|------------|------------------------------------------|
|
||||
| `ABS(n)` | Absolute value |
|
||||
| `INT(n)` | Truncate to integer |
|
||||
| `SQR(n)` | Square root |
|
||||
| `SIN(n)` | Sine (radians) |
|
||||
| `COS(n)` | Cosine (radians) |
|
||||
| `TAN(n)` | Tangent (radians) |
|
||||
| `ATN(n)` | Arctangent (returns radians) |
|
||||
| `LOG(n)` | Natural logarithm |
|
||||
| `EXP(n)` | e raised to the power n |
|
||||
| `SGN(n)` | Sign: -1, 0, or 1 |
|
||||
| `RND` | Random number between 0 and 1 |
|
||||
|
||||
Numeric expressions also support `^` for exponentiation (emitted as `pow()`).
|
||||
|
||||
### Print Formatting Functions
|
||||
|
||||
| Function | Description |
|
||||
|------------|------------------------------------------|
|
||||
| `TAB(n)` | Output spaces to reach column n |
|
||||
| `SPC(n)` | Output exactly n spaces |
|
||||
|
||||
These functions are used within PRINT statements:
|
||||
|
||||
```basic
|
||||
PRINT "Name"; TAB(20); "Value"
|
||||
PRINT "A"; SPC(5); "B" ' Outputs "A B"
|
||||
```
|
||||
|
||||
`RND` can be called with or without parentheses, and accepts an optional argument
|
||||
(which is ignored) for compatibility with other BASIC dialects. Use `RANDOMIZE`
|
||||
to seed the random number generator:
|
||||
|
||||
```basic
|
||||
RANDOMIZE ' Seed from system clock
|
||||
RANDOMIZE 12345 ' Seed with specific value
|
||||
x = RND ' Random double 0..1
|
||||
x = RND(1) ' Same as RND (argument ignored)
|
||||
```
|
||||
|
||||
### Array Functions
|
||||
|
||||
| Function | Description |
|
||||
|---------------|----------------------------------------------|
|
||||
| `LBOUND(arr)` | Lower bound of array (always 0) |
|
||||
| `UBOUND(arr)` | Upper bound of array |
|
||||
|
||||
### I/O Functions
|
||||
|
||||
| Function | Description |
|
||||
|--------------|--------------------------------------------------|
|
||||
| `EOF(n)` | Returns true (-1) if at end of file n |
|
||||
| `LOF(n)` | Returns byte length of file n |
|
||||
| `FREEFILE()` | Returns the next available file number |
|
||||
|
||||
## Console I/O
|
||||
|
||||
### PRINT
|
||||
|
||||
```basic
|
||||
PRINT "Hello, World!"
|
||||
PRINT "x = "; x
|
||||
PRINT x; " "; y ' Semicolon suppresses newline between items
|
||||
PRINT x, y ' Comma advances to next tab stop
|
||||
PRINT "no newline"; ' Trailing semicolon suppresses final newline
|
||||
? "shortcut" ' ? is a shortcut for PRINT
|
||||
```
|
||||
|
||||
The `?` character can be used as a shortcut for `PRINT`, for compatibility with
|
||||
classic BASIC dialects and interactive use.
|
||||
|
||||
### PRINT USING
|
||||
|
||||
```basic
|
||||
PRINT USING "###.##"; 123.456 ' Outputs: 123.46
|
||||
PRINT USING "$$#,###.##"; 1234.56 ' Outputs: $1,234.56
|
||||
PRINT USING "+###.##"; -45.6 ' Outputs: -45.60
|
||||
PRINT USING "**###.##"; 9.99 ' Outputs: ****9.99
|
||||
PRINT USING "!"; "Hello" ' Outputs: H
|
||||
PRINT USING "&"; "World" ' Outputs: World
|
||||
PRINT USING "\ \"; "Testing" ' Outputs: Testin (6 chars)
|
||||
```
|
||||
|
||||
Format specifiers for numbers:
|
||||
|
||||
| Format | Description |
|
||||
|--------|-------------|
|
||||
| `#` | Digit placeholder |
|
||||
| `.` | Decimal point position |
|
||||
| `,` | Thousands separator (in format, not output) |
|
||||
| `+` | Show sign (+ or -) at start |
|
||||
| `-` | Trailing minus for negative numbers |
|
||||
| `$$` | Floating dollar sign |
|
||||
| `**` | Fill leading spaces with asterisks |
|
||||
|
||||
Format specifiers for strings:
|
||||
|
||||
| Format | Description |
|
||||
|--------|-------------|
|
||||
| `!` | First character only |
|
||||
| `&` | Entire string |
|
||||
| `\ \` | Fixed width (spaces between backslashes + 2) |
|
||||
|
||||
Multiple values can be formatted with one format string:
|
||||
|
||||
```basic
|
||||
PRINT USING "### + ### = ###"; 10; 20; 30
|
||||
' Outputs: 10 + 20 = 30
|
||||
```
|
||||
|
||||
### INPUT
|
||||
|
||||
```basic
|
||||
INPUT "Enter name: "; name$
|
||||
INPUT x
|
||||
```
|
||||
|
||||
### LINE INPUT
|
||||
|
||||
```basic
|
||||
LINE INPUT "Enter text: "; line$
|
||||
```
|
||||
|
||||
Reads an entire line including commas and spaces.
|
||||
|
||||
## File I/O
|
||||
|
||||
### Sequential Files
|
||||
|
||||
```basic
|
||||
' Write
|
||||
OPEN "data.txt" FOR OUTPUT AS #1
|
||||
PRINT #1, "Hello"
|
||||
PRINT #1, 42
|
||||
CLOSE #1
|
||||
|
||||
' Read
|
||||
OPEN "data.txt" FOR INPUT AS #1
|
||||
LINE INPUT #1, text$
|
||||
INPUT #1, value
|
||||
CLOSE #1
|
||||
|
||||
' Append
|
||||
OPEN "log.txt" FOR APPEND AS #1
|
||||
PRINT #1, "new entry"
|
||||
CLOSE #1
|
||||
```
|
||||
|
||||
### WRITE #
|
||||
|
||||
```basic
|
||||
WRITE #1, name$, age, salary
|
||||
```
|
||||
|
||||
Outputs CSV-style: strings are quoted, values are comma-separated, terminated
|
||||
with a newline.
|
||||
|
||||
### Binary Files
|
||||
|
||||
```basic
|
||||
OPEN "file.dat" FOR BINARY AS #1
|
||||
```
|
||||
|
||||
### Random-Access Files
|
||||
|
||||
```basic
|
||||
TYPE Record
|
||||
name AS STRING * 20
|
||||
value AS DOUBLE
|
||||
END TYPE
|
||||
|
||||
DIM rec AS Record
|
||||
rec.name = "test"
|
||||
rec.value = 3.14
|
||||
|
||||
OPEN "data.dat" FOR RANDOM AS #1 LEN = SIZEOF(Record)
|
||||
PUT #1, 1, rec ' Write record at position 1 (1-based)
|
||||
GET #1, 1, rec ' Read record at position 1
|
||||
CLOSE #1
|
||||
```
|
||||
|
||||
Random-access uses `GET` and `PUT` with 1-based record numbers. The `LEN`
|
||||
clause specifies record size in bytes. Records can be read and written in any
|
||||
order.
|
||||
|
||||
### File Modes
|
||||
|
||||
| Mode | C Mode | Description |
|
||||
|----------|--------|--------------------------------------|
|
||||
| `INPUT` | `"r"` | Read sequential text |
|
||||
| `OUTPUT` | `"w"` | Write sequential text (truncates) |
|
||||
| `APPEND` | `"a"` | Append sequential text |
|
||||
| `BINARY` | `"rb"` | Binary read |
|
||||
| `RANDOM` | `"r+b"`| Random access (creates if not found) |
|
||||
|
||||
## DATA / READ / RESTORE
|
||||
|
||||
```basic
|
||||
DATA 10, 20, 30, "hello"
|
||||
|
||||
DIM x AS INTEGER
|
||||
DIM s AS STRING
|
||||
READ x ' x = 10
|
||||
READ x ' x = 20
|
||||
READ x ' x = 30
|
||||
READ s ' s = "hello"
|
||||
|
||||
RESTORE ' Reset read pointer to beginning
|
||||
READ x ' x = 10 again
|
||||
```
|
||||
|
||||
`DATA` statements define a pool of literal values. `READ` consumes them in
|
||||
order. `RESTORE` resets the read pointer (optionally to a specific line number).
|
||||
|
||||
## Comments
|
||||
|
||||
```basic
|
||||
' This is a comment
|
||||
REM This is also a comment
|
||||
x = 5 ' Inline comment
|
||||
```
|
||||
|
||||
## $INCLUDE Metacommand
|
||||
|
||||
```basic
|
||||
'$INCLUDE: 'helpers.bas'
|
||||
```
|
||||
|
||||
The `$INCLUDE` metacommand inserts the contents of another file at the point
|
||||
of the directive, before lexing and parsing. The directive is placed inside a
|
||||
comment (the leading `'` makes it invisible to editors that don't understand it).
|
||||
|
||||
### Syntax
|
||||
|
||||
The filename is enclosed in single quotes after `'$INCLUDE:`. The keyword is
|
||||
case-insensitive. Any amount of whitespace may appear between the colon and the
|
||||
opening quote.
|
||||
|
||||
### Nested Includes
|
||||
|
||||
Included files may themselves contain `$INCLUDE` directives:
|
||||
|
||||
```basic
|
||||
' main.bas
|
||||
'$INCLUDE: 'math_lib.bas'
|
||||
'$INCLUDE: 'string_lib.bas'
|
||||
```
|
||||
|
||||
```basic
|
||||
' math_lib.bas — can include further files
|
||||
'$INCLUDE: 'constants.bas'
|
||||
FUNCTION Square(x AS DOUBLE) AS DOUBLE
|
||||
Square = x * x
|
||||
END FUNCTION
|
||||
```
|
||||
|
||||
### Path Resolution
|
||||
|
||||
Filenames are resolved relative to the **including file's directory**, not the
|
||||
working directory. If `src/main.bas` includes `'lib/util.bas'`, the transpiler
|
||||
looks for `src/lib/util.bas`.
|
||||
|
||||
### Error Reporting
|
||||
|
||||
When `$INCLUDE` is used, error messages show the originating file and line:
|
||||
|
||||
```
|
||||
Error (math_lib.bas:12): undeclared variable 'q'
|
||||
```
|
||||
|
||||
Without includes, the format is the same but shows the input filename:
|
||||
|
||||
```
|
||||
Error (main.bas:5): type mismatch
|
||||
```
|
||||
|
||||
### Circular Include Detection
|
||||
|
||||
If file A includes file B which includes file A, the transpiler reports a fatal
|
||||
error rather than looping infinitely:
|
||||
|
||||
```
|
||||
Error: Circular include detected: main.bas
|
||||
```
|
||||
|
||||
## Extensible Functions
|
||||
|
||||
The transpiler supports two mechanisms for defining additional functions:
|
||||
|
||||
### Built-in Functions (builtins.def)
|
||||
|
||||
The `builtins.def` file is compiled into basic2c and provides functions that are
|
||||
always available. To add permanent built-in functions, edit `builtins.def` and
|
||||
recompile basic2c.
|
||||
|
||||
Default built-ins include:
|
||||
|
||||
**Math functions:**
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `SQR(n)` | Square root |
|
||||
| `SIN(n)` | Sine (radians) |
|
||||
| `COS(n)` | Cosine (radians) |
|
||||
| `TAN(n)` | Tangent (radians) |
|
||||
| `ATN(n)` | Arctangent (returns radians) |
|
||||
| `LOG(n)` | Natural logarithm |
|
||||
| `EXP(n)` | e raised to power n |
|
||||
| `SGN(n)` | Sign: -1, 0, or 1 |
|
||||
| `RND()` | Random number 0 to 1 |
|
||||
| `CEIL(n)` | Round up to integer |
|
||||
| `FLOOR(n)` | Round down to integer |
|
||||
| `ROUND(n)` | Round to nearest integer |
|
||||
| `FIX(n)` | Truncate toward zero |
|
||||
| `FRAC(n)` | Fractional part |
|
||||
| `HYPOT(x, y)` | Hypotenuse (sqrt(x² + y²)) |
|
||||
| `MAX(a, b)` | Maximum of two values |
|
||||
| `MIN(a, b)` | Minimum of two values |
|
||||
|
||||
**String functions:**
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `CHR$(n)` | Character from ASCII code |
|
||||
| `STR$(n)` | Convert number to string |
|
||||
| `UCASE$(s)` | Convert to uppercase |
|
||||
| `LCASE$(s)` | Convert to lowercase |
|
||||
| `LTRIM$(s)` | Remove leading spaces |
|
||||
| `RTRIM$(s)` | Remove trailing spaces |
|
||||
| `TRIM$(s)` | Remove leading and trailing spaces |
|
||||
| `SPACE$(n)` | String of n spaces |
|
||||
| `HEX$(n)` | Hexadecimal representation |
|
||||
| `OCT$(n)` | Octal representation |
|
||||
| `TAB(n)` | Spaces to reach column n |
|
||||
| `SPC(n)` | Output n spaces |
|
||||
| `ENVIRON$(name)` | Get environment variable |
|
||||
|
||||
**System:**
|
||||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `TIMER()` | Seconds since program start |
|
||||
|
||||
### External Functions (functions.def)
|
||||
|
||||
The `functions.def` file is loaded at runtime from two locations (both if present):
|
||||
|
||||
1. The directory containing the `basic2c` binary (global extensions)
|
||||
2. The directory containing the input `.bas` file (project-specific)
|
||||
|
||||
Functions from the input file's directory are loaded second, allowing project-specific
|
||||
definitions to supplement or override earlier ones.
|
||||
|
||||
### Definition Format
|
||||
|
||||
Both `builtins.def` and `functions.def` use the same format:
|
||||
|
||||
```
|
||||
# Comment lines start with #
|
||||
# Format: name : type : c_template
|
||||
|
||||
SQUARE : double : ((%) * (%))
|
||||
CUBE : double : ((%) * (%) * (%))
|
||||
```
|
||||
|
||||
Each line defines:
|
||||
- **name** — The BASIC function name (case-insensitive)
|
||||
- **type** — Return type: `byte`, `integer`, `long`, `float`, `double`, or `string`
|
||||
- **c_template** — C code with argument placeholders
|
||||
|
||||
### Argument Placeholders
|
||||
|
||||
- `%` or `%1` — First argument
|
||||
- `%2` — Second argument
|
||||
- `%3` — Third argument (and so on)
|
||||
|
||||
Arguments are substituted directly, so use parentheses in templates to ensure
|
||||
correct precedence: `((%) * (%2))` not `% * %2`.
|
||||
|
||||
### Usage
|
||||
|
||||
```basic
|
||||
PRINT CEIL(3.7) ' Outputs: 4
|
||||
PRINT MAX(5, 10) ' Outputs: 10
|
||||
t = TIMER() ' Get elapsed time
|
||||
PRINT ENVIRON$("HOME") ' Print home directory
|
||||
```
|
||||
|
||||
Extensible functions require parentheses, even with no arguments: `TIMER()` not `TIMER`.
|
||||
|
||||
## Runtime Modes
|
||||
|
||||
The transpiler supports two runtime modes selected at transpile time:
|
||||
|
||||
### Debug Mode (default)
|
||||
|
||||
The debug runtime includes error checking and diagnostics:
|
||||
|
||||
- NULL guards on string function arguments
|
||||
- `malloc`/`calloc` failure checks with error messages
|
||||
- File number bounds checking
|
||||
- `fopen` failure reporting with filename
|
||||
- GOSUB stack overflow/underflow detection
|
||||
- All errors print to stderr and call `exit(1)`
|
||||
|
||||
### Release Mode (`--release` or `-r`)
|
||||
|
||||
The release runtime strips all diagnostic checks for minimal generated code:
|
||||
|
||||
- No NULL guards on string functions
|
||||
- No malloc failure checks
|
||||
- No file number bounds checking
|
||||
- No GOSUB stack overflow/underflow checks
|
||||
- ~8% fewer lines of generated C code
|
||||
|
||||
Functional guards are preserved in release mode to prevent crashes:
|
||||
|
||||
- `EOF()` returns true (-1) for NULL file handles (enables file existence checks)
|
||||
- `LOF()` returns 0 for NULL file handles
|
||||
- `CLOSE` is a no-op for NULL file handles
|
||||
- `LINE INPUT` is a no-op for NULL file handles
|
||||
- Temp string pool management (`_bfree_temps`, `_btmp`)
|
||||
- String variable management (`_bstr_assign`)
|
||||
|
||||
## Limits
|
||||
|
||||
| Resource | Maximum |
|
||||
|------------------------|---------|
|
||||
| Token length | 4096 |
|
||||
| Identifier length | 128 |
|
||||
| Parameters per procedure | 32 |
|
||||
| Symbol table entries | 2048 |
|
||||
| GOSUB return sites | 512 |
|
||||
| Line number labels | 4096 |
|
||||
| AST nodes | 65536 |
|
||||
| Arguments per call | 64 |
|
||||
| User-defined types | 64 |
|
||||
| Fields per type | 32 |
|
||||
| Constants | 256 |
|
||||
| Include nesting depth | 16 |
|
||||
| Included files | 64 |
|
||||
| Total source lines | 65536 |
|
||||
|
||||
## Example
|
||||
|
||||
```basic
|
||||
TYPE Item
|
||||
name AS STRING * 20
|
||||
price AS DOUBLE
|
||||
END TYPE
|
||||
|
||||
DIM items(2) AS Item
|
||||
items(0).name = "Widget"
|
||||
items(0).price = 9.99
|
||||
items(1).name = "Gadget"
|
||||
items(1).price = 24.95
|
||||
items(2).name = "Doohickey"
|
||||
items(2).price = 4.50
|
||||
|
||||
DIM i AS INTEGER
|
||||
DIM total AS DOUBLE
|
||||
total = 0
|
||||
FOR i = 0 TO 2
|
||||
PRINT items(i).name; " $"; items(i).price
|
||||
total = total + items(i).price
|
||||
NEXT i
|
||||
PRINT "Total: $"; total
|
||||
```
|
||||
|
||||
Transpile and run:
|
||||
|
||||
```
|
||||
./basic2c example.bas example.c
|
||||
cc -Wall -o example example.c -lm
|
||||
./example
|
||||
```
|
||||
45
builtins.def
Normal file
45
builtins.def
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// Built-in function definitions for basic2c
|
||||
// Format: BUILTIN(name, return_type, c_template)
|
||||
// This file is #included into basic2c.c with BUILTIN macro defined
|
||||
//
|
||||
// Template placeholders: % or %1 = first arg, %2 = second arg, etc.
|
||||
// Return types: TYPE_BYTE, TYPE_INT, TYPE_LONG, TYPE_FLOAT, TYPE_DBL, TYPE_STR
|
||||
|
||||
// Standard math functions
|
||||
BUILTIN("SQR", TYPE_DBL, "sqrt(%)")
|
||||
BUILTIN("SIN", TYPE_DBL, "sin(%)")
|
||||
BUILTIN("COS", TYPE_DBL, "cos(%)")
|
||||
BUILTIN("TAN", TYPE_DBL, "tan(%)")
|
||||
BUILTIN("ATN", TYPE_DBL, "atan(%)")
|
||||
BUILTIN("LOG", TYPE_DBL, "log(%)")
|
||||
BUILTIN("EXP", TYPE_DBL, "exp(%)")
|
||||
BUILTIN("SGN", TYPE_INT, "((%) > 0 ? 1 : ((%) < 0 ? -1 : 0))")
|
||||
BUILTIN("RND", TYPE_DBL, "((double)rand() / (double)RAND_MAX)")
|
||||
|
||||
// Extended math functions
|
||||
BUILTIN("CEIL", TYPE_DBL, "ceil(%)")
|
||||
BUILTIN("FLOOR", TYPE_DBL, "floor(%)")
|
||||
BUILTIN("ROUND", TYPE_DBL, "round(%)")
|
||||
BUILTIN("FRAC", TYPE_DBL, "((%) - floor(%))")
|
||||
BUILTIN("FIX", TYPE_DBL, "trunc(%)")
|
||||
BUILTIN("HYPOT", TYPE_DBL, "hypot(%, %2)")
|
||||
BUILTIN("MAX", TYPE_DBL, "((double)((%) > (%2) ? (%) : (%2)))")
|
||||
BUILTIN("MIN", TYPE_DBL, "((double)((%) < (%2) ? (%) : (%2)))")
|
||||
|
||||
// System/Time
|
||||
BUILTIN("TIMER", TYPE_DBL, "((double)clock() / CLOCKS_PER_SEC)")
|
||||
|
||||
// String functions
|
||||
BUILTIN("CHR$", TYPE_STR, "_bchr((int)(%))")
|
||||
BUILTIN("STR$", TYPE_STR, "_bstr_of_int(%)")
|
||||
BUILTIN("UCASE$", TYPE_STR, "_bucase(%)")
|
||||
BUILTIN("LCASE$", TYPE_STR, "_blcase(%)")
|
||||
BUILTIN("LTRIM$", TYPE_STR, "_bltrim(%)")
|
||||
BUILTIN("RTRIM$", TYPE_STR, "_brtrim(%)")
|
||||
BUILTIN("TRIM$", TYPE_STR, "_btrim(%)")
|
||||
BUILTIN("SPACE$", TYPE_STR, "_bspace((int)(%))")
|
||||
BUILTIN("HEX$", TYPE_STR, "_bhex((int)(%))")
|
||||
BUILTIN("OCT$", TYPE_STR, "_boct((int)(%))")
|
||||
BUILTIN("TAB", TYPE_STR, "_btab((int)(%))")
|
||||
BUILTIN("SPC", TYPE_STR, "_bspace((int)(%))")
|
||||
BUILTIN("ENVIRON$", TYPE_STR, "_bgetenv(%)")
|
||||
12
functions.def
Normal file
12
functions.def
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# External function definitions for basic2c
|
||||
# Format: name : type : c_template
|
||||
# Types: byte, integer, long, float, double, string
|
||||
# Template: % or %1 = first arg, %2 = second arg, etc.
|
||||
#
|
||||
# Note: Common functions like CEIL, FLOOR, MAX, MIN, TIMER, ENVIRON$
|
||||
# are now built into basic2c via builtins.def. This file is for
|
||||
# user-defined extensions that supplement or override the built-ins.
|
||||
#
|
||||
# Example custom functions:
|
||||
# SQUARE : double : ((%) * (%))
|
||||
# CUBE : double : ((%) * (%) * (%))
|
||||
202
test.bas
Normal file
202
test.bas
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
' ============================================
|
||||
' Test program for the basic2c transpiler
|
||||
' Tests all major features
|
||||
' ============================================
|
||||
|
||||
' --- Variable declarations ---
|
||||
DIM x AS INTEGER
|
||||
DIM y AS INTEGER
|
||||
DIM pi AS DOUBLE
|
||||
DIM greeting AS STRING
|
||||
DIM name$ AS STRING
|
||||
|
||||
' --- Assignments ---
|
||||
x = 10
|
||||
y = 20
|
||||
pi = 3.14159
|
||||
greeting = "Hello, World!"
|
||||
name$ = "BASIC"
|
||||
|
||||
' --- PRINT with various separators ---
|
||||
PRINT greeting
|
||||
PRINT "x = "; x; " y = "; y
|
||||
PRINT "PI is approximately "; pi
|
||||
PRINT "Name: "; name$
|
||||
|
||||
' --- Arithmetic expressions ---
|
||||
DIM result AS INTEGER
|
||||
result = x + y * 2 - 5
|
||||
PRINT "x + y * 2 - 5 = "; result
|
||||
|
||||
DIM quotient AS DOUBLE
|
||||
quotient = x / 3
|
||||
PRINT "x / 3 = "; quotient
|
||||
|
||||
DIM remainder AS INTEGER
|
||||
remainder = y MOD 3
|
||||
PRINT "y MOD 3 = "; remainder
|
||||
|
||||
' --- String operations ---
|
||||
DIM full$ AS STRING
|
||||
full$ = greeting + " from " + name$
|
||||
PRINT full$
|
||||
PRINT "Length of greeting: "; LEN(greeting)
|
||||
PRINT "First 5 chars: "; LEFT$(greeting, 5)
|
||||
PRINT "Last 6 chars: "; RIGHT$(greeting, 6)
|
||||
PRINT "Middle: "; MID$(greeting, 3, 5)
|
||||
PRINT "Upper: "; UCASE$(name$)
|
||||
PRINT "Lower: "; LCASE$(greeting)
|
||||
|
||||
' --- IF / ELSEIF / ELSE ---
|
||||
IF x > 15 THEN
|
||||
PRINT "x is greater than 15"
|
||||
ELSEIF x > 5 THEN
|
||||
PRINT "x is between 6 and 15"
|
||||
ELSE
|
||||
PRINT "x is 5 or less"
|
||||
END IF
|
||||
|
||||
' --- Single-line IF ---
|
||||
IF y = 20 THEN PRINT "y is twenty"
|
||||
|
||||
' --- FOR loop ---
|
||||
PRINT "Counting 1 to 5:"
|
||||
DIM i AS INTEGER
|
||||
FOR i = 1 TO 5
|
||||
PRINT i;
|
||||
NEXT i
|
||||
PRINT ""
|
||||
|
||||
' --- WHILE loop ---
|
||||
DIM count AS INTEGER
|
||||
count = 5
|
||||
PRINT "Countdown:"
|
||||
WHILE count > 0
|
||||
PRINT count;
|
||||
count = count - 1
|
||||
WEND
|
||||
PRINT " Go!"
|
||||
|
||||
' --- DO LOOP WHILE (bottom test) ---
|
||||
DIM n AS INTEGER
|
||||
n = 1
|
||||
PRINT "DO LOOP WHILE:"
|
||||
DO
|
||||
PRINT n;
|
||||
n = n + 1
|
||||
LOOP WHILE n <= 5
|
||||
PRINT ""
|
||||
|
||||
' --- DO WHILE LOOP (top test) ---
|
||||
n = 10
|
||||
PRINT "DO WHILE LOOP:"
|
||||
DO WHILE n > 5
|
||||
PRINT n;
|
||||
n = n - 1
|
||||
LOOP
|
||||
PRINT ""
|
||||
|
||||
' --- DO UNTIL ---
|
||||
n = 1
|
||||
PRINT "DO UNTIL:"
|
||||
DO UNTIL n > 5
|
||||
PRINT n;
|
||||
n = n + 1
|
||||
LOOP
|
||||
PRINT ""
|
||||
|
||||
' --- Dynamic arrays ---
|
||||
DIM arr(10) AS INTEGER
|
||||
FOR i = 0 TO 10
|
||||
arr(i) = i * i
|
||||
NEXT i
|
||||
PRINT "Array squares:"
|
||||
FOR i = 0 TO 10
|
||||
PRINT arr(i);
|
||||
NEXT i
|
||||
PRINT ""
|
||||
|
||||
' --- FUNCTION with BYVAL ---
|
||||
FUNCTION Square(BYVAL n AS INTEGER) AS INTEGER
|
||||
Square = n * n
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION Factorial(BYVAL n AS INTEGER) AS INTEGER
|
||||
IF n <= 1 THEN
|
||||
Factorial = 1
|
||||
ELSE
|
||||
Factorial = n * Factorial(n - 1)
|
||||
END IF
|
||||
END FUNCTION
|
||||
|
||||
PRINT "Square(7) = "; Square(7)
|
||||
PRINT "Factorial(6) = "; Factorial(6)
|
||||
|
||||
' --- FUNCTION returning DOUBLE ---
|
||||
FUNCTION CircleArea(BYVAL radius AS DOUBLE) AS DOUBLE
|
||||
CircleArea = 3.14159 * radius * radius
|
||||
END FUNCTION
|
||||
|
||||
PRINT "Area of circle r=5: "; CircleArea(5.0)
|
||||
|
||||
' --- SUB with BYREF (modifies caller's variables) ---
|
||||
SUB Swap(BYREF a AS INTEGER, BYREF b AS INTEGER)
|
||||
LOCAL temp AS INTEGER
|
||||
temp = a
|
||||
a = b
|
||||
b = temp
|
||||
END SUB
|
||||
|
||||
DIM p AS INTEGER
|
||||
DIM q AS INTEGER
|
||||
p = 100
|
||||
q = 200
|
||||
PRINT "Before swap: p="; p; " q="; q
|
||||
CALL Swap(p, q)
|
||||
PRINT "After swap: p="; p; " q="; q
|
||||
|
||||
' --- SUB with BYVAL ---
|
||||
SUB ShowMessage(BYVAL msg AS STRING)
|
||||
PRINT ">>> "; msg; " <<<"
|
||||
END SUB
|
||||
|
||||
CALL ShowMessage("This is a test message")
|
||||
|
||||
' --- STATIC variable in a function ---
|
||||
FUNCTION Counter() AS INTEGER
|
||||
STATIC c AS INTEGER
|
||||
c = c + 1
|
||||
Counter = c
|
||||
END FUNCTION
|
||||
|
||||
PRINT "Counter: "; Counter()
|
||||
PRINT "Counter: "; Counter()
|
||||
PRINT "Counter: "; Counter()
|
||||
|
||||
' --- Nested IF ---
|
||||
DIM score AS INTEGER
|
||||
score = 85
|
||||
IF score >= 90 THEN
|
||||
PRINT "Grade: A"
|
||||
ELSEIF score >= 80 THEN
|
||||
PRINT "Grade: B"
|
||||
ELSEIF score >= 70 THEN
|
||||
PRINT "Grade: C"
|
||||
ELSE
|
||||
PRINT "Grade: F"
|
||||
END IF
|
||||
|
||||
' --- Boolean expressions ---
|
||||
IF x > 5 AND y < 30 THEN
|
||||
PRINT "x>5 AND y<30 is TRUE"
|
||||
END IF
|
||||
|
||||
IF x > 100 OR y = 20 THEN
|
||||
PRINT "x>100 OR y=20 is TRUE"
|
||||
END IF
|
||||
|
||||
IF NOT (x = 5) THEN
|
||||
PRINT "NOT (x=5) is TRUE"
|
||||
END IF
|
||||
|
||||
PRINT "Done!"
|
||||
1049
test_big.bas
Normal file
1049
test_big.bas
Normal file
File diff suppressed because it is too large
Load diff
17
test_classic.bas
Normal file
17
test_classic.bas
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
' Classic BASIC with line numbers
|
||||
' Tests GOTO, GOSUB/RETURN, and line-numbered code
|
||||
|
||||
10 DIM x AS INTEGER
|
||||
20 x = 1
|
||||
30 PRINT "Start"
|
||||
40 GOSUB 100
|
||||
50 PRINT "Back from first gosub, x="; x
|
||||
60 GOSUB 100
|
||||
70 PRINT "Back from second gosub, x="; x
|
||||
80 GOTO 200
|
||||
90 PRINT "This should not print"
|
||||
100 x = x + 10
|
||||
110 PRINT "In subroutine, x="; x
|
||||
120 RETURN
|
||||
200 PRINT "After GOTO, x="; x
|
||||
210 PRINT "Classic BASIC done!"
|
||||
46
test_continue.bas
Normal file
46
test_continue.bas
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
' Test CONTINUE statement
|
||||
PRINT "==== CONTINUE Test ===="
|
||||
|
||||
' CONTINUE FOR — skip even numbers
|
||||
PRINT "Odd numbers 1-10:"
|
||||
DIM i AS INTEGER
|
||||
FOR i = 1 TO 10
|
||||
IF i MOD 2 = 0 THEN CONTINUE FOR
|
||||
PRINT i; " ";
|
||||
NEXT i
|
||||
PRINT ""
|
||||
|
||||
' CONTINUE WHILE — skip multiples of 3
|
||||
PRINT "Non-multiples of 3 (1-12):"
|
||||
DIM w AS INTEGER
|
||||
w = 0
|
||||
WHILE w < 12
|
||||
w = w + 1
|
||||
IF w MOD 3 = 0 THEN CONTINUE WHILE
|
||||
PRINT w; " ";
|
||||
WEND
|
||||
PRINT ""
|
||||
|
||||
' CONTINUE DO — skip value 5
|
||||
PRINT "1-8 without 5:"
|
||||
DIM d AS INTEGER
|
||||
d = 0
|
||||
DO
|
||||
d = d + 1
|
||||
IF d = 5 THEN CONTINUE DO
|
||||
PRINT d; " ";
|
||||
LOOP UNTIL d >= 8
|
||||
PRINT ""
|
||||
|
||||
' CONTINUE DO with WHILE form
|
||||
PRINT "DO WHILE skip 3:"
|
||||
DIM e AS INTEGER
|
||||
e = 0
|
||||
DO WHILE e < 6
|
||||
e = e + 1
|
||||
IF e = 3 THEN CONTINUE DO
|
||||
PRINT e; " ";
|
||||
LOOP
|
||||
PRINT ""
|
||||
|
||||
PRINT "CONTINUE test complete!"
|
||||
173
test_data.bas
Normal file
173
test_data.bas
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
' ============================================================
|
||||
' Test program for DATA/READ/RESTORE statements
|
||||
' All DATA items form a single global pool, read sequentially.
|
||||
' ============================================================
|
||||
|
||||
' All DATA statements (pool order matters)
|
||||
DATA 10, 20, 30
|
||||
DATA "Hello", "World", "BASIC"
|
||||
DATA 42, "Alice", 3.14, "Bob", 100
|
||||
DATA -5, -10, -3.14
|
||||
DATA 1.5, 2.7, 3.9, 4.1
|
||||
|
||||
PRINT "==== DATA/READ/RESTORE Tests ===="
|
||||
|
||||
' ---- Test 1: Simple integer DATA/READ ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 1: Integer DATA/READ ----"
|
||||
DIM a AS INTEGER
|
||||
DIM b AS INTEGER
|
||||
DIM c AS INTEGER
|
||||
READ a, b, c
|
||||
PRINT "Read: "; a; " "; b; " "; c
|
||||
|
||||
' ---- Test 2: String DATA/READ ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 2: String DATA/READ ----"
|
||||
DIM s1$ AS STRING
|
||||
DIM s2$ AS STRING
|
||||
DIM s3$ AS STRING
|
||||
READ s1$, s2$, s3$
|
||||
PRINT "Read: "; s1$; " "; s2$; " "; s3$
|
||||
|
||||
' ---- Test 3: Mixed types ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 3: Mixed types ----"
|
||||
DIM n AS INTEGER
|
||||
DIM name$ AS STRING
|
||||
DIM pi AS DOUBLE
|
||||
DIM name2$ AS STRING
|
||||
DIM m AS INTEGER
|
||||
READ n, name$, pi, name2$, m
|
||||
PRINT "Int: "; n
|
||||
PRINT "Str: "; name$
|
||||
PRINT "Dbl: "; pi
|
||||
PRINT "Str: "; name2$
|
||||
PRINT "Int: "; m
|
||||
|
||||
' ---- Test 4: Negative numbers ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 4: Negative numbers ----"
|
||||
DIM neg1 AS INTEGER
|
||||
DIM neg2 AS INTEGER
|
||||
DIM neg3 AS DOUBLE
|
||||
READ neg1, neg2, neg3
|
||||
PRINT "Negatives: "; neg1; " "; neg2; " "; neg3
|
||||
|
||||
' ---- Test 5: Double values ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 5: Double values ----"
|
||||
DIM f1 AS DOUBLE
|
||||
DIM f2 AS DOUBLE
|
||||
DIM f3 AS DOUBLE
|
||||
DIM f4 AS DOUBLE
|
||||
READ f1, f2, f3, f4
|
||||
PRINT "Doubles: "; f1; " "; f2; " "; f3; " "; f4
|
||||
|
||||
' ---- Test 6: RESTORE (reset to beginning) ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 6: RESTORE ----"
|
||||
RESTORE
|
||||
DIM ra AS INTEGER
|
||||
DIM rb AS INTEGER
|
||||
DIM rc AS INTEGER
|
||||
READ ra, rb, rc
|
||||
PRINT "After RESTORE: "; ra; " "; rb; " "; rc
|
||||
|
||||
' ---- Test 7: READ in a loop ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 7: READ in loop ----"
|
||||
RESTORE
|
||||
DIM val AS INTEGER
|
||||
DIM i AS INTEGER
|
||||
PRINT "First 3 via loop: ";
|
||||
FOR i = 1 TO 3
|
||||
READ val
|
||||
PRINT val; " ";
|
||||
NEXT i
|
||||
PRINT ""
|
||||
|
||||
' ---- Test 8: RESTORE then skip with multiple READs ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 8: Skip and read ----"
|
||||
RESTORE
|
||||
' Skip first 3 integers
|
||||
DIM skip AS INTEGER
|
||||
READ skip, skip, skip
|
||||
' Now read the 3 strings
|
||||
DIM rs1$ AS STRING
|
||||
DIM rs2$ AS STRING
|
||||
DIM rs3$ AS STRING
|
||||
READ rs1$, rs2$, rs3$
|
||||
PRINT "Skipped to strings: "; rs1$; " "; rs2$; " "; rs3$
|
||||
|
||||
' ---- Test 9: RESTORE with line number ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 9: RESTORE with line number ----"
|
||||
9000 DATA 111, 222, 333
|
||||
9010 DATA 444, 555, 666
|
||||
RESTORE 9010
|
||||
DIM r1 AS INTEGER
|
||||
DIM r2 AS INTEGER
|
||||
DIM r3 AS INTEGER
|
||||
READ r1, r2, r3
|
||||
PRINT "RESTORE 9010: "; r1; " "; r2; " "; r3
|
||||
|
||||
RESTORE 9000
|
||||
READ r1, r2, r3
|
||||
PRINT "RESTORE 9000: "; r1; " "; r2; " "; r3
|
||||
|
||||
' ---- Test 10: Typed variables ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 10: Typed variables ----"
|
||||
DATA 200, 1000, 100000, 1.5
|
||||
RESTORE
|
||||
' Skip to the typed-variable DATA at the end.
|
||||
' The pool has: 10,20,30, Hello,World,BASIC, 42,Alice,3.14,Bob,100,
|
||||
' -5,-10,-3.14, 1.5,2.7,3.9,4.1, 111,222,333, 444,555,666, 200,1000,100000,1.5
|
||||
' Items 23-26 are 200,1000,100000,1.5
|
||||
' Use a known restore point instead: just re-read after RESTORE
|
||||
' Actually, let's just read in order after a targeted DATA
|
||||
9020 DATA 255, 32000, 100000, 2.5
|
||||
RESTORE 9020
|
||||
DIM bval AS BYTE
|
||||
DIM ival AS INTEGER
|
||||
DIM lval AS LONG
|
||||
DIM fval AS FLOAT
|
||||
READ bval, ival, lval, fval
|
||||
PRINT "BYTE: "; bval
|
||||
PRINT "INTEGER: "; ival
|
||||
PRINT "LONG: "; lval
|
||||
PRINT "FLOAT: "; fval
|
||||
|
||||
' ---- Test 11: Re-read same data ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 11: Re-read same data ----"
|
||||
RESTORE 9020
|
||||
READ bval, ival, lval, fval
|
||||
PRINT "Re-read BYTE: "; bval
|
||||
PRINT "Re-read INTEGER: "; ival
|
||||
|
||||
' ---- Test 12: RESTORE with named label ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 12: RESTORE with named label ----"
|
||||
myData: DATA 777, 888, 999
|
||||
moreData: DATA 50, 60, 70
|
||||
RESTORE myData
|
||||
DIM nd1 AS INTEGER
|
||||
DIM nd2 AS INTEGER
|
||||
DIM nd3 AS INTEGER
|
||||
READ nd1, nd2, nd3
|
||||
PRINT "RESTORE myData: "; nd1; " "; nd2; " "; nd3
|
||||
|
||||
RESTORE moreData
|
||||
READ nd1, nd2, nd3
|
||||
PRINT "RESTORE moreData: "; nd1; " "; nd2; " "; nd3
|
||||
|
||||
' Re-read from named label again
|
||||
RESTORE myData
|
||||
READ nd1
|
||||
PRINT "Re-read first from myData: "; nd1
|
||||
|
||||
PRINT ""
|
||||
PRINT "==== ALL DATA/READ/RESTORE TESTS COMPLETE ===="
|
||||
190
test_fileio.bas
Normal file
190
test_fileio.bas
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
' ============================================================
|
||||
' Test program for File I/O features
|
||||
' OPEN, CLOSE, PRINT #, INPUT #, LINE INPUT #, WRITE #,
|
||||
' EOF(), LOF(), FREEFILE()
|
||||
' ============================================================
|
||||
|
||||
PRINT "==== File I/O Tests ===="
|
||||
|
||||
' ---- Test 1: Basic OPEN/PRINT #/CLOSE (write a file) ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 1: Write file with PRINT # ----"
|
||||
OPEN "/tmp/basic_test1.txt" FOR OUTPUT AS #1
|
||||
PRINT #1, "Hello, File World!"
|
||||
PRINT #1, "Line two"
|
||||
PRINT #1, "Line three"
|
||||
CLOSE #1
|
||||
PRINT "Wrote 3 lines to /tmp/basic_test1.txt"
|
||||
|
||||
' ---- Test 2: Read file back with LINE INPUT # ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 2: Read file with LINE INPUT # ----"
|
||||
DIM line$ AS STRING
|
||||
OPEN "/tmp/basic_test1.txt" FOR INPUT AS #1
|
||||
DIM lineCount AS INTEGER
|
||||
lineCount = 0
|
||||
DO WHILE NOT EOF(1)
|
||||
LINE INPUT #1, line$
|
||||
lineCount = lineCount + 1
|
||||
PRINT "Line "; lineCount; ": "; line$
|
||||
LOOP
|
||||
CLOSE #1
|
||||
PRINT "Read "; lineCount; " lines"
|
||||
|
||||
' ---- Test 3: FREEFILE ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 3: FREEFILE ----"
|
||||
DIM f AS INTEGER
|
||||
f = FREEFILE()
|
||||
PRINT "First free file number: "; f
|
||||
OPEN "/tmp/basic_test2.txt" FOR OUTPUT AS #f
|
||||
PRINT #f, "Written using FREEFILE"
|
||||
CLOSE #f
|
||||
PRINT "Wrote using file #"; f
|
||||
|
||||
' Read it back to verify
|
||||
OPEN "/tmp/basic_test2.txt" FOR INPUT AS #1
|
||||
LINE INPUT #1, line$
|
||||
CLOSE #1
|
||||
PRINT "Read back: "; line$
|
||||
|
||||
' ---- Test 4: Numeric data with PRINT # and INPUT # ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 4: Numeric I/O ----"
|
||||
DIM x AS DOUBLE
|
||||
DIM y AS DOUBLE
|
||||
DIM n AS INTEGER
|
||||
x = 3.14159
|
||||
y = 2.71828
|
||||
n = 42
|
||||
|
||||
OPEN "/tmp/basic_test3.txt" FOR OUTPUT AS #1
|
||||
PRINT #1, x
|
||||
PRINT #1, y
|
||||
PRINT #1, n
|
||||
CLOSE #1
|
||||
|
||||
DIM rx AS DOUBLE
|
||||
DIM ry AS DOUBLE
|
||||
DIM rn AS INTEGER
|
||||
OPEN "/tmp/basic_test3.txt" FOR INPUT AS #1
|
||||
INPUT #1, rx
|
||||
INPUT #1, ry
|
||||
INPUT #1, rn
|
||||
CLOSE #1
|
||||
PRINT "Read x = "; rx
|
||||
PRINT "Read y = "; ry
|
||||
PRINT "Read n = "; rn
|
||||
|
||||
' ---- Test 5: WRITE # (CSV-style output) ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 5: WRITE # (CSV) ----"
|
||||
DIM name$ AS STRING
|
||||
DIM age AS INTEGER
|
||||
DIM score AS DOUBLE
|
||||
name$ = "Alice"
|
||||
age = 30
|
||||
score = 95.5
|
||||
|
||||
OPEN "/tmp/basic_test4.csv" FOR OUTPUT AS #1
|
||||
WRITE #1, name$, age, score
|
||||
name$ = "Bob"
|
||||
age = 25
|
||||
score = 88.3
|
||||
WRITE #1, name$, age, score
|
||||
CLOSE #1
|
||||
|
||||
' Read back and display
|
||||
OPEN "/tmp/basic_test4.csv" FOR INPUT AS #1
|
||||
DO WHILE NOT EOF(1)
|
||||
LINE INPUT #1, line$
|
||||
PRINT "CSV: "; line$
|
||||
LOOP
|
||||
CLOSE #1
|
||||
|
||||
' ---- Test 6: LOF (file length) ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 6: LOF ----"
|
||||
OPEN "/tmp/basic_test1.txt" FOR INPUT AS #1
|
||||
DIM fsize AS LONG
|
||||
fsize = LOF(1)
|
||||
PRINT "File size of test1.txt: "; fsize; " bytes"
|
||||
CLOSE #1
|
||||
|
||||
' ---- Test 7: APPEND mode ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 7: APPEND mode ----"
|
||||
OPEN "/tmp/basic_test1.txt" FOR APPEND AS #1
|
||||
PRINT #1, "Appended line four"
|
||||
PRINT #1, "Appended line five"
|
||||
CLOSE #1
|
||||
|
||||
' Read all lines back
|
||||
OPEN "/tmp/basic_test1.txt" FOR INPUT AS #1
|
||||
lineCount = 0
|
||||
DO WHILE NOT EOF(1)
|
||||
LINE INPUT #1, line$
|
||||
lineCount = lineCount + 1
|
||||
PRINT "Line "; lineCount; ": "; line$
|
||||
LOOP
|
||||
CLOSE #1
|
||||
PRINT "Total lines after append: "; lineCount
|
||||
|
||||
' ---- Test 8: PRINT # with semicolons (no newline) ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 8: PRINT # with separators ----"
|
||||
OPEN "/tmp/basic_test5.txt" FOR OUTPUT AS #1
|
||||
PRINT #1, "A"; "B"; "C"
|
||||
PRINT #1, 10; " "; 20; " "; 30
|
||||
CLOSE #1
|
||||
|
||||
OPEN "/tmp/basic_test5.txt" FOR INPUT AS #1
|
||||
DO WHILE NOT EOF(1)
|
||||
LINE INPUT #1, line$
|
||||
PRINT " "; line$
|
||||
LOOP
|
||||
CLOSE #1
|
||||
|
||||
' ---- Test 9: Multiple files open simultaneously ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 9: Multiple files ----"
|
||||
OPEN "/tmp/basic_multi1.txt" FOR OUTPUT AS #1
|
||||
OPEN "/tmp/basic_multi2.txt" FOR OUTPUT AS #2
|
||||
PRINT #1, "File one content"
|
||||
PRINT #2, "File two content"
|
||||
CLOSE #2
|
||||
CLOSE #1
|
||||
|
||||
OPEN "/tmp/basic_multi1.txt" FOR INPUT AS #1
|
||||
LINE INPUT #1, line$
|
||||
PRINT "File 1: "; line$
|
||||
CLOSE #1
|
||||
|
||||
OPEN "/tmp/basic_multi2.txt" FOR INPUT AS #2
|
||||
LINE INPUT #2, line$
|
||||
PRINT "File 2: "; line$
|
||||
CLOSE #2
|
||||
|
||||
' ---- Test 10: File I/O in a FUNCTION ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 10: File I/O in FUNCTION ----"
|
||||
|
||||
FUNCTION CountLines(BYVAL fname AS STRING) AS INTEGER
|
||||
LOCAL count AS INTEGER
|
||||
LOCAL buf$ AS STRING
|
||||
count = 0
|
||||
OPEN fname FOR INPUT AS #3
|
||||
DO WHILE NOT EOF(3)
|
||||
LINE INPUT #3, buf$
|
||||
count = count + 1
|
||||
LOOP
|
||||
CLOSE #3
|
||||
CountLines = count
|
||||
END FUNCTION
|
||||
|
||||
DIM cnt AS INTEGER
|
||||
cnt = CountLines("/tmp/basic_test1.txt")
|
||||
PRINT "CountLines result: "; cnt
|
||||
|
||||
PRINT ""
|
||||
PRINT "==== ALL FILE I/O TESTS COMPLETE ===="
|
||||
6
test_inc_b.bas
Normal file
6
test_inc_b.bas
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
' Level B - includes C
|
||||
'$INCLUDE: 'test_inc_c.bas'
|
||||
|
||||
SUB FromB()
|
||||
PRINT "Hello from file B (depth 2)"
|
||||
END SUB
|
||||
6
test_inc_c.bas
Normal file
6
test_inc_c.bas
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
' Level C - deepest include
|
||||
CONST DEPTH_C = 3
|
||||
|
||||
SUB FromC()
|
||||
PRINT "Hello from file C (depth 3)"
|
||||
END SUB
|
||||
23
test_include.bas
Normal file
23
test_include.bas
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
' Test $INCLUDE metacommand
|
||||
PRINT "==== $INCLUDE Test ===="
|
||||
|
||||
' Include a library file
|
||||
'$INCLUDE: 'test_include_lib.bas'
|
||||
|
||||
PRINT "Library version: "; LIB_VERSION
|
||||
|
||||
' Call functions from included file
|
||||
LibGreet "World"
|
||||
PRINT "3 + 4 = "; LibAdd(3, 4)
|
||||
|
||||
' Include a file that itself has no further includes
|
||||
'$INCLUDE: 'test_include_nested.bas'
|
||||
NestedGreet
|
||||
|
||||
' Test nested include chain: this includes B which includes C
|
||||
'$INCLUDE: 'test_inc_b.bas'
|
||||
FromB
|
||||
FromC
|
||||
PRINT "Depth C constant: "; DEPTH_C
|
||||
|
||||
PRINT "Include test complete!"
|
||||
10
test_include_lib.bas
Normal file
10
test_include_lib.bas
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
' Library file for $INCLUDE testing
|
||||
CONST LIB_VERSION = 1
|
||||
|
||||
SUB LibGreet(name AS STRING)
|
||||
PRINT "Hello from library, "; name; "!"
|
||||
END SUB
|
||||
|
||||
FUNCTION LibAdd(a AS DOUBLE, b AS DOUBLE) AS DOUBLE
|
||||
LibAdd = a + b
|
||||
END FUNCTION
|
||||
4
test_include_nested.bas
Normal file
4
test_include_nested.bas
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
' Nested include file (no further includes)
|
||||
SUB NestedGreet()
|
||||
PRINT "Hello from nested include!"
|
||||
END SUB
|
||||
93
test_labels.bas
Normal file
93
test_labels.bas
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
' ============================================================
|
||||
' Test program for named labels with GOTO and GOSUB
|
||||
' ============================================================
|
||||
|
||||
PRINT "==== Named Label Tests ===="
|
||||
|
||||
' ---- Test 1: Simple GOTO with named label ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 1: GOTO named label ----"
|
||||
GOTO skipSection
|
||||
PRINT "ERROR: This should be skipped"
|
||||
skipSection:
|
||||
PRINT "Jumped to skipSection"
|
||||
|
||||
' ---- Test 2: GOSUB with named label ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 2: GOSUB named label ----"
|
||||
GOSUB mySubroutine
|
||||
PRINT "Returned from GOSUB"
|
||||
GOTO afterSub
|
||||
|
||||
mySubroutine:
|
||||
PRINT "Inside mySubroutine"
|
||||
RETURN
|
||||
|
||||
afterSub:
|
||||
|
||||
' ---- Test 3: Labels with underscores ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 3: Labels with underscores ----"
|
||||
GOTO my_label_here
|
||||
PRINT "ERROR: should not print"
|
||||
my_label_here:
|
||||
PRINT "Reached my_label_here"
|
||||
|
||||
' ---- Test 4: Mixed numeric and named labels ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 4: Mixed numeric and named ----"
|
||||
GOTO 100
|
||||
PRINT "ERROR: should not print"
|
||||
100 PRINT "At line 100"
|
||||
GOTO namedAfter100
|
||||
PRINT "ERROR: should not print"
|
||||
namedAfter100:
|
||||
PRINT "At namedAfter100"
|
||||
|
||||
' ---- Test 5: Label with statement on same line ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 5: Label with trailing statement ----"
|
||||
GOTO inlineLabel
|
||||
PRINT "ERROR: should not print"
|
||||
inlineLabel: PRINT "Statement on same line as label"
|
||||
|
||||
' ---- Test 6: GOSUB to named label, then GOTO past RETURN ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 6: GOSUB named + flow control ----"
|
||||
DIM count AS INTEGER
|
||||
count = 0
|
||||
loopStart:
|
||||
count = count + 1
|
||||
IF count <= 3 THEN
|
||||
GOSUB doWork
|
||||
GOTO loopStart
|
||||
END IF
|
||||
PRINT "Loop done, count = "; count
|
||||
GOTO afterWork
|
||||
|
||||
doWork:
|
||||
PRINT " Working, count = "; count
|
||||
RETURN
|
||||
|
||||
afterWork:
|
||||
|
||||
' ---- Test 7: Multiple GOSUBs to different named labels ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 7: Multiple named GOSUBs ----"
|
||||
GOSUB subA
|
||||
GOSUB subB
|
||||
GOSUB subA
|
||||
GOTO afterSubs
|
||||
|
||||
subA:
|
||||
PRINT "In subA"
|
||||
RETURN
|
||||
|
||||
subB:
|
||||
PRINT "In subB"
|
||||
RETURN
|
||||
|
||||
afterSubs:
|
||||
|
||||
PRINT ""
|
||||
PRINT "==== ALL NAMED LABEL TESTS COMPLETE ===="
|
||||
176
test_multidim.bas
Normal file
176
test_multidim.bas
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
' ============================================================
|
||||
' Test program for multidimensional arrays
|
||||
' ============================================================
|
||||
|
||||
PRINT "==== Multidimensional Array Tests ===="
|
||||
|
||||
' ---- Test 1: 2D integer array ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 1: 2D integer array ----"
|
||||
DIM matrix(3, 4) AS INTEGER
|
||||
DIM i AS INTEGER
|
||||
DIM j AS INTEGER
|
||||
|
||||
' Fill with i*10 + j
|
||||
FOR i = 0 TO 3
|
||||
FOR j = 0 TO 4
|
||||
matrix(i, j) = i * 10 + j
|
||||
NEXT j
|
||||
NEXT i
|
||||
|
||||
' Read back specific elements
|
||||
PRINT "matrix(0,0) = "; matrix(0, 0)
|
||||
PRINT "matrix(1,2) = "; matrix(1, 2)
|
||||
PRINT "matrix(3,4) = "; matrix(3, 4)
|
||||
PRINT "matrix(2,3) = "; matrix(2, 3)
|
||||
|
||||
' ---- Test 2: 2D string array ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 2: 2D string array ----"
|
||||
DIM grid$(2, 2) AS STRING
|
||||
grid$(0, 0) = "NW"
|
||||
grid$(0, 1) = "N"
|
||||
grid$(0, 2) = "NE"
|
||||
grid$(1, 0) = "W"
|
||||
grid$(1, 1) = "C"
|
||||
grid$(1, 2) = "E"
|
||||
grid$(2, 0) = "SW"
|
||||
grid$(2, 1) = "S"
|
||||
grid$(2, 2) = "SE"
|
||||
|
||||
FOR i = 0 TO 2
|
||||
FOR j = 0 TO 2
|
||||
PRINT grid$(i, j); " ";
|
||||
NEXT j
|
||||
PRINT ""
|
||||
NEXT i
|
||||
|
||||
' ---- Test 3: 3D array ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 3: 3D array ----"
|
||||
DIM cube(2, 3, 4) AS INTEGER
|
||||
DIM k AS INTEGER
|
||||
|
||||
' Fill with i*100 + j*10 + k
|
||||
FOR i = 0 TO 2
|
||||
FOR j = 0 TO 3
|
||||
FOR k = 0 TO 4
|
||||
cube(i, j, k) = i * 100 + j * 10 + k
|
||||
NEXT k
|
||||
NEXT j
|
||||
NEXT i
|
||||
|
||||
PRINT "cube(0,0,0) = "; cube(0, 0, 0)
|
||||
PRINT "cube(1,2,3) = "; cube(1, 2, 3)
|
||||
PRINT "cube(2,3,4) = "; cube(2, 3, 4)
|
||||
PRINT "cube(2,0,1) = "; cube(2, 0, 1)
|
||||
|
||||
' ---- Test 4: 2D with expressions ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 4: 2D with expressions ----"
|
||||
DIM r AS INTEGER
|
||||
DIM c AS INTEGER
|
||||
r = 5
|
||||
c = 3
|
||||
DIM table(r, c) AS DOUBLE
|
||||
table(1, 1) = 3.14
|
||||
table(4, 2) = 2.71
|
||||
table(r, c) = 9.99
|
||||
PRINT "table(1,1) = "; table(1, 1)
|
||||
PRINT "table(4,2) = "; table(4, 2)
|
||||
PRINT "table(5,3) = "; table(r, c)
|
||||
|
||||
' ---- Test 5: Loop over 2D array ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 5: Loop over 2D ----"
|
||||
DIM small(2, 3) AS INTEGER
|
||||
DIM sum AS INTEGER
|
||||
sum = 0
|
||||
FOR i = 0 TO 2
|
||||
FOR j = 0 TO 3
|
||||
small(i, j) = (i + 1) * (j + 1)
|
||||
sum = sum + small(i, j)
|
||||
NEXT j
|
||||
NEXT i
|
||||
PRINT "Sum of multiplication table: "; sum
|
||||
|
||||
' Print the table
|
||||
FOR i = 0 TO 2
|
||||
FOR j = 0 TO 3
|
||||
PRINT small(i, j); " ";
|
||||
NEXT j
|
||||
PRINT ""
|
||||
NEXT i
|
||||
|
||||
' ---- Test 6: REDIM 2D ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 6: REDIM 2D ----"
|
||||
DIM resizable(2, 2) AS INTEGER
|
||||
resizable(0, 0) = 1
|
||||
resizable(1, 1) = 5
|
||||
resizable(2, 2) = 9
|
||||
PRINT "Before REDIM: "; resizable(1, 1)
|
||||
REDIM resizable(4, 4) AS INTEGER
|
||||
PRINT "After REDIM (0,0): "; resizable(0, 0)
|
||||
resizable(3, 3) = 42
|
||||
PRINT "After REDIM (3,3): "; resizable(3, 3)
|
||||
|
||||
' ---- Test 7: Mixed 1D and 2D ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 7: Mixed 1D and 2D ----"
|
||||
DIM arr1d(5) AS INTEGER
|
||||
DIM arr2d(3, 3) AS INTEGER
|
||||
FOR i = 0 TO 5
|
||||
arr1d(i) = i * 2
|
||||
NEXT i
|
||||
FOR i = 0 TO 3
|
||||
FOR j = 0 TO 3
|
||||
arr2d(i, j) = arr1d(i) + arr1d(j)
|
||||
NEXT j
|
||||
NEXT i
|
||||
PRINT "arr1d(3) = "; arr1d(3)
|
||||
PRINT "arr2d(2,3) = "; arr2d(2, 3)
|
||||
PRINT "arr2d(1,2) = "; arr2d(1, 2)
|
||||
|
||||
' ---- Test 8: 2D array in SUB (BYREF) ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 8: 2D array element BYREF ----"
|
||||
DIM val AS INTEGER
|
||||
val = 100
|
||||
DIM testArr(3, 3) AS INTEGER
|
||||
testArr(1, 2) = 50
|
||||
GOSUB addTen
|
||||
PRINT "val after GOSUB: "; val
|
||||
GOTO afterAddTen
|
||||
|
||||
addTen:
|
||||
val = val + 10
|
||||
RETURN
|
||||
|
||||
afterAddTen:
|
||||
|
||||
' ---- Test 9: 2D DOUBLE array ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 9: 2D DOUBLE array ----"
|
||||
DIM dbl2d(2, 2) AS DOUBLE
|
||||
dbl2d(0, 0) = 1.1
|
||||
dbl2d(0, 1) = 2.2
|
||||
dbl2d(0, 2) = 3.3
|
||||
dbl2d(1, 0) = 4.4
|
||||
dbl2d(1, 1) = 5.5
|
||||
dbl2d(1, 2) = 6.6
|
||||
dbl2d(2, 0) = 7.7
|
||||
dbl2d(2, 1) = 8.8
|
||||
dbl2d(2, 2) = 9.9
|
||||
|
||||
DIM dsum AS DOUBLE
|
||||
dsum = 0
|
||||
FOR i = 0 TO 2
|
||||
FOR j = 0 TO 2
|
||||
dsum = dsum + dbl2d(i, j)
|
||||
NEXT j
|
||||
NEXT i
|
||||
PRINT "Sum of doubles: "; dsum
|
||||
|
||||
PRINT ""
|
||||
PRINT "==== ALL MULTIDIMENSIONAL TESTS COMPLETE ===="
|
||||
192
test_newfeatures.bas
Normal file
192
test_newfeatures.bas
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
' Test file for all new features
|
||||
' ============================================================
|
||||
|
||||
' === CONST ===
|
||||
PRINT "==== CONST ===="
|
||||
CONST PI = 3.14159
|
||||
CONST MAX_SIZE = 100
|
||||
CONST GREETING$ = "Hello"
|
||||
PRINT "PI = "; PI
|
||||
PRINT "MAX_SIZE = "; MAX_SIZE
|
||||
PRINT "GREETING$ = "; GREETING$
|
||||
|
||||
' === New Math Functions ===
|
||||
PRINT ""
|
||||
PRINT "==== Math Functions ===="
|
||||
PRINT "TAN(0.7854) = "; TAN(0.7854)
|
||||
PRINT "ATN(1) = "; ATN(1)
|
||||
PRINT "LOG(2.71828) = "; LOG(2.71828)
|
||||
PRINT "EXP(1) = "; EXP(1)
|
||||
PRINT "SGN(-5) = "; SGN(-5)
|
||||
PRINT "SGN(0) = "; SGN(0)
|
||||
PRINT "SGN(42) = "; SGN(42)
|
||||
|
||||
' === RND / RANDOMIZE ===
|
||||
PRINT ""
|
||||
PRINT "==== RND / RANDOMIZE ===="
|
||||
RANDOMIZE 12345
|
||||
DIM r AS DOUBLE
|
||||
r = RND
|
||||
PRINT "RND (with seed 12345): "; r
|
||||
r = RND()
|
||||
PRINT "RND() again: "; r
|
||||
RANDOMIZE
|
||||
PRINT "RANDOMIZE (time-based) done"
|
||||
|
||||
' === New String Functions ===
|
||||
PRINT ""
|
||||
PRINT "==== String Functions ===="
|
||||
PRINT "LTRIM$(' hello') = '"; LTRIM$(" hello"); "'"
|
||||
PRINT "RTRIM$('hello ') = '"; RTRIM$("hello "); "'"
|
||||
PRINT "TRIM$(' hello ') = '"; TRIM$(" hello "); "'"
|
||||
PRINT "SPACE$(5) = '"; SPACE$(5); "'"
|
||||
PRINT "HEX$(255) = "; HEX$(255)
|
||||
PRINT "HEX$(16) = "; HEX$(16)
|
||||
PRINT "OCT$(8) = "; OCT$(8)
|
||||
PRINT "OCT$(255) = "; OCT$(255)
|
||||
PRINT "STRING$(5, '*') = "; STRING$(5, "*")
|
||||
PRINT "STRING$(3, 'AB') = "; STRING$(3, "AB")
|
||||
|
||||
' === SWAP ===
|
||||
PRINT ""
|
||||
PRINT "==== SWAP ===="
|
||||
DIM a AS INTEGER
|
||||
DIM b AS INTEGER
|
||||
a = 10
|
||||
b = 20
|
||||
PRINT "Before: a="; a; " b="; b
|
||||
SWAP a, b
|
||||
PRINT "After: a="; a; " b="; b
|
||||
|
||||
DIM s1 AS STRING
|
||||
DIM s2 AS STRING
|
||||
s1 = "first"
|
||||
s2 = "second"
|
||||
PRINT "Before: s1="; s1; " s2="; s2
|
||||
SWAP s1, s2
|
||||
PRINT "After: s1="; s1; " s2="; s2
|
||||
|
||||
' === LBOUND / UBOUND ===
|
||||
PRINT ""
|
||||
PRINT "==== LBOUND / UBOUND ===="
|
||||
DIM arr(10) AS INTEGER
|
||||
PRINT "LBOUND(arr) = "; LBOUND(arr)
|
||||
PRINT "UBOUND(arr) = "; UBOUND(arr)
|
||||
|
||||
' === Bitwise Operators ===
|
||||
PRINT ""
|
||||
PRINT "==== Bitwise Operators ===="
|
||||
DIM x AS INTEGER
|
||||
DIM y AS INTEGER
|
||||
x = 15
|
||||
y = 9
|
||||
PRINT "15 AND 9 = "; x AND y
|
||||
PRINT "15 OR 9 = "; x OR y
|
||||
PRINT "15 XOR 9 = "; x XOR y
|
||||
PRINT "NOT 0 = "; NOT 0
|
||||
PRINT "NOT -1 = "; NOT -1
|
||||
|
||||
' Logical-style usage (with comparisons)
|
||||
DIM c AS INTEGER
|
||||
c = 5
|
||||
IF c > 3 AND c < 10 THEN
|
||||
PRINT "5 is between 3 and 10"
|
||||
END IF
|
||||
IF c > 10 OR c < 6 THEN
|
||||
PRINT "5 > 10 OR 5 < 6 is TRUE"
|
||||
END IF
|
||||
IF NOT (c = 10) THEN
|
||||
PRINT "NOT (5 = 10) is TRUE"
|
||||
END IF
|
||||
|
||||
' === SELECT CASE ===
|
||||
PRINT ""
|
||||
PRINT "==== SELECT CASE ===="
|
||||
|
||||
DIM grade AS INTEGER
|
||||
grade = 85
|
||||
SELECT CASE grade
|
||||
CASE 90 TO 100
|
||||
PRINT "Grade A"
|
||||
CASE 80 TO 89
|
||||
PRINT "Grade B"
|
||||
CASE 70 TO 79
|
||||
PRINT "Grade C"
|
||||
CASE ELSE
|
||||
PRINT "Grade F"
|
||||
END SELECT
|
||||
|
||||
DIM val AS INTEGER
|
||||
val = 3
|
||||
SELECT CASE val
|
||||
CASE 1
|
||||
PRINT "One"
|
||||
CASE 2, 3
|
||||
PRINT "Two or Three"
|
||||
CASE IS > 5
|
||||
PRINT "Greater than 5"
|
||||
CASE ELSE
|
||||
PRINT "Something else"
|
||||
END SELECT
|
||||
|
||||
' String SELECT CASE
|
||||
DIM color AS STRING
|
||||
color = "red"
|
||||
SELECT CASE color
|
||||
CASE "red"
|
||||
PRINT "Color is RED"
|
||||
CASE "blue"
|
||||
PRINT "Color is BLUE"
|
||||
CASE ELSE
|
||||
PRINT "Unknown color"
|
||||
END SELECT
|
||||
|
||||
' === ON GOTO ===
|
||||
PRINT ""
|
||||
PRINT "==== ON GOTO ===="
|
||||
DIM choice AS INTEGER
|
||||
choice = 2
|
||||
ON choice GOTO opt1, opt2, opt3
|
||||
GOTO skipOpts
|
||||
opt1:
|
||||
PRINT "Option 1"
|
||||
GOTO skipOpts
|
||||
opt2:
|
||||
PRINT "Option 2"
|
||||
GOTO skipOpts
|
||||
opt3:
|
||||
PRINT "Option 3"
|
||||
skipOpts:
|
||||
|
||||
' === ON GOSUB ===
|
||||
PRINT ""
|
||||
PRINT "==== ON GOSUB ===="
|
||||
choice = 1
|
||||
ON choice GOSUB sub1, sub2, sub3
|
||||
choice = 3
|
||||
ON choice GOSUB sub1, sub2, sub3
|
||||
GOTO skipSubs
|
||||
sub1:
|
||||
PRINT "Subroutine 1"
|
||||
RETURN
|
||||
sub2:
|
||||
PRINT "Subroutine 2"
|
||||
RETURN
|
||||
sub3:
|
||||
PRINT "Subroutine 3"
|
||||
RETURN
|
||||
skipSubs:
|
||||
|
||||
' === MID$ Assignment ===
|
||||
PRINT ""
|
||||
PRINT "==== MID$ Assignment ===="
|
||||
DIM msg AS STRING
|
||||
msg = "Hello World"
|
||||
PRINT "Before: "; msg
|
||||
MID$(msg, 7, 5) = "BASIC"
|
||||
PRINT "After: "; msg
|
||||
MID$(msg, 1, 5) = "Howdy"
|
||||
PRINT "After2: "; msg
|
||||
|
||||
PRINT ""
|
||||
PRINT "All new feature tests complete!"
|
||||
70
test_redim.bas
Normal file
70
test_redim.bas
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
' Test dynamic arrays and REDIM
|
||||
DIM arr(5) AS INTEGER
|
||||
DIM i AS INTEGER
|
||||
|
||||
' Fill initial array
|
||||
FOR i = 0 TO 5
|
||||
arr(i) = i * 10
|
||||
NEXT i
|
||||
|
||||
PRINT "Before REDIM:"
|
||||
FOR i = 0 TO 5
|
||||
PRINT arr(i);
|
||||
NEXT i
|
||||
PRINT ""
|
||||
|
||||
' Resize array larger
|
||||
REDIM arr(10) AS INTEGER
|
||||
arr(8) = 88
|
||||
arr(10) = 100
|
||||
|
||||
PRINT "After REDIM:"
|
||||
FOR i = 0 TO 10
|
||||
PRINT arr(i);
|
||||
NEXT i
|
||||
PRINT ""
|
||||
|
||||
' Test string array
|
||||
DIM names(3) AS STRING
|
||||
names(0) = "Alice"
|
||||
names(1) = "Bob"
|
||||
names(2) = "Charlie"
|
||||
names(3) = "Diana"
|
||||
|
||||
PRINT "Names:"
|
||||
FOR i = 0 TO 3
|
||||
PRINT names(i)
|
||||
NEXT i
|
||||
|
||||
' Test FOR with STEP
|
||||
PRINT "Even numbers 0 to 10:"
|
||||
FOR i = 0 TO 10 STEP 2
|
||||
PRINT i;
|
||||
NEXT i
|
||||
PRINT ""
|
||||
|
||||
' Test negative step
|
||||
PRINT "Countdown by 2:"
|
||||
FOR i = 10 TO 0 STEP -2
|
||||
PRINT i;
|
||||
NEXT i
|
||||
PRINT ""
|
||||
|
||||
' Test integer division and MOD
|
||||
DIM a AS INTEGER
|
||||
DIM b AS INTEGER
|
||||
a = 17
|
||||
b = 5
|
||||
PRINT "17 \ 5 = "; a \ b
|
||||
PRINT "17 MOD 5 = "; a MOD b
|
||||
|
||||
' Test nested loops with EXIT
|
||||
PRINT "Nested loop with EXIT FOR:"
|
||||
DIM j AS INTEGER
|
||||
FOR i = 1 TO 5
|
||||
FOR j = 1 TO 5
|
||||
IF j = 3 THEN EXIT FOR
|
||||
PRINT i * 10 + j;
|
||||
NEXT j
|
||||
PRINT "";
|
||||
NEXT i
|
||||
338
test_types.bas
Normal file
338
test_types.bas
Normal file
|
|
@ -0,0 +1,338 @@
|
|||
' ============================================================
|
||||
' Test program for extended data types
|
||||
' BYTE -> uint8_t, INTEGER -> int16_t, LONG -> int32_t,
|
||||
' FLOAT -> float, DOUBLE -> double
|
||||
' ============================================================
|
||||
|
||||
' ---- BYTE (uint8_t): range 0 to 255 ----
|
||||
PRINT "==== BYTE (uint8_t) ===="
|
||||
DIM b1 AS BYTE
|
||||
DIM b2 AS BYTE
|
||||
|
||||
b1 = 100
|
||||
b2 = 200
|
||||
PRINT "100 + 200 (byte) = "; b1 + b2
|
||||
|
||||
b1 = 255
|
||||
PRINT "Max uint8_t = "; b1
|
||||
b2 = b1 + 1
|
||||
PRINT "255 + 1 overflows to "; b2
|
||||
|
||||
' BYTE arithmetic
|
||||
b1 = 50
|
||||
b2 = 30
|
||||
PRINT "50 - 30 = "; b1 - b2
|
||||
PRINT "50 * 3 = "; b1 * 3
|
||||
|
||||
' BYTE in array
|
||||
DIM byteArr(3) AS BYTE
|
||||
DIM bi AS BYTE
|
||||
FOR bi = 0 TO 3
|
||||
byteArr(bi) = bi * 10
|
||||
NEXT bi
|
||||
PRINT "BYTE array: ";
|
||||
FOR bi = 0 TO 3
|
||||
PRINT byteArr(bi); " ";
|
||||
NEXT bi
|
||||
PRINT ""
|
||||
|
||||
' BYTE + INTEGER promotion
|
||||
DIM bv AS BYTE
|
||||
DIM iv AS INTEGER
|
||||
bv = 100
|
||||
iv = 1000
|
||||
PRINT "byte + int16: "; bv + iv
|
||||
|
||||
' BYTE function
|
||||
FUNCTION ByteMax(BYVAL x AS BYTE, BYVAL y AS BYTE) AS BYTE
|
||||
IF x > y THEN
|
||||
ByteMax = x
|
||||
ELSE
|
||||
ByteMax = y
|
||||
END IF
|
||||
END FUNCTION
|
||||
|
||||
PRINT "ByteMax(100, 200) = "; ByteMax(100, 200)
|
||||
|
||||
' ---- INTEGER (int16_t): range -32768 to 32767 ----
|
||||
PRINT ""
|
||||
PRINT "==== INTEGER (int16_t) ===="
|
||||
DIM i AS INTEGER
|
||||
DIM j AS INTEGER
|
||||
|
||||
i = 100
|
||||
j = 200
|
||||
PRINT "100 + 200 = "; i + j
|
||||
|
||||
i = 32000
|
||||
PRINT "32000 as INTEGER = "; i
|
||||
|
||||
' Demonstrate int16_t overflow wrapping
|
||||
i = 32767
|
||||
PRINT "Max int16_t = "; i
|
||||
j = i + 1
|
||||
PRINT "32767 + 1 overflows to "; j
|
||||
|
||||
' Integer suffix %
|
||||
DIM count% AS INTEGER
|
||||
count% = 42
|
||||
PRINT "count% = "; count%
|
||||
|
||||
' ---- LONG (int32_t): range -2147483648 to 2147483647 ----
|
||||
PRINT ""
|
||||
PRINT "==== LONG (int32_t) ===="
|
||||
DIM a AS LONG
|
||||
DIM b AS LONG
|
||||
DIM big AS LONG
|
||||
|
||||
a = 100000
|
||||
b = 200000
|
||||
PRINT "100000 + 200000 = "; a + b
|
||||
|
||||
big = 1000000
|
||||
big = big * 1000
|
||||
PRINT "1000000 * 1000 = "; big
|
||||
|
||||
' LONG can hold values far beyond int16_t range
|
||||
big = 32767
|
||||
big = big * 100
|
||||
PRINT "32767 * 100 = "; big
|
||||
|
||||
' Arithmetic with LONGs
|
||||
a = 123456
|
||||
b = 789012
|
||||
PRINT "123456 + 789012 = "; a + b
|
||||
PRINT "789012 - 123456 = "; b - a
|
||||
PRINT "123456 * 2 = "; a * 2
|
||||
|
||||
' LONG division and modulo
|
||||
a = 1000000
|
||||
b = 7
|
||||
PRINT "1000000 \\ 7 = "; a \ b
|
||||
PRINT "1000000 MOD 7 = "; a MOD b
|
||||
|
||||
' ---- FLOAT (float: ~7 digits precision) ----
|
||||
PRINT ""
|
||||
PRINT "==== FLOAT ===="
|
||||
DIM f1 AS FLOAT
|
||||
DIM f2 AS FLOAT
|
||||
DIM fResult AS FLOAT
|
||||
|
||||
f1 = 3.14
|
||||
f2 = 2.71
|
||||
PRINT "3.14 + 2.71 = "; f1 + f2
|
||||
PRINT "3.14 * 2.71 = "; f1 * f2
|
||||
PRINT "3.14 / 2.71 = "; f1 / f2
|
||||
|
||||
' Float suffix !
|
||||
DIM pi! AS FLOAT
|
||||
pi! = 3.14159
|
||||
PRINT "pi! = "; pi!
|
||||
|
||||
' Float precision test
|
||||
f1 = 1.0
|
||||
f2 = 3.0
|
||||
fResult = f1 / f2
|
||||
PRINT "1.0 / 3.0 (float) = "; fResult
|
||||
|
||||
f1 = 123456.789
|
||||
PRINT "123456.789 as float = "; f1
|
||||
|
||||
' ---- DOUBLE (double: ~15 digits precision) ----
|
||||
PRINT ""
|
||||
PRINT "==== DOUBLE ===="
|
||||
DIM d1 AS DOUBLE
|
||||
DIM d2 AS DOUBLE
|
||||
DIM dResult AS DOUBLE
|
||||
|
||||
d1 = 3.14159265358979
|
||||
d2 = 2.71828182845904
|
||||
PRINT "pi = "; d1
|
||||
PRINT "e = "; d2
|
||||
PRINT "pi + e = "; d1 + d2
|
||||
PRINT "pi * e = "; d1 * d2
|
||||
|
||||
' Double suffix #
|
||||
DIM exact# AS DOUBLE
|
||||
exact# = 1.0 / 3.0
|
||||
PRINT "1/3 (double) = "; exact#
|
||||
|
||||
' ---- Type promotion in expressions ----
|
||||
PRINT ""
|
||||
PRINT "==== Type Promotion ===="
|
||||
|
||||
' INTEGER + LONG -> LONG
|
||||
DIM si AS INTEGER
|
||||
DIM sl AS LONG
|
||||
si = 100
|
||||
sl = 100000
|
||||
PRINT "int16 + int32: "; si + sl
|
||||
|
||||
' INTEGER + FLOAT -> FLOAT
|
||||
DIM sf AS FLOAT
|
||||
sf = 3.14
|
||||
PRINT "int16 + float: "; si + sf
|
||||
|
||||
' INTEGER + DOUBLE -> DOUBLE
|
||||
DIM sd AS DOUBLE
|
||||
sd = 3.14159265358979
|
||||
PRINT "int16 + double: "; si + sd
|
||||
|
||||
' LONG + FLOAT -> FLOAT
|
||||
PRINT "int32 + float: "; sl + sf
|
||||
|
||||
' LONG + DOUBLE -> DOUBLE
|
||||
PRINT "int32 + double: "; sl + sd
|
||||
|
||||
' FLOAT + DOUBLE -> DOUBLE
|
||||
PRINT "float + double: "; sf + sd
|
||||
|
||||
' ---- Mixed type functions ----
|
||||
PRINT ""
|
||||
PRINT "==== Functions with New Types ===="
|
||||
|
||||
FUNCTION AddLongs(BYVAL x AS LONG, BYVAL y AS LONG) AS LONG
|
||||
AddLongs = x + y
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION MultiplyFloat(BYVAL x AS FLOAT, BYVAL y AS FLOAT) AS FLOAT
|
||||
MultiplyFloat = x * y
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION LongFactorial(BYVAL n AS LONG) AS LONG
|
||||
IF n <= 1 THEN
|
||||
LongFactorial = 1
|
||||
ELSE
|
||||
LongFactorial = n * LongFactorial(n - 1)
|
||||
END IF
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION FloatSqrt(BYVAL x AS FLOAT) AS FLOAT
|
||||
FloatSqrt = SQR(x)
|
||||
END FUNCTION
|
||||
|
||||
PRINT "AddLongs(100000, 200000) = "; AddLongs(100000, 200000)
|
||||
PRINT "MultiplyFloat(3.14, 2.0) = "; MultiplyFloat(3.14, 2.0)
|
||||
PRINT "LongFactorial(12) = "; LongFactorial(12)
|
||||
PRINT "FloatSqrt(2.0) = "; FloatSqrt(2.0)
|
||||
|
||||
' ---- BYREF with new types ----
|
||||
PRINT ""
|
||||
PRINT "==== BYREF with New Types ===="
|
||||
|
||||
SUB SwapLongs(BYREF x AS LONG, BYREF y AS LONG)
|
||||
LOCAL t AS LONG
|
||||
t = x
|
||||
x = y
|
||||
y = t
|
||||
END SUB
|
||||
|
||||
SUB ScaleFloat(BYREF val AS FLOAT, BYVAL factor AS FLOAT)
|
||||
val = val * factor
|
||||
END SUB
|
||||
|
||||
DIM lx AS LONG
|
||||
DIM ly AS LONG
|
||||
lx = 100000
|
||||
ly = 999999
|
||||
PRINT "Before SwapLongs: "; lx; " "; ly
|
||||
CALL SwapLongs(lx, ly)
|
||||
PRINT "After SwapLongs: "; lx; " "; ly
|
||||
|
||||
DIM fv AS FLOAT
|
||||
fv = 2.5
|
||||
PRINT "Before ScaleFloat: "; fv
|
||||
CALL ScaleFloat(fv, 4.0)
|
||||
PRINT "After ScaleFloat(2.5, 4.0): "; fv
|
||||
|
||||
' ---- Arrays with new types ----
|
||||
PRINT ""
|
||||
PRINT "==== Arrays with New Types ===="
|
||||
|
||||
DIM longArr(5) AS LONG
|
||||
DIM fltArr(5) AS FLOAT
|
||||
DIM idx AS INTEGER
|
||||
|
||||
FOR idx = 0 TO 5
|
||||
longArr(idx) = (idx + 1) * 100000
|
||||
fltArr(idx) = (idx + 1) * 1.5
|
||||
NEXT idx
|
||||
|
||||
PRINT "LONG array: ";
|
||||
FOR idx = 0 TO 5
|
||||
PRINT longArr(idx); " ";
|
||||
NEXT idx
|
||||
PRINT ""
|
||||
|
||||
PRINT "FLOAT array: ";
|
||||
FOR idx = 0 TO 5
|
||||
PRINT fltArr(idx); " ";
|
||||
NEXT idx
|
||||
PRINT ""
|
||||
|
||||
' ---- STATIC with new types ----
|
||||
PRINT ""
|
||||
PRINT "==== STATIC with New Types ===="
|
||||
|
||||
FUNCTION LongCounter() AS LONG
|
||||
STATIC lc AS LONG
|
||||
lc = lc + 100000
|
||||
LongCounter = lc
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION FloatAccum(BYVAL v AS FLOAT) AS FLOAT
|
||||
STATIC fs AS FLOAT
|
||||
fs = fs + v
|
||||
FloatAccum = fs
|
||||
END FUNCTION
|
||||
|
||||
PRINT "LongCounter: ";
|
||||
FOR idx = 1 TO 5
|
||||
PRINT LongCounter(); " ";
|
||||
NEXT idx
|
||||
PRINT ""
|
||||
|
||||
PRINT "FloatAccum: ";
|
||||
DIM ftemp AS FLOAT
|
||||
ftemp = FloatAccum(1.1)
|
||||
PRINT ftemp; " ";
|
||||
ftemp = FloatAccum(2.2)
|
||||
PRINT ftemp; " ";
|
||||
ftemp = FloatAccum(3.3)
|
||||
PRINT ftemp; " ";
|
||||
PRINT ""
|
||||
|
||||
' ---- Mixing classic line numbers with new types ----
|
||||
PRINT ""
|
||||
PRINT "==== Classic Lines with New Types ===="
|
||||
|
||||
DIM counter AS LONG
|
||||
counter = 0
|
||||
|
||||
2000 counter = counter + 50000
|
||||
2010 IF counter >= 250000 THEN GOTO 2030
|
||||
2020 GOTO 2000
|
||||
2030 PRINT "LONG counter reached: "; counter
|
||||
|
||||
' ---- Range demonstration ----
|
||||
PRINT ""
|
||||
PRINT "==== Type Ranges ===="
|
||||
DIM maxInt AS INTEGER
|
||||
DIM maxLong AS LONG
|
||||
maxInt = 32767
|
||||
maxLong = 2147483647
|
||||
PRINT "Max INTEGER (int16_t): "; maxInt
|
||||
PRINT "Max LONG (int32_t): "; maxLong
|
||||
|
||||
' Show that LONG can handle what overflows INTEGER
|
||||
DIM intVal AS INTEGER
|
||||
DIM longVal AS LONG
|
||||
intVal = 200
|
||||
longVal = 200
|
||||
intVal = intVal * intVal
|
||||
longVal = longVal * longVal
|
||||
PRINT "200 * 200 as INTEGER: "; intVal
|
||||
PRINT "200 * 200 as LONG: "; longVal
|
||||
|
||||
PRINT ""
|
||||
PRINT "==== ALL TYPE TESTS COMPLETE ===="
|
||||
361
test_udt.bas
Normal file
361
test_udt.bas
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
' ============================================================
|
||||
' Test program for user-defined types and random-access file I/O
|
||||
' ============================================================
|
||||
|
||||
PRINT "==== User-Defined Type Tests ===="
|
||||
|
||||
' ---- Test 1: Basic TYPE definition and field access ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 1: Basic TYPE and field access ----"
|
||||
|
||||
TYPE PersonRecord
|
||||
firstName AS STRING * 20
|
||||
lastName AS STRING * 30
|
||||
age AS INTEGER
|
||||
salary AS DOUBLE
|
||||
END TYPE
|
||||
|
||||
DIM person AS PersonRecord
|
||||
person.firstName = "John"
|
||||
person.lastName = "Doe"
|
||||
person.age = 30
|
||||
person.salary = 55000.50
|
||||
|
||||
PRINT "Name: "; person.firstName; " "; person.lastName
|
||||
PRINT "Age: "; person.age
|
||||
PRINT "Salary: "; person.salary
|
||||
|
||||
' ---- Test 2: Multiple TYPE definitions ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 2: Multiple TYPE definitions ----"
|
||||
|
||||
TYPE Point2D
|
||||
x AS DOUBLE
|
||||
y AS DOUBLE
|
||||
END TYPE
|
||||
|
||||
TYPE Rectangle
|
||||
width AS DOUBLE
|
||||
height AS DOUBLE
|
||||
END TYPE
|
||||
|
||||
DIM pt AS Point2D
|
||||
pt.x = 3.0
|
||||
pt.y = 4.0
|
||||
PRINT "Point: ("; pt.x; ", "; pt.y; ")"
|
||||
|
||||
DIM rect AS Rectangle
|
||||
rect.width = 10.5
|
||||
rect.height = 20.3
|
||||
PRINT "Rect: "; rect.width; " x "; rect.height
|
||||
|
||||
' ---- Test 3: UDT with integer fields ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 3: UDT integer fields ----"
|
||||
|
||||
TYPE Color
|
||||
r AS INTEGER
|
||||
g AS INTEGER
|
||||
b AS INTEGER
|
||||
END TYPE
|
||||
|
||||
DIM c AS Color
|
||||
c.r = 255
|
||||
c.g = 128
|
||||
c.b = 64
|
||||
PRINT "Color: ("; c.r; ", "; c.g; ", "; c.b; ")"
|
||||
|
||||
' ---- Test 4: Array of UDTs ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 4: Array of UDTs ----"
|
||||
|
||||
DIM points(3) AS Point2D
|
||||
DIM i AS INTEGER
|
||||
|
||||
FOR i = 0 TO 3
|
||||
points(i).x = i * 1.5
|
||||
points(i).y = i * 2.5
|
||||
NEXT i
|
||||
|
||||
FOR i = 0 TO 3
|
||||
PRINT "points("; i; "): ("; points(i).x; ", "; points(i).y; ")"
|
||||
NEXT i
|
||||
|
||||
' ---- Test 5: UDT field expressions ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 5: UDT field expressions ----"
|
||||
|
||||
DIM p1 AS Point2D
|
||||
DIM p2 AS Point2D
|
||||
p1.x = 1.0
|
||||
p1.y = 2.0
|
||||
p2.x = 4.0
|
||||
p2.y = 6.0
|
||||
|
||||
DIM dist AS DOUBLE
|
||||
dist = SQR((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y))
|
||||
PRINT "Distance: "; dist
|
||||
|
||||
' ---- Test 6: SIZEOF ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 6: SIZEOF ----"
|
||||
|
||||
DIM szPerson AS LONG
|
||||
szPerson = SIZEOF(PersonRecord)
|
||||
PRINT "SIZEOF(PersonRecord) = "; szPerson
|
||||
|
||||
DIM szPoint AS LONG
|
||||
szPoint = SIZEOF(Point2D)
|
||||
PRINT "SIZEOF(Point2D) = "; szPoint
|
||||
|
||||
DIM szColor AS LONG
|
||||
szColor = SIZEOF(Color)
|
||||
PRINT "SIZEOF(Color) = "; szColor
|
||||
|
||||
' ---- Test 7: Random-access file I/O ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 7: Random-access file I/O ----"
|
||||
|
||||
TYPE Item
|
||||
name AS STRING * 16
|
||||
price AS DOUBLE
|
||||
qty AS INTEGER
|
||||
END TYPE
|
||||
|
||||
DIM item1 AS Item
|
||||
DIM item2 AS Item
|
||||
DIM item3 AS Item
|
||||
|
||||
item1.name = "Widget"
|
||||
item1.price = 9.99
|
||||
item1.qty = 100
|
||||
|
||||
item2.name = "Gadget"
|
||||
item2.price = 24.95
|
||||
item2.qty = 50
|
||||
|
||||
item3.name = "Doohickey"
|
||||
item3.price = 4.50
|
||||
item3.qty = 200
|
||||
|
||||
' Write records
|
||||
OPEN "/tmp/test_items.dat" FOR RANDOM AS #1 LEN = SIZEOF(Item)
|
||||
PUT #1, 1, item1
|
||||
PUT #1, 2, item2
|
||||
PUT #1, 3, item3
|
||||
CLOSE #1
|
||||
|
||||
' Read records back in different order
|
||||
DIM loaded AS Item
|
||||
OPEN "/tmp/test_items.dat" FOR RANDOM AS #1 LEN = SIZEOF(Item)
|
||||
|
||||
GET #1, 2, loaded
|
||||
PRINT "Record 2: "; loaded.name; " $"; loaded.price; " qty="; loaded.qty
|
||||
|
||||
GET #1, 1, loaded
|
||||
PRINT "Record 1: "; loaded.name; " $"; loaded.price; " qty="; loaded.qty
|
||||
|
||||
GET #1, 3, loaded
|
||||
PRINT "Record 3: "; loaded.name; " $"; loaded.price; " qty="; loaded.qty
|
||||
|
||||
CLOSE #1
|
||||
|
||||
' ---- Test 8: Overwrite and re-read record ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 8: Overwrite and re-read ----"
|
||||
|
||||
DIM updated AS Item
|
||||
updated.name = "NewWidget"
|
||||
updated.price = 19.99
|
||||
updated.qty = 75
|
||||
|
||||
OPEN "/tmp/test_items.dat" FOR RANDOM AS #1 LEN = SIZEOF(Item)
|
||||
PUT #1, 1, updated
|
||||
GET #1, 1, loaded
|
||||
PRINT "Updated record 1: "; loaded.name; " $"; loaded.price; " qty="; loaded.qty
|
||||
|
||||
' Verify record 3 is unchanged
|
||||
GET #1, 3, loaded
|
||||
PRINT "Record 3 unchanged: "; loaded.name; " $"; loaded.price; " qty="; loaded.qty
|
||||
CLOSE #1
|
||||
|
||||
' ---- Test 9: UDT with mixed field types ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 9: Mixed field types ----"
|
||||
|
||||
TYPE MixedRecord
|
||||
label AS STRING * 10
|
||||
byteVal AS BYTE
|
||||
intVal AS INTEGER
|
||||
longVal AS LONG
|
||||
floatVal AS FLOAT
|
||||
dblVal AS DOUBLE
|
||||
END TYPE
|
||||
|
||||
DIM m AS MixedRecord
|
||||
m.label = "TestMixed"
|
||||
m.byteVal = 42
|
||||
m.intVal = -1000
|
||||
m.longVal = 100000
|
||||
m.floatVal = 3.14
|
||||
m.dblVal = 2.71828
|
||||
|
||||
PRINT "Label: "; m.label
|
||||
PRINT "Byte: "; m.byteVal
|
||||
PRINT "Int: "; m.intVal
|
||||
PRINT "Long: "; m.longVal
|
||||
PRINT "Double: "; m.dblVal
|
||||
|
||||
' ---- Test 10: Nested UDTs ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 10: Nested UDTs ----"
|
||||
|
||||
TYPE Vec2
|
||||
x AS DOUBLE
|
||||
y AS DOUBLE
|
||||
END TYPE
|
||||
|
||||
TYPE Circle
|
||||
center AS Vec2
|
||||
radius AS DOUBLE
|
||||
END TYPE
|
||||
|
||||
DIM circ AS Circle
|
||||
circ.center.x = 10.0
|
||||
circ.center.y = 20.0
|
||||
circ.radius = 5.5
|
||||
PRINT "Circle center: ("; circ.center.x; ", "; circ.center.y; ")"
|
||||
PRINT "Circle radius: "; circ.radius
|
||||
|
||||
' ---- Test 11: Nested UDT read in expressions ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 11: Nested UDT expressions ----"
|
||||
|
||||
DIM circ2 AS Circle
|
||||
circ2.center.x = 30.0
|
||||
circ2.center.y = 40.0
|
||||
circ2.radius = 2.0
|
||||
|
||||
DIM dx AS DOUBLE
|
||||
DIM dy AS DOUBLE
|
||||
dx = circ2.center.x - circ.center.x
|
||||
dy = circ2.center.y - circ.center.y
|
||||
PRINT "dx = "; dx
|
||||
PRINT "dy = "; dy
|
||||
|
||||
' ---- Test 12: UDT whole-struct copy ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 12: UDT copy ----"
|
||||
|
||||
DIM orig AS Vec2
|
||||
orig.x = 42.0
|
||||
orig.y = 99.0
|
||||
|
||||
DIM copy AS Vec2
|
||||
copy = orig
|
||||
PRINT "copy.x = "; copy.x
|
||||
PRINT "copy.y = "; copy.y
|
||||
|
||||
' Modify original, verify copy is independent
|
||||
orig.x = 0.0
|
||||
PRINT "After modify orig, copy.x = "; copy.x
|
||||
|
||||
' ---- Test 13: Nested UDT sub-struct copy ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 13: Nested UDT sub-struct copy ----"
|
||||
|
||||
DIM savedCenter AS Vec2
|
||||
savedCenter = circ.center
|
||||
PRINT "savedCenter: ("; savedCenter.x; ", "; savedCenter.y; ")"
|
||||
|
||||
' Assign sub-struct to nested field
|
||||
DIM circ3 AS Circle
|
||||
DIM newCenter AS Vec2
|
||||
newCenter.x = 77.0
|
||||
newCenter.y = 88.0
|
||||
circ3.center = newCenter
|
||||
circ3.radius = 3.0
|
||||
PRINT "circ3 center: ("; circ3.center.x; ", "; circ3.center.y; ")"
|
||||
PRINT "circ3 radius: "; circ3.radius
|
||||
|
||||
' ---- Test 14: Array of UDTs with nested types ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 14: Array of nested UDTs ----"
|
||||
|
||||
DIM circles(2) AS Circle
|
||||
circles(0).center.x = 1.0
|
||||
circles(0).center.y = 2.0
|
||||
circles(0).radius = 10.0
|
||||
circles(1).center.x = 3.0
|
||||
circles(1).center.y = 4.0
|
||||
circles(1).radius = 20.0
|
||||
circles(2).center.x = 5.0
|
||||
circles(2).center.y = 6.0
|
||||
circles(2).radius = 30.0
|
||||
|
||||
DIM ci AS INTEGER
|
||||
FOR ci = 0 TO 2
|
||||
PRINT "circles("; ci; "): ("; circles(ci).center.x; ", "; circles(ci).center.y; ") r="; circles(ci).radius
|
||||
NEXT ci
|
||||
|
||||
' ---- Test 15: Copy between array elements ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 15: Array element copy ----"
|
||||
|
||||
circles(0) = circles(2)
|
||||
PRINT "After copy, circles(0).center.x = "; circles(0).center.x
|
||||
PRINT "After copy, circles(0).radius = "; circles(0).radius
|
||||
|
||||
' ---- Test 16: Three-level nesting ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 16: Three-level nesting ----"
|
||||
|
||||
TYPE LineSegment
|
||||
start AS Vec2
|
||||
finish AS Vec2
|
||||
END TYPE
|
||||
|
||||
TYPE Shape
|
||||
outline AS LineSegment
|
||||
label AS STRING * 16
|
||||
END TYPE
|
||||
|
||||
DIM shape AS Shape
|
||||
shape.outline.start.x = 1.0
|
||||
shape.outline.start.y = 2.0
|
||||
shape.outline.finish.x = 10.0
|
||||
shape.outline.finish.y = 20.0
|
||||
shape.label = "MyLine"
|
||||
|
||||
PRINT "Shape: "; shape.label
|
||||
PRINT " from ("; shape.outline.start.x; ", "; shape.outline.start.y; ")"
|
||||
PRINT " to ("; shape.outline.finish.x; ", "; shape.outline.finish.y; ")"
|
||||
|
||||
' ---- Test 17: Nested UDT file I/O ----
|
||||
PRINT ""
|
||||
PRINT "---- Test 17: Nested UDT file I/O ----"
|
||||
|
||||
DIM c1 AS Circle
|
||||
DIM c2 AS Circle
|
||||
c1.center.x = 111.0
|
||||
c1.center.y = 222.0
|
||||
c1.radius = 333.0
|
||||
c2.center.x = 444.0
|
||||
c2.center.y = 555.0
|
||||
c2.radius = 666.0
|
||||
|
||||
OPEN "/tmp/test_circles.dat" FOR RANDOM AS #1 LEN = SIZEOF(Circle)
|
||||
PUT #1, 1, c1
|
||||
PUT #1, 2, c2
|
||||
CLOSE #1
|
||||
|
||||
DIM ld AS Circle
|
||||
OPEN "/tmp/test_circles.dat" FOR RANDOM AS #1 LEN = SIZEOF(Circle)
|
||||
GET #1, 2, ld
|
||||
PRINT "Loaded circle 2: ("; ld.center.x; ", "; ld.center.y; ") r="; ld.radius
|
||||
GET #1, 1, ld
|
||||
PRINT "Loaded circle 1: ("; ld.center.x; ", "; ld.center.y; ") r="; ld.radius
|
||||
CLOSE #1
|
||||
|
||||
PRINT ""
|
||||
PRINT "==== ALL UDT TESTS COMPLETE ===="
|
||||
Loading…
Add table
Reference in a new issue