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