Skip to content

Commit 6a2bd5c

Browse files
committed
lcase ucase clr
1 parent e8e692a commit 6a2bd5c

File tree

6 files changed

+78
-10
lines changed

6 files changed

+78
-10
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Run this once after unpacking, and macOS will stop treating the binary as “fro
6767
- **`READ` / `DATA`**: load numeric and string literals from `DATA` statements into variables.
6868
- **`DEF FN`**: define simple user functions, e.g. `DEF FNY(X) = SIN(X)`.
6969
- **`POKE`**: accepted as a no‑op (for compatibility with old listings; it does not touch real memory).
70+
- **`CLR`**: resets all variables to 0/empty, clears GOSUB/FOR stacks and DATA pointer; DEF FN definitions are kept.
7071
- **Variables**
7172
- **Numeric variables**: `A`, `B1`, `AB`, `ATAKFLAG`, etc. Names may be longer than two characters; CBM-style **first two characters** identify the variable (e.g. `ATAKFLAG` and `ATA` refer to the same variable).
7273
- **String variables**: names ending in `$`, e.g. `A$`, `NAME$`.
@@ -75,6 +76,7 @@ Run this once after unpacking, and macOS will stop treating the binary as “fro
7576
- **Math**: `SIN`, `COS`, `TAN`, `ABS`, `INT`, `SQR`, `SGN`, `EXP`, `LOG`, `RND`.
7677
- **Strings**:
7778
- `LEN`, `VAL`, `STR$`, `CHR$`, `ASC`.
79+
- `UCASE$`, `LCASE$`: convert string to upper or lower case (ASCII).
7880
- `MID$`, `LEFT$`, `RIGHT$` with C64‑style semantics:
7981
- `MID$(S$, start)` or `MID$(S$, start, len)` (1‑based `start`).
8082
- `LEFT$(S$, n)` – first `n` characters.

basic

48 Bytes
Binary file not shown.

basic.c

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,7 @@ static void statement_get(char **p);
750750
static void do_sleep_ticks(double ticks);
751751
static void statement_locate(char **p);
752752
static void statement_on(char **p);
753+
static void statement_clr(char **p);
753754
static int function_lookup(const char *name, int len);
754755

755756
enum func_code {
@@ -773,7 +774,9 @@ enum func_code {
773774
FN_SPC = 17,
774775
FN_MID = 18,
775776
FN_LEFT = 19,
776-
FN_RIGHT = 20
777+
FN_RIGHT = 20,
778+
FN_UCASE = 21,
779+
FN_LCASE = 22
777780
};
778781

779782
/* Report an error and halt further execution.
@@ -1460,6 +1463,9 @@ static int function_lookup(const char *name, int len)
14601463
if ((len == 4 && name[0] == 'L' && name[1] == 'E' && name[2] == 'F' && name[3] == 'T') ||
14611464
(len == 5 && name[0] == 'L' && name[1] == 'E' && name[2] == 'F' && name[3] == 'T' && name[4] == '$'))
14621465
return FN_LEFT;
1466+
if ((len == 5 && name[0] == 'L' && name[1] == 'C' && name[2] == 'A' && name[3] == 'S' && name[4] == 'E') ||
1467+
(len == 6 && name[0] == 'L' && name[1] == 'C' && name[2] == 'A' && name[3] == 'S' && name[4] == 'E' && name[5] == '$'))
1468+
return FN_LCASE;
14631469
return FN_NONE;
14641470
case 'M':
14651471
if ((len == 3 && name[0] == 'M' && name[1] == 'I' && name[2] == 'D') ||
@@ -1475,6 +1481,11 @@ static int function_lookup(const char *name, int len)
14751481
case 'V':
14761482
if (len == 3 && name[0] == 'V' && name[1] == 'A' && name[2] == 'L') return FN_VAL;
14771483
return FN_NONE;
1484+
case 'U':
1485+
if ((len == 5 && name[0] == 'U' && name[1] == 'C' && name[2] == 'A' && name[3] == 'S' && name[4] == 'E') ||
1486+
(len == 6 && name[0] == 'U' && name[1] == 'C' && name[2] == 'A' && name[3] == 'S' && name[4] == 'E' && name[5] == '$'))
1487+
return FN_UCASE;
1488+
return FN_NONE;
14781489
default:
14791490
return FN_NONE;
14801491
}
@@ -2039,6 +2050,28 @@ static struct value eval_function(const char *name, char **p)
20392050
return make_str(out);
20402051
}
20412052
}
2053+
case FN_UCASE: {
2054+
char out[MAX_STR_LEN];
2055+
size_t i, n;
2056+
ensure_str(&arg);
2057+
n = strlen(arg.str);
2058+
if (n >= MAX_STR_LEN) n = MAX_STR_LEN - 1;
2059+
for (i = 0; i < n; i++)
2060+
out[i] = (char)toupper((unsigned char)arg.str[i]);
2061+
out[n] = '\0';
2062+
return make_str(out);
2063+
}
2064+
case FN_LCASE: {
2065+
char out[MAX_STR_LEN];
2066+
size_t i, n;
2067+
ensure_str(&arg);
2068+
n = strlen(arg.str);
2069+
if (n >= MAX_STR_LEN) n = MAX_STR_LEN - 1;
2070+
for (i = 0; i < n; i++)
2071+
out[i] = (char)tolower((unsigned char)arg.str[i]);
2072+
out[n] = '\0';
2073+
return make_str(out);
2074+
}
20422075
default:
20432076
runtime_error("Unknown function");
20442077
return make_num(0.0);
@@ -2303,7 +2336,8 @@ static struct value eval_factor(char **p)
23032336
starts_with_kw(*p, "RND") || starts_with_kw(*p, "LEN") || starts_with_kw(*p, "VAL") ||
23042337
starts_with_kw(*p, "STR") || starts_with_kw(*p, "CHR") || starts_with_kw(*p, "ASC") ||
23052338
starts_with_kw(*p, "TAB") || starts_with_kw(*p, "SPC") || starts_with_kw(*p, "MID") ||
2306-
starts_with_kw(*p, "LEFT") || starts_with_kw(*p, "RIGHT")) {
2339+
starts_with_kw(*p, "LEFT") || starts_with_kw(*p, "RIGHT") ||
2340+
starts_with_kw(*p, "UCASE") || starts_with_kw(*p, "LCASE")) {
23072341
char namebuf[8];
23082342
char *q;
23092343
q = *p;
@@ -2864,6 +2898,27 @@ static void statement_on(char **p)
28642898
/* If index is out of range, ON expression simply falls through. */
28652899
}
28662900

2901+
/* CLR: reset all variables to 0/empty, clear GOSUB/FOR stacks, reset DATA pointer.
2902+
* DEF FN definitions are left intact (CBM-style). */
2903+
static void statement_clr(char **p)
2904+
{
2905+
int i, j;
2906+
(void)p;
2907+
2908+
for (i = 0; i < var_count; i++) {
2909+
struct var *v = &vars[i];
2910+
v->scalar = v->is_string ? make_str("") : make_num(0.0);
2911+
if (v->array && v->size > 0) {
2912+
for (j = 0; j < v->size; j++) {
2913+
v->array[j] = v->is_string ? make_str("") : make_num(0.0);
2914+
}
2915+
}
2916+
}
2917+
gosub_top = 0;
2918+
for_top = 0;
2919+
data_index = 0;
2920+
}
2921+
28672922
static void statement_let(char **p)
28682923
{
28692924
struct value *vp;
@@ -3341,6 +3396,11 @@ static void execute_statement(char **p)
33413396
*p += strlen(*p);
33423397
return;
33433398
}
3399+
if (c == 'C' && starts_with_kw(*p, "CLR")) {
3400+
*p += 3;
3401+
statement_clr(p);
3402+
return;
3403+
}
33443404
if (isalpha((unsigned char)c)) {
33453405
/* Fallback: treat as implicit LET (assignment). Any syntax issues
33463406
* inside the assignment will be reported by the expression parser.

examples/test_clr.bas

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
10 A=1
2+
20 B$="before"
3+
30 PRINT "A=";A;" B$=";B$
4+
40 CLR
5+
50 PRINT "After CLR: A=";A;" B$='";B$;"'"
6+
60 END

examples/test_ucase_lcase.bas

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
10 PRINT UCASE$("hello "); LCASE$("WORLD")
2+
20 END

to-do.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ Colour without pokes
1313
* Subroutines and Functions
1414
* Syntax sugar before actual implementation?
1515

16-
* CLR statement
17-
* Implement CBM-style CLR (reset variables, stacks, execution state)
18-
1916
* Program text preprocessor
2017
* Replace current ad-hoc whitespace tweaks with a small lexer-like pass for keywords and operators
2118
* Normalize compact CBM forms while preserving semantics, e.g.:
@@ -26,14 +23,15 @@ Colour without pokes
2623
* Ensure keywords are only recognized when not inside identifiers (e.g. avoid splitting `ORD(7)` or `FOR`), and never mangling string literals
2724
* Validate behavior against reference interpreter (`cbmbasic`) with a regression suite of tricky lines
2825

29-
* String case utilities
30-
* Add CBM-style upper/lower-case string manipulation functions (e.g. `UCASE$`, `LCASE$`)
31-
* Ensure they round-trip correctly with PETSCII/ANSI mappings and existing `ASC`/`CHR$` behavior
32-
3326
* Include files / libraries
3427
* Design a simple `INCLUDE "file.bas"` or similar directive processed at load time
3528
* Allow splitting larger programs into multiple source files / libraries while preserving line-numbered semantics
3629
* Consider search paths and guarding against recursive includes
3730

38-
* Multi-dimensional arrays(x,y,x)
31+
---
32+
33+
**Completed (removed from list):**
34+
- Multi-dimensional arrays — `DIM A(x,y)` (and up to 3 dimensions) in `basic.c`.
35+
- **CLR statement** — Resets all variables (scalar and array elements) to 0/empty, clears GOSUB and FOR stacks, resets DATA pointer; DEF FN definitions are kept.
36+
- **String case utilities**`UCASE$(s)` and `LCASE$(s)` implemented (ASCII `toupper`/`tolower`); use in expressions and PRINT.
3937

0 commit comments

Comments
 (0)