Compare commits

..
Author SHA1 Message Date
Hermes Agent ed23524817 fix: harden Claro diagnostics and runtime guards
Preserve source locations, validate malformed JSON, and add opt-in resource limits. Include executable error-guide coverage, learner pilot materials, native Windows acceptance helpers, and updated CI/documentation.
2026-10-25 02:42:53 +00:00
Hermes Agent bf230f354e test: execute First Hour examples 2026-10-21 22:30:12 +00:00
Hermes Agent d555071406 fix: stabilize learning path, reliability and legacy Windows builds
Unify beginner syntax while preserving compatibility, repair ownership and parser guards, add verified distribution tooling and VC6 Windows 98 build inputs, and clarify documentation and release status. Preserve DOS build-input line endings and exclude generated binaries/caches.
2026-10-20 22:40:19 +00:00
Hermes Agent 8ea2f772cd refactor: keep Claro in one strict C89 source file 2026-10-20 16:12:26 +00:00
Hermes Agent dd7e8b8b20 chore: adopt learning-first charter and UTC run versions 2026-10-20 15:57:54 +00:00
Hermes Agent 9dfd777c41 fix: release GET index expression values 2026-10-09 03:53:44 +00:00
Hermes Agent e0543a1971 fix: release WRITE FILE expression values 2026-10-08 01:36:25 +00:00
Hermes Agent 7275f457ad fix: release READ FILE expression values 2026-10-07 23:34:49 +00:00
Hermes Agent e6a1e02790 fix: release GET ENV expression values 2026-10-07 19:30:56 +00:00
Hermes Agent aa0d77bc38 fix: release GET ARGUMENT temporary values 2026-10-07 15:27:22 +00:00
Hermes Agent e64bfcd30e fix: release caught runtime diagnostic values 2026-10-06 07:08:28 +00:00
Hermes Agent 3ce494b56c fix: release RUN COMMAND expression temporaries 2026-10-05 06:50:04 +00:00
Hermes Agent d844d8cb85 fix: release check-mode expression tokens 2026-10-04 00:30:22 +00:00
Hermes Agent da51e33411 fix: release typecheck environment allocations 2026-10-01 23:53:08 +00:00
Hermes Agent 68ae75129d fix: release removed list item values 2026-09-30 17:30:55 +00:00
Hermes Agent 114d92063c fix: prevent random.int full-range overflow 2026-09-28 00:42:24 +00:00
Hermes Agent 2d0e2a7430 fix: release LIST FOLDER expression temporaries 2026-09-27 22:38:11 +00:00
Hermes Agent c0f72a8d0a fix: release DELETE path expression temporaries 2026-09-27 20:35:40 +00:00
Hermes Agent 047c8acda9 fix: release CREATE FOLDER path temporary 2026-09-27 18:34:00 +00:00
Hermes Agent 0b22ebc58b fix: release COPY and MOVE path temporaries 2026-09-27 12:29:21 +00:00
Hermes Agent 8aa1e49658 fix: release EXISTS expression temporary 2026-09-26 20:19:14 +00:00
Hermes Agent 970a446f4e fix: release GET KEY expression temporaries 2026-09-26 04:09:02 +00:00
Hermes Agent e4ded3acc7 fix: release PUT expression temporaries 2026-09-26 02:07:00 +00:00
Hermes Agent d668594478 fix: release ADD expression temporaries 2026-09-25 11:54:16 +00:00
Hermes Agent a57b51d365 fix: release REMOVE expression temporaries 2026-09-25 07:50:17 +00:00
Hermes Agent 437fd30313 fix: release FIND expression temporaries 2026-09-25 01:44:36 +00:00
Hermes Agent 8183d0a9fa fix: release GET AT expression values 2026-09-24 21:39:29 +00:00
Hermes Agent eca642d8d8 fix: release COUNT expression values 2026-09-24 17:34:46 +00:00
Hermes Agent 4f0219dd11 fix: release ASK temporary values 2026-09-24 15:32:12 +00:00
Hermes Agent 60978f0fcf fix: release DO TIMES control values 2026-09-23 15:15:56 +00:00
Hermes Agent 569b4a3665 fix: release discarded if conditions 2026-09-23 13:13:00 +00:00
Hermes Agent 00f1453ec8 fix: release copied for-each collections 2026-09-23 11:09:33 +00:00
Hermes Agent 0b440109dd fix: release discarded expression values 2026-09-23 09:06:11 +00:00
Hermes Agent 3fdc5a90f0 fix: release remaining runtime-owned values 2026-09-23 07:01:45 +00:00
Hermes Agent 56b72d8ba5 fix: release loaded program storage 2026-09-23 04:58:43 +00:00
Hermes Agent d05465b299 docs: record builtin arity sanitizer verification 2026-09-22 22:50:56 +00:00
Hermes Agent 51b69bc291 docs: mark RUN COMMAND as trusted-only 2026-09-22 20:47:12 +00:00
Hermes Agent 3ef86e6479 harden HTTP response handling 2026-09-22 18:44:03 +00:00
Hermes Agent 974e2db019 fix: release temporary split arguments 2026-09-22 14:36:17 +00:00
Hermes Agent 3bfe881fba fix: release overwritten runtime values 2026-09-22 12:29:00 +00:00
Hermes Agent d60672f575 fix: release expression token storage 2026-09-22 10:24:43 +00:00
128 changed files with 13172 additions and 1663 deletions
+18
View File
@@ -25,6 +25,24 @@ jobs:
run: python3 tools/validate_package_security.py run: python3 tools/validate_package_security.py
- name: Validate compiler warnings - name: Validate compiler warnings
run: python3 tools/validate_compiler_warnings.py run: python3 tools/validate_compiler_warnings.py
- name: Validate trusted command documentation
run: python3 tools/validate_trusted_command_docs.py
- name: Validate documentation and release status
run: python3 tools/validate_documentation_status.py
- name: Validate VC6 Windows 98 build settings
run: python3 tools/validate_msvc6_source.py
- name: Validate single-file C89 build
run: python3 tools/validate_c89.py
- name: Validate executable learning path
run: python3 tools/validate_learning_syntax.py
- name: Validate diagnostics, error guide and learner pilot code
run: |
python3 tools/validate_error_locations.py
python3 tools/validate_error_guide.py
python3 tools/validate_learner_pilot.py
python3 tools/validate_resource_guards.py
- name: Validate memory and malformed-input reliability
run: python3 tools/validate_reliability.py
- name: Validate CI workflow coverage - name: Validate CI workflow coverage
run: python3 tools/validate_ci_workflow.py run: python3 tools/validate_ci_workflow.py
- name: Check lessons - name: Check lessons
+5
View File
@@ -0,0 +1,5 @@
# Preserve DOS line endings for the Windows 98 COMMAND.COM build inputs.
build98.bat -text whitespace=cr-at-eol
build98.rsp -text whitespace=cr-at-eol
test98.bat -text whitespace=cr-at-eol
testwin.bat -text whitespace=cr-at-eol
+1
View File
@@ -1,5 +1,6 @@
claro claro
claro.exe claro.exe
claro98.exe
*.o *.o
*.obj *.obj
*.out *.out
+10
View File
@@ -1,5 +1,15 @@
# Changelog # Changelog
### Release GET list-index expression temporaries
- `GET ... AT ... AS ...` now releases its evaluated index value after converting it to a list position.
- Added `tools/validate_get_at_expression_cleanup.py` with a repeated string-index regression check under ASan/UBSan/LSan.
### Release caught runtime diagnostic values
- Runtime errors caught by `TRY` now release their prior owned message and file strings when the error state is cleared.
- Added `tools/validate_runtime_error_cleanup.py` to exercise builtin-arity and inverted-range diagnostics under ASan/UBSan/LSan.
### Diagnose missing values in untyped object-field assignments ### Diagnose missing values in untyped object-field assignments
- `claro typecheck` now explains how to repair `SET player.score` by adding a value expression after the field name. - `claro typecheck` now explains how to repair `SET player.score` by adding a value expression after the field name.
+80
View File
@@ -0,0 +1,80 @@
# Contributing to Claro
Claro is a learning tool first. Read [`docs/LEARNING_FIRST.md`](docs/LEARNING_FIRST.md)
before proposing learner-facing changes. Keep each change small and predictable,
teach one recommended syntax per concept, preserve supported compatibility
syntax, and prioritize runnable examples and plain-language diagnostics over
feature breadth. Do not claim accessibility validation without testing with
intended learners.
For behavior changes, add a focused regression before implementation and verify
that it fails for the intended reason. Then build and run the affected examples
and the repository validation gates. See [`docs/ROADMAP.md`](docs/ROADMAP.md) for
how the current charter relates to older planning documents.
## Repository layout
- `src/claro.c` — C89 interpreter.
- `lessons/` — ordered beginner lessons.
- `examples/` — programs to try and change.
- `docs/` — learning guides, reference, and historical notes.
- `tests/` — regression fixtures and expected output.
- `tools/` — validation and release helpers.
- `lib/` — existing library helpers.
- `support/` — optional editor support.
## Build and verify
At the beginning of a development/release run, stamp its UTC version once:
```bash
python3 tools/stamp_version.py
make -s all
```
Ordinary rebuilds use `make -s all` without changing the run timestamp.
Validation helpers also require Python 3. Run:
```bash
./claro test
./claro doctor
./claro validate
python3 tools/validate_v1_17.py
```
The last command is the current full validation entry point; its filename is
historical. Use focused sanitizer checks for relevant C runtime changes and
report known failures explicitly.
Execute teaching examples with suitable input and assert their expected output;
syntax checking alone is not enough. Update the relevant guide, rerun checks
after the final edit, and finish with `git diff --check`. Preserve unrelated
local work; exclude generated binaries, caches, and unverified experiments
from commits. See [versioning](docs/VERSIONING.md) for the timestamp convention.
The runtime must remain a single C89 implementation file. The full gate runs
`python3 tools/validate_c89.py` to compile it independently with strict C89 flags
and test formatting, numeric boundaries, and simple functions. Do not introduce
C99 syntax or depend on generated project headers. See [C89 notes](docs/C89.md).
## Learning-first charter
Claro is a learning tool first and a general-purpose language second. Keep the
core small, predictable, and beginner-focused; prioritize executable lessons,
useful diagnostics, and reliability over feature breadth. Teach one recommended
spelling per concept while preserving supported compatibility forms. Do not
claim accessibility validation without feedback from intended learners. The
[learning-first charter](docs/LEARNING_FIRST.md) is the current product guidance.
Older ambitious plans are retained as historical context, not active priorities;
they do not imply that existing features are removed. See the current
[learning-first roadmap](docs/ROADMAP.md).
## Beginner syntax consistency
Follow [the syntax guide](docs/SYNTAX.md) in introductory teaching material.
Keep older spellings in [compatibility reference](docs/COMPATIBILITY.md), not
parallel first-lesson examples. `tools/validate_learning_syntax.py` enforces the
policy and checks actual output; extend its explicit coverage when adding a
lesson or recommended example. Preserve supported parser aliases.
+4 -1
View File
@@ -1,6 +1,9 @@
CC = gcc CC = gcc
CFLAGS = -std=c99 -O0 CFLAGS = -std=c89 -pedantic-errors -Werror=implicit-function-declaration -O0
LDFLAGS = -lm LDFLAGS = -lm
ifeq ($(OS),Windows_NT)
LDFLAGS += -Wl,--stack,16777216
endif
all: claro claro.exe all: claro claro.exe
+47 -256
View File
@@ -2,291 +2,82 @@
<img src="assets/Claro_Logo.jpg" alt="Claro logo" width="160"> <img src="assets/Claro_Logo.jpg" alt="Claro logo" width="160">
**Claro** is a small, readable scripting language for beginners. It is designed to make programming approachable without requiring punctuation-heavy syntax, while still providing a path toward functions, types, objects, files, packages, and networking. **A learning tool first. A programming language second.**
Current release: **v1.18.26** Claro helps beginners learn programming through readable commands, simple rules,
and small programs. Accessibility for people with learning disabilities is a
core design goal; it still needs testing with learners.
## Quick start Development version: **v2026.10.25.014405**
Build Claro with a C99 compiler and `make`: This is a development snapshot, not a newly published release. See
[current status](docs/CURRENT_STATUS.md) and the dated
[release ledger](docs/RELEASE_STATUS.md) for verified builds and limitations.
## Start here
1. [Build and run Claro](docs/QUICK_START.md).
2. [Try your first programs](docs/FIRST_HOUR.md).
3. [Follow the ten lessons](lessons/README.md), one at a time.
Build with a C89 compiler and `make`, then run the first lesson:
```bash ```bash
make make
```
Run a script:
```bash
./claro lessons/01_hello.claro ./claro lessons/01_hello.claro
./claro examples/quiz.claro
``` ```
Check a script without running it: On Windows, see the [quick start](docs/QUICK_START.md) for build instructions.
```bash ## Your first program
./claro check examples/quiz.claro
```
Run the built-in test suite and validation checks:
```bash
./claro test
./claro validate
```
Useful commands:
```text
claro help
claro --version
claro doctor
claro examples
claro repl
claro new MyProject
claro run
claro fmt file.claro
claro typecheck file.claro
```
## First Claro program
```claro ```claro
SAY "Hello from Claro!" SAY "Hello!"
ASK "What is your name?" name ASK "What is your name?" name
SAY "Hello " + name SAY "Hello " + name
``` ```
Claro accepts both the short beginner-friendly form and older compatibility forms. For example, these are both valid: Start with input/output, variables, basic math, decisions, loops, lists,
simple functions, and saving text. You do not need classes, packages,
networking, or advanced types to learn with Claro.
```claro The [beginner syntax guide](docs/SYNTAX.md) gives one recommended form per
SET name "Jon" concept. Older forms remain supported in a separate compatibility reference.
SET name TO "Jon"
```
## Core language features ## Find your way
### Decisions and loops - `lessons/` — small, ordered lessons.
- `examples/` — programs to try and change.
- [Documentation](docs/README.md) — learning guides and reference.
- [Current roadmap](docs/ROADMAP.md) — the small set of active priorities.
- [Learning-first rules](docs/LEARNING_FIRST.md) — how Claro is designed.
- [Active learning-first roadmap](docs/ROADMAP.md) — current priorities; older platform plans are historical context.
- [Contributing](CONTRIBUTING.md) — source layout, builds, and validation.
```claro Existing features and compatibility syntax remain supported; they are not
SET score 10 required for the beginner path. Experimental features are described in the
[detailed status](docs/CURRENT_STATUS.md), not promised as ready.
IF score >= 5 Claro scripts run with your permissions. Run only scripts you trust.
SAY "Good job!"
ELSE
SAY "Try again."
END
DO 3 TIMES ## Single-file C89 interpreter
SAY "Practice"
DONE
```
### Lists and maps The complete runtime is in [`src/claro.c`](src/claro.c). Build it directly:
```claro
SET names AS LIST OF TEXT TO LIST
ADD "Ada" TO names
ADD "Grace" TO names
FOR EACH name IN names
SAY name
DONE
```
### Functions
```claro
TEACH greet name
SAY "Hello " + name
END
DO greet "Jon"
```
The older compatibility syntax remains supported:
```claro
TEACH greet TAKES name
RETURN "Hello " + name
LEARNED
CALL greet WITH "Jon"
SAY RESULT
```
### Objects and classes
```claro
CLASS Player
HAS name TEXT
HAS score NUMBER
TEACH add points
SET score score + points
END
END
NEW Player player
SET player.name "Jon"
SET player.score 10
DO player.add 5
SAY player.score
```
### Type checking
Type annotations are optional for beginners:
```claro
SET score NUMBER 10
SET name TEXT "Jon"
SET ready YESNO YES
CHECK TYPE score IS NUMBER
```
Run static checks with:
```bash ```bash
./claro typecheck examples/type_hardening.claro gcc -std=c89 -pedantic-errors src/claro.c -o claro -lm
``` ```
Claro provides learner-facing diagnostics for many incorrect variable, function, method, and object-field types. For an explicitly typed object field, the separated form needs both `AS TYPE TO value`, so `SET player.score AS NUMBER` explains how to add `TO` and a value. Type checking is still a focused foundation rather than a complete static type system. No generated source or project header is required. See [C89 build notes](docs/C89.md)
for platform dependencies, testing, and limitations.
## Files, JSON, and standard helpers ## Downloads and portability
Claro includes commands and libraries for practical scripts: Use the [distribution guide](docs/PORTABILITY.md) to build and verify clean
source, Linux x86-64, and Windows x86-64 archives. Windows execution is tested
```claro under Wine, not yet on a native Windows machine. For VC6 / Windows 98 SE
WRITE FILE "note.txt" WITH "Hello from Claro" compilation, use [build98.bat and its guide](docs/WINDOWS98.md).
READ FILE "note.txt" AS note
SAY note
```
Standard-library modules include path and collection helpers:
```claro
IMPORT "lib/path.claro" AS path
CALL path.join WITH "notes", "today.txt"
SAY RESULT
```
See `docs/STDLIB.md` for the current helper API.
## Projects and packages
Create a project:
```bash
claro new MyProject
cd MyProject
claro run
```
Manage local project packages:
```bash
claro package init
claro package add text
claro package list
claro package doctor
claro package lock
```
Project metadata is stored in:
```text
claro.project
claro.lock
packages/
```
The current package system is local and foundational. Remote registries, publishing, version constraints, and package signature verification are not yet stable features.
## Networking
Claro supports beginner-oriented HTTP commands and offline `claro://` test URLs:
```claro
HTTP GET "claro://hello" AS page STATUS status
SAY page
SAY status
```
Real HTTP and HTTPS requests use `curl` when available. The offline URLs make networking lessons and tests deterministic.
## Current status
### Stable foundation
- Variables, output, input, expressions, and control flow
- Loops, lists, maps, text, JSON, and file operations
- Functions and compatibility syntax
- Friendly errors and script checking
- Path and collection helpers
- Offline networking examples
### Foundation present
- Static type checking
- Objects and classes
- Local projects and packages
- Networking beyond the offline test layer
- Cooperative tasks/concurrency helpers
- IDE metadata and completion helpers
These areas work in focused scenarios but still need broader coverage and polish.
### Experimental or planned
- Real SDL graphics and game-oriented graphics support
- Web-server syntax and routes
- Remote package registry
- Full editor extension/LSP support
- A complete concurrency scheduler
Do not treat experimental graphics as a stable cross-platform game runtime yet.
## Documentation map
Recommended reading order for new learners:
1. `docs/QUICK_START.md`
2. `docs/FIRST_HOUR.md`
3. `lessons/README.md`
4. `docs/CLI.md`
5. `docs/SPEC.md`
6. `docs/CURRENT_STATUS.md`
Useful references:
- `docs/ERRORS.md` — friendly diagnostics
- `docs/SIMPLE_FUNCTIONS.md` — function syntax
- `docs/STDLIB.md` — standard helpers
- `docs/TESTING.md` — test and validation workflow
- `docs/FORMATTER.md` — formatting scripts
- `docs/IDE.md` — editor metadata and helper support
- `docs/ROADMAP.md` — current development direction
Historical release notes are kept under `docs/RC*_*.md` and older `docs/V1_*_VALIDATION.md` files. They describe project history rather than the current beginner path.
## Development
The interpreter is implemented in `src/claro.c` and currently builds as a C99 program:
```bash
make
make test
make check
```
The repository includes examples, lessons, validation tools, and a test suite. Before submitting changes, run:
```bash
./claro test
./claro validate
```
## License ## License
See [`LICENSE`](LICENSE) for the project license. See [LICENSE](LICENSE).
+2 -2
View File
@@ -2,8 +2,8 @@
setlocal setlocal
set CC=gcc set CC=gcc
if not "%~1"=="" set CC=%~1 if not "%~1"=="" set CC=%~1
echo Building Claro v1.18.26... echo Building Claro (C89)...
%CC% -std=c99 src\claro.c -O0 -o claro.exe -lm %CC% -std=c89 -pedantic-errors -Werror=implicit-function-declaration src\claro.c -O0 -o claro.exe -Wl,--stack,16777216 -lm
if errorlevel 1 ( if errorlevel 1 (
echo Build failed. echo Build failed.
exit /b 1 exit /b 1
+2 -2
View File
@@ -3,8 +3,8 @@ param(
[string]$Out = "claro.exe" [string]$Out = "claro.exe"
) )
Write-Host "Building Claro v1.18.26..." -ForegroundColor Cyan Write-Host "Building Claro (C89)..." -ForegroundColor Cyan
& $CC "-std=c99" "src\claro.c" "-O0" "-o" $Out "-lm" & $CC "-std=c89" "-pedantic-errors" "-Werror=implicit-function-declaration" "src\claro.c" "-O0" "-o" $Out "-Wl,--stack,16777216" "-lm"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Write-Host "Built: $Out" -ForegroundColor Green Write-Host "Built: $Out" -ForegroundColor Green
+5
View File
@@ -0,0 +1,5 @@
@echo off
rem Run from the Claro folder after the VC6 VCVARS32.BAT environment setup.
rem Keep this file compatible with the Windows 98 SE COMMAND.COM shell.
echo Building Claro for Windows 98 SE with Microsoft Visual C++ 6.0...
cl @build98.rsp
+11
View File
@@ -0,0 +1,11 @@
/nologo
/TC
/W3
/ML
/O1
/DWINVER=0x0410
/D_WIN32_WINDOWS=0x0410
/Foclaro98.obj
/Feclaro98.exe
src\claro.c
/link /MACHINE:I386 /SUBSYSTEM:CONSOLE,4.10 /STACK:16777216
+73
View File
@@ -0,0 +1,73 @@
# Claro: single-file C89 runtime
The complete interpreter is `src/claro.c`. It needs no project-specific header,
generated source, or second C implementation file. Tests, lessons, documentation,
and optional scripts remain separate development resources, not build inputs.
## Build
```bash
gcc -std=c89 -pedantic-errors -Werror=implicit-function-declaration src/claro.c -o claro -lm
```
`make -s all` uses the same strict C89 gate and builds `claro` and `claro.exe`.
The Windows build scripts select C89 too and reserve a 16 MiB stack.
A MinGW x64 executable has been compiled and exercised under Wine; native
Windows acceptance is still unverified. See [portability notes](PORTABILITY.md). The `.exe` name built by Linux make
still contains a Linux executable; it is not a Windows cross-build.
The source is formatted with four-space indentation and a 100-column limit.
C99-only declarations, loop declarations, compound literals, and `long long`
have been replaced without removing language features or compatibility syntax.
## Formatting and numbers
C89 does not define `snprintf` or `vsnprintf`. Claro uses a bounded internal
formatter in the same source file. It supports only the fixed conversions used
inside Claro, not arbitrary user-controlled printf formats. Strings are copied
with explicit bounds; fixed numeric formats use C89 `sprintf` into a buffer
sized for their maximum output. Unsupported internal formats fail explicitly.
Number display uses `modf` instead of casting to a non-C89 integer type.
`random.int` computes the inclusive integer range with floating-point arithmetic
and `fmod`, avoiding signed overflow across the tested 32-bit int range while
preserving the existing seeded random algorithm. This is not cryptographic
randomness and does not claim improved distribution.
## Platform requirements
C89 describes the C language and standard library, not an entire operating
system. Directory listing, subprocesses, filesystem checks, and HTTP execution
still use existing platform branches (POSIX or Windows); real HTTP needs curl.
The separate [MSVC6 / Windows 98 build](WINDOWS98.md) uses the actual legacy
compiler with a guarded CRT handle type and dedicated linker settings. Wine
execution does not establish native Windows 98, DOS, or freestanding support.
The C89 gate is tested with the host GCC and the host's integer/double model.
It does not establish compatibility with every minimum-capability C89 compiler
or an unusual integer/floating-point representation.
## Verify
```bash
python3 tools/validate_c89.py
python3 tools/validate_first_hour.py
python3 tools/validate_v1_17.py
```
The C89 validator copies only `claro.c` to an isolated directory, compiles it
with strict flags, verifies that a deliberately C99-only source is rejected,
and exercises bounded formatting/truncation, large and negative numbers,
functions, and extreme random bounds. The full validation gate includes this
check and the nine executable First Hour examples.
Sanitizer command for these portability boundaries:
```bash
ASAN_OPTIONS=detect_leaks=0 UBSAN_OPTIONS=halt_on_error=1 CLARO_CC="gcc -fsanitize=address,undefined -g" python3 tools/validate_c89.py
```
Disabling leak detection here isolates invalid memory access and undefined
behavior checks; it is not a claim that every existing runtime path is leak-free.
Historical validation scripts may retain their original C99 commands; current
builds and current runtime/sanitizer validators use C89.
+39 -28
View File
@@ -1,35 +1,46 @@
# Classroom Readiness Checklist # Classroom pilot checklist
Claro v1.18.26 is suitable for beginner classroom-style testing of the stable console-language foundation. Advanced platform features should still be introduced carefully and only after the basics are comfortable. **Status: ready for supervised learner testing, not classroom certification.**
Accessibility for learners with learning disabilities is a design goal and is
**not yet validated** by intended learners. Passing tests does not establish it.
Use [current status](CURRENT_STATUS.md) for scope, not older feature roadmaps.
## Ready to test The [ready-to-run pilot kit](LEARNER_PILOT.md) includes short tasks and a
[blank feedback form](LEARNER_FEEDBACK_TEMPLATE.md). No real session is implied.
- Beginner console programs ## Before a session
- Interactive examples
- Variables, math, choices, loops, lists, maps, functions
- Simple files and JSON
- Friendly `check` command
- Windows and Linux/macOS build paths
- Local project/package workflow basics
- Offline networking lessons with `claro://` URLs
## Use with caution - Use a build whose platform was actually tested; check `claro --version`.
On the legacy Windows build the executable is `claro98.exe`.
- Run `claro test` and `claro validate` in the extracted project folder.
- Prepare a writable practice folder and explain how to stop/close a stuck run.
- Use only reviewed, trusted scripts. Claro is not a sandbox and runs with the
learner's permissions; do not collect sensitive information in exercises.
- Advanced scripts ## Observe the first hour
- Large programs
- Graphics/game examples
- Remote networking examples
- Package registry or publishing workflows
- Concurrency examples beyond documented safe task patterns
## Recommended testing session 1. Run [hello](../lessons/01_hello.claro) and change its message.
2. Try [input](../lessons/02_ask_name.claro), variables and simple arithmetic.
3. Introduce one decision, then one small loop, at the learner's pace.
4. Follow the [ten lessons](../lessons/README.md) and [first-hour guide](FIRST_HOUR.md).
5. Try a small quiz; ask the learner to explain what changed and why.
6. Introduce a harmless mistake, run `claro check`, and observe whether the
message identifies the right line and suggests an understandable repair.
1. Build Claro or use the included executable. Show one recommended spelling per concept. Do not introduce classes, typing,
2. Run `claro --version` and confirm `Claro v1.18.26`. packages, networking, concurrency or graphics as prerequisites to first programs.
3. Run `claro test`.
4. Run `claro validate`. ## Record feedback
5. Run `lessons/01_hello.claro`.
6. Run `lessons/02_ask_name.claro`. - Can the learner start, run, change and save a program without repeated coaching?
7. Run `examples/quiz.claro`. - Which instructions, words, prompts or errors cause confusion?
8. Deliberately mistype `SAY` as `PRNT` and run `claro check`. - Are examples short enough? Is the expected output obvious?
9. Try one foundation feature example, such as `examples/objects_classes.claro` or `examples/networking.claro`. - Can the learner recover from a mistake and understand what a loop does?
- What presentation, pacing or assistive-technology adjustments help this person?
Record observations without claiming one design suits every disability. Get
consent before retaining feedback and avoid unnecessary personal/health data.
Make one small improvement, then repeat learner testing.
Platform checks, hosted release verification and accessibility feedback are
separate milestones. See [release status](RELEASE_STATUS.md).
+2 -2
View File
@@ -1,6 +1,6 @@
# Claro Command Line # Claro Command Line
Claro v1.18.26 supports these main commands: Claro v2026.10.25.014405 supports these main commands:
```bash ```bash
claro file.claro claro file.claro
@@ -58,7 +58,7 @@ claro package doctor
claro package lock claro package lock
``` ```
The v1.18.26 package command is a safer local project-file helper. It creates `claro.project`, `claro.lock`, and local folders under `packages/`. Package names may appear only once in `claro.project`; `claro package doctor` reports duplicate entries so the lockfile stays unambiguous. It also requires the project manifest to have exactly one non-empty `name:` field. It is not yet an online package registry. The v2026.10.25.014405 package command is a safer local project-file helper. It creates `claro.project`, `claro.lock`, and local folders under `packages/`. Package names may appear only once in `claro.project`; `claro package doctor` reports duplicate entries so the lockfile stays unambiguous. It also requires the project manifest to have exactly one non-empty `name:` field. It is not yet an online package registry.
## IDE metadata ## IDE metadata
+36
View File
@@ -0,0 +1,36 @@
# Compatibility syntax
You do not need these spellings for your first programs. Learn the
[recommended beginner syntax](SYNTAX.md) first. These older forms remain
supported so existing scripts do not break.
- Older `SET score TO 8` means the same as `SET score 8`.
- Older `ASK "Number?" AS answer` means the same as `ASK "Number?" answer`.
- Older `ENDIF`, `DONE`, and `LEARNED` close choices, loops, and functions;
the beginner path uses `END` for all three.
- Older `TEACH add TAKES a, b` and `CALL add WITH 2, 3` correspond to
`TEACH add a b` and `DO add 2 3`.
This complete older-style program is covered by an executable compatibility test:
```claro
TEACH add TAKES a, b
RETURN a + b
LEARNED
CALL add WITH 2, 3
SAY RESULT
SET score TO 8
ASK "Number?" AS answer
IF answer = 4
DO 2 TIMES
SAY score
DONE
ELSE
SAY "no"
ENDIF
```
Enter `4`. It prints `5`, asks `Number?`, then prints `8` twice.
Do not automatically replace every `AS` or `TO`: commands such as
`READ FILE "note.txt" AS note` and `ADD "milk" TO items` still need those words.
+1 -1
View File
@@ -1,6 +1,6 @@
# Claro Complete Platform Roadmap # Claro Complete Platform Roadmap
Current release: **Claro v1.18.26** Current release: **Claro v2026.10.25.014405**
This roadmap tracks the larger goal: Claro as a complete beginner-friendly programming platform, not only a toy scripting interpreter. This roadmap tracks the larger goal: Claro as a complete beginner-friendly programming platform, not only a toy scripting interpreter.
+52 -267
View File
@@ -1,284 +1,69 @@
# Claro v1.18.26 Current Status # Claro current status
This file is the beginner-safe status map for the current package. It separates what is ready to use from what is historical, experimental, or still planned. Development version: **v2026.10.25.014405**
## How to read the status labels Claro is a **learning tool first**. The development timestamp identifies source;
it does not mean that a release was published or every platform was tested.
See [release status](RELEASE_STATUS.md) for dated, artifact-specific evidence.
- **Stable foundation**: good enough for normal examples and beginner learning. ## Beginner core — tested learning path
- **Foundation present**: usable, but still missing important polish or coverage.
- **Experimental/planned**: do not rely on it in beginner lessons yet.
- **Historical**: kept for release history, not current instructions.
## Security and correctness review progress - Input/output, variables, arithmetic, decisions, loops, lists, simple functions,
and text-file save/load have executable lesson/example coverage.
- Teach short `ASK` and `SET`, `TEACH` with `DO`, and `END` for introductory blocks.
[Older forms](COMPATIBILITY.md) remain supported, outside the first lessons.
- The [first hour](FIRST_HOUR.md), [ten lessons](../lessons/README.md), generated
starter and introductory examples are checked for behavior, not just syntax.
- The runtime remains one C89 file: [src/claro.c](../src/claro.c).
The v1.18.26 review identified missing-argument reads in standard-library built-ins. The current runtime now validates required argument counts centrally before any builtin indexes `args[]`. Covered calls include `math.abs`, `math.clamp`, `random.seed`, `random.int`, text helpers, CSV helpers, path helpers, and collection helpers. Missing arguments produce a beginner-facing `needs N arguments` runtime error. This is a foundation for learner trials, not proof of accessibility or classroom
readiness. Intended learners have **not yet validated** accessibility; use the
[classroom pilot checklist](CLASSROOM_READINESS.md) to gather that evidence.
Focused regression coverage: `tests/38_builtin_arity.claro`, `tests/39_random_inverted_range.claro`, and `tests/40_call_depth_guard.claro`. ## Reliability and trust boundary
The runtime now rejects inverted `random.int` ranges before modulo arithmetic with: `random.int needs the lower bound to be less than or equal to the upper bound.` This prevents invalid ranges from producing incorrect values or a divide-by-zero signal. Maintained checks cover ownership cleanup, malformed expressions, nested blocks,
call depth and the learning path under leak-enabled sanitizers. See
[reliability](RELIABILITY.md) for scope and [testing](TESTING.md) to reproduce it.
Earlier failure logs are history, not current unresolved findings.
User-defined function and object-method calls now have a documented maximum active call depth of 256. Exceeding it produces: `Claro function call depth exceeded the safe limit of 256. Simplify the recursion or add a stopping condition.` Ordinary non-recursive beginner programs are unaffected. Claro runs trusted scripts with the user's permissions. It is **not a sandbox**.
Loops can run indefinitely by default. Opt-in step and READ FILE budgets are
available; parsed JSON nesting is capped. Global file/output/memory and CPU-time
budgets are not provided. See [actual runtime guards](RUNTIME_LIMITS.md).
Verified in this checkout on 2026-09-22: ## Platforms — do not confuse compilation with OS acceptance
- `gcc -std=c99 -O0 -g -fsanitize=address,undefined src/claro.c -o /tmp/claro-call-depth-asan -lm` plus `ASAN_OPTIONS=detect_leaks=0 /tmp/claro-call-depth-asan tests/40_call_depth_guard.claro`: expected diagnostic; no AddressSanitizer or UndefinedBehaviorSanitizer report. - Strict GCC C89 and extracted source validation have passed on Linux.
- `make -s all`: rebuilt both `claro` and `claro.exe` from current source. - Linux x86-64 static-musl downloads have been tested on the build host.
- `./claro test`: `PASS: 0 failure(s)`. - MinGW x64 and MSVC600 x86 executables have been built and tested under Wine.
- `./claro doctor`: all checks `OK`. - The dedicated [Windows 98 SE build](WINDOWS98.md) uses `build98.bat`.
- `./claro validate`: validation passed. - **Native modern Windows and Windows 98 SE execution remain unverified.**
- macOS, ARM, DOS and other untested targets are not implied by C89 compliance.
The `RUN COMMAND` path now decodes POSIX `pclose()` wait status before storing `LASTEXIT`, so a child that exits with code 3 exposes `3` rather than the encoded status `768`. Focused coverage is `tests/42_last_exit_code.claro`. This slice does not yet fix memory cleanup, HTTP buffering/status handling, or other review findings. Claro remains a trusted-script interpreter, not a sandbox. Exact versions and tested environments are in the [release ledger](RELEASE_STATUS.md).
The [portability guide](PORTABILITY.md) explains the standard archive targets.
## Feature matrix ## Existing optional features versus plans
### Beginner scripting core Types, objects, local projects/packages, HTTP and editor helpers remain available
in their reference guides; beginners do not need them. Existing deterministic
task helpers are not a claim of native parallel execution or a full scheduler.
Status: **Stable foundation** Graphics/SDL, a web server, remote registry, richer concurrency and a full editor
integration are plans or experiments, not beginner-ready platform promises.
They are **not active expansion priorities** without a separate request.
Ready now: ## What is next
- variables with `SET`
- output with `SAY`
- input with `ASK`
- choices with `IF` / `ELSE` / `END`
- loops
- lists and maps
- files, JSON, text helpers, and tested path/collection library helpers
- friendly checking with `claro check`
Good starting docs: 1. Use the [learner pilot kit](LEARNER_PILOT.md) with a willing person; code checks
- `QUICK_START.md` are not human feedback. Source-located expression diagnostics now have regressions.
- `FIRST_HOUR.md` 2. Use the [native Windows kit](NATIVE_WINDOWS_TESTING.md) on an authorized target.
- `CLI.md` Wine is explicitly rejected by its OS evidence helper.
- `SPEC.md` 3. Hosted CI/release delivery needs authenticated Gitea access and enabled Actions.
See the [six-track status](SIX_TRACK_STATUS.md) for current blockers.
### Functions Follow the [small active roadmap](ROADMAP.md). The
[previous detailed status](history/CURRENT_STATUS_BEFORE_STATUS_CLEANUP.md) and
Status: **Stable foundation** [hardening log](history/HARDENING_BEFORE_STATUS_CLEANUP.md) preserve older claims,
implementation notes and investigations; they are not current acceptance criteria.
Ready now:
- simple modern form: `TEACH name arg` ... `END`
- compatibility form: `TEACH name TAKES arg` ... `LEARNED`
- calls with `DO` or older `CALL ... WITH`
Good starting docs:
- `SIMPLE_FUNCTIONS.md`
- `README.md`
### Static type safety
Status: **Foundation present**
Ready now:
- typed variables such as `SET score NUMBER 10`
- `TYPE OF` and `CHECK TYPE`
- `CHECK TYPE` gives a direct repair hint when the learner forgets the `IS` keyword, for example `CHECK TYPE score` reports `CHECK TYPE needs IS. Try: CHECK TYPE score IS NUMBER.`
- `CHECK TYPE` gives a direct repair hint when `IS` has no expected type, for example `CHECK TYPE score IS` reports `CHECK TYPE score needs a type after IS. Try: CHECK TYPE score IS NUMBER.`
- `CHECK TYPE` explains when the learner leaves out the expression as well, for example bare `CHECK TYPE` reports `CHECK TYPE needs an expression, IS, and a type. Try: CHECK TYPE score IS NUMBER.`
- typed function and method returns reject simple extra tokens after a return value with a direct repair hint, such as `RETURN amount extra` reporting `Keep only one expression after RETURN.` This is covered for modern functions and both modern and compatibility object methods.
- `CHECK TYPE` explains when `IS` is present but the expression is missing, for example `CHECK TYPE IS NUMBER` reports `CHECK TYPE needs an expression before IS. Try: CHECK TYPE score IS NUMBER.`
- `CHECK TYPE` rejects extra words after a valid expected type with a direct repair hint, so `CHECK TYPE score IS NUMBER TEXT` explains that only one type belongs in the check
- `CHECK TYPE` rejects unknown expected type names with a beginner-facing list of supported types
- typed function return diagnostics also identify an unknown return expression in compatibility `TAKES` / `LEARNED` functions, matching the modern function and object-method guidance
- `TYPE OF` reports a direct repair hint when the learner forgets `AS`, for example `TYPE OF score` suggests `TYPE OF score AS kind`
- bare `TYPE OF` reports that the expression, `AS`, and result name are all required, with a complete repair example
- `TYPE OF` reports a direct repair hint when the learner forgets the expression before `AS`, for example `TYPE OF AS kind` reports `TYPE OF needs an expression before AS. Try: TYPE OF score AS kind.`
- `TYPE OF` prioritizes the missing expression diagnostic even when extra words follow the incomplete form, so `TYPE OF AS kind extra` still points the learner to the missing expression first.
- `TYPE OF` reports a direct repair hint when the learner forgets the result name after `AS`, for example `TYPE OF score AS` reports `TYPE OF needs a result name after AS. Try: TYPE OF score AS kind.`
- A valid `TYPE OF score AS kind` statement has a focused positive typecheck fixture, so the complete form is protected from diagnostic-only regression coverage.
- `TYPE OF` remains case-insensitive like other Claro keywords, with focused positive coverage for lowercase `type of score as kind` syntax.
- `CHECK TYPE` remains case-insensitive like other Claro keywords, with focused positive coverage for lowercase `check type score is number` syntax.
- Object declarations and field checks remain case-insensitive like other Claro keywords, with focused positive coverage for lowercase `class`, `has`, `end`, `new`, `set`, and `check type` in one typed object-field example.
- `TYPE OF` rejects extra words after its result name with a direct repair hint, so `TYPE OF score AS kind extra` explains that only one result name belongs there; typed `TEACH ... RETURNS TYPE` declarations likewise reject extra words after the return type with a direct repair hint
- typed `TEACH ... RETURNS` declarations reject a missing return type with a direct repair hint, such as `Function greet needs a return type after RETURNS. Add a type such as NUMBER.` The same diagnostic names the complete class and method, including compatibility `TAKES` / `LEARNED` methods: `Method Player.score needs a return type after RETURNS. Add a type such as NUMBER.`
- typed list/map checks through `claro typecheck`, including a nested `LIST OF MAP` insertion example
- compatibility `TAKES` / `LEARNED` functions also have focused positive numeric-addition, subtraction, division, and multiplication return fixtures, keeping arithmetic return coverage aligned with modern functions and compatibility methods.
- compatibility `TAKES` / `LEARNED` functions have positive complete-branch return coverage beside the incomplete-branch diagnostic, so older conditional examples are checked for both accepted and rejected paths.
- a narrow function/method argument check: `CHECK TYPE parameter IS TYPE` inside a function or simple object method lets `claro typecheck` accept correct checked calls, catch mismatched `DO`, `CALL ... WITH`, `DO object.method ...`, and compatibility `CALL ... WITH` arguments, including checked methods that appear after another method in the same class, report missing checked function and method arguments, report missing unchecked arguments for simple functions and simple object methods in both modern `DO` and compatibility `CALL ... WITH` forms, treat empty compatibility calls such as `CALL greet WITH` and `CALL player.rename WITH` as missing-argument mistakes, catch extra arguments to simple functions and checked methods even when the checked method appears after another method in the same class, catch modern `DO` and compatibility `CALL ... WITH` calls to undeclared simple functions, explain when `DO object.method ...` or compatibility `CALL ...` happens before the object is created with `NEW` even if the method name is also wrong, and catch modern plus compatibility calls to undeclared object methods with a class-specific `TEACH` hint
- simple function and object-method return checks with `TEACH name ... RETURNS TYPE` now include a compatibility `TAKES` / `LEARNED` function whose conditional returns are incomplete, preserving the same every-path diagnostic as modern functions.
- function and method declarations now reject repeated parameter names with a direct repair hint before calls are checked, including modern and compatibility `TAKES` / `LEARNED` functions and methods; object methods with the same name are also rejected within a class with a rename hint in both modern and compatibility method syntax
- top-level function declarations now reject repeated names with a direct repair hint, so two `TEACH greet` blocks cannot silently compete for one callable function; a missing function name after `TEACH` gets a direct beginner-facing repair hint, and a missing method name inside `CLASS` names the class and gives a method example
- class declarations now reject a missing class name after `CLASS` with a direct beginner-facing repair hint
- class declarations now reject repeated field names within the same class with a direct repair hint before field types are used, so two `HAS score ...` lines cannot silently choose conflicting metadata; different classes may independently use the same field name
- class field declarations now reject missing names such as bare `HAS`, missing types such as `HAS score`, unknown types such as `HAS score BANANA`, and extra tokens such as `HAS score NUMBER TEXT` with the field name, class name, supported type examples, and a repair hint before object-field checks use that metadata
- class declarations now reject repeated class names with a direct repair hint, so two `CLASS Player` blocks cannot silently compete for the same name; extra words after a class name also get a direct repair hint instead of becoming part of the class identity
- object creation now rejects repeated object names with a direct repair hint, so two `NEW Player player` statements cannot silently replace one another during type checking; when a script declares at least one class, a misspelled `NEW` class name gets a direct declaration hint without changing the older permissive no-class form; a missing class name or object name gets a direct example repair; extra words after the object name get a direct repair hint instead of being silently ignored
- a narrow object-field assignment/check-type check for simple `NEW Class object` plus direct `SET object.field value` and `CHECK TYPE object.field IS TYPE` cases when the class declares `HAS field TYPE`; validation now covers correct NUMBER, TEXT, and YESNO direct assignments, dedicated positive and explicitly typed NUMBER/TEXT field assignments, missing values in untyped direct field assignments with a repair hint, explicitly typed method-body assignments to undeclared fields with a `HAS field TYPE` repair hint in modern and compatibility method syntax, dedicated positive NUMBER/TEXT/YESNO `CHECK TYPE` metadata fixtures, simple and chained object aliases for both field assignments and `CHECK TYPE` metadata (including a chained TEXT-field check), aliased object-method calls including chained aliases in modern `DO` and compatibility `CALL ... WITH` forms (with a dedicated positive modern `DO` chained-alias fixture), field-to-field, compound arithmetic field expressions, and arithmetic/text expression result types, negative NUMBER/TEXT/YESNO field `CHECK TYPE` metadata mismatches, NUMBER/TEXT/YESNO-expectation unknown-field `CHECK TYPE` diagnostics, missing-object field assignment and `CHECK TYPE` diagnostics, NUMBER/TEXT/YESNO wrong-type diagnostics, field collection when a `HAS` field appears after a simple method, NUMBER/TEXT/YESNO-valued unknown-field diagnostics for direct assignments to undeclared fields including explicitly typed assignments, numeric and text results from simple arithmetic/text expressions in object-field assignments, plus method-body field assignment and `CHECK TYPE` diagnostics for NUMBER, TEXT, and YESNO fields in modern and compatibility syntax, including compatibility YESNO `CHECK TYPE` metadata coverage, direct method assignments to undeclared fields now produce a beginner-facing missing-field diagnostic when the assigned expression has a known type, and a plain beginner-facing unknown-field diagnostic when the assigned expression is not inferable yet in both modern and compatibility method syntax; method-body `CHECK TYPE` now reports an unknown field with a repair hint in both method syntaxes, including compatibility `TAKES` / `LEARNED` methods; explicitly typed assignments to declared method fields now validate the class-declared field type instead of trusting only the inline type annotation, so a wrong value such as `SET score TEXT \"oops\"` reports the method and field in the diagnostic.
- inline method-field annotations are checked against the class `HAS` declaration in modern and compatibility methods; conflicting known types and unknown annotation names produce field-specific repair guidance, with positive and negative focused fixtures for both syntaxes.
- direct object-field `AS ... TO` assignments reject extra words after the value with a repair hint, so `SET player.score AS NUMBER TO 10 extra` cannot silently ignore the trailing text; they explain when `TO` is missing after the type and when the value is missing after `TO`; short explicitly typed assignments such as `SET player.score NUMBER 10 extra` receive the same repair-oriented check and explain when the value is missing after the type
- Direct object-field validation also has positive coverage for explicit `NUMBER`, `TEXT`, and `YESNO` annotations, such as `SET player.ready YESNO YES`, plus the compatibility-shaped `AS ... TO` form such as `SET player.score AS NUMBER TO 10`, while the shorter unannotated form remains supported; method-body field assignments have matching positive coverage for both modern and compatibility `TEACH` forms using `SET score AS NUMBER TO 10`, and those two `AS ... TO` method fixtures are now included in `claro validate`; conflicting annotations such as `SET player.score TEXT "oops"` explain the class-declared type and how to repair it, and unknown annotations such as `SET player.score AS BANANA TO 10` or `SET player.score BANANA 10` identify the invalid type and suggest the declared field type.
Still needed:
- Keep the focused typecheck validator's fixture list complete as new positive and negative examples are added. `claro validate` now executes the complete focused fixture matrix as well, so release validation cannot silently omit a listed typecheck example. Short explicitly typed field assignments inside modern and compatibility methods also have focused coverage: `SET score NUMBER` explains that one value expression is required after the type, matching direct object-field assignment guidance.
- The expression checker now carries a known TEXT operand through all arithmetic operators so object-field and typed-function-return diagnostics do not hide addition, subtraction, multiplication, or division mistakes; focused numeric-subtraction, numeric-division, and numeric-multiplication positives plus text-operand addition, subtraction, multiplication, and division negatives protect this behavior. Typed-function return diagnostics now have focused subtraction and division positives alongside multiplication coverage. Text concatenation into a TEXT field has focused positive fixtures for both operand orders, including concatenation of two TEXT object fields. Each numeric operator names the text operand and explains that it needs NUMBER values.
- Method return validation now has focused positive subtraction and division fixtures alongside the existing arithmetic return coverage, including compatibility `TAKES` / `LEARNED` subtraction and division cases, so checked NUMBER method parameters and numeric subtraction or division remain accepted by the release validator in both method spellings.
- Modern and compatibility `TAKES` / `LEARNED` methods have positive NUMBER, TEXT, and YESNO field-assignment coverage, and modern plus compatibility method-body `CHECK TYPE` metadata now have positive YESNO coverage beside the wrong-value diagnostics.
- Modern method-body TEXT concatenation has positive coverage in both operand orders, such as `SET first first + "!"` and `SET last "Dr. " + last`.
- Compatibility method-body TEXT concatenation has matching positive coverage in both operand orders, so `TAKES` / `LEARNED` methods keep the same beginner-safe field update behavior.
- richer typed function signatures and return values beyond the focused simple function/method `RETURNS TYPE` check
- type checking through branches and loops
- richer object field type checking beyond simple direct assignments
- typed imports/modules
Good starting docs:
- `ADVANCED_STATIC_TYPING.md`
- `V1_14_STATIC_TYPES.md` (feature history plus examples)
### Objects and classes
Status: **Foundation present**
Ready now:
- `CLASS`
- typed `HAS` fields
- `NEW`
- field access such as `player.score`
- simple methods, including static diagnostics for checked method parameters
- direct object-field assignments with a narrow static diagnostic for wrong value types
- object helper commands
Still needed:
- broader object field type checking across aliases, methods, and more complex object flows
- constructor/default-value polish
- clearer object debugging and teaching examples
Good starting docs:
- `V1_15_OBJECTS_CLASSES.md`
- `README.md`
### Package and project workflow
Status: **Foundation present**
Ready now:
- `claro new`
- `claro package init`
- `claro package add/list/remove/doctor/lock`
- local project files such as `claro.project`, `claro.lock`, and `packages/`
- starter projects from `claro new` now use the same manifest-version and lock-version headers as `claro package init`
- the default `make` target builds both `claro` and `claro.exe`, so cross-platform release validators exercise freshly built executables
- project-name safety checks for `claro new`, so unsafe names such as `../bad` are rejected before Claro creates folders
- package-name safety checks when adding packages, including the 64-character package-name limit, when `claro package doctor` audits an existing `claro.project`, when `claro package lock` writes lockfile data, when `claro package list` shows existing packages, when `claro package init` sees unsafe names already present in `claro.project`, when `claro package add` sees unsafe names already present in `claro.project`, when `claro package remove` refreshes the lockfile after an edit, when learners need to remove an unsafe package entry that is already present, when `claro package doctor` verifies lockfile checksums for listed packages, and when `claro package doctor` rejects lockfile package entries that are not listed in `claro.project`
- `claro package doctor` rejects a listed package manifest whose `name:` does not match the package name in `claro.project`
- `claro package doctor` compares the complete manifest `name:` value, so a prefix such as `math-tools-extra` cannot be accepted for package `math-tools`
- `claro package doctor` requires exactly one complete supported package `manifest-version: 1` field; missing, blank, unsupported, or duplicate format fields get a `BAD package manifest version` diagnostic with the file path and a repair hint, rather than calling an existing file missing
- `claro package doctor` compares the complete manifest `checksum:` value, so a valid checksum followed by extra text is rejected
- `claro package doctor` requires the complete supported `manifest-version: 1` value in `claro.project`, so a prefix such as `manifest-version: 10` is rejected
- `claro package doctor` requires the complete supported `version: 1` value in each local package manifest, so a prefix such as `version: 10` is rejected
- `claro package doctor` requires exactly one current `version: v1.18.26` line in `claro.lock`, so stale, missing, blank, or duplicate lockfile release versions are rejected
- `claro package doctor` requires the complete supported `source: local` value in each local package manifest, so a prefix such as `source: local-extra` is rejected
- package project entries must be unique; `claro package doctor`, `list`, `add`, `init`, and `lock` reject duplicate names instead of generating ambiguous package or lockfile data
- lockfile package entries must also be unique; `claro package doctor` reports duplicate lock entries instead of accepting ambiguous package/checksum data
- package manifest `name:` entries must also be unique; `claro package doctor` reports `BAD package manifest name` instead of accepting ambiguous package identity data
- package manifest `checksum:` entries must also be unique; `claro package doctor` reports `BAD package checksum` instead of accepting ambiguous integrity data
- package manifest `version:` entries must also be unique and must contain exactly `1`; `claro package doctor` reports `BAD package version` instead of accepting ambiguous package metadata
- project manifests must contain exactly one `manifest-version: 1` field; duplicate or prefix-matching values are rejected with `BAD project manifest version: expected 1`
- project manifests must contain exactly one non-empty `name:` field; `claro package doctor` reports `BAD project manifest name: expected a non-empty name` when the project identity is missing or blank
Still needed:
- install from local path
- package version constraints
- publish/export format
- remote registry protocol
- checksum/signature verification before downloads
Good starting docs:
- `V1_16_PACKAGES_PROJECTS.md`
- `PACKAGE_REGISTRY.md`
### Networking
Status: **Foundation present**
Ready now:
- beginner HTTP commands
- offline `claro://` URLs for tests and lessons
- `LASTHTTP`
- real `http://` and `https://` through `curl` when available
Still needed:
- web server syntax
- routes
- request/response helpers
- localhost-safe server mode
Good starting docs:
- `V1_17_NETWORKING.md`
- `WEB_SERVER_PLAN.md`
### Concurrency and tasks
Status: **Foundation present**
Ready now:
- deterministic task/concurrency helpers where documented
- beginner-safe direction toward cooperative tasks
Still needed:
- full cooperative scheduler semantics
- cancellation
- timeouts
- message passing
- decision on whether native threads should ever be exposed
Good starting docs:
- `CONCURRENCY.md`
### IDE/editor support
Status: **Foundation present**
Ready now:
- `claro ide`
- metadata JSON
- completion list, including the current typed-language keywords `RETURNS`, `CHECK`, and `TYPE`
- diagnostics helper
- Forgejo/Gitea CI coverage for the current release validation gates (`claro validate`, typecheck diagnostics validation, version convention validation, package security validation, compiler warning validation, and the CI workflow coverage check); the compiler warning validator rebuilds `src/claro.c` and rejects path-truncation warnings, while the workflow validator verifies these commands, including its own command, occur in executable `run` steps rather than comments
Still needed:
- syntax highlighting package
- editor extension
- hover help
- go-to-definition
- code actions/fixes
- full LSP server if/when useful
Good starting docs:
- `IDE.md`
- `EDITOR_EXTENSION_PLAN.md`
### Graphics and SDL
Status: **Experimental/planned**
Ready now:
- placeholder/planned graphics documentation
- experimental examples may exist for future SDL work
Not ready in the stable executable:
- real SDL window/drawing support
- beginner game lessons that run everywhere
Good starting docs:
- `GRAPHICS.md`
- `SDL12.md`
## Historical docs
Files named `RC*_NOTES.md`, `RC*_VALIDATION.md`, older `V1_*_VALIDATION.md`, and older release notes are kept for project history. They may mention old version numbers, old planned milestones, or old validation scripts. Use them to understand how Claro evolved; do not treat them as the current beginner path.
## Which docs should a new learner trust first?
For the current v1.18.26 package, read docs in this order:
1. `QUICK_START.md`, `FIRST_HOUR.md`, and `lessons/README.md` for first programs.
2. `README.md` and this file for the current feature map.
3. `ROADMAP.md` for the next v1 work.
Use feature docs with these expectations:
- **Ready for beginner lessons:** `SPEC.md`, `SIMPLE_FUNCTIONS.md`, `ERRORS.md`, `LINTER.md`, `TESTING.md`, `FORMATTER.md`.
- **Foundation present:** `ADVANCED_STATIC_TYPING.md`, `V1_14_STATIC_TYPES.md`, `V1_15_OBJECTS_CLASSES.md`, `V1_16_PACKAGES_PROJECTS.md`, `V1_17_NETWORKING.md`, `CONCURRENCY.md`, `IDE.md`.
- **Plans or experiments:** `PACKAGE_REGISTRY.md`, `WEB_SERVER_PLAN.md`, `EDITOR_EXTENSION_PLAN.md`, `GRAPHICS.md`, `SDL12.md`, `COMPLETE_PLATFORM_ROADMAP.md`, `FUTURE_FEATURES_ROADMAP.md`.
If a doc sounds more ambitious than this status map, treat this file as the current source of truth and update the older doc before teaching from it.
+127 -12
View File
@@ -1,29 +1,144 @@
# Claro Errors # Errors and small repairs
Claro tries to report mistakes in beginner-friendly language. An error starts with `filename:line:`. Open that file and find that line.
The line belongs to the function or imported file where the error happened,
not automatically to the caller. Caught errors expose the same origin through
`LASTERRORFILE` and `LASTERRORLINE`.
Examples: Use `claro check my_program.claro` for command/block mistakes; running the
program also detects runtime expression/value errors. A checker is not a sandbox.
The examples below are executed by `tools/validate_error_guide.py`.
## Quote
Save as `quote.claro`. Run `claro quote.claro`.
```claro
SAY "Start"
SAY "unfinished
```
Message:
```text ```text
I do not know the command "PRNT". Did you mean "SAY"? quote.claro:2: Text is missing a closing quote. Add a double quote at the end.
``` ```
One repair:
```claro
SAY "Start"
SAY "finished"
```
## Operand
Save as `operand.claro`. Run `claro operand.claro`.
```claro
SET score 1
SAY score +
```
Message:
```text ```text
SET needs TO. operand.claro:2: This expression needs a value after the operator.
Try:
SET name TO "Alex"
``` ```
One repair:
```claro
SET score 1
SAY score + 1
```
## Command
Save as `command.claro`. Run `claro check command.claro`.
```claro
PRNT "Hello"
```
Message:
```text ```text
IF block opened here is missing ENDIF command.claro:1: I do not know the command "PRNT". Did you mean "SAY"?
``` ```
One repair:
```claro
SAY "Hello"
```
## Assignment
Save as `assignment.claro`. Run `claro check assignment.claro`.
```claro
SET name
```
Message:
```text ```text
I do not know the variable "socre" yet. Did you mean "score"? assignment.claro:1: SET needs a name and a value. Try: SET name "Alex"
``` ```
For safer learning, run this before running a new program: One repair:
```bash ```claro
./claro check my_program.claro SET name "Alex"
SAY name
``` ```
## Choice
Save as `choice.claro`. Run `claro check choice.claro`.
```claro
IF YES
SAY "Yes"
```
Message:
```text
choice.claro:1: IF block opened here needs END
```
One repair:
```claro
IF YES
SAY "Yes"
END
```
## Variable
Save as `variable.claro`. Run `claro variable.claro`.
```claro
SET score 10
SAY socre
```
Message:
```text
variable.claro:2: I do not know the variable "socre" yet.
```
One repair:
```claro
SET score 10
SAY score
```
These teach short `SET` and `END`. Older forms remain supported in
[compatibility syntax](COMPATIBILITY.md), not required as repairs.
For a caught error, read its metadata before another error changes it.
+52 -16
View File
@@ -1,19 +1,55 @@
# Claro Final Release Checklist # Release checklist
Before publishing `Claro v1.18.26` as the current stable release: This is an **unchecked template**, not a claim that every item is still missing
from earlier verified builds. Record evidence for the exact candidate; see
[release status](RELEASE_STATUS.md) for completed artifact checkpoints.
- Build from a clean unzip on Windows. ## Identify the candidate
- Build from a clean unzip on Linux or macOS.
- Run `claro --version`.
- Run `claro test`.
- Run `claro validate`.
- Run every lesson that does not require user input.
- Manually test `examples/quiz.claro`.
- Manually test `examples/simple_functions.claro`.
- Manually test `examples/text_polish.claro`.
- Confirm the README shows the Claro logo.
- Confirm docs use the `v1.xx.yy` version format.
- Confirm beginner docs teach simple syntax first.
- Do not add large new systems before stable v1.
Avoid adding large new systems during a stability release; prioritize buildability, tests, documentation consistency, and beginner safety. - [ ] Review the working tree and preserve unrelated local changes.
- [ ] Stamp a new run once with `python3 tools/stamp_version.py`, using UTC
`vYYYY.MM.DD.HHMMSS`; keep that stamp through rebuilding and verification.
- [ ] Confirm runtime, IDE metadata, generated starter/lockfiles and current docs
agree. Never re-label old verified binaries with a new timestamp.
- [ ] Name each intended target and distinguish build, Wine and native OS tests.
## Verify source and learning behavior
- [ ] Build both executable names required by the developer checks: `make -s all`.
- [ ] Run `python3 tools/validate_v1_17.py` (the current full gate despite its name).
- [ ] Run compiler-warning, CI-coverage and documentation-status checks from
[testing](TESTING.md). Confirm expected failures, skips and actual exit codes.
- [ ] Exercise interactive lessons and common mistakes with intended learners;
automated examples alone do not validate accessibility.
- [ ] Keep the recommended introductory syntax consistent and preserve aliases.
- [ ] Describe trust boundaries and unverified behavior without claiming a sandbox.
## Verify deliverables, not just the checkout
- [ ] Build clean archives with [distribution tooling](PORTABILITY.md).
- [ ] Extract each artifact, verify its inventory/hashes, and test its actual
executable, lessons, files and paths containing spaces.
- [ ] Record artifact/source SHA-256, compiler, environment, results and limitations.
- [ ] Include the license and applicable runtime notices; exclude compiler/SDK
bundles, credentials, caches and development build outputs.
- [ ] For the VC6 target, compile through [build98.bat](../build98.bat) without
a global `intptr_t` macro; inspect PE32, subsystem 4.10 and stack reserve.
- [ ] If claiming Windows or Windows 98 SE runtime support, execute the tests on
that actual OS. Otherwise label it unverified; Wine is not a substitute.
- [ ] Confirm checksums identify exact files, not publisher authenticity/signatures.
The generic archive helper covers source, Linux x64 and MinGW Windows x64.
The separately verified VC6 build ZIP has its own checksum manifest and report;
do not claim it was produced or accepted by that helper.
## Publish only with authorization
- [ ] Review and test the exact proposed commit/package snapshot.
- [ ] Obtain authorization before committing, pushing or publishing releases.
- [ ] Verify the remote branch after a push and download/test hosted artifacts
before claiming a hosted release or CI result.
- [ ] Update the dated release ledger with the actual version, hashes, scope,
publication location and remaining limitations. Keep older evidence intact.
Do not expand the language to complete a release. Reliability, clear learning
instructions and honest platform claims take priority.
+8 -5
View File
@@ -2,6 +2,9 @@
This is a simple first-hour lesson path for new learners. Keep each lesson short. Let the learner run the program after every change. This is a simple first-hour lesson path for new learners. Keep each lesson short. Let the learner run the program after every change.
Use the same short forms throughout this guide. See [the syntax rules](SYNTAX.md)
for a small reference; older forms are kept in [compatibility syntax](COMPATIBILITY.md).
## 1. Say hello ## 1. Say hello
```claro ```claro
@@ -15,8 +18,6 @@ ASK "What is your name?" name
SAY "Hello " + name SAY "Hello " + name
``` ```
Older examples may use `ASK "What is your name?" AS name`; that still works, but the shorter form is easier for new learners.
## 3. Store a variable ## 3. Store a variable
```claro ```claro
@@ -54,8 +55,6 @@ DO 3 TIMES
END END
``` ```
Older examples may use `DONE` for loop endings; that compatibility form still works.
## 7. Use a list ## 7. Use a list
```claro ```claro
@@ -79,7 +78,11 @@ DO add 2 3
SAY RESULT SAY RESULT
``` ```
Older examples may use `TEACH add TAKES a, b`, `LEARNED`, and `CALL add WITH 2, 3`; those forms still work. Expected output:
```text
5
```
## 9. Save and load text ## 9. Save and load text
+1 -1
View File
@@ -1,6 +1,6 @@
# Claro Graphics Status # Claro Graphics Status
Current release: **Claro v1.18.26** Current release: **Claro v2026.10.25.014405**
Graphics support is intentionally separated from the stable beginner console workflow. Graphics support is intentionally separated from the stable beginner console workflow.
+14 -7
View File
@@ -1,10 +1,17 @@
# Hardening Notes (Beta23) # Hardening notes: current entry point
## File loading safety The earlier Beta23 notes and later cleanup investigation are **historical**.
Claro now reads source lines dynamically (no 4KB truncation). Their former failure reports, resource-cap claims and pending-work statements
must not be read as the status of today's tested runtime.
Safety caps (to prevent memory abuse): - [Current reliability checkpoint](RELIABILITY.md): repairs, maintained checks
- Maximum single line length: 65,535 characters and their limitations, including the resolved OBJECT ownership regressions.
- Maximum program lines: 500,000 - [Actual runtime guards](RUNTIME_LIMITS.md): implemented bounds and absent limits.
- [Testing](TESTING.md): commands to reproduce current evidence.
- [Preserved hardening log](history/HARDENING_BEFORE_STATUS_CLEANUP.md): earlier
observations retained without rewriting them as modern acceptance results.
If a file exceeds these limits, the loader fails cleanly. Claro runs trusted scripts with the user's permissions and is **not a sandbox**.
Passing maintained regressions does not establish universal leak freedom or
untrusted-script safety. Treat newly reproduced failures as new findings with
exact versions, inputs, diagnostics and regression tests.
+32
View File
@@ -0,0 +1,32 @@
# Learner feedback — blank observation form
Status: NOT RUN. Leave observations blank until a consenting person participates.
Do not fill this form with predictions, agent simulations or invented participants.
- Anonymous session ID:
- Consent to retain anonymous notes: yes / no
- Build version and artifact checksum:
- OS/device and assistive tools (only if voluntarily relevant):
- Tasks attempted / skipped:
- Breaks, pacing and requested adjustments:
For each attempted task record:
- What the learner tried or said (anonymous, no health information):
- What worked independently:
- What help or clarification was requested:
- Exact confusing instruction/error and its filename/line:
- Whether the suggested repair helped:
- Learner's suggested change:
Use plain observations, not ability rankings. Do not infer a medical diagnosis.
Debrief:
- Clearest part:
- Most confusing part:
- One small change to try:
- Follow-up session agreed, if any:
- Notes retention/deletion agreement:
Automated example execution verifies code/output only, not these observations.
+92
View File
@@ -0,0 +1,92 @@
# Beginner pilot — observer kit
**Status: prepared, not learner-validated.** No participant feedback has been
collected by these automated checks. Do not infer accessibility from test passes.
Ask for willing participation and permission to keep anonymous notes. Explain
that we are testing the instructions, not the person. Collect no diagnosis,
medical history, real names or sensitive information. Let the learner decline,
ask for help, take breaks or stop. Use any assistive technology they prefer.
Use a platform-tested build, a fresh writable practice folder and reviewed scripts.
Show how to stop/close the interpreter before starting. Do not run downloaded
untrusted scripts. Explain that optional limits are not a sandbox.
Try the first three tasks at the learner's pace. Offer the rest only if wanted.
Let the learner predict, run and change the program. Observe before giving a
hint; never leave someone distressed just to measure independence. Record help
without judging it. Answers/output for observers are in `tests/learner_pilot.json`.
## Say hello
Run this once. Change the message to something you choose and run it again.
```claro
SAY "Hello!"
```
## Ask a name
Run it and type a pretend name. Explain where that name is remembered.
```claro
ASK "Name? " name
SAY "Hello " + name
```
## Change a number
Predict the result, run it, then change 2 to another small number.
```claro
SET score 2
SAY score + 1
```
## Make a choice
Optional: change score to 0. Predict which message appears.
```claro
SET score 2
IF score > 1
SAY "More than one"
ELSE
SAY "One or less"
END
```
## Repeat
Optional: change how many times the message appears. Ask how to stop a stuck run.
```claro
DO 3 TIMES
SAY "Again"
END
```
## Save a file
Optional: use a new writable practice folder. Find the file after running it.
```claro
WRITE FILE "practice.txt" WITH "My first file"
READ FILE "practice.txt" AS text
SAY text
```
## Gentle mistake and recovery
In the hello task, replace SAY with PRNT, run `claro check practice.claro`, and
ask what the message suggests. Restore SAY and rerun. This mistake is optional;
skip it if it adds stress. Do not teach alternative compatibility spellings here.
## Debrief and next change
Ask: What was clear? What was confusing? What would you change? Observe whether
the person can start/run/save, predict a result and use a repair hint. Do not
calculate a disability score or call one session proof of universal accessibility.
Use [the blank feedback form](LEARNER_FEEDBACK_TEMPLATE.md); keep only consented,
anonymous notes, record the exact build/platform, and choose one small improvement.
The [classroom checklist](CLASSROOM_READINESS.md) is a pilot guide, not certification.
+16
View File
@@ -0,0 +1,16 @@
# Claro Learning-First Development Charter
Claro is a learning tool first and a general-purpose programming language second. Prioritize an easy first experience, especially for learners with learning disabilities, without assuming one design suits every learner or claiming accessibility validation without learner testing.
## Active priorities
- Keep syntax, rules, and behavior simple, predictable, and consistent.
- Favor the small core: `SAY`/`ASK`, variables, basic arithmetic, decisions, loops, lists, simple functions, and text-file save/load.
- Teach one recommended syntax per concept. Keep supported compatibility forms working, but outside the initial learning path.
- Teach one concept at a time with short explanations, runnable examples, small exercises, visible results, and concrete expected output.
- Make errors identify the line, explain the problem without blame, and show a repair.
- Fix broken examples, inconsistent core behavior, and simple-core crashes before considering features.
- Do not pursue advanced type systems, concurrency, package registries, web frameworks, complex object models, or graphics systems absent an explicit request.
- Claro runs trusted scripts; it is not a sandbox.
Older roadmap documents preserve project history and describe prior ambitions. This charter guides current work without removing supported features or erasing that history.
+53
View File
@@ -0,0 +1,53 @@
# Native Windows acceptance kit
**Prepared, not executed on a native target.** Wine results do not satisfy this
checklist. A Windows login/authorized VM was not supplied for this run.
Use a **fresh extracted test folder**, not personal documents. Scripts write
`oscheck.txt`, `nver.txt`, `ntests.txt`, `ncheck.txt` and replace their own `native.ok`
marker. Preserve failed logs. A marker alone is not proof: review the OS evidence,
version, all discovered fixtures and every test result together.
## OS evidence helper
`tools/native_platform_probe.c` builds a separate `osprobe.exe`; it is not a
second interpreter source file. With VC6 configured:
```bat
cl /nologo /TC /ML /O1 tools\native_platform_probe.c /Feosprobe.exe /link /SUBSYSTEM:CONSOLE,4.10
```
The probe records numeric OS API fields, detects Wine's exported implementation
entry point, and refuses Wine with exit 2. `--require-win98se` additionally needs
Win32-Windows platform ID and version 4.10 build 2222. A real Windows VM is an
acceptable target; a compatibility layer is not. Old API version reporting on
modern Windows can be affected by application compatibility, so also record the
actual OS/version from its own system information screen. The probe is evidence,
not cryptographic attestation or a substitute for inspecting a real target.
## Automated checks
Put the compiled probe beside the matching interpreter and the complete package.
For Windows 98 SE run [test98.bat](../test98.bat). For modern x64 Windows run
[testwin.bat](../testwin.bat) with the MinGW `claro.exe` build.
These batch scripts avoid modern CMD-only extensions and reject a wrong target
before writing a PASS marker. Review `ntests.txt`: every packaged golden fixture
must appear, with zero failures. `ncheck.txt` must show built-in validation passed.
## Manual target observations — leave blank until actually tested
- Artifact version/SHA-256 and OS/VM identity:
- `oscheck.txt` reviewed, Wine false, target version independently confirmed:
- All packaged fixtures discovered/passed:
- Keyboard input, editing and non-ASCII display observations:
- Save/read a text file in a writable folder:
- File paths containing spaces:
- Start/stop a loop and recover to the console:
- First-hour examples and quiz interaction:
- Missing input or failed command recovery:
- Tester/date, consent to retain anonymous notes, unresolved failures:
Transfer only consented technical logs, not passwords, personal files or medical
information. Do not change an unknown remote machine or install services just to
make a test pass. The native target remains **unverified** until these observations
and actual target logs exist. See [Windows 98 compilation](WINDOWS98.md).
+1 -1
View File
@@ -1,6 +1,6 @@
# Claro Performance and Optimization Notes # Claro Performance and Optimization Notes
Current release: **Claro v1.18.26** Current release: **Claro v2026.10.25.014405**
Performance work should come after language behavior is stable and measured. Do not optimize blindly. Performance work should come after language behavior is stable and measured. Do not optimize blindly.
+102
View File
@@ -0,0 +1,102 @@
# Distribution and portability
See the dated [release ledger](RELEASE_STATUS.md) for exact verified archive
versions and publication status. A development stamp is not a hosted release.
## Supported verification targets
- **Source:** one C89 implementation file, `src/claro.c`. The distribution checker
compiles a copy in isolation, without the rest of the project, then builds and
validates the full extracted source package.
- **Linux x86-64:** a statically linked musl executable named `claro`. It does not
depend on the build host's glibc or a separately installed musl shared library.
- **Windows x86-64:** a genuine PE executable named `claro.exe`, built with MinGW.
Current execution verification uses Wine on Linux, not a native Windows machine.
The Linux and Windows archives include lessons, examples, documentation, regression
fixtures, source and tooling. No compiler or Python is required merely to run the
included executable and lessons. Developer checks require Python and build tools.
## Build archives
From the repository root on the tested Debian/Ubuntu x86-64 build host:
```bash
python3 tools/build_distribution.py --target source --output /path/to/releases
python3 tools/build_distribution.py --target linux-x86_64 --output /path/to/releases
python3 tools/build_distribution.py --target windows-x86_64 --output /path/to/releases
```
Binary builds require `musl-tools` or the x64 MinGW cross-compiler respectively.
The helper also includes installed toolchain copyright notices; its notice paths
currently match the tested Debian/Ubuntu packages. It refuses to overwrite an archive.
Each archive has:
- `START_HERE.txt` with target-specific instructions.
- `DISTRIBUTION.json` identifying the target, compiler, command and source hash.
- `MANIFEST.json` listing SHA-256 hashes for every other packaged file.
- The project license and, for binaries, third-party runtime notices.
A checksum detects changes; it is not a digital signature or proof of publisher identity.
Generated executables/caches from the development tree are not copied into releases.
In particular, the Linux archive does not contain a misleading Linux `claro.exe`.
## Windows 98 SE / MSVC6 compilation
Use the dedicated [Windows 98 build guide](WINDOWS98.md) and `build98.bat`.
This is a separate x86 build, not the MinGW x64 archive target. Compiler and
Wine verification are distinct from native Windows 98 SE acceptance. The separately
verified VC6 build ZIP carries its own checksum manifest/report; it is not a
target accepted by the generic builder or distribution checker below.
## Windows stack reserve
The default MinGW PE stack reserve was too small for the existing 256-call
recursion limit: the initial Windows test crashed before reaching Claro's guard.
A 16 MiB stack reserve allowed the call-depth regression and full golden suite
to complete under Wine. Windows build scripts and the archive builder now set:
```text
-Wl,--stack,16777216
```
This reserves address space; pages are committed as needed. Use the Windows build
scripts, or include this setting when cross-compiling directly. It is a linker
requirement, not a claim that C89 source alone establishes OS portability.
## Verify the actual archives
```bash
python3 tools/validate_distribution.py /path/to/archive.zip --report /path/to/report.json
```
For Windows execution from Linux, first initialize a separate Wine prefix and supply
`--wine-prefix /path/to/isolated-prefix`. The checker will not silently use the default
Wine profile. Reports must have a new filename.
The checker extracts into a directory containing spaces, verifies every manifest
entry, checks executable format (including the Windows stack reserve), and executes:
- The executable alone, with a tiny script and no project files.
- `--version`, `doctor`, all packaged golden fixtures, and built-in validation.
- The exact introductory learning path and all nine First Hour examples.
- Full Python release validation for the source archive.
Source archive validation rebuilds within the extracted copy; it does not rely on
the development checkout's executable. Binary tests run the shipped binaries.
## Limits of the evidence
Wine verifies Windows executable/CRT behavior in a compatibility environment. It
is not native Windows acceptance, Windows 98/XP support, or an installer test.
The PE header's minimum subsystem version does not establish compatibility.
Likewise, static Linux linking removes a glibc-version dependency but does not prove
operation on every distribution, old kernel, CPU or architecture. ARM, 32-bit Linux, macOS, DOS and real older Windows systems require separate
builds and tests. The separate x86 MSVC6 compilation path does not establish
Windows 98 SE execution.
Real HTTP still requires curl. Offline networking fixtures need no Internet access.
Claro remains a trusted-script runtime, not a sandbox. Full validation is evidence
for the exercised behavior, not a claim that every possible script is correct.
+2
View File
@@ -64,3 +64,5 @@ SAY LASTEXIT
``` ```
Use this carefully. It runs commands on the user's computer. Use this carefully. It runs commands on the user's computer.
`RUN COMMAND` is for trusted code only. It is not a sandbox: it can start programs, read or change files, and use the same permissions as the user running Claro. Do not run scripts from an untrusted source, and do not treat this feature as a security boundary.
+24 -12
View File
@@ -6,6 +6,17 @@ Programming Made Simple
Claro is a small scripting language designed for beginners. It uses readable commands like `SAY`, `ASK`, `SET`, and `TEACH`. Claro is a small scripting language designed for beginners. It uses readable commands like `SAY`, `ASK`, `SET`, and `TEACH`.
## Before you start
Build Claro with `make` from the project folder, or use its C89 build command
in the [README](../README.md). Save the first example below as `hello.claro`
in that folder. Run the command shown beneath it. Try the later examples one
at a time, saving your changes before running again.
Use one spelling throughout these lessons: short `ASK` and `SET`, `TEACH`
with `DO`, and `END` for choices, counted loops, and functions. The
[syntax guide](SYNTAX.md) keeps these rules in one place.
## 1. Show a message ## 1. Show a message
```claro ```claro
@@ -21,25 +32,27 @@ Run it:
## 2. Ask a question ## 2. Ask a question
```claro ```claro
ASK "What is your name?" AS name ASK "What is your name?" name
SAY "Hello " + name SAY "Hello " + name
``` ```
## 3. Remember information ## 3. Remember information
```claro ```claro
SET score TO 10 SET score 10
SAY score SAY score
``` ```
## 4. Make a choice ## 4. Make a choice
```claro ```claro
SET score 8
IF score >= 5 IF score >= 5
SAY "Good job!" SAY "Good job!"
ELSE ELSE
SAY "Try again." SAY "Try again."
ENDIF END
``` ```
## 5. Repeat something ## 5. Repeat something
@@ -47,29 +60,28 @@ ENDIF
```claro ```claro
DO 3 TIMES DO 3 TIMES
SAY "Practice" SAY "Practice"
DONE END
``` ```
## 6. Use a list ## 6. Use a list
```claro ```claro
SET items TO LIST SET items LIST
ADD "milk" TO items ADD "milk" TO items
ADD "bread" TO items ADD "bread" TO items
FOR EACH item IN items FOR EACH item IN items
SAY item SAY item
DONE END
``` ```
## 7. Teach a function ## 7. Teach a function
```claro ```claro
TEACH greet TAKES name TEACH greet name
RETURN "Hello " + name RETURN "Hello " + name
LEARNED END
DO greet "Alex"
CALL greet WITH "Alex"
SAY RESULT SAY RESULT
``` ```
@@ -85,13 +97,13 @@ SAY note
```claro ```claro
SAY "Tiny Quiz" SAY "Tiny Quiz"
ASK "What is 2 + 2?" AS answer ASK "What is 2 + 2?" answer
IF answer = 4 IF answer = 4
SAY "Correct!" SAY "Correct!"
ELSE ELSE
SAY "The answer is 4." SAY "The answer is 4."
ENDIF END
``` ```
## Tips ## Tips
+50
View File
@@ -0,0 +1,50 @@
# Claro documentation
## Learn — start here
1. [Quick start](QUICK_START.md) — build and run your first program.
2. [First hour](FIRST_HOUR.md) — try one concept at a time.
3. [Ten lessons](../lessons/README.md) — small programs in order.
4. [Examples](../examples/) — try a quiz, guessing game, or shopping list.
## Status, evidence and safety
- [Current status](CURRENT_STATUS.md) — the short, current scope and next priorities.
- [Release status](RELEASE_STATUS.md) — dated artifact verification and publication status.
- [Classroom pilot](CLASSROOM_READINESS.md) — learner testing, not accessibility certification.
- [Reliability](RELIABILITY.md) and [runtime guards](RUNTIME_LIMITS.md) — tested boundaries, not sandbox guarantees.
- [Portability](PORTABILITY.md) and [Windows 98 / VC6](WINDOWS98.md) — compilation versus target execution.
- [Learner pilot kit](LEARNER_PILOT.md) and [blank feedback](LEARNER_FEEDBACK_TEMPLATE.md)
- [Native Windows acceptance kit](NATIVE_WINDOWS_TESTING.md)
- [Six-track progress and blockers](SIX_TRACK_STATUS.md)
## Look something up
- [Beginner syntax](SYNTAX.md) and [older compatibility forms](COMPATIBILITY.md)
- [Commands](CLI.md)
- [Errors and repairs](ERRORS.md)
- [Simple functions](SIMPLE_FUNCTIONS.md)
- [Text helpers](TEXT_HELPERS.md)
- [File and scripting helpers](PRACTICAL_SCRIPTING.md)
## For contributors
- [Build and contribute](../CONTRIBUTING.md)
- [Testing](TESTING.md) and [release checklist](FINAL_RELEASE_CHECKLIST.md)
- [Learning-first rules](LEARNING_FIRST.md) and [small active roadmap](ROADMAP.md)
- [Language specification](SPEC.md)
- [Versioning](VERSIONING.md)
## History and optional features
Older RC notes, versioned reports and expansion plans are reference, not the
active roadmap or prerequisites for learners. Existing features remain supported;
they have not been removed to simplify the teaching path.
- [Previous roadmap](history/ROADMAP_BEFORE_LEARNING_FIRST.md)
- [Previous detailed status](history/CURRENT_STATUS_BEFORE_STATUS_CLEANUP.md)
- [Preserved hardening log](history/HARDENING_BEFORE_STATUS_CLEANUP.md)
If an older plan conflicts with the current status or learning-first rules,
treat it as historical rather than as a promised feature.
+91
View File
@@ -0,0 +1,91 @@
# Release status and verified builds
## Publication snapshot — 2026-10-20
The accumulated learning-syntax, reliability, distribution and Windows 98 build
changes are **not published** to Gitea `main` in the check performed for this cleanup.
The local HEAD and a read-only `git ls-remote --heads gitea main` both returned:
```text
8ea2f772cd8552d4651bf551bed9a8a03b4ed2a9
```
That commit is the earlier single-file C89 refactor, not the current dirty tree.
This is a dated branch check, not a live remote badge. Hosted tags, release pages
and hosted CI were not audited by that check; no new hosted release or CI pass
is claimed. Refresh the remote state before any authorized publication.
A development timestamp is not a stable-release designation. Use
[current status](CURRENT_STATUS.md) for the learning scope and
[the release checklist](FINAL_RELEASE_CHECKLIST.md) before publishing.
## Candidate continuation — 2026-10-21
The last pushed source checkpoint is `d55507140660186e594303306647e0f0e9facf76`.
The continuation candidate is a separate development run; see
[six-track status](SIX_TRACK_STATUS.md) for scope and blocked acceptance.
Gitea currently reports Actions disabled, and authenticated runs/release access
was not available for this continuation. No hosted CI success or hosted release
is claimed. Earlier artifact records below retain their original hashes/versions.
## Immutable artifact checkpoints
These are verified build artifacts previously delivered for testing, **not a
claim of a hosted release**. Their reports/logs were delivered outside Git.
The filenames and SHA-256 values identify the exact archives; they are not
public download URLs or digital signatures. Do not substitute a newly stamped
binary for one of these archived checkpoints.
### Source — v2026.10.20.184951
- Archive: `Claro-v2026.10.20.184951-source.zip`.
- SHA-256: `3531d2fc82e2d008fee8affc82864a56e7d8f5c917ef5556f5cfac0b3b828d38`.
- Extracted-source verification on Linux: isolated strict C89 compilation, make,
golden/learning/First Hour checks and the full release gate, including 35
maintained reliability checks. This is not proof for an arbitrary OS/compiler.
- Report: `source-verification.json`.
### Linux x86-64 — v2026.10.20.180131
- Archive: `Claro-v2026.10.20.180131-linux-x86_64.zip`.
- SHA-256: `9f53394643e1bd9360940ed90e77af89a43ad160f976bf12fabe500bda0d16e6`.
- Static-musl ELF; extracted payload passed all 51 golden fixtures, learning
checks, nine First Hour examples, built-in validation and standalone checks
on the Linux x86-64 build host. Other distributions/kernels/CPUs are unverified.
- Report: `final-linux-verification.json`.
### MinGW Windows x86-64 — v2026.10.20.180131
- Archive: `Claro-v2026.10.20.180131-windows-x86_64.zip`.
- SHA-256: `59b452ccfe96848fa45db3915aa7d03610cc5fba35cf6a294718d1cff67c6ec0`.
- Genuine PE x64, 16 MiB stack; extracted executable passed all 51 golden fixtures,
learning checks, nine First Hour examples and built-in/standalone checks under
**Wine on Linux**. Native Windows execution remains **unverified**.
- Report: `final-windows-verification.json`.
### MSVC600 / Windows 98 SE compilation — v2026.10.20.184951
- Archive: `Claro-v2026.10.20.184951-Win98SE-VC6-build.zip`.
- SHA-256: `914d14373ec6f202fea4ff74ed0ffbde20e0ff3a9d708b9a2ac72ddf37c488cd`.
- Actual Microsoft compiler **12.00.8804** compiled/linked through `build98.bat`
without an `intptr_t` alias flag. PE32 x86, subsystem 4.10, static VC6 CRT,
16 MiB stack reserve, only KERNEL32.dll imported.
- The executable passed all 51 golden fixtures, the introductory checker, nine
First Hour examples and built-in validation under **Wine**. The final ZIP was
re-extracted, its manifest/source/binary hashes checked, and its golden suite,
doctor, version and built-in validation rerun.
- **Actual Windows 98 SE execution remains unverified.** PE headers and Wine
behavior do not establish the real OS loader, CRT, console or filesystem behavior.
- Reports: `WINDOWS98_BUILD_REPORT.md` and `package-verification.json`.
- Frozen source SHA-256: `b3efcf9f3d60627a2b98159d3638c3b3639cb329998017c163d815046c32d1bf`.
## Reproduce or extend evidence
[Testing](TESTING.md), [portability](PORTABILITY.md) and the
[Windows 98 build guide](WINDOWS98.md) describe the commands and boundaries.
The standard archive helper covers source/Linux x64/MinGW x64. The separate VC6
build ZIP uses its own checksum manifest and report, not the generic validator.
macOS, ARM, native modern Windows, Windows 98 SE/XP, DOS and other targets need
separate execution evidence. Accessibility needs intended-learner feedback;
automated tests alone do not establish it. Claro is trusted code, not a sandbox.
+68
View File
@@ -0,0 +1,68 @@
# Reliability checkpoint
Use [release status](RELEASE_STATUS.md) for artifact-specific evidence and
publication state. This page describes tested boundaries, not a universal pass.
The known OBJECT cleanup investigation is resolved for the maintained regression
cases. Historical notes in `HARDENING.md` describe earlier failures, not the current
status of those tested boundaries.
## Repairs and coverage
- Borrowed type/class metadata no longer creates abandoned deep copies.
- Checked assignments, object copies, construction, and OBJECT result temporaries
release owned values after storage has copied them.
- Function declaration and typechecker parameter arrays, per-file type metadata,
text transforms, sorting, and REPL teardown have explicit cleanup.
- A lone `!` no longer leaves the tokenizer at the same input position until memory
is exhausted. It produces a `NOT` repair hint; `!=` remains supported.
- Missing quotes/parentheses/operands and trailing expression tokens are diagnosed.
- Expression recursion and nested control blocks have bounded guardrails. Deep
block input no longer writes beyond the checker's fixed array.
- Existing function recursion, HTTP response-size, and trusted-command boundaries
retain their focused checks.
## Reproduce verification
```bash
make -s all
python3 tools/validate_reliability.py
python3 tools/validate_v1_17.py
```
The current reliability runner executes 35 maintained checks and records each exit
status and log in a scratch result directory. It includes the full introductory
learning-path checker and First Hour under ASan/UBSan/LeakSanitizer, with leak
detection enabled. Full release validation invokes this runner; CI has explicit
reliability, single-file C89, and learning-path checks.
The OBJECT regression verifies class name, field count and field content, not a
fictional printed list representation. Five isolated ownership tests separate
metadata, field storage and object construction so one leak cannot hide another.
Malformed-expression and nesting tests check failure diagnostics and valid inputs.
## Diagnostics, JSON and opt-in budgets
Expression errors keep source origins across calls, imports and TRY/CATCH.
The error guide is executable-verified. Malformed JSON now fails instead of
hanging, nesting is bounded, control-character round trips are checked, and
Unicode escapes/surrogate pairs are covered. JSON temporaries, serialization
copies and popped import-stack entries have focused leak-enabled regressions.
The optional step budget counts empty loop entries; READ FILE can enforce a
per-read byte cap. These are not process isolation or wall-clock bounds.
`validate_error_locations.py`, `validate_error_guide.py`, `validate_learner_pilot.py`
and `validate_resource_guards.py` run in the full gate and inside the existing
learning-ownership sanitizer check. The reliability runner still counts 35
maintained top-level checks; subcase coverage has expanded, not been relabeled
as 35 new independent platform tests. Human feedback and native Windows tests
remain separate, blocked acceptance tracks.
## Scope
A passing run establishes the exercised boundaries, not universal leak freedom,
security or correctness for arbitrary programs. POSIX sanitizer coverage does not
establish native Windows behavior. See [runtime limits](RUNTIME_LIMITS.md) for
implemented guards and [portability](PORTABILITY.md) for distribution evidence.
Intentional infinite loops and untrusted-script execution are not made safe by
these checks. Historical beta environment-limit claims are not current guarantees.
+20 -182
View File
@@ -1,191 +1,29 @@
# Claro Roadmap # Claro Roadmap
Current release: **Claro v1.18.26** Claro is a **learning tool first**, not a race to add language features.
The [learning-first charter](LEARNING_FIRST.md) guides every change.
Claro is now in the stable v1 development line. Older RC milestones are kept as historical notes in files such as `RC0_NOTES.md`, `RC3_NOTES.md`, and `RC9_VALIDATION.md`; they are not the current roadmap. ## Active priorities
## Current direction 1. **Make the first hour dependable.** Execute every lesson example and check its output, including input and simple functions.
2. **Keep the rules consistent.** Teach one recommended syntax per concept; preserve older forms without putting alternatives in the first lessons.
3. **Help learners recover.** Errors should identify the relevant line, explain the problem plainly, and show a small correction.
4. **Teach in small steps.** One concept at a time, short exercises, and useful console projects such as quizzes, lists, and text-file tools.
5. **Keep the core reliable.** Fix crashes and incorrect behavior with focused regression tests. Keep existing safety checks and compatibility tests.
6. **Learn from real learners.** Test with people with different learning needs; record feedback without claiming accessibility has already been validated.
Keep Claro beginner-first while growing into a complete small programming platform: ## Out of scope without a new request
- readable syntax before clever syntax Advanced type systems, complex object models, concurrency, web frameworks,
- friendly diagnostics before advanced compiler features remote package registries, and new graphics systems. Existing implementations
- safe local/offline defaults before networked behavior are preserved, not removed.
- small features that are easy to teach, test, and document
- normal feature releases inside the v1 line; reserve v2 for a future full rewrite
## v1.18.26 status snapshot ## How work lands
See `CURRENT_STATUS.md` for the detailed feature matrix. Make one small, tested improvement at a time. Update the relevant guide and
run the [contributor checks](../CONTRIBUTING.md). A passing syntax check alone
is not proof that a teaching example works.
- Beginner scripting core: stable foundation See [current status](CURRENT_STATUS.md) for implementation details.
- Objects/classes: foundation present; needs polish and stronger type checks The [previous roadmap](history/ROADMAP_BEFORE_LEARNING_FIRST.md) is retained
- Package workflow: local foundation present; registry and publishing not complete as history, not an active development checklist.
- Networking: HTTP client foundation present; beginner web server not complete
- Concurrency: deterministic task foundation; native threads not exposed as beginner API
- Static typing: variables and containers foundation; function/object/import typing incomplete
- IDE support: metadata/helper foundation; full editor/LSP experience incomplete
- Graphics/SDL: experimental/planned; not enabled in the stable executable
## Near-term cleanup priorities
1. Keep beginner-facing docs current and separate from historical release notes.
2. Keep examples aligned with the modern simple syntax (`END`, `DO`, short `SET`, short `ASK`) while documenting older compatibility forms separately.
3. Expand validation around typecheck diagnostics and package/networking safety. Current Forgejo/Gitea CI runs the documented release gates (`claro validate`, typecheck diagnostics validation, version convention validation, package security validation, compiler warning validation, and CI workflow coverage validation). The compiler warning validator rebuilds `src/claro.c` and blocks path-truncation warnings; the workflow coverage validator checks executable `run` steps rather than comments and requires its own command. Current package safety validation covers safe project creation, rejecting unsafe project names during `claro new`, safe manifest/lockfile creation, rejecting unsafe package names during `package add`, detecting unsafe package names already present in `claro.project` during `package doctor`, refusing to write lockfile data for unsafe package names during `package lock`, making `package list` fail instead of displaying unsafe package entries as normal package names, making `package init` fail instead of reporting the project ready when unsafe package names are already present, making `package add` fail before changing files when unsafe package names are already present, making `package remove` fail instead of reporting success when unsafe package names remain during lockfile refresh, allowing `package remove` to remove an exact unsafe package entry so learners can repair a bad `claro.project`, making `package doctor` reject stale lockfile checksums for listed packages, and making `package doctor` reject lockfile package entries that are not listed in `claro.project`. Current function validation covers correct checked calls, wrong-type arguments, missing checked-argument diagnostics, missing unchecked-argument diagnostics for simple functions in both modern `DO greet` and empty compatibility `CALL greet WITH` forms, extra-argument diagnostics, and beginner-facing unknown-function diagnostics for mistyped modern `DO` and compatibility `CALL ... WITH` calls; current object-method parameter validation covers correct modern `DO object.method ...` and compatibility `CALL object.method WITH ...` checked calls, wrong-type diagnostics for both call forms, checked methods after another method in the same class, missing and extra checked-argument diagnostics including the extra-argument case for a checked method after another method in the same class, missing unchecked-argument diagnostics for simple object methods, missing-object guidance when `DO player.method ...` or compatibility `CALL object.method WITH ...` appears before `NEW` even if the method name is also wrong, and class-specific unknown-method diagnostics for both modern and compatibility calls when a learner calls a method the class does not declare; current object-field validation covers direct NUMBER/TEXT/YESNO field-assignment positives, direct object-field `CHECK TYPE` metadata positives for NUMBER/TEXT/YESNO fields, negative NUMBER/TEXT/YESNO field metadata mismatches, NUMBER/TEXT/YESNO-expectation unknown-field `CHECK TYPE` diagnostics, direct wrong-type field assignment diagnostics, field collection when a `HAS` field appears after a simple method, direct unknown-field diagnostics for NUMBER/TEXT/YESNO values, a beginner-facing fallback for unknown fields assigned from expressions whose type is not inferable yet, and missing-object diagnostics for both `SET object.field value` and `CHECK TYPE object.field IS TYPE` before `NEW`.
3a. Keep package diagnostics actionable: invalid package manifest format fields now name the file and explain how to repair it, separately from an absent manifest. Keep package validation honest by checking that the project manifest has one non-empty project name, that the project and each listed package manifest declare the exact supported manifest version, package version, local source, and expected package name, and that lockfiles declare exactly one current release version.
3b. Keep package security validation in Forgejo/Gitea CI so unsafe names, exact manifest-version/name/version/checksum mismatches, missing manifests, duplicate project manifest versions and packages, duplicate lock packages, duplicate package manifest names, versions, and checksums, and lockfile errors remain release blockers.
4. Add small examples for each foundation feature before adding bigger syntax. The object-field foundation now includes positive and negative validation for explicitly typed NUMBER/TEXT assignments and field expressions such as `SET player.score player.name`, `SET player.score player.score + 1`, `SET player.name player.score + 1`, explicitly typed field assignments, explicit typed assignments to undeclared fields, simple and chained aliases such as `SET alias player`, `SET backup alias`, followed by `SET backup.score ...` or `CHECK TYPE backup.score IS ...`, chained aliases in object-method calls through both modern `DO` and compatibility `CALL ... WITH` forms (including a dedicated positive modern `DO` fixture), field-to-field, compound arithmetic field expressions, and arithmetic/text expression mismatches; typed containers also accept a map with nested type metadata when it is added to a `LIST OF MAP`; broader alias/control-flow checking remains planned.
5. Keep the focused typecheck validator complete: the method-body field diagnostic fixtures are now exercised by `claro validate` alongside the standalone diagnostic validator; continue wiring each new `typecheck_*.claro` fixture into both gates. Modern and compatibility unknown-field expression diagnostics now have matching focused coverage, explicitly typed unknown method-field assignments have matching modern and compatibility fixtures with the expected `HAS field TYPE` repair hint, and short explicitly typed method-field assignments have matching modern and compatibility missing-value coverage.
-6. Continue narrowing expression diagnostics: known TEXT operands now remain visible through arithmetic, with focused numeric-subtraction, numeric-division, and numeric-multiplication positives plus text-operand addition, subtraction, multiplication, and division coverage. Typed function returns now include positive subtraction and division examples alongside multiplication, including compatibility `TAKES` / `LEARNED` numeric-addition, subtraction, division, and multiplication return fixtures. Text concatenation into TEXT fields is covered in both operand orders, including field-to-field concatenation. Each numeric operator names the text operand and explains that it needs NUMBER values. Compatibility method return diagnostics now cover addition, subtraction, division, and multiplication. `CHECK TYPE` also rejects misspelled expected type names.
- Keep typed method return examples balanced: numeric addition, subtraction, division, and multiplication now have dedicated positive compatibility `TAKES` / `LEARNED` method fixtures in the focused validator, alongside compatibility addition, subtraction, and division mismatch coverage and the existing method return mismatch diagnostics.
- Keep method field examples balanced across syntax generations: modern and compatibility `TAKES` / `LEARNED` NUMBER, TEXT, and YESNO assignments now have positive fixtures beside the modern and compatibility mismatch fixtures, and modern plus compatibility YESNO method-field metadata now have dedicated positive fixtures beside the negative fixture.
- Keep IDE metadata aligned with the current beginner syntax: typed-language keywords such as `RETURNS`, `CHECK`, and `TYPE` are now included in the metadata used by editor helpers.
- Keep compatibility-call coverage aligned with modern calls: empty `CALL object.method WITH` forms now have a focused missing-argument diagnostic fixture alongside the modern `DO object.method` case.
Keep declared return types honest: `claro typecheck` now reports friendly diagnostics for unknown declared return types, missing return types, mismatched, not-yet-inferable, and empty return expressions (including compatibility `TAKES` / `LEARNED` methods), declarations with no `RETURN` in modern and compatibility function forms, and incomplete conditional branches, when a simple function or object method declares `RETURNS TYPE`; missing-type coverage includes modern functions and both modern and compatibility methods, with a compatibility-function fixture as well. Incomplete conditional-return coverage now also includes the compatibility `TAKES` / `LEARNED` function spelling, so older lessons receive the same every-path diagnostic. The declaration keyword is case-insensitive like other Claro keywords, with positive coverage in modern and compatibility function forms, including lowercase compatibility declarations and lowercase compatibility method declarations, plus a lowercase compatibility-method mismatch fixture. Complete `IF`/`ELSE` return branches, including nested complete conditionals in functions and methods, are accepted, and modern and compatibility function syntax plus compatibility method syntax are covered by focused fixtures. `CHECK TYPE` parameter metadata also informs arithmetic return-expression checking, including object methods; positive numeric multiplication return coverage now sits beside the mismatch fixtures, including a dedicated compatibility-method multiplication success fixture; typed methods can also return class-declared fields by simple name, with positive and mismatch fixtures in both modern and compatibility method syntax, and unknown method return expressions now have dedicated modern and compatibility diagnostic fixtures, and NUMBER return expressions with known TEXT arithmetic operands identify the operation and offending operand, including focused addition, subtraction, multiplication, and division coverage for functions and methods. Compatibility method subtraction diagnostics are also covered alongside the existing addition diagnostic. Full path-sensitive analysis across nested conditionals and loops remains planned.
7. Keep method-body field diagnostics learner-facing: typed methods now name the class method when a simple class field receives a known mismatch in `CHECK TYPE` or assignment, or a known TEXT operand in a NUMBER expression, with matching text and numeric coverage in modern and compatibility method syntax; direct assignments to undeclared method fields now report the missing field when the value type is known, and bare `CHECK TYPE missing IS TYPE` statements inside methods now use the expected type in the repair hint in both modern and compatibility method syntax; broader control-flow and object-flow analysis remains planned.
8: Keep method-body coverage balanced across beginner field types and syntax generations: YESNO field assignment mismatches are now validated for both modern `DO` and compatibility `CALL ... WITH` method syntax, method-body `CHECK TYPE` metadata mismatches now have matching compatibility `TAKES` / `LEARNED` coverage alongside the modern form, compatibility YESNO field metadata has dedicated positive and negative fixtures, and compatibility method assignments to undeclared fields have the same focused missing-field diagnostic as modern methods, including a beginner-facing fallback when the assigned expression is not inferable yet.
8a. Keep method-body text expressions balanced: modern TEXT field concatenation is covered when the field is on either side of `+`, so both common beginner word-order patterns remain accepted.
8b. Keep compatibility method-body text expressions aligned: `TAKES` / `LEARNED` methods now have positive TEXT concatenation coverage in both operand orders beside the modern `TEACH` / `END` examples.
8c. Keep explicit method-field annotations aligned with class declarations: `SET score TEXT "oops"` inside a method with `HAS score NUMBER` now reports the declared field mismatch instead of accepting the inline annotation; broader annotation consistency remains planned.
8d. Keep inline field annotations consistent with `HAS` declarations: `claro typecheck` now rejects conflicting annotations on method-body and direct object-field assignments, explains which type to use, and gives a known-type repair hint for unknown direct object-field annotations in both `AS ... TO` and short `SET field TYPE value` forms.
8d.1. Keep direct field annotation forms balanced: the compatibility-shaped `SET player.score AS NUMBER TO 10` form now has a dedicated positive fixture beside short explicit annotations, so both accepted spellings remain protected by focused validation.
8d.2. Keep method field annotation forms balanced: modern and compatibility methods now have dedicated positive fixtures for `SET score AS NUMBER TO 10`, so the compatibility-shaped annotation remains protected inside both supported method syntaxes.
8d.3. Keep release validation aligned with method annotation coverage: the modern and compatibility `AS ... TO` method fixtures now run through `claro validate`, not only the focused Python diagnostic validator.
8d.4. Keep separated direct field assignments unambiguous: `SET player.score AS NUMBER TO 10 extra` now reports the trailing text and explains the one-expression form instead of accepting a partially parsed value.
8d.5. Keep short direct field assignments unambiguous: `SET player.score NUMBER 10 extra` now rejects trailing words with a repair hint, matching the separated `AS ... TO` form.
8d.6. Keep separated direct field assignments complete: `SET player.score AS NUMBER TO` now explains that one value expression is required after `TO`.
8d.7. Keep short direct field assignments complete: `SET player.score NUMBER` now explains that one value expression is required after the type.
8d.8. Keep separated direct field assignments structurally complete: `SET player.score AS NUMBER` now explains that `TO` must appear before the value, with a complete repair example.
8d.9. Keep untyped direct field assignments complete: `SET player.score` now explains that a value expression is required after the field name instead of silently accepting an empty assignment.
8e. Keep inline method-field annotation coverage aligned across syntax generations: compatibility `TAKES` / `LEARNED` methods now have matching positive and negative fixtures, so older lessons retain the same class-declared-type guidance.
8f. Keep inline method-field annotations learner-facing: an unknown annotation such as `BANANA` now names the field and method and suggests the `HAS` type instead of silently treating the annotation as an expression.
8g. Keep unknown inline method-field annotation diagnostics aligned across syntax generations: compatibility `TAKES` / `LEARNED` methods now have matching focused coverage for misspelled annotations.
8h. Keep function and method declarations unambiguous: `claro typecheck` now reports repeated parameter names with a repair hint, before duplicate names can make argument diagnostics confusing, including modern and compatibility `TAKES` / `LEARNED` functions and methods. Duplicate object method names within one class now get the same declaration-time protection and a rename hint, with focused coverage for both modern and compatibility method syntax. Broader signature validation remains planned.
8h.1. Keep top-level function declarations unambiguous: duplicate `TEACH greet` blocks now get a declaration-time diagnostic with a rename hint, preventing two functions from competing for one name. Broader signature validation remains planned.
8h.2. Keep function declarations understandable: a bare `TEACH` now gets a direct diagnostic explaining that a function name is required, with a small example repair. Broader signature validation remains planned.
8h.3. Keep method declarations understandable: a bare `TEACH` inside a class now names the class and explains how to add a method name, instead of using the less-specific top-level function diagnostic.
8i. Keep class declarations unambiguous: duplicate `HAS` field names within one class now get a declaration-time diagnostic with a repair hint, preventing conflicting field types from being silently accepted, while same-named fields in different classes remain valid. Broader object declaration validation remains planned.
8j. Keep class names unambiguous: duplicate `CLASS Player` declarations now get a declaration-time diagnostic with a rename hint, preventing two class definitions from competing for one name. Extra words after a class name also get a declaration-time repair hint, so a typo such as `CLASS Player Extra` cannot silently create a surprising class identity.
8k. Keep object names unambiguous: repeated `NEW Player player` statements now get a declaration-time diagnostic with a rename hint, preventing one object type environment from silently replacing another.
8l. Keep object creation understandable: when classes are declared in a script, an unknown `NEW` class name now gets a direct class-name diagnostic and repair hint; the older permissive `NEW` behavior remains when no class declarations are present.
8m. Keep class field metadata understandable: a missing or unknown type in a `HAS field TYPE` declaration, or extra text after a valid type, now gets a class-and-field diagnostic with supported type examples or a direct repair hint before object-field checks use that metadata.
8m.1. Keep class field declarations understandable: a bare `HAS` now gets a direct diagnostic explaining that both a field name and type are required, with a small `HAS score NUMBER` repair example.
8n. Keep object creation unambiguous: extra words after `NEW ClassName object` now get a direct repair hint, so a malformed object declaration cannot silently discard part of the learner's input.
8o. Keep class declarations understandable: a bare `CLASS` now gets a direct diagnostic explaining that a class name is required, with a small example repair.
8p. Keep object declarations understandable: a `NEW` statement with a class but no object name now gets a direct diagnostic with a complete `NEW Player player` repair example; a bare `NEW` now separately explains that both a class and object name are required.
8q. Keep `CHECK TYPE` syntax understandable: a missing `IS` now gets a direct repair hint with a complete `CHECK TYPE score IS NUMBER` example.
8q.1. Keep `CHECK TYPE` declarations understandable: `CHECK TYPE score IS` now gets a direct repair hint naming the missing expected type and showing a complete example.
8q.2. Keep `CHECK TYPE` declarations unambiguous: extra words after a valid expected type now get a direct repair hint instead of being reported as an unknown type.
8q.3. Keep `CHECK TYPE` declarations complete: a bare `CHECK TYPE` now explains that the expression, `IS`, and expected type are all required, with a complete repair example.
8q.4. Keep `CHECK TYPE` declarations ordered: `CHECK TYPE IS NUMBER` now explains that an expression belongs before `IS`, with a complete repair example.
8q.5. Keep `TYPE OF` declarations understandable: a missing `AS` now gets a direct repair hint with a complete `TYPE OF score AS kind` example.
8q.5.1. Keep `TYPE OF` declarations complete: a missing expression before `AS` now gets a direct repair hint with a complete `TYPE OF score AS kind` example.
8q.5.1a. Keep incomplete `TYPE OF` diagnostics focused: when the expression is missing, report that repair before secondary trailing-token errors.
8q.5.2. Keep `TYPE OF` declarations named: a missing result name after `AS` now gets a direct repair hint with a complete `TYPE OF score AS kind` example.
8q.5.3. Keep complete `TYPE OF` examples protected: the valid `TYPE OF score AS kind` form now has a positive fixture in the focused typecheck validation matrix.
8q.5.3a. Keep `TYPE OF` keyword handling consistent: lowercase `type of score as kind` now has positive fixture coverage alongside the uppercase form.
8q.5.3b. Keep `CHECK TYPE` keyword handling consistent: lowercase `check type score is number` now has positive fixture coverage alongside the uppercase form.
8q.5.3c. Keep object-field examples consistent with keyword rules: lowercase `class`, `has`, `new`, `set`, and `check type` now have one focused positive typed-field fixture.
8q.5.4. Keep bare `TYPE OF` declarations understandable: report that the expression, `AS`, and result name are all required, with a complete repair example.
8q.6. Keep `TYPE OF` declarations unambiguous: extra words after the result name now get a direct repair hint instead of being silently included in the variable name.
8q.7. Keep typed `TEACH` declarations unambiguous: extra words after a declared return type now get a direct repair hint instead of being treated as part of an unknown type.
8q.8. Keep typed `TEACH` declarations complete: a bare `RETURNS` now gets a direct repair hint naming the missing type and showing a supported example such as `NUMBER`.
8q.9. Keep missing return-type diagnostics aligned across functions and methods: modern and compatibility methods now name the complete `Class.method` identity when `RETURNS` has no type, with focused fixtures in the typecheck and release validation matrices.
8q.10. Keep return statements beginner-readable: typed returns now identify simple extra tokens after a value and explain that `RETURN` accepts one expression, with focused release validation coverage.
8q.11. Keep extra-token return diagnostics aligned across syntax generations: modern and compatibility functions and object methods now have focused fixtures for `RETURN value extra`, so errors keep the function or class-and-method name and the same repair hint.
8q.12. Keep empty typed-return diagnostics aligned across syntax generations: modern and compatibility functions and object methods now have focused fixtures for bare `RETURN`, so each form explains that the declared type needs an expression.
8q.13. Keep unknown typed-return diagnostics aligned across syntax generations: compatibility `TAKES` / `LEARNED` functions now have a focused fixture for an uninferable return expression, matching modern functions and methods.
### 1. Strong static types
Goal: make larger beginner programs safer without making first scripts harder.
Needed next:
- richer typed function signatures beyond the focused simple `RETURNS TYPE` check
- typed function returns beyond the focused simple function/method `RETURNS TYPE` check
- type checking across branches and loops
- typed imports/modules
- broader object field checking and richer method return typing
- clearer error messages for type mismatches
### 2. Objects and classes polish
Goal: keep object-oriented examples readable enough for beginners.
Needed next:
- constructor-style defaults or beginner-friendly initialization helpers
- richer method return checks beyond the focused simple `RETURNS TYPE` check
- object printing/debugging helpers
- better examples that avoid abstract toy OOP
### 3. Package manager and registry
Goal: make sharing beginner libraries safe and predictable.
Needed next:
- local package install from a folder
- version constraints
- package export/publish format
- lock-file verification
- remote registry design with checksums/signatures before downloads
### 4. Networking and web apps
Goal: allow safe beginner networking and small local web apps.
Needed next:
- beginner web server syntax
- routes
- request/response helpers
- local-only safe default mode
- examples that work offline or on localhost
### 5. Tasks, concurrency, and possible threads
Goal: teach concurrency as safe tasks first, not low-level thread hazards.
Needed next:
- cooperative scheduler semantics
- cancellation
- timeouts
- message passing or channels
- explicit decision on whether native threads are needed later
### 6. IDE/editor support
Goal: give beginners fast feedback in their editor.
Needed next:
- syntax highlighting grammar
- editor extension packaging
- hover/help text
- go-to-definition where practical
- code actions for common mistakes
- LSP server after metadata and diagnostics stabilize
### 7. Graphics and SDL
Goal: make visual/game examples possible without making the portable stable build fragile.
Needed next:
- keep placeholder graphics commands beginner-safe
- define optional SDL build flag/backend
- add graceful errors when graphics are unavailable
- document which examples require experimental builds
## Documentation rule
Every new feature should update three places before it is treated as release-ready:
1. `README.md` or the relevant feature doc for beginner-facing usage
2. `CURRENT_STATUS.md` for status and limitations
3. a validation note or test command showing how it was checked
+50 -25
View File
@@ -1,33 +1,58 @@
# Runtime Guards & Limits (Beta25) # Current runtime guards
Claro includes optional safety limits to prevent hangs and memory abuse. Claro runs trusted scripts with the user's permissions. These guards are **not
a sandbox**, a wall-clock timeout, or proof that arbitrary scripts use bounded resources.
## 1) Step limit (runaway protection) ## Always-on nesting/response guards
Environment variable:
- `CLARO_STEP_LIMIT=200000`
Stops a program if it executes too many steps/lines. - Function/method calls: at most 256 active calls.
- Expressions and additional nested control blocks: at most 128 levels.
- Parsed JSON: at most 128 nested arrays/objects. A scalar inside 128 containers
is allowed; a 129th container is rejected. Malformed syntax fails with a Claro
diagnostic rather than looping without consuming input. JSON Unicode escapes
and surrogate pairs are decoded; embedded zero characters are rejected because
Claro text uses zero-terminated strings. This is not a general encoding validator.
- HTTP response bodies: the existing 1,048,576-byte cap remains.
## 2) File size limit Errors raised during expressions keep the executing source file and line,
Environment variable: including imported functions and caught `LASTERRORFILE`/`LASTERRORLINE` metadata.
- `CLARO_MAX_FILE_BYTES=16777216` (default 16 MiB) REPEAT without UNTIL is rejected before accessing a missing terminator.
Applies to: ## Optional guards — disabled by default
- `READ FILE ...`
- `WRITE FILE ... WITH ...`
- `APPEND FILE ... WITH ...`
## 3) JSON size limit `CLARO_STEP_LIMIT` sets a non-negative interpreter-work budget. Zero/unset means
Environment variable: unlimited. It counts source-line dispatches and block/range entries, including
- `CLARO_MAX_JSON_BYTES=8388608` (default 8 MiB) empty loop iterations; it is **not** an exact statement count or elapsed time.
Once exhausted it reports a source-located error suggesting the loop be checked.
The consumed budget is not reset by TRY/CATCH or by calling a function.
Applies to: `CLARO_READ_FILE_LIMIT` sets the maximum bytes read by each `READ FILE` command.
- `PARSE JSON ...` Zero/unset means unlimited. The exact boundary is accepted; larger input errors
- `MAKE JSON ...` without storing a partial result. It does not cap source loading, package metadata,
- `MAKE JSON PRETTY ...` all file operations, aggregate memory or output. Invalid/overflowing limit values
fail configuration; supported numeric range depends on the compiler's unsigned long.
## 4) Error output For example, in a Windows command prompt before running a reviewed script:
Runtime errors include:
- `file:line: message` ```bat
- stack trace (CALL chain) set CLARO_STEP_LIMIT=10000
- and JSON parse errors include `Near: ...` snippet set CLARO_READ_FILE_LIMIT=1048576
claro98.exe practice.claro
```
On Linux/macOS shells the equivalent is an environment assignment before `./claro`.
These optional budgets do not interrupt a blocked ASK, a shell command, curl,
or an individual long-running builtin. They are not untrusted-code protection.
## Not provided
There is no general CPU/time budget, process isolation, total-allocation limit,
global file/output limit or JSON byte-size control. The old beta names
`CLARO_MAX_FILE_BYTES` and `CLARO_MAX_JSON_BYTES` are still not implemented.
The newer READ FILE guard is deliberately narrower. The former unimplemented
step-limit claim is preserved as history; the current step guard is the explicit
implementation described above, not validation of those older promises.
Without the optional step budget ordinary intentional loops can run indefinitely.
Run only trusted scripts. See [reliability](RELIABILITY.md),
[errors and repairs](ERRORS.md), and the [historical limits](history/RUNTIME_LIMITS_BETA25.md).
+1 -1
View File
@@ -2,7 +2,7 @@
SDL 1.2 support is **not included in the current stable executable**. SDL 1.2 support is **not included in the current stable executable**.
The `examples/experimental_sdl/` files are design experiments / future examples only. In Claro v1.18.26, running them with the included `claro.exe` will fail with: The `examples/experimental_sdl/` files are design experiments / future examples only. In Claro v2026.10.25.014405, running them with the included `claro.exe` will fail with:
```text ```text
Unknown command: SDL Unknown command: SDL
+3 -9
View File
@@ -12,12 +12,6 @@ END
DO greet "Jon" DO greet "Jon"
``` ```
Older compatible style: `TEACH` defines an action. `DO` runs it. `END` closes the definition.
Older programs are still supported; see [compatibility syntax](COMPATIBILITY.md)
```claro only when reading one.
TEACH greet TAKES name
SAY "Hello " + name
LEARNED
CALL greet WITH "Jon"
```
+61
View File
@@ -0,0 +1,61 @@
# Six-track progress — development candidate v2026.10.21.010934
This is a work/acceptance map, not a stable release or a claim all six external
milestones are complete. Real learner sessions and native OS access were not
supplied in the clarification request. No simulated observations are recorded.
## 1. Error reporting — implemented, regression-tested
Expressions retain file/line origins through functions, imports, UNTIL and
TRY/CATCH metadata. The first failing location is not overwritten by its caller.
Focused tests cover direct, nested and caught paths, including leak-enabled runs.
## 2. Error guide — aligned and executable-verified
The [guide](ERRORS.md) now shows current short SET/END forms and six actual
failure/repair pairs. `tests/error_examples.json` is checked against every guide
code block and against the interpreter's diagnostic and repaired output.
## 3. Beginner testing — kit ready; actual learner acceptance BLOCKED
The [pilot kit](LEARNER_PILOT.md) contains six small starter tasks (three primary,
three optional), observer instructions and a [blank feedback form](LEARNER_FEEDBACK_TEMPLATE.md).
Automated code/output checks are not a person testing the language. A willing,
consenting learner and anonymous real observations are still required.
## 4. Native Windows — test kit prepared; target acceptance BLOCKED
[Native checks](NATIVE_WINDOWS_TESTING.md) require an authorized Windows login or
real Windows 98 SE VM. No such access was supplied. The OS helper explicitly
rejects Wine and can require Win98 SE 4.10.2222 before a PASS marker is written.
Compiler/Wine checks are separate evidence and never counted as target acceptance.
## 5. Robustness — focused fixes and guards, not a sandbox
Malformed JSON such as `[?]` previously hung; it now produces a located diagnostic.
Containers are bounded at 128, separators/numbers/escapes are checked, Unicode
escapes/surrogate pairs and control-character serialization are covered. JSON
parse/serialize temporaries and popped import entries have cleanup regressions.
Missing UNTIL is rejected. Opt-in step and per-READ FILE byte budgets are described
in [runtime guards](RUNTIME_LIMITS.md). Empty loops consume work budget; TRY/CATCH
does not reset it. These do not bound all memory, time, output or external commands.
## 6. Release delivery — candidate validation; hosted gate BLOCKED
Local source/Linux/Windows candidate archives must pass extracted-payload checks
before delivery. Actual checksums/reports belong to those frozen artifacts, not
just the source checkout. The VC6 ZIP uses a separate manifest/report path.
On this run the Gitea repository metadata reported `has_actions: false`; the
Actions runs API returned HTTP 401, the Actions web page was unavailable, and
there was no authenticated browser/API credential. Secure vault entry was not
available in this session. Hosted CI cannot be verified or enabled from Git SSH
access alone. No hosted release or CI success is claimed and no release assets
have been uploaded. Authenticated administration and enabled/configured Actions
are needed before completing the requested hosted CI/release gate.
The repository API also reported `private: false`. No visibility change was made;
do not put credentials or sensitive learner/native-host information into this repo.
See [release status](RELEASE_STATUS.md) for dated earlier checkpoints. Candidate
build evidence does not retroactively validate native Windows or accessibility.
+1 -1
View File
@@ -1,6 +1,6 @@
# Claro Language Specification # Claro Language Specification
Current package: **Claro v1.18.26** Current package: **Claro v2026.10.25.014405**
Status: **Living v1 specification** Status: **Living v1 specification**
This document defines the intended behavior of the Claro language. It is written to reduce breaking changes and keep the v1 line teachable and predictable. This document defines the intended behavior of the Claro language. It is written to reduce breaking changes and keep the v1 line teachable and predictable.
+41
View File
@@ -0,0 +1,41 @@
# Claro's beginner syntax
Learn one spelling for each action. Use these same forms in the Quick Start,
First Hour, numbered lessons, and introductory examples.
- Show text: `SAY "Hello!"`
- Ask for input: `ASK "What is your name?" name`
- Store a value: `SET score 0`
- Change a value: `SET score score + 1`
- Make a list: `SET items LIST`
- Add an item: `ADD "milk" TO items`
- Make a choice: `IF condition`, optional `ELSE`, then `END`.
- Repeat a fixed number of times: `DO 3 TIMES`, then `END`.
- Visit list items: `FOR EACH item IN items`, then `END`.
- Define an action: `TEACH greet name`, then `END`.
- Run an action: `DO greet "Ada"`.
- Return an answer from an action: `RETURN value`; read it as `RESULT` after `DO`.
- Save text: `WRITE FILE "note.txt" WITH "Hello"`.
- Read text: `READ FILE "note.txt" AS note`.
## Small rules
1. Put one command on each line. Write keywords in uppercase in lessons to make
them easy to spot; the interpreter also accepts lowercase keywords.
2. Put literal text in double quotes. Do not quote a variable when you want its value.
3. Use a simple name such as `score`, `name`, or `items` consistently.
4. Indent the body of a choice, loop, or action by four spaces for readability.
5. Close introductory `IF`, `DO ... TIMES`, `FOR`, and `TEACH` blocks with `END`.
6. Separate function parameters and simple call arguments with spaces.
`AS` is still part of commands that name a result, such as `READ FILE`.
`TO` is still part of `ADD item TO list`. The short forms above do not mean
removing those words from every command.
The optional `REPEAT` loop finishes with `UNTIL condition`, not `END`;
learn it after the introductory loops. Other advanced commands are outside this
first learning path.
Follow [First Hour](FIRST_HOUR.md) for complete programs and the
[numbered lessons](../lessons/README.md) for practice. Older spellings still run;
[compatibility syntax](COMPATIBILITY.md) is only needed when reading older programs.
+69 -33
View File
@@ -1,47 +1,83 @@
# Testing Claro # Testing Claro
Run the built-in regression suite: Run from the repository root. Rebuild rather than trusting an old executable.
The developer gate below is exercised on Linux; Windows sanitizer checks may
skip and do not establish native Windows or Win98 SE acceptance.
## Full developer gate
```bash ```bash
make -s all
python3 tools/validate_v1_17.py
python3 tools/validate_compiler_warnings.py
python3 tools/validate_ci_workflow.py
python3 tools/validate_documentation_status.py
```
`validate_v1_17.py` is the current full gate despite its historical filename.
It compiles current source and runs strict C89, VC6 source/build settings, golden
fixtures, built-in validation, First Hour, introductory syntax/behavior, editor
metadata, deterministic task helpers, package security, typecheck diagnostics,
reliability, version consistency and documentation-status checks.
The [reliability runner](RELIABILITY.md) includes leak-enabled ASan/UBSan learning
coverage. Read each exit status and skip; a disabled sanitizer or platform skip
is not a pass for that boundary. No test suite proves arbitrary-program safety.
## Small changes and teaching examples
```bash
./claro --version
./claro test ./claro test
``` ./claro doctor
Expected result:
```text
PASS: 0 failure(s)
```
Check a lesson:
```bash
./claro check lessons/01_hello.claro
```
Validate the current v1.18.26 package after building:
```bash
./claro validate ./claro validate
python tools/validate_typecheck_diagnostics.py python3 tools/validate_learning_syntax.py
python tools/validate_version_convention.py python3 tools/validate_first_hour.py
python tools/validate_ci_workflow.py
``` ```
Useful focused validation scripts: A successful built-in golden run ends with `PASS: 0 failure(s)`. The learning
checkers execute input-dependent examples with fixed input and compare output;
a syntax-only check is not sufficient. `CLARO_EXE` can select a specific actual
executable through a launcher, including a sanitizer or Wine build.
Run `./claro check lessons/01_hello.claro` for a focused syntax check. Still test
interaction and recovery with learners using the [pilot checklist](CLASSROOM_READINESS.md).
## Target and archive acceptance
Use [distribution verification](PORTABILITY.md) for standard source/Linux x64/
MinGW Windows x64 archives. It rebuilds or executes extracted payloads, checks
manifests, and records exact artifact results. Source builds are not evidence
that a shipped binary is correct.
For [VC6 / Windows 98 SE](WINDOWS98.md), `build98.bat` builds the x86 executable;
run `claro98.exe test` and `claro98.exe validate`. Modern Python developer tooling
is not a Windows 98 prerequisite. The legacy build ZIP uses its own checksum
manifest and report rather than the generic distribution validator.
Wine results must be labeled Wine results. Claim native Windows/Win98 support
only after running the target tests there. [Release status](RELEASE_STATUS.md)
records the dated scope; [the checklist](FINAL_RELEASE_CHECKLIST.md) governs publication.
## New focused checks
```bash ```bash
python tools/validate_v1_17.py python3 tools/validate_error_locations.py
python tools/validate_v1_16.py python3 tools/validate_error_guide.py
python tools/validate_v1_15.py python3 tools/validate_learner_pilot.py
python tools/validate_v1_14.py python3 tools/validate_resource_guards.py
python tools/validate_concurrency.py
python tools/validate_ide_metadata.py
python tools/validate_lsp_helper.py
python tools/validate_package_security.py
``` ```
The Forgejo/Gitea CI workflow is expected to run the current release gates above, not only the older smoke tests. This includes `tools/validate_compiler_warnings.py`, which rebuilds the C source as an object file and rejects the known path-truncation warning class. `tools/validate_ci_workflow.py` checks that coverage so CI does not drift away from the documented package validation path. They are also part of the full gate. `validate_learning_ownership.py` runs them
with leak detection enabled, including negative paths. Pilot code checks are
not learner observations. [Native acceptance](NATIVE_WINDOWS_TESTING.md) requires
real target logs; the Wine-rejecting probe prevents silently substituting a
compatibility-layer result. Current [six-track status](SIX_TRACK_STATUS.md)
separates implemented work from blocked human/hosted acceptance.
Older `validate_rc*.py` scripts are kept for historical release notes. They are not the recommended current package validation path. ## Historical tools
On Windows, after `build.bat` or `build.ps1`, run the same Python commands from the project root. Older `validate_rc*.py` and versioned validators remain for history and focused
compatibility checks. They are not alternative current release entry points.
CI coverage is checked from executable `run` steps, not comments. Hosted CI
success must be verified separately from these local commands.
+5 -5
View File
@@ -1,6 +1,6 @@
# Claro v1.18.26 - Packages and Projects # Claro v2026.10.25.014405 - Packages and Projects
Claro v1.18.26 makes projects and packages safer and more useful while keeping the beginner syntax simple. Claro v2026.10.25.014405 makes projects and packages safer and more useful while keeping the beginner syntax simple.
## Create a project ## Create a project
@@ -28,7 +28,7 @@ README.md
manifest-version: 1 manifest-version: 1
name: MyProject name: MyProject
main: main.claro main: main.claro
version: v1.18.26 version: v2026.10.25.014405
packages: packages:
package: text package: text
package: net_tools package: net_tools
@@ -77,10 +77,10 @@ If `package doctor` reports `BAD lock checksum: math-tools`, check the `math-too
BAD lock version: expected exactly one lock-version: 1 line BAD lock version: expected exactly one lock-version: 1 line
``` ```
The lockfile must also contain exactly one current release line, `version: v1.18.26`. A missing, stale, blank, or duplicate release line fails with: The lockfile must also contain exactly one current release line, `version: v2026.10.25.014405`. A missing, stale, blank, or duplicate release line fails with:
```text ```text
BAD lock release version: expected exactly one version: v1.18.26 line BAD lock release version: expected exactly one version: v2026.10.25.014405 line
``` ```
The doctor leaves your files unchanged. After reviewing your project and package manifests, run `claro package lock` to regenerate the lockfile, then run `claro package doctor` again. Spaces around values and capitalization of the lock-format field name are accepted, as with other lockfile fields. Valid generated lockfiles continue to work unchanged; this check does not add registry downloads or verify package content hashes. The doctor leaves your files unchanged. After reviewing your project and package manifests, run `claro package lock` to regenerate the lockfile, then run `claro package doctor` again. Spaces around values and capitalization of the lock-format field name are accepted, as with other lockfile fields. Valid generated lockfiles continue to work unchanged; this check does not add registry downloads or verify package content hashes.
+16 -20
View File
@@ -1,31 +1,27 @@
# Claro Versioning # Claro Versioning
Claro uses visible versions in this format: Development/release runs use their current UTC date and time:
```text ```text
v1.xx.yy vYYYY.MM.DD.HHMMSS
``` ```
Meaning: For example, `v2026.10.20.155641` identifies a run started on
2026-10-20 at 15:56:41 UTC. A timestamp identifies a run, not its maturity.
```text At the start of a new development/release run:
v1 = overall major version
xx = revision / release number ```bash
yy = year python3 tools/stamp_version.py
make -s all
``` ```
Example: The stamper updates current runtime, IDE metadata, generated project and
lockfile versions, validators, and selected current documentation. Historical
release records are preserved. Rebuilding or testing within the same run
retains the stamp; do not stamp again before each validation command.
```text Check the built version with `./claro --version`. Existing project lockfiles
Claro v1.15.26 from older runs can be refreshed with `claro package lock`.
```
That means: Older `v1.xx.yy` labels describe historical releases, not the current convention.
```text
major version: 1
revision: 10
year: 2026
```
Normal Claro development should stay in the `v1` line. A future `Claro v2` should mean an entire rewrite many years later, not a normal feature update.
+1 -1
View File
@@ -1,6 +1,6 @@
# Claro Web Server Plan # Claro Web Server Plan
Current release: **Claro v1.18.26** Current release: **Claro v2026.10.25.014405**
Claro currently has beginner HTTP client commands and offline `claro://` test URLs. A web server API is not complete yet. Claro currently has beginner HTTP client commands and offline `claro://` test URLs. A web server API is not complete yet.
+64
View File
@@ -0,0 +1,64 @@
# Windows 98 SE compilation (Visual C++ 6.0)
Claro has a dedicated **32-bit x86, MSVC6** build. It keeps the runtime in the
single `src/claro.c` file. This is separate from the modern MinGW x64 download.
## Build on a configured VC6 system
1. Extract the project to a short path such as `C:\CLARO`.
2. Open the compiler environment installed with Visual C++ 6.0, or run that
installation's original `VCVARS32.BAT`. Check `cl` reports compiler 12.00.
Compiler, include and library paths remain installation-specific; the project
does not bundle Microsoft's toolchain.
3. Change to the Claro folder and run:
```bat
build98.bat
```
The output is `claro98.exe`. Try:
```bat
claro98.exe --version
claro98.exe lessons\01_hello.claro
claro98.exe test
```
`build98.bat` and `build98.rsp` use 8.3-compatible names. The batch file uses no
`SETLOCAL`, `%~dp0`, `EXIT /B`, `WHERE`, or command-chaining extensions. Compiler
options live in a response file to avoid COMMAND.COM's short command-line limit.
Both files use CRLF line endings. Modernized toolchain environment wrappers may
use CMD-only syntax; use the VC6 environment appropriate to the actual host.
## Target settings
- `/TC /W3`: compile as C with normal warnings.
- `/ML /O1`: VC6 static single-threaded CRT, optimized for size. No newer Visual
C++ runtime DLL needs to be installed for this build.
- `WINVER=0x0410`, `_WIN32_WINDOWS=0x0410`: Windows 98 header target selection.
- `/MACHINE:I386 /SUBSYSTEM:CONSOLE,4.10`: 32-bit x86 console executable with a
Windows 98-era subsystem version.
- `/STACK:16777216`: 16 MiB reserve so nested calls reach Claro's existing
recursion guard instead of overflowing the default Windows stack first.
The source selects `long` for `_findfirst` handles on pre-VC7 MSVC, matching the
VC6 CRT declaration. Modern compilers retain their pointer-sized handle type.
No `/Dintptr_t=long` workaround is used.
## Verification boundary
The build is exercised with the actual MSVC600 compiler and linker under Wine.
The source/build-policy check is `python3 tools/validate_msvc6_source.py`, also
included in full release validation and CI. It does not replace the actual VC6
compiler test.
A PE header and a successful Wine test are not proof of execution on Windows
98 SE. Native Win98 SE loader/CRT, filesystem, console, shell, encoding and device
behavior still need testing on that OS or a faithful emulator. Some static VC6
CRT imports include Wide-character API entry points and fallback paths; their
presence must not be mistaken for a modern-NT-only requirement or for proof of
Win98 correctness without a real target test.
Real HTTP requires a separately compatible curl executable. No modern TLS stack,
GUI installer, Microsoft SDK or runtime DLL bundle is supplied. Run only trusted
scripts. See [portability](PORTABILITY.md) and [runtime limits](RUNTIME_LIMITS.md).
@@ -0,0 +1,353 @@
# Historical record — not current status
Preserved before the documentation cleanup. Claims and pending-work notes below
are dated history, not current acceptance results. Use [current status](../CURRENT_STATUS.md)
and [release status](../RELEASE_STATUS.md) instead.
# Claro v2026.10.20.220528 Current Status
This file is the beginner-safe status map for the current package. It separates what is ready to use from what is historical, experimental, or still planned.
## How to read the status labels
- **Stable foundation**: good enough for normal examples and beginner learning.
- **Foundation present**: usable, but still missing important polish or coverage.
- **Experimental/planned**: do not rely on it in beginner lessons yet.
- **Historical**: kept for release history, not current instructions.
## Security and correctness review progress
The v2026.10.20.220528 review identified missing-argument reads in standard-library built-ins. The current runtime now validates required argument counts centrally before any builtin indexes `args[]`. Covered calls include `math.abs`, `math.clamp`, `random.seed`, `random.int`, text helpers, CSV helpers, path helpers, and collection helpers. Missing arguments produce a beginner-facing `needs N arguments` runtime error.
Focused regression coverage: `tests/38_builtin_arity.claro`, `tests/39_random_inverted_range.claro`, and `tests/40_call_depth_guard.claro`.
The runtime now rejects inverted `random.int` ranges before modulo arithmetic with: `random.int needs the lower bound to be less than or equal to the upper bound.` This prevents invalid ranges from producing incorrect values or a divide-by-zero signal.
User-defined function and object-method calls now have a documented maximum active call depth of 256. Exceeding it produces: `Claro function call depth exceeded the safe limit of 256. Simplify the recursion or add a stopping condition.` Ordinary non-recursive beginner programs are unaffected.
Verified in this checkout on 2026-09-23:
- `gcc -std=c99 -O0 -g -fsanitize=address,undefined src/claro.c -o /tmp/claro-call-depth-asan -lm` plus `ASAN_OPTIONS=detect_leaks=0 /tmp/claro-call-depth-asan tests/40_call_depth_guard.claro`: expected diagnostic; no AddressSanitizer or UndefinedBehaviorSanitizer report.
- `gcc -std=c99 -O0 -g -fsanitize=address,undefined src/claro.c -o /tmp/claro-arity-asan -lm` plus `ASAN_OPTIONS=detect_leaks=0 UBSAN_OPTIONS=halt_on_error=1 /tmp/claro-arity-asan tests/38_builtin_arity.claro`: all four missing-argument diagnostics; no sanitizer report.
- `make -s all`: rebuilt both `claro` and `claro.exe` from current source.
- `./claro test`: `PASS: 0 failure(s)`.
- `./claro doctor`: all checks `OK`.
- `./claro validate`: validation passed.
The memory cleanup slices release the previous deep value when a runtime variable or map entry is overwritten, release temporary split argument arrays and strings from `DO`, `CALL`, `TEXT ... CONTAINS`, and `RANDOM`, release loaded program paths, lines, and pointer arrays at the end of each script run, and now release the remaining runtime-owned variables, functions, modules, classes, import paths, captured output, return value, and error strings before the interpreter exits. The expression evaluator now releases discarded intermediate `Value` operands and command boundaries release evaluated `SET`/`SAY` values after copying or printing them. `FOR EACH` now releases its copied collection after iteration, preventing discarded list/map copies from accumulating in long scripts. `IF` and `DO ... TIMES` now release their temporary control-expression values after branch/loop selection. `ASK` now releases evaluated prompt values and temporary input values after converting/copying them into runtime storage. Focused coverage is `tools/validate_memory_cleanup.py`, `tools/validate_expression_cleanup.py`, `tools/validate_control_flow_cleanup.py`, `tools/validate_control_expression_cleanup.py`, and `tools/validate_ask_prompt_cleanup.py`; each focused cleanup validator runs an ASan/UBSan build with LeakSanitizer checking. Verified on 2026-09-24: `python3 tools/validate_ask_prompt_cleanup.py` passes (2,000 prompt/input operations under ASan/UBSan/LSan); `make -s all`, `./claro test`, `./claro doctor`, and `./claro validate` all pass. Caught `TRY` errors now release their previous message and source-path values when cleared; `python3 tools/validate_runtime_error_cleanup.py` confirms caught builtin-arity and inverted-range errors pass ASan/UBSan/LSan. Additional malformed-input leak sites may remain, so this does not claim all runtime allocations are leak-free. The `COUNT ... AS` command now releases its evaluated list/map value after extracting the item count. `python3 tools/validate_count_expression_cleanup.py` first reproduced a 28,000-... [truncated]
The `FIND ... IN ... AS ...` command now releases its evaluated search value and copied list/map after storing the result. Focused coverage is `tools/validate_find_expression_cleanup.py`; it runs 2,000 searches under ASan/UBSan with LeakSanitizer enabled. Verified on 2026-09-24: `python3 tools/validate_find_expression_cleanup.py` passes with no reported leaks; `make -s all`, `./claro test` (0 failures), `./claro doctor`, `./claro validate`, and `git diff --check` pass. This slice is runtime-verified under sanitizers; broader malformed-input sanitizer coverage remains blocked by the previously documented `rt_error` message leak.
`REMOVE` now releases its evaluated needle and copied list/map value after updating runtime storage. `python3 tools/validate_remove_expression_cleanup.py` passed with 2,000 repeated string-needle operations under ASan/UBSan/LSan. Removed elements from populated copied lists remain a separate ownership case. This slice is runtime-verified under sanitizers; malformed-input diagnostics still have the previously recorded `rt_error` leak.
`ADD ... TO ...` now releases both the evaluated item and copied list after `list_add` and `rt_set` have made their owned copies. `python3 tools/validate_add_expression_cleanup.py` first reproduced leaks under ASan/UBSan/LSan (2,000 repeated string concatenations), then passed after the ownership cleanup. Full checks passed: `make -s all`, `./claro test` (0 failures), `./claro doctor`, `./claro validate`, all existing focused cleanup validators, and `git diff --check`. This slice is runtime-verified under sanitizers; malformed-input diagnostics still have the previously recorded `rt_error` leak.
`PUT ... KEY ... VALUE ...` now releases the evaluated map, key, and value copies after runtime storage has copied them. `python3 tools/validate_put_expression_cleanup.py` passes with 2,000 repeated string writes under ASan/UBSan/LSan. Verified 2026-09-26: `make -s all`, `./claro test` (0 failures), `./claro doctor`, `./claro validate`, the focused sanitizer validator, and `git diff --check` pass. Separate malformed-argument sanitizer smoke tests for the existing builtin-arity and inverted-range diagnostics do not report out-of-bounds access, but LeakSanitizer reports the already documented `rt_error` diagnostic-string leaks (552 bytes/14 allocations for arity cases; 79 bytes/2 allocations for the inverted-range case). This slice is runtime-verified; those error-path leaks remain a separate blocker to claiming sanitizer-clean diagnostics.
The `GET ARGUMENT ... AS ...` command now releases its evaluated index and copied temporary argument values after storage takes ownership. `tools/validate_get_argument_expression_cleanup.py` confirmed and fixed a 6,000-byte leak across 2,000 repetitions under ASan/UBSan/LSan; `GET ENV ... AS ...` now also releases its evaluated name value after conversion, with `tools/validate_get_env_expression_cleanup.py` detecting 1,688,000 leaked bytes across 2,000 lookups before the fix. Both focused checks are wired into the v1.17 validation script. This is runtime-verified narrow ownership work; other GET expression branches remain separate boundaries.
The `RUN COMMAND` path now decodes POSIX `pclose()` wait status before storing `LASTEXIT`, so a child that exits with code 3 exposes `3` rather than the encoded status 768. Focused coverage is `tests/42_last_exit_code.claro`. HTTP responses now have a 1,048,576-byte cap and marker-like response bodies are preserved while extracting the final HTTP status marker. Focused coverage is `tools/validate_http_hardening.py`. Remaining memory-growth areas include other expression temporaries. Claro remains a trusted-script interpreter, not a sandbox. `RUN COMMAND` now releases its evaluated command expression and temporary captured-output value after copying output into runtime storage. The new `tools/validate_run_command_expression_cleanup.py` first detected 12,000 bytes leaked across 2,000 command executions, then passed under ASan/UBSan/LSan after the ownership fix.
`RUN COMMAND` is documented and validated as a trusted-code capability: it executes shell commands with the user's permissions and is not a sandbox. Claro does not claim untrusted-script safety or use a fragile blacklist sanitizer. Focused documentation coverage is `tools/validate_trusted_command_docs.py`.
`REMOVE` also releases the removed element from its copied list before shifting later elements. Focused verification: `python3 tools/validate_remove_list_item_cleanup.py` passed with 2,000 add/remove pairs under ASan/UBSan/LSan and no reported leaks. `make -s all`, `./claro test` (0 failures), `./claro doctor`, `./claro validate`, and `git diff --check` also passed. This is a narrow, runtime-verified ownership cleanup; it does not change `REMOVE` syntax or behavior.
`GET ... KEY ... AS ...` now releases its temporary map and key values after lookup. `python3 tools/validate_get_key_expression_cleanup.py` reproduced 1,004,000 leaked bytes before the change and passes after it with ASan/UBSan/LSan enabled. `EXISTS FILE ... AS ...` now releases its evaluated path value after checking it; `python3 tools/validate_exists_expression_cleanup.py` first reproduced 62,000 leaked bytes across 2,000 calls, then passed with ASan/UBSan/LSan enabled. These are narrow expression-temporary cleanup slices, not a claim that all runtime allocations are leak-free.
`GET ... AT ... AS ...` now releases its evaluated index expression after converting it to a list position. `python3 tools/validate_get_at_expression_cleanup.py` first reproduced 4,000 leaked bytes across 2,000 string-valued indices, then passed under ASan/UBSan/LSan with the selected value preserved. The validator is wired into `tools/validate_v1_17.py`; this is a narrow ownership fix, not a claim that all runtime allocations are leak-free.
`LIST FOLDER ... AS ...` now releases its evaluated path and the temporary folder-list value after runtime storage copies the list. `python3 tools/validate_list_folder_expression_cleanup.py` reproduced 220,000 leaked bytes across 2,000 operations under ASan/UBSan/LSan before the change and passes after it with no leak report. The working-tree checks `make -s all`, `./claro test` (0 failures), `./claro doctor`, `./claro validate`, and `git diff --check` pass. This is a narrow ownership improvement, not a claim that all runtime allocations are leak-free.
The `DELETE FILE ...` and `DELETE FOLDER ...` commands now release their evaluated path values after converting the paths to owned strings. `python3 tools/validate_delete_expression_cleanup.py` first reproduced 178,000 leaked bytes across 2,000 `DELETE FILE` expression evaluations under ASan/UBSan/LSan, then passed after cleanup. This check covers expression ownership; malformed-input diagnostic paths still have the previously documented `rt_error` message leak. The `COPY FILE` and `MOVE FILE` commands now also release their evaluated source/destination path values after conversion to strings; `python3 tools/validate_copy_move_expression_cleanup.py` reproduced 74,000 bytes leaked across 1,000 copy/move pairs before the fix and is the focused ASan/UBSan/LSan regression gate. `CREATE FOLDER ...` now releases its evaluated path value after converting it to an owned path string; `python3 tools/validate_create_folder_expression_cleanup.py` reproduced 200,000 leaked bytes across 2,000 calls before the fix and passes with ASan/UBSan/LSan enabled. This narrow cleanup slice does not establish that all runtime allocations are leak-free.
`typecheck` now frees its temporary type environment on return, including the owned variable names and type-string values; replacing an inferred type releases the previous value first. `python3 tools/validate_typecheck_token_cleanup.py` reproduced 479 bytes of leaked type-environment allocations before the change, then passed under ASan/UBSan/LSan with the fix. The validator is named for the original local work file; it specifically checks type-environment cleanup, not tokenizer allocations. This is a narrow cleanup slice and does not establish that all typecheck metadata allocations are leak-free.
`claro check` now releases expression-token arrays and strings after variable-name analysis, and frees its temporary variable environment at the end of each check. The untracked focused regression validator `tools/validate_check_token_cleanup.py` first reproduced 310,447 leaked bytes across 2,000 repeated-string expressions; after the cleanup, `python3 tools/validate_check_token_cleanup.py` passes under ASan/UBSan/LSan. Fresh verification: `make -s all`, `./claro test` (0 failures), `./claro doctor`, `./claro validate`, and `git diff --check` pass. This is a narrow, runtime-verified cleanup for check mode; it does not establish that all allocations are leak-free.
`READ FILE ... AS ...` now releases both its evaluated path and the temporary file-content value after copying contents into runtime storage. `python3 tools/validate_read_file_expression_cleanup.py` reproduced 204,000 leaked bytes across 2,000 reads before the fix, then passed under ASan/UBSan/LSan. The check is wired into `tools/validate_v1_17.py`; this is one verified cleanup boundary, not a claim that all file-command temporaries are leak-free.
`WRITE FILE` and `APPEND FILE` now release their evaluated path and content values after the operation. `python3 tools/validate_write_file_expression_cleanup.py` reproduced 206,000 leaked bytes across 2,000 writes before the fix; the focused ASan/UBSan/LSan validator is wired into `tools/validate_v1_17.py`. Other file-command temporary ownership remains separate.
## Introductory syntax consistency
The introductory learning path recommends short `ASK` and `SET`, `TEACH` with
`DO`, and `END` for `IF`, counted/`FOR` loops, and functions. Quick Start,
First Hour, all ten numbered lessons, ten selected introductory examples,
generated starters, REPL help, and beginner editor snippets follow that policy.
See [syntax rules](../SYNTAX.md); older spellings remain supported in the separate
[compatibility reference](../COMPATIBILITY.md). `READ FILE ... AS` and
`ADD ... TO` retain their command-specific words; `REPEAT` still ends in `UNTIL`.
The syntax checker now shares the interpreter's function-parameter parser, and
modern `DO` type checking uses the same space-separated argument splitting as
the runtime. Bare `SET items LIST` (and existing `MAP` construction) is treated
as a container value, not an incomplete type annotation. This repairs checker
rejections of valid teaching programs without removing compatibility syntax.
Missing introductory block endings recommend `END`; `REPEAT` still recommends
`UNTIL`.
`python3 tools/validate_learning_syntax.py` enforces the introductory spelling
policy and executes the numbered lessons, Quick Start blocks, selected examples,
starter, snippets, common-mistake hints, and compatibility program. The full
release validator runs this checker alongside the executable First Hour checks.
This coverage does not claim every optional advanced reference uses only the
introductory syntax.
## Feature matrix
### Beginner scripting core
Status: **Stable foundation**
Ready now:
- variables with `SET`
- output with `SAY`
- input with `ASK`
- choices with `IF` / `ELSE` / `END`
- loops
- lists and maps
- files, JSON, text helpers, and tested path/collection library helpers
- friendly checking with `claro check`
Good starting docs:
- `QUICK_START.md`
- `FIRST_HOUR.md`
- `CLI.md`
- `SPEC.md`
### Functions
Status: **Stable foundation**
Ready now:
- simple modern form: `TEACH name arg` ... `END`
- compatibility form: `TEACH name TAKES arg` ... `LEARNED`
- calls with `DO` or older `CALL ... WITH`
Good starting docs:
- `SIMPLE_FUNCTIONS.md`
- `README.md`
### Static type safety
Status: **Foundation present**
Ready now:
- typed variables such as `SET score NUMBER 10`
- `TYPE OF` and `CHECK TYPE`
- `CHECK TYPE` gives a direct repair hint when the learner forgets the `IS` keyword, for example `CHECK TYPE score` reports `CHECK TYPE needs IS. Try: CHECK TYPE score IS NUMBER.`
- `CHECK TYPE` gives a direct repair hint when `IS` has no expected type, for example `CHECK TYPE score IS` reports `CHECK TYPE score needs a type after IS. Try: CHECK TYPE score IS NUMBER.`
- `CHECK TYPE` explains when the learner leaves out the expression as well, for example bare `CHECK TYPE` reports `CHECK TYPE needs an expression, IS, and a type. Try: CHECK TYPE score IS NUMBER.`
- typed function and method returns reject simple extra tokens after a return value with a direct repair hint, such as `RETURN amount extra` reporting `Keep only one expression after RETURN.` This is covered for modern functions and both modern and compatibility object methods.
- `CHECK TYPE` explains when `IS` is present but the expression is missing, for example `CHECK TYPE IS NUMBER` reports `CHECK TYPE needs an expression before IS. Try: CHECK TYPE score IS NUMBER.`
- `CHECK TYPE` rejects extra words after a valid expected type with a direct repair hint, so `CHECK TYPE score IS NUMBER TEXT` explains that only one type belongs in the check
- `CHECK TYPE` rejects unknown expected type names with a beginner-facing list of supported types
- typed function return diagnostics also identify an unknown return expression in compatibility `TAKES` / `LEARNED` functions, matching the modern function and object-method guidance
- `TYPE OF` reports a direct repair hint when the learner forgets `AS`, for example `TYPE OF score` suggests `TYPE OF score AS kind`
- bare `TYPE OF` reports that the expression, `AS`, and result name are all required, with a complete repair example
- `TYPE OF` reports a direct repair hint when the learner forgets the expression before `AS`, for example `TYPE OF AS kind` reports `TYPE OF needs an expression before AS. Try: TYPE OF score AS kind.`
- `TYPE OF` prioritizes the missing expression diagnostic even when extra words follow the incomplete form, so `TYPE OF AS kind extra` still points the learner to the missing expression first.
- `TYPE OF` reports a direct repair hint when the learner forgets the result name after `AS`, for example `TYPE OF score AS` reports `TYPE OF needs a result name after AS. Try: TYPE OF score AS kind.`
- A valid `TYPE OF score AS kind` statement has a focused positive typecheck fixture, so the complete form is protected from diagnostic-only regression coverage.
- `TYPE OF` remains case-insensitive like other Claro keywords, with focused positive coverage for lowercase `type of score as kind` syntax.
- `CHECK TYPE` remains case-insensitive like other Claro keywords, with focused positive coverage for lowercase `check type score is number` syntax.
- Object declarations and field checks remain case-insensitive like other Claro keywords, with focused positive coverage for lowercase `class`, `has`, `end`, `new`, `set`, and `check type` in one typed object-field example.
- `TYPE OF` rejects extra words after its result name with a direct repair hint, so `TYPE OF score AS kind extra` explains that only one result name belongs there; typed `TEACH ... RETURNS TYPE` declarations likewise reject extra words after the return type with a direct repair hint
- typed `TEACH ... RETURNS` declarations reject a missing return type with a direct repair hint, such as `Function greet needs a return type after RETURNS. Add a type such as NUMBER.` The same diagnostic names the complete class and method, including compatibility `TAKES` / `LEARNED` methods: `Method Player.score needs a return type after RETURNS. Add a type such as NUMBER.`
- typed list/map checks through `claro typecheck`, including a nested `LIST OF MAP` insertion example
- compatibility `TAKES` / `LEARNED` functions also have focused positive numeric-addition, subtraction, division, and multiplication return fixtures, keeping arithmetic return coverage aligned with modern functions and compatibility methods.
- compatibility `TAKES` / `LEARNED` functions have positive complete-branch return coverage beside the incomplete-branch diagnostic, so older conditional examples are checked for both accepted and rejected paths.
- a narrow function/method argument check: `CHECK TYPE parameter IS TYPE` inside a function or simple object method lets `claro typecheck` accept correct checked calls, catch mismatched `DO`, `CALL ... WITH`, `DO object.method ...`, and compatibility `CALL ... WITH` arguments, including checked methods that appear after another method in the same class, report missing checked function and method arguments, report missing unchecked arguments for simple functions and simple object methods in both modern `DO` and compatibility `CALL ... WITH` forms, treat empty compatibility calls such as `CALL greet WITH` and `CALL player.rename WITH` as missing-argument mistakes, catch extra arguments to simple functions and checked methods even when the checked method appears after another method in the same class, catch modern `DO` and compatibility `CALL ... WITH` calls to undeclared simple functions, explain when `DO object.method ...` or compatibility `CALL ...` happens before the object is created with `NEW` even if the method name is also wrong, and catch modern plus compatibility calls to undeclared object methods with a class-specific `TEACH` hint
- simple function and object-method return checks with `TEACH name ... RETURNS TYPE` now include a compatibility `TAKES` / `LEARNED` function whose conditional returns are incomplete, preserving the same every-path diagnostic as modern functions.
- function and method declarations now reject repeated parameter names with a direct repair hint before calls are checked, including modern and compatibility `TAKES` / `LEARNED` functions and methods; object methods with the same name are also rejected within a class with a rename hint in both modern and compatibility method syntax
- top-level function declarations now reject repeated names with a direct repair hint, so two `TEACH greet` blocks cannot silently compete for one callable function; a missing function name after `TEACH` gets a direct beginner-facing repair hint, and a missing method name inside `CLASS` names the class and gives a method example
- class declarations now reject a missing class name after `CLASS` with a direct beginner-facing repair hint
- class declarations now reject repeated field names within the same class with a direct repair hint before field types are used, so two `HAS score ...` lines cannot silently choose conflicting metadata; different classes may independently use the same field name
- class field declarations now reject missing names such as bare `HAS`, missing types such as `HAS score`, unknown types such as `HAS score BANANA`, and extra tokens such as `HAS score NUMBER TEXT` with the field name, class name, supported type examples, and a repair hint before object-field checks use that metadata
- class declarations now reject repeated class names with a direct repair hint, so two `CLASS Player` blocks cannot silently compete for the same name; extra words after a class name also get a direct repair hint instead of becoming part of the class identity
- object creation now rejects repeated object names with a direct repair hint, so two `NEW Player player` statements cannot silently replace one another during type checking; when a script declares at least one class, a misspelled `NEW` class name gets a direct declaration hint without changing the older permissive no-class form; a missing class name or object name gets a direct example repair; extra words after the object name get a direct repair hint instead of being silently ignored
- a narrow object-field assignment/check-type check for simple `NEW Class object` plus direct `SET object.field value` and `CHECK TYPE object.field IS TYPE` cases when the class declares `HAS field TYPE`; validation now covers correct NUMBER, TEXT, and YESNO direct assignments, dedicated positive and explicitly typed NUMBER/TEXT field assignments, missing values in untyped direct field assignments with a repair hint, explicitly typed method-body assignments to undeclared fields with a `HAS field TYPE` repair hint in modern and compatibility method syntax, dedicated positive NUMBER/TEXT/YESNO `CHECK TYPE` metadata fixtures, simple and chained object aliases for both field assignments and `CHECK TYPE` metadata (including a chained TEXT-field check), aliased object-method calls including chained aliases in modern `DO` and compatibility `CALL ... WITH` forms (with a dedicated positive modern `DO` chained-alias fixture), field-to-field, compound arithmetic field expressions, and arithmetic/text expression result types, negative NUMBER/TEXT/YESNO field `CHECK TYPE` metadata mismatches, NUMBER/TEXT/YESNO-expectation unknown-field `CHECK TYPE` diagnostics, missing-object field assignment and `CHECK TYPE` diagnostics, NUMBER/TEXT/YESNO wrong-type diagnostics, field collection when a `HAS` field appears after a simple method, NUMBER/TEXT/YESNO-valued unknown-field diagnostics for direct assignments to undeclared fields including explicitly typed assignments, numeric and text results from simple arithmetic/text expressions in object-field assignments, plus method-body field assignment and `CHECK TYPE` diagnostics for NUMBER, TEXT, and YESNO fields in modern and compatibility syntax, including compatibility YESNO `CHECK TYPE` metadata coverage, direct method assignments to undeclared fields now produce a beginner-facing missing-field diagnostic when the assigned expression has a known type, and a plain beginner-facing unknown-field diagnostic when the assigned expression is not inferable yet in both modern and compatibility method syntax; method-body `CHECK TYPE` now reports an unknown field with a repair hint in both method syntaxes, including compatibility `TAKES` / `LEARNED` methods; explicitly typed assignments to declared method fields now validate the class-declared field type instead of trusting only the inline type annotation, so a wrong value such as `SET score TEXT \"oops\"` reports the method and field in the diagnostic.
- inline method-field annotations are checked against the class `HAS` declaration in modern and compatibility methods; conflicting known types and unknown annotation names produce field-specific repair guidance, with positive and negative focused fixtures for both syntaxes.
- direct object-field `AS ... TO` assignments reject extra words after the value with a repair hint, so `SET player.score AS NUMBER TO 10 extra` cannot silently ignore the trailing text; they explain when `TO` is missing after the type and when the value is missing after `TO`; short explicitly typed assignments such as `SET player.score NUMBER 10 extra` receive the same repair-oriented check and explain when the value is missing after the type
- Direct object-field validation also has positive coverage for explicit `NUMBER`, `TEXT`, and `YESNO` annotations, such as `SET player.ready YESNO YES`, plus the compatibility-shaped `AS ... TO` form such as `SET player.score AS NUMBER TO 10`, while the shorter unannotated form remains supported; method-body field assignments have matching positive coverage for both modern and compatibility `TEACH` forms using `SET score AS NUMBER TO 10`, and those two `AS ... TO` method fixtures are now included in `claro validate`; conflicting annotations such as `SET player.score TEXT "oops"` explain the class-declared type and how to repair it, and unknown annotations such as `SET player.score AS BANANA TO 10` or `SET player.score BANANA 10` identify the invalid type and suggest the declared field type.
Still needed:
- Keep the focused typecheck validator's fixture list complete as new positive and negative examples are added. `claro validate` now executes the complete focused fixture matrix as well, so release validation cannot silently omit a listed typecheck example. Short explicitly typed field assignments inside modern and compatibility methods also have focused coverage: `SET score NUMBER` explains that one value expression is required after the type, matching direct object-field assignment guidance.
- The expression checker now carries a known TEXT operand through all arithmetic operators so object-field and typed-function-return diagnostics do not hide addition, subtraction, multiplication, or division mistakes; focused numeric-subtraction, numeric-division, and numeric-multiplication positives plus text-operand addition, subtraction, multiplication, and division negatives protect this behavior. Typed-function return diagnostics now have focused subtraction and division positives alongside multiplication coverage. Text concatenation into a TEXT field has focused positive fixtures for both operand orders, including concatenation of two TEXT object fields. Each numeric operator names the text operand and explains that it needs NUMBER values.
- Method return validation now has focused positive subtraction and division fixtures alongside the existing arithmetic return coverage, including compatibility `TAKES` / `LEARNED` subtraction and division cases, so checked NUMBER method parameters and numeric subtraction or division remain accepted by the release validator in both method spellings.
- Modern and compatibility `TAKES` / `LEARNED` methods have positive NUMBER, TEXT, and YESNO field-assignment coverage, and modern plus compatibility method-body `CHECK TYPE` metadata now have positive YESNO coverage beside the wrong-value diagnostics.
- Modern method-body TEXT concatenation has positive coverage in both operand orders, such as `SET first first + "!"` and `SET last "Dr. " + last`.
- Compatibility method-body TEXT concatenation has matching positive coverage in both operand orders, so `TAKES` / `LEARNED` methods keep the same beginner-safe field update behavior.
- richer typed function signatures and return values beyond the focused simple function/method `RETURNS TYPE` check
- type checking through branches and loops
- richer object field type checking beyond simple direct assignments
- typed imports/modules
Good starting docs:
- `ADVANCED_STATIC_TYPING.md`
- `V1_14_STATIC_TYPES.md` (feature history plus examples)
### Objects and classes
Status: **Foundation present**
Ready now:
- `CLASS`
- typed `HAS` fields
- `NEW`
- field access such as `player.score`
- simple methods, including static diagnostics for checked method parameters
- direct object-field assignments with a narrow static diagnostic for wrong value types
- object helper commands
Still needed:
- broader object field type checking across aliases, methods, and more complex object flows
- constructor/default-value polish
- clearer object debugging and teaching examples
Good starting docs:
- `V1_15_OBJECTS_CLASSES.md`
- `README.md`
### Package and project workflow
Status: **Foundation present**
Ready now:
- `claro new`
- `claro package init`
- `claro package add/list/remove/doctor/lock`
- local project files such as `claro.project`, `claro.lock`, and `packages/`
- starter projects from `claro new` now use the same manifest-version and lock-version headers as `claro package init`
- the default `make` target builds both `claro` and `claro.exe`, so cross-platform release validators exercise freshly built executables
- project-name safety checks for `claro new`, so unsafe names such as `../bad` are rejected before Claro creates folders
- package-name safety checks when adding packages, including the 64-character package-name limit, when `claro package doctor` audits an existing `claro.project`, when `claro package lock` writes lockfile data, when `claro package list` shows existing packages, when `claro package init` sees unsafe names already present in `claro.project`, when `claro package add` sees unsafe names already present in `claro.project`, when `claro package remove` refreshes the lockfile after an edit, when learners need to remove an unsafe package entry that is already present, when `claro package doctor` verifies lockfile checksums for listed packages, and when `claro package doctor` rejects lockfile package entries that are not listed in `claro.project`
- `claro package doctor` rejects a listed package manifest whose `name:` does not match the package name in `claro.project`
- `claro package doctor` compares the complete manifest `name:` value, so a prefix such as `math-tools-extra` cannot be accepted for package `math-tools`
- `claro package doctor` requires exactly one complete supported package `manifest-version: 1` field; missing, blank, unsupported, or duplicate format fields get a `BAD package manifest version` diagnostic with the file path and a repair hint, rather than calling an existing file missing
- `claro package doctor` compares the complete manifest `checksum:` value, so a valid checksum followed by extra text is rejected
- `claro package doctor` requires the complete supported `manifest-version: 1` value in `claro.project`, so a prefix such as `manifest-version: 10` is rejected
- `claro package doctor` requires the complete supported `version: 1` value in each local package manifest, so a prefix such as `version: 10` is rejected
- `claro package doctor` requires exactly one current `version: v2026.10.20.220528` line in `claro.lock`, so stale, missing, blank, or duplicate lockfile release versions are rejected
- `claro package doctor` requires the complete supported `source: local` value in each local package manifest, so a prefix such as `source: local-extra` is rejected
- package project entries must be unique; `claro package doctor`, `list`, `add`, `init`, and `lock` reject duplicate names instead of generating ambiguous package or lockfile data
- lockfile package entries must also be unique; `claro package doctor` reports duplicate lock entries instead of accepting ambiguous package/checksum data
- package manifest `name:` entries must also be unique; `claro package doctor` reports `BAD package manifest name` instead of accepting ambiguous package identity data
- package manifest `checksum:` entries must also be unique; `claro package doctor` reports `BAD package checksum` instead of accepting ambiguous integrity data
- package manifest `version:` entries must also be unique and must contain exactly `1`; `claro package doctor` reports `BAD package version` instead of accepting ambiguous package metadata
- project manifests must contain exactly one `manifest-version: 1` field; duplicate or prefix-matching values are rejected with `BAD project manifest version: expected 1`
- project manifests must contain exactly one non-empty `name:` field; `claro package doctor` reports `BAD project manifest name: expected a non-empty name` when the project identity is missing or blank
Still needed:
- install from local path
- package version constraints
- publish/export format
- remote registry protocol
- checksum/signature verification before downloads
Good starting docs:
- `V1_16_PACKAGES_PROJECTS.md`
- `PACKAGE_REGISTRY.md`
### Networking
Status: **Foundation present**
Ready now:
- beginner HTTP commands
- offline `claro://` URLs for tests and lessons
- `LASTHTTP`
- real `http://` and `https://` through `curl` when available
Still needed:
- web server syntax
- routes
- request/response helpers
- localhost-safe server mode
Good starting docs:
- `V1_17_NETWORKING.md`
- `WEB_SERVER_PLAN.md`
### Concurrency and tasks
Status: **Foundation present**
Ready now:
- deterministic task/concurrency helpers where documented
- beginner-safe direction toward cooperative tasks
Still needed:
- full cooperative scheduler semantics
- cancellation
- timeouts
- message passing
- decision on whether native threads should ever be exposed
Good starting docs:
- `CONCURRENCY.md`
### IDE/editor support
Status: **Foundation present**
Ready now:
- `claro ide`
- metadata JSON
- completion list, including the current typed-language keywords `RETURNS`, `CHECK`, and `TYPE`
- diagnostics helper
- Forgejo/Gitea CI coverage for the current release validation gates (`claro validate`, typecheck diagnostics validation, version convention validation, package security validation, compiler warning validation, and the CI workflow coverage check); the compiler warning validator rebuilds `src/claro.c` and rejects path-truncation warnings, while the workflow validator verifies these commands, including its own command, occur in executable `run` steps rather than comments
Still needed:
- syntax highlighting package
- editor extension
- hover help
- go-to-definition
- code actions/fixes
- full LSP server if/when useful
Good starting docs:
- `IDE.md`
- `EDITOR_EXTENSION_PLAN.md`
### Graphics and SDL
Status: **Experimental/planned**
Ready now:
- placeholder/planned graphics documentation
- experimental examples may exist for future SDL work
Not ready in the stable executable:
- real SDL window/drawing support
- beginner game lessons that run everywhere
Good starting docs:
- `GRAPHICS.md`
- `SDL12.md`
## Historical docs
Files named `RC*_NOTES.md`, `RC*_VALIDATION.md`, older `V1_*_VALIDATION.md`, and older release notes are kept for project history. They may mention old version numbers, old planned milestones, or old validation scripts. Use them to understand how Claro evolved; do not treat them as the current beginner path.
## Which docs should a new learner trust first?
For the current v2026.10.20.220528 package, read docs in this order:
1. `QUICK_START.md`, `FIRST_HOUR.md`, and `lessons/README.md` for first programs.
2. `README.md` and this file for the current feature map.
3. `ROADMAP.md` for the next v1 work.
Use feature docs with these expectations:
- **Ready for beginner lessons:** `SPEC.md`, `SIMPLE_FUNCTIONS.md`, `ERRORS.md`, `LINTER.md`, `TESTING.md`, `FORMATTER.md`.
- **Foundation present:** `ADVANCED_STATIC_TYPING.md`, `V1_14_STATIC_TYPES.md`, `V1_15_OBJECTS_CLASSES.md`, `V1_16_PACKAGES_PROJECTS.md`, `V1_17_NETWORKING.md`, `CONCURRENCY.md`, `IDE.md`.
- **Plans or experiments:** `PACKAGE_REGISTRY.md`, `WEB_SERVER_PLAN.md`, `EDITOR_EXTENSION_PLAN.md`, `GRAPHICS.md`, `SDL12.md`, `COMPLETE_PLATFORM_ROADMAP.md`, `FUTURE_FEATURES_ROADMAP.md`.
If a doc sounds more ambitious than this status map, treat this file as the current source of truth and update the older doc before teaching from it.
## 2026-09-28 random.int full-range arithmetic follow-up
`random.int` now calculates its inclusive range width and result in `long long`, avoiding signed-int overflow for the full accepted C `int` range. The regression fixture is `tests/43_random_int_extreme_bounds.claro`.
Verified: built with `gcc -std=c99 -O0 -g -fsanitize=address,undefined src/claro.c -o /home/ubuntu/.hermes/profiles/maxwell/cache/scratch/claro-rng-probe -lm`; ran `ASAN_OPTIONS=detect_leaks=0 UBSAN_OPTIONS=halt_on_error=1 /home/ubuntu/.hermes/profiles/maxwell/cache/scratch/claro-rng-probe tests/43_random_int_extreme_bounds.claro` (exit 0, no sanitizer report). Before the change, the same full-range call failed with UBSan signed integer overflow at `src/claro.c:347`. This validates the extreme-range runtime behavior under ASan/UBSan; `make -s all`, `./claro test` (0 failures, including this fixture), `./claro doctor`, `./claro validate`, and `git diff --check` pass.
@@ -0,0 +1,142 @@
# Historical record — not current status
Preserved before the documentation cleanup. Claims and pending-work notes below
are dated history, not current acceptance results. Use [current status](../CURRENT_STATUS.md)
and [release status](../RELEASE_STATUS.md) instead.
# Hardening Notes (Beta23)
## File loading safety
Claro now reads source lines dynamically (no 4KB truncation).
Safety caps (to prevent memory abuse):
- Maximum single line length: 65,535 characters
- Maximum program lines: 500,000
If a file exceeds these limits, the loader fails cleanly.
## Expression-token cleanup
Each expression now releases its token strings and token-array storage before returning. This is a narrow cleanup boundary; runtime-owned variables, loaded programs, and other allocations remain separate follow-up work.
Focused verification:
```text
python3 tools/validate_memory_cleanup.py
```
The validator builds an AddressSanitizer/UndefinedBehaviorSanitizer binary, runs a repeated variable-overwrite probe, and confirms LeakSanitizer no longer reports allocations from `tokenize`/`toks_add`. The interpreter remains a trusted-script runtime, not a sandbox.
## Overwritten-value cleanup
Runtime variables and map entries own deep copies of their values. Replacing an existing variable or map entry now releases the previous string, list, or map value before storing its replacement. This is intentionally limited to overwrite boundaries; final runtime teardown remains a follow-up cleanup slice.
## Split-argument cleanup
Command argument lists created by `DO`, `CALL`, `TEXT ... CONTAINS`, and `RANDOM` are temporary parser storage. They now share one cleanup helper, so repeated calls do not retain the duplicated argument strings or pointer array. The helper does not change argument evaluation or syntax compatibility.
Focused verification builds with AddressSanitizer/UndefinedBehaviorSanitizer, repeatedly exercises a four-argument `DO`, and checks the cleanup helper before confirming the existing string, list, and map overwrite behavior.
The `REMOVE` command now releases its evaluated needle and copied list/map value after updating runtime storage. Focused verification: `python3 tools/validate_remove_expression_cleanup.py` runs 2,000 discarded string needles under ASan/UBSan/LSan; it passed with no reported leaks. This slice does not yet address the separate ownership of an item removed from a populated copied list.
When `REMOVE` finds an item in its copied list, it now frees that removed item before shifting the remaining owned values left. `python3 tools/validate_remove_list_item_cleanup.py` repeatedly adds and removes strings under ASan/UBSan/LSan to check that the item ownership is released without changing the list update behavior.
The `PUT ... KEY ... VALUE ...` command now releases its evaluated map, key, and value copies after `map_put`/`rt_set` have copied the data they own. Focused verification: `python3 tools/validate_put_expression_cleanup.py` exercises 2,000 string-valued writes under ASan/UBSan/LSan and passes with no reported leaks. This covers expression-temporary ownership only; caught runtime diagnostic-value leaks are covered by the separate cleanup slice below.
Caught runtime errors release the previous owned error-message and source-path strings when `TRY` clears the error state. `python3 tools/validate_runtime_error_cleanup.py` covers repeated caught builtin-arity errors and an inverted `random.int` range under ASan/UBSan/LSan.
Verified 2026-10-06: `python3 tools/validate_runtime_error_cleanup.py` passes with LeakSanitizer enabled; `make -s all`, `./claro test` (0 failures), `./claro doctor`, and `./claro validate` pass. The focused validator is also part of `tools/validate_v1_17.py`. This addresses caught diagnostic-value ownership only; it does not claim every malformed-input path is leak-free.
The `GET ARGUMENT ... AS ...` command now releases both its evaluated index expression and temporary argument value after runtime storage has copied the result. `python3 tools/validate_get_argument_expression_cleanup.py` first reproduced 6,000 leaked bytes over 2,000 accesses, then passes with AddressSanitizer, UndefinedBehaviorSanitizer, and LeakSanitizer enabled. This is a focused ownership cleanup; other `GET` forms have separate temporary-value ownership boundaries.
`GET ENV ... AS ...` now releases its evaluated environment-name value after converting it to the lookup string. `python3 tools/validate_get_env_expression_cleanup.py` reproduced 1,688,000 leaked bytes across 2,000 lookups before the fix and checks the same repeated lookup under ASan/UBSan/LSan. This is a narrow ownership improvement; other `GET` expression branches remain separate boundaries.
HTTP responses are capped at 1,048,576 bytes. Exceeding the cap produces a beginner-facing runtime error instead of retaining an unbounded response. The curl status suffix is taken from the final status marker, so a response body containing marker-like text is preserved. Existing `HTTP CHECK` URL safety rules remain unchanged.
Focused verification:
```text
python3 tools/validate_http_hardening.py
```
This validator uses a local HTTP server to check marker-like response text, status `200`, and the oversized-response diagnostic.
## READ FILE expression cleanup
`READ FILE ... AS ...` now releases its evaluated path value and the temporary file-content value after runtime storage copies the contents. `python3 tools/validate_read_file_expression_cleanup.py` first reproduced 204,000 leaked bytes across 2,000 reads under ASan/UBSan/LSan; after the cleanup it passes with the expected output and no reported leak. This is a narrow ownership fix; other file-command temporaries remain separate.
`WRITE FILE ... WITH ...` and `APPEND FILE ... WITH ...` now release their evaluated path and content values after writing. `python3 tools/validate_write_file_expression_cleanup.py` reproduced 206,000 leaked bytes across 2,000 writes before the cleanup; it verifies repeated writes under ASan/UBSan/LSan and checks the resulting file content.
## Verification
2026-10-07: `python3 tools/validate_read_file_expression_cleanup.py` passes under ASan/UBSan/LSan; `make -s all`, `./claro test` (0 failures), `./claro doctor`, `./claro validate`, and `python3 tools/validate_v1_17.py` all pass. Runtime-verified narrow cleanup; full validation does not establish that every allocation path is leak-free.
## External command trust boundary
`RUN COMMAND` intentionally executes a shell command with the user's permissions. It is a trusted-code capability, not a sandbox or an untrusted-script safety feature. Claro does not attempt a fragile blacklist sanitizer; users must review scripts before running them.
Focused documentation verification:
```text
python3 tools/validate_trusted_command_docs.py
```
## Control-flow expression cleanup
Conditions evaluated by `IF` are temporary runtime values. The `IF` command now
releases its condition after choosing a branch, including when the condition is
a text expression. This is a narrow cleanup boundary; other expression
temporaries remain separate follow-up work.
Focused verification:
```text
python3 tools/validate_control_expression_cleanup.py
```
The validator builds with AddressSanitizer/UndefinedBehaviorSanitizer,
executes 2,000 temporary `IF` conditions under LeakSanitizer, and checks the
expected output.
## ASK temporary-value cleanup
`ASK` releases the evaluated prompt value after converting it to display text,
and releases the temporary input value after `rt_set_checked` copies it into
runtime storage. This keeps prompt and input strings from accumulating during
repeated input loops without changing prompt or input behavior.
Focused verification:
```text
python3 tools/validate_ask_prompt_cleanup.py
```
The validator runs 2,000 prompt/input operations with AddressSanitizer, UndefinedBehaviorSanitizer, and LeakSanitizer enabled. The dedicated validator passes. A separate malformed-input sanitizer smoke run (`gcc -std=c99 -O0 -g -fsanitize=address,undefined src/claro.c -o ... -lm` followed by the generated malformed script) exits 1 due to two existing leaked error-message strings (140 bytes total) allocated in `rt_error` at `src/claro.c:142`; this is outside the ASK cleanup slice and remains a blocker to sanitizer-clean malformed-input validation.
## Recursive call-depth guard
User-defined function and object-method calls stop at 256 active calls with a clear runtime diagnostic instead of continuing into an uncontrolled stack overflow. `python3 tools/validate_call_depth_guard.py` runs an intentionally unbounded recursive function and checks the diagnostic. This limit does not affect ordinary non-recursive programs; it is a documented safety bound, not a sandbox guarantee.
## OBJECT expression cleanup investigation (blocked)
The current uncommitted `OBJECT CLASS` / `OBJECT FIELDS` cleanup probe is not
verified. On 2026-10-12, `python3 tools/validate_object_expression_cleanup.py`
compiled and ran under ASan/UBSan/LSan but failed: LeakSanitizer reported
58,996 bytes in 10,016 allocations over the 2,000-iteration object probe.
Stacks include evaluated `OBJECT` values and `object_class_name`'s copied
`map_get` result, but also allocations from object creation and checked runtime
storage. This shows the probe crosses multiple ownership boundaries; no claim
of leak-free OBJECT expressions is made. Preserve the working tree and isolate
one ownership boundary with a narrower reproduction before changing more code.
The normal golden suite passed (`python3 tools/run_tests.py`, 0 failures). A narrower class-only probe (`CLASS Person`; 2,000 repetitions of `OBJECT CLASS person AS kind`) also fails LeakSanitizer: 58,024 bytes in 8,002 allocations. The trace isolates 14,000 bytes from the copied `class` value returned by `map_get` in `object_class_name` (2,000 allocations), 14,000 bytes from each result's `v_str`, and 10,000 bytes in 2,000 `rt_set_checked` result temporaries; baseline object creation additionally leaks. The existing combined validator therefore cannot prove this expression boundary independently. No further ownership change is made until the `rt_set_checked`/runtime-storage ownership and baseline object lifetime are isolated. The attempted OBJECT cleanup remains uncommitted and must not be described as verified.
## GET KEY temporary-value cleanup
`GET ... KEY ... AS ...` releases its evaluated map copy and key value after storing the selected value. `python3 tools/validate_get_key_expression_cleanup.py` runs 2,000 lookups under ASan/UBSan/LSan; before the cleanup it reproduced 1,004,000 bytes leaked, and after it passes with the expected output and no reported leaks.
`GET ... AT ... AS ...` now releases its evaluated index expression after converting it to a list position. The regression validator `python3 tools/validate_get_at_expression_cleanup.py` repeats a string-valued index 2,000 times under ASan/UBSan/LSan; before the fix, LeakSanitizer reported 4,000 leaked bytes, and after it passes with the expected selected value.
## COPY/MOVE expression temporary cleanup
`COPY FILE ... TO ...` and `MOVE FILE ... TO ...` release both evaluated path values after converting them to owned path strings. `python3 tools/validate_copy_move_expression_cleanup.py` runs 1,000 copy/move pairs under ASan/UBSan/LSan; before the cleanup it reproduced 74,000 bytes leaked across 4,000 path-expression values, and after it passes with no reported leaks.
@@ -0,0 +1,196 @@
# Claro Roadmap
> **Current direction:** Follow [`LEARNING_FIRST.md`](../LEARNING_FIRST.md) for
> active priorities. The broader milestones below are historical planning
> context, not a mandate to expand the language or a statement that features
> have been removed.
Current release: **Claro v1.18.26**
Claro is now in the stable v1 development line. Older RC milestones are kept as historical notes in files such as `RC0_NOTES.md`, `RC3_NOTES.md`, and `RC9_VALIDATION.md`; they are not the current roadmap.
## Current direction
Keep Claro beginner-first while growing into a complete small programming platform:
- readable syntax before clever syntax
- friendly diagnostics before advanced compiler features
- safe local/offline defaults before networked behavior
- small features that are easy to teach, test, and document
- normal feature releases inside the v1 line; reserve v2 for a future full rewrite
## v1.18.26 status snapshot
See `CURRENT_STATUS.md` for the detailed feature matrix.
- Beginner scripting core: stable foundation
- Objects/classes: foundation present; needs polish and stronger type checks
- Package workflow: local foundation present; registry and publishing not complete
- Networking: HTTP client foundation present; beginner web server not complete
- Concurrency: deterministic task foundation; native threads not exposed as beginner API
- Static typing: variables and containers foundation; function/object/import typing incomplete
- IDE support: metadata/helper foundation; full editor/LSP experience incomplete
- Graphics/SDL: experimental/planned; not enabled in the stable executable
## Near-term cleanup priorities
1. Keep beginner-facing docs current and separate from historical release notes.
2. Keep examples aligned with the modern simple syntax (`END`, `DO`, short `SET`, short `ASK`) while documenting older compatibility forms separately.
3. Expand validation around typecheck diagnostics and package/networking safety. Current Forgejo/Gitea CI runs the documented release gates (`claro validate`, typecheck diagnostics validation, version convention validation, package security validation, compiler warning validation, and CI workflow coverage validation). The compiler warning validator rebuilds `src/claro.c` and blocks path-truncation warnings; the workflow coverage validator checks executable `run` steps rather than comments and requires its own command. Current package safety validation covers safe project creation, rejecting unsafe project names during `claro new`, safe manifest/lockfile creation, rejecting unsafe package names during `package add`, detecting unsafe package names already present in `claro.project` during `package doctor`, refusing to write lockfile data for unsafe package names during `package lock`, making `package list` fail instead of displaying unsafe package entries as normal package names, making `package init` fail instead of reporting the project ready when unsafe package names are already present, making `package add` fail before changing files when unsafe package names are already present, making `package remove` fail instead of reporting success when unsafe package names remain during lockfile refresh, allowing `package remove` to remove an exact unsafe package entry so learners can repair a bad `claro.project`, making `package doctor` reject stale lockfile checksums for listed packages, and making `package doctor` reject lockfile package entries that are not listed in `claro.project`. Current function validation covers correct checked calls, wrong-type arguments, missing checked-argument diagnostics, missing unchecked-argument diagnostics for simple functions in both modern `DO greet` and empty compatibility `CALL greet WITH` forms, extra-argument diagnostics, and beginner-facing unknown-function diagnostics for mistyped modern `DO` and compatibility `CALL ... WITH` calls; current object-method parameter validation covers correct modern `DO object.method ...` and compatibility `CALL object.method WITH ...` checked calls, wrong-type diagnostics for both call forms, checked methods after another method in the same class, missing and extra checked-argument diagnostics including the extra-argument case for a checked method after another method in the same class, missing unchecked-argument diagnostics for simple object methods, missing-object guidance when `DO player.method ...` or compatibility `CALL object.method WITH ...` appears before `NEW` even if the method name is also wrong, and class-specific unknown-method diagnostics for both modern and compatibility calls when a learner calls a method the class does not declare; current object-field validation covers direct NUMBER/TEXT/YESNO field-assignment positives, direct object-field `CHECK TYPE` metadata positives for NUMBER/TEXT/YESNO fields, negative NUMBER/TEXT/YESNO field metadata mismatches, NUMBER/TEXT/YESNO-expectation unknown-field `CHECK TYPE` diagnostics, direct wrong-type field assignment diagnostics, field collection when a `HAS` field appears after a simple method, direct unknown-field diagnostics for NUMBER/TEXT/YESNO values, a beginner-facing fallback for unknown fields assigned from expressions whose type is not inferable yet, and missing-object diagnostics for both `SET object.field value` and `CHECK TYPE object.field IS TYPE` before `NEW`.
3a. Keep package diagnostics actionable: invalid package manifest format fields now name the file and explain how to repair it, separately from an absent manifest. Keep package validation honest by checking that the project manifest has one non-empty project name, that the project and each listed package manifest declare the exact supported manifest version, package version, local source, and expected package name, and that lockfiles declare exactly one current release version.
3b. Keep package security validation in Forgejo/Gitea CI so unsafe names, exact manifest-version/name/version/checksum mismatches, missing manifests, duplicate project manifest versions and packages, duplicate lock packages, duplicate package manifest names, versions, and checksums, and lockfile errors remain release blockers.
4. Add small examples for each foundation feature before adding bigger syntax. The object-field foundation now includes positive and negative validation for explicitly typed NUMBER/TEXT assignments and field expressions such as `SET player.score player.name`, `SET player.score player.score + 1`, `SET player.name player.score + 1`, explicitly typed field assignments, explicit typed assignments to undeclared fields, simple and chained aliases such as `SET alias player`, `SET backup alias`, followed by `SET backup.score ...` or `CHECK TYPE backup.score IS ...`, chained aliases in object-method calls through both modern `DO` and compatibility `CALL ... WITH` forms (including a dedicated positive modern `DO` fixture), field-to-field, compound arithmetic field expressions, and arithmetic/text expression mismatches; typed containers also accept a map with nested type metadata when it is added to a `LIST OF MAP`; broader alias/control-flow checking remains planned.
5. Keep the focused typecheck validator complete: the method-body field diagnostic fixtures are now exercised by `claro validate` alongside the standalone diagnostic validator; continue wiring each new `typecheck_*.claro` fixture into both gates. Modern and compatibility unknown-field expression diagnostics now have matching focused coverage, explicitly typed unknown method-field assignments have matching modern and compatibility fixtures with the expected `HAS field TYPE` repair hint, and short explicitly typed method-field assignments have matching modern and compatibility missing-value coverage.
-6. Continue narrowing expression diagnostics: known TEXT operands now remain visible through arithmetic, with focused numeric-subtraction, numeric-division, and numeric-multiplication positives plus text-operand addition, subtraction, multiplication, and division coverage. Typed function returns now include positive subtraction and division examples alongside multiplication, including compatibility `TAKES` / `LEARNED` numeric-addition, subtraction, division, and multiplication return fixtures. Text concatenation into TEXT fields is covered in both operand orders, including field-to-field concatenation. Each numeric operator names the text operand and explains that it needs NUMBER values. Compatibility method return diagnostics now cover addition, subtraction, division, and multiplication. `CHECK TYPE` also rejects misspelled expected type names.
- Keep typed method return examples balanced: numeric addition, subtraction, division, and multiplication now have dedicated positive compatibility `TAKES` / `LEARNED` method fixtures in the focused validator, alongside compatibility addition, subtraction, and division mismatch coverage and the existing method return mismatch diagnostics.
- Keep method field examples balanced across syntax generations: modern and compatibility `TAKES` / `LEARNED` NUMBER, TEXT, and YESNO assignments now have positive fixtures beside the modern and compatibility mismatch fixtures, and modern plus compatibility YESNO method-field metadata now have dedicated positive fixtures beside the negative fixture.
- Keep IDE metadata aligned with the current beginner syntax: typed-language keywords such as `RETURNS`, `CHECK`, and `TYPE` are now included in the metadata used by editor helpers.
- Keep compatibility-call coverage aligned with modern calls: empty `CALL object.method WITH` forms now have a focused missing-argument diagnostic fixture alongside the modern `DO object.method` case.
Keep declared return types honest: `claro typecheck` now reports friendly diagnostics for unknown declared return types, missing return types, mismatched, not-yet-inferable, and empty return expressions (including compatibility `TAKES` / `LEARNED` methods), declarations with no `RETURN` in modern and compatibility function forms, and incomplete conditional branches, when a simple function or object method declares `RETURNS TYPE`; missing-type coverage includes modern functions and both modern and compatibility methods, with a compatibility-function fixture as well. Incomplete conditional-return coverage now also includes the compatibility `TAKES` / `LEARNED` function spelling, so older lessons receive the same every-path diagnostic. The declaration keyword is case-insensitive like other Claro keywords, with positive coverage in modern and compatibility function forms, including lowercase compatibility declarations and lowercase compatibility method declarations, plus a lowercase compatibility-method mismatch fixture. Complete `IF`/`ELSE` return branches, including nested complete conditionals in functions and methods, are accepted, and modern and compatibility function syntax plus compatibility method syntax are covered by focused fixtures. `CHECK TYPE` parameter metadata also informs arithmetic return-expression checking, including object methods; positive numeric multiplication return coverage now sits beside the mismatch fixtures, including a dedicated compatibility-method multiplication success fixture; typed methods can also return class-declared fields by simple name, with positive and mismatch fixtures in both modern and compatibility method syntax, and unknown method return expressions now have dedicated modern and compatibility diagnostic fixtures, and NUMBER return expressions with known TEXT arithmetic operands identify the operation and offending operand, including focused addition, subtraction, multiplication, and division coverage for functions and methods. Compatibility method subtraction diagnostics are also covered alongside the existing addition diagnostic. Full path-sensitive analysis across nested conditionals and loops remains planned.
7. Keep method-body field diagnostics learner-facing: typed methods now name the class method when a simple class field receives a known mismatch in `CHECK TYPE` or assignment, or a known TEXT operand in a NUMBER expression, with matching text and numeric coverage in modern and compatibility method syntax; direct assignments to undeclared method fields now report the missing field when the value type is known, and bare `CHECK TYPE missing IS TYPE` statements inside methods now use the expected type in the repair hint in both modern and compatibility method syntax; broader control-flow and object-flow analysis remains planned.
8: Keep method-body coverage balanced across beginner field types and syntax generations: YESNO field assignment mismatches are now validated for both modern `DO` and compatibility `CALL ... WITH` method syntax, method-body `CHECK TYPE` metadata mismatches now have matching compatibility `TAKES` / `LEARNED` coverage alongside the modern form, compatibility YESNO field metadata has dedicated positive and negative fixtures, and compatibility method assignments to undeclared fields have the same focused missing-field diagnostic as modern methods, including a beginner-facing fallback when the assigned expression is not inferable yet.
8a. Keep method-body text expressions balanced: modern TEXT field concatenation is covered when the field is on either side of `+`, so both common beginner word-order patterns remain accepted.
8b. Keep compatibility method-body text expressions aligned: `TAKES` / `LEARNED` methods now have positive TEXT concatenation coverage in both operand orders beside the modern `TEACH` / `END` examples.
8c. Keep explicit method-field annotations aligned with class declarations: `SET score TEXT "oops"` inside a method with `HAS score NUMBER` now reports the declared field mismatch instead of accepting the inline annotation; broader annotation consistency remains planned.
8d. Keep inline field annotations consistent with `HAS` declarations: `claro typecheck` now rejects conflicting annotations on method-body and direct object-field assignments, explains which type to use, and gives a known-type repair hint for unknown direct object-field annotations in both `AS ... TO` and short `SET field TYPE value` forms.
8d.1. Keep direct field annotation forms balanced: the compatibility-shaped `SET player.score AS NUMBER TO 10` form now has a dedicated positive fixture beside short explicit annotations, so both accepted spellings remain protected by focused validation.
8d.2. Keep method field annotation forms balanced: modern and compatibility methods now have dedicated positive fixtures for `SET score AS NUMBER TO 10`, so the compatibility-shaped annotation remains protected inside both supported method syntaxes.
8d.3. Keep release validation aligned with method annotation coverage: the modern and compatibility `AS ... TO` method fixtures now run through `claro validate`, not only the focused Python diagnostic validator.
8d.4. Keep separated direct field assignments unambiguous: `SET player.score AS NUMBER TO 10 extra` now reports the trailing text and explains the one-expression form instead of accepting a partially parsed value.
8d.5. Keep short direct field assignments unambiguous: `SET player.score NUMBER 10 extra` now rejects trailing words with a repair hint, matching the separated `AS ... TO` form.
8d.6. Keep separated direct field assignments complete: `SET player.score AS NUMBER TO` now explains that one value expression is required after `TO`.
8d.7. Keep short direct field assignments complete: `SET player.score NUMBER` now explains that one value expression is required after the type.
8d.8. Keep separated direct field assignments structurally complete: `SET player.score AS NUMBER` now explains that `TO` must appear before the value, with a complete repair example.
8d.9. Keep untyped direct field assignments complete: `SET player.score` now explains that a value expression is required after the field name instead of silently accepting an empty assignment.
8e. Keep inline method-field annotation coverage aligned across syntax generations: compatibility `TAKES` / `LEARNED` methods now have matching positive and negative fixtures, so older lessons retain the same class-declared-type guidance.
8f. Keep inline method-field annotations learner-facing: an unknown annotation such as `BANANA` now names the field and method and suggests the `HAS` type instead of silently treating the annotation as an expression.
8g. Keep unknown inline method-field annotation diagnostics aligned across syntax generations: compatibility `TAKES` / `LEARNED` methods now have matching focused coverage for misspelled annotations.
8h. Keep function and method declarations unambiguous: `claro typecheck` now reports repeated parameter names with a repair hint, before duplicate names can make argument diagnostics confusing, including modern and compatibility `TAKES` / `LEARNED` functions and methods. Duplicate object method names within one class now get the same declaration-time protection and a rename hint, with focused coverage for both modern and compatibility method syntax. Broader signature validation remains planned.
8h.1. Keep top-level function declarations unambiguous: duplicate `TEACH greet` blocks now get a declaration-time diagnostic with a rename hint, preventing two functions from competing for one name. Broader signature validation remains planned.
8h.2. Keep function declarations understandable: a bare `TEACH` now gets a direct diagnostic explaining that a function name is required, with a small example repair. Broader signature validation remains planned.
8h.3. Keep method declarations understandable: a bare `TEACH` inside a class now names the class and explains how to add a method name, instead of using the less-specific top-level function diagnostic.
8i. Keep class declarations unambiguous: duplicate `HAS` field names within one class now get a declaration-time diagnostic with a repair hint, preventing conflicting field types from being silently accepted, while same-named fields in different classes remain valid. Broader object declaration validation remains planned.
8j. Keep class names unambiguous: duplicate `CLASS Player` declarations now get a declaration-time diagnostic with a rename hint, preventing two class definitions from competing for one name. Extra words after a class name also get a declaration-time repair hint, so a typo such as `CLASS Player Extra` cannot silently create a surprising class identity.
8k. Keep object names unambiguous: repeated `NEW Player player` statements now get a declaration-time diagnostic with a rename hint, preventing one object type environment from silently replacing another.
8l. Keep object creation understandable: when classes are declared in a script, an unknown `NEW` class name now gets a direct class-name diagnostic and repair hint; the older permissive `NEW` behavior remains when no class declarations are present.
8m. Keep class field metadata understandable: a missing or unknown type in a `HAS field TYPE` declaration, or extra text after a valid type, now gets a class-and-field diagnostic with supported type examples or a direct repair hint before object-field checks use that metadata.
8m.1. Keep class field declarations understandable: a bare `HAS` now gets a direct diagnostic explaining that both a field name and type are required, with a small `HAS score NUMBER` repair example.
8n. Keep object creation unambiguous: extra words after `NEW ClassName object` now get a direct repair hint, so a malformed object declaration cannot silently discard part of the learner's input.
8o. Keep class declarations understandable: a bare `CLASS` now gets a direct diagnostic explaining that a class name is required, with a small example repair.
8p. Keep object declarations understandable: a `NEW` statement with a class but no object name now gets a direct diagnostic with a complete `NEW Player player` repair example; a bare `NEW` now separately explains that both a class and object name are required.
8q. Keep `CHECK TYPE` syntax understandable: a missing `IS` now gets a direct repair hint with a complete `CHECK TYPE score IS NUMBER` example.
8q.1. Keep `CHECK TYPE` declarations understandable: `CHECK TYPE score IS` now gets a direct repair hint naming the missing expected type and showing a complete example.
8q.2. Keep `CHECK TYPE` declarations unambiguous: extra words after a valid expected type now get a direct repair hint instead of being reported as an unknown type.
8q.3. Keep `CHECK TYPE` declarations complete: a bare `CHECK TYPE` now explains that the expression, `IS`, and expected type are all required, with a complete repair example.
8q.4. Keep `CHECK TYPE` declarations ordered: `CHECK TYPE IS NUMBER` now explains that an expression belongs before `IS`, with a complete repair example.
8q.5. Keep `TYPE OF` declarations understandable: a missing `AS` now gets a direct repair hint with a complete `TYPE OF score AS kind` example.
8q.5.1. Keep `TYPE OF` declarations complete: a missing expression before `AS` now gets a direct repair hint with a complete `TYPE OF score AS kind` example.
8q.5.1a. Keep incomplete `TYPE OF` diagnostics focused: when the expression is missing, report that repair before secondary trailing-token errors.
8q.5.2. Keep `TYPE OF` declarations named: a missing result name after `AS` now gets a direct repair hint with a complete `TYPE OF score AS kind` example.
8q.5.3. Keep complete `TYPE OF` examples protected: the valid `TYPE OF score AS kind` form now has a positive fixture in the focused typecheck validation matrix.
8q.5.3a. Keep `TYPE OF` keyword handling consistent: lowercase `type of score as kind` now has positive fixture coverage alongside the uppercase form.
8q.5.3b. Keep `CHECK TYPE` keyword handling consistent: lowercase `check type score is number` now has positive fixture coverage alongside the uppercase form.
8q.5.3c. Keep object-field examples consistent with keyword rules: lowercase `class`, `has`, `new`, `set`, and `check type` now have one focused positive typed-field fixture.
8q.5.4. Keep bare `TYPE OF` declarations understandable: report that the expression, `AS`, and result name are all required, with a complete repair example.
8q.6. Keep `TYPE OF` declarations unambiguous: extra words after the result name now get a direct repair hint instead of being silently included in the variable name.
8q.7. Keep typed `TEACH` declarations unambiguous: extra words after a declared return type now get a direct repair hint instead of being treated as part of an unknown type.
8q.8. Keep typed `TEACH` declarations complete: a bare `RETURNS` now gets a direct repair hint naming the missing type and showing a supported example such as `NUMBER`.
8q.9. Keep missing return-type diagnostics aligned across functions and methods: modern and compatibility methods now name the complete `Class.method` identity when `RETURNS` has no type, with focused fixtures in the typecheck and release validation matrices.
8q.10. Keep return statements beginner-readable: typed returns now identify simple extra tokens after a value and explain that `RETURN` accepts one expression, with focused release validation coverage.
8q.11. Keep extra-token return diagnostics aligned across syntax generations: modern and compatibility functions and object methods now have focused fixtures for `RETURN value extra`, so errors keep the function or class-and-method name and the same repair hint.
8q.12. Keep empty typed-return diagnostics aligned across syntax generations: modern and compatibility functions and object methods now have focused fixtures for bare `RETURN`, so each form explains that the declared type needs an expression.
8q.13. Keep unknown typed-return diagnostics aligned across syntax generations: compatibility `TAKES` / `LEARNED` functions now have a focused fixture for an uninferable return expression, matching modern functions and methods.
### 1. Strong static types
Goal: make larger beginner programs safer without making first scripts harder.
Needed next:
- richer typed function signatures beyond the focused simple `RETURNS TYPE` check
- typed function returns beyond the focused simple function/method `RETURNS TYPE` check
- type checking across branches and loops
- typed imports/modules
- broader object field checking and richer method return typing
- clearer error messages for type mismatches
### 2. Objects and classes polish
Goal: keep object-oriented examples readable enough for beginners.
Needed next:
- constructor-style defaults or beginner-friendly initialization helpers
- richer method return checks beyond the focused simple `RETURNS TYPE` check
- object printing/debugging helpers
- better examples that avoid abstract toy OOP
### 3. Package manager and registry
Goal: make sharing beginner libraries safe and predictable.
Needed next:
- local package install from a folder
- version constraints
- package export/publish format
- lock-file verification
- remote registry design with checksums/signatures before downloads
### 4. Networking and web apps
Goal: allow safe beginner networking and small local web apps.
Needed next:
- beginner web server syntax
- routes
- request/response helpers
- local-only safe default mode
- examples that work offline or on localhost
### 5. Tasks, concurrency, and possible threads
Goal: teach concurrency as safe tasks first, not low-level thread hazards.
Needed next:
- cooperative scheduler semantics
- cancellation
- timeouts
- message passing or channels
- explicit decision on whether native threads are needed later
### 6. IDE/editor support
Goal: give beginners fast feedback in their editor.
Needed next:
- syntax highlighting grammar
- editor extension packaging
- hover/help text
- go-to-definition where practical
- code actions for common mistakes
- LSP server after metadata and diagnostics stabilize
### 7. Graphics and SDL
Goal: make visual/game examples possible without making the portable stable build fragile.
Needed next:
- keep placeholder graphics commands beginner-safe
- define optional SDL build flag/backend
- add graceful errors when graphics are unavailable
- document which examples require experimental builds
## Documentation rule
Every new feature should update three places before it is treated as release-ready:
1. `README.md` or the relevant feature doc for beginner-facing usage
2. `CURRENT_STATUS.md` for status and limitations
3. a validation note or test command showing how it was checked
+33
View File
@@ -0,0 +1,33 @@
# Runtime Guards & Limits (Beta25)
Claro includes optional safety limits to prevent hangs and memory abuse.
## 1) Step limit (runaway protection)
Environment variable:
- `CLARO_STEP_LIMIT=200000`
Stops a program if it executes too many steps/lines.
## 2) File size limit
Environment variable:
- `CLARO_MAX_FILE_BYTES=16777216` (default 16 MiB)
Applies to:
- `READ FILE ...`
- `WRITE FILE ... WITH ...`
- `APPEND FILE ... WITH ...`
## 3) JSON size limit
Environment variable:
- `CLARO_MAX_JSON_BYTES=8388608` (default 8 MiB)
Applies to:
- `PARSE JSON ...`
- `MAKE JSON ...`
- `MAKE JSON PRETTY ...`
## 4) Error output
Runtime errors include:
- `file:line: message`
- stack trace (CALL chain)
- and JSON parse errors include `Near: ...` snippet
+2 -2
View File
@@ -1,6 +1,6 @@
SAY "Claro Calculator" SAY "Claro Calculator"
ASK "First number?" AS a ASK "First number?" a
ASK "Second number?" AS b ASK "Second number?" b
SAY "Sum:" SAY "Sum:"
SAY a + b SAY a + b
+5 -6
View File
@@ -2,20 +2,19 @@
# Shows how to use DO TIMES and REPEAT loops # Shows how to use DO TIMES and REPEAT loops
SAY "Counting with DO TIMES:" SAY "Counting with DO TIMES:"
SET counter TO 1 SET counter 1
DO 5 TIMES DO 5 TIMES
SAY counter SAY counter
SET counter TO counter + 1 SET counter counter + 1
DONE END
SAY "" SAY ""
SAY "Counting down with REPEAT:" SAY "Counting down with REPEAT:"
SET n TO 5 SET n 5
REPEAT REPEAT
SAY n SAY n
SET n TO n - 1 SET n n - 1
UNTIL n = 0 UNTIL n = 0
SAY "Blastoff!" SAY "Blastoff!"
+3 -3
View File
@@ -1,12 +1,12 @@
SAY "Guess the Number Game" SAY "Guess the Number Game"
SET secret TO 7 SET secret 7
ASK "Guess a number from 1 to 10:" AS guess ASK "Guess a number from 1 to 10:" guess
IF guess = secret IF guess = secret
SAY "You got it!" SAY "You got it!"
ELSE ELSE
SAY "Not this time. The number was:" SAY "Not this time. The number was:"
SAY secret SAY secret
ENDIF END
+1 -1
View File
@@ -1,5 +1,5 @@
SAY "Name Input Example" SAY "Name Input Example"
ASK "What is your name?" AS name ASK "What is your name?" name
SAY "Hello " + name + "!" SAY "Hello " + name + "!"
+9 -11
View File
@@ -1,28 +1,26 @@
SAY "Welcome to the Claro quiz!" SAY "Welcome to the Claro quiz!"
ASK "What is your name?" AS name ASK "What is your name?" name
SAY "Hello " + name + "!" SAY "Hello " + name + "!"
SET score TO 0 SET score 0
ASK "What is 2 + 2?" AS answer ASK "What is 2 + 2?" answer
IF answer = 4 IF answer = 4
SAY "Correct!" SAY "Correct!"
SET score TO score + 1 SET score score + 1
ELSE ELSE
SAY "Not quite. The answer is 4." SAY "Not quite. The answer is 4."
ENDIF END
ASK "What is 5 + 5?" answer2
ASK "What is 5 + 5?" AS answer2
IF answer2 = 10 IF answer2 = 10
SAY "Correct!" SAY "Correct!"
SET score TO score + 1 SET score score + 1
ELSE ELSE
SAY "Not quite. The answer is 10." SAY "Not quite. The answer is 10."
ENDIF END
SAY "Your final score is:" SAY "Your final score is:"
SAY score SAY score
@@ -30,4 +28,4 @@ IF score = 2
SAY "Great job!" SAY "Great job!"
ELSE ELSE
SAY "Good try. Keep practicing!" SAY "Good try. Keep practicing!"
ENDIF END
+2 -2
View File
@@ -1,6 +1,6 @@
SAY "Shopping List Example" SAY "Shopping List Example"
SET items TO LIST SET items LIST
ADD "milk" TO items ADD "milk" TO items
ADD "bread" TO items ADD "bread" TO items
ADD "apples" TO items ADD "apples" TO items
@@ -9,4 +9,4 @@ SAY "Items to buy:"
FOR EACH item IN items FOR EACH item IN items
SAY item SAY item
DONE END
+1 -1
View File
@@ -1,3 +1,3 @@
SAY "Lesson 2: Ask a question" SAY "Lesson 2: Ask a question"
ASK "What is your name?" AS name ASK "What is your name?" name
SAY "Hello " + name SAY "Hello " + name
+2 -2
View File
@@ -1,5 +1,5 @@
SAY "Lesson 3: Variables" SAY "Lesson 3: Variables"
SET name TO "Alex" SET name "Alex"
SET score TO 5 SET score 5
SAY name SAY name
SAY score SAY score
+3 -3
View File
@@ -1,5 +1,5 @@
SAY "Lesson 4: Math" SAY "Lesson 4: Math"
SET apples TO 3 SET apples 3
SET oranges TO 2 SET oranges 2
SET total TO apples + oranges SET total apples + oranges
SAY total SAY total
+2 -2
View File
@@ -1,8 +1,8 @@
SAY "Lesson 5: Choices" SAY "Lesson 5: Choices"
SET score TO 8 SET score 8
IF score >= 5 IF score >= 5
SAY "You passed!" SAY "You passed!"
ELSE ELSE
SAY "Try again." SAY "Try again."
ENDIF END
+2 -3
View File
@@ -1,8 +1,7 @@
SAY "Lesson 6: Loops" SAY "Lesson 6: Loops"
DO 3 TIMES DO 3 TIMES
SAY "Practice" SAY "Practice"
DONE END
FOR i FROM 1 TO 3 FOR i FROM 1 TO 3
SAY i SAY i
DONE END
+2 -2
View File
@@ -1,8 +1,8 @@
SAY "Lesson 7: Lists" SAY "Lesson 7: Lists"
SET items TO LIST SET items LIST
ADD "milk" TO items ADD "milk" TO items
ADD "bread" TO items ADD "bread" TO items
FOR EACH item IN items FOR EACH item IN items
SAY item SAY item
DONE END
+3 -4
View File
@@ -1,8 +1,7 @@
SAY "Lesson 8: Functions with TEACH" SAY "Lesson 8: Functions with TEACH"
TEACH greet TAKES name TEACH greet name
RETURN "Hello " + name RETURN "Hello " + name
LEARNED END
DO greet "Alex"
CALL greet WITH "Alex"
SAY RESULT SAY RESULT
+2 -2
View File
@@ -1,8 +1,8 @@
SAY "Lesson 10: Tiny Quiz" SAY "Lesson 10: Tiny Quiz"
ASK "What is 2 + 2?" AS answer ASK "What is 2 + 2?" answer
IF answer = 4 IF answer = 4
SAY "Correct!" SAY "Correct!"
ELSE ELSE
SAY "The answer is 4." SAY "The answer is 4."
ENDIF END
+3
View File
@@ -2,6 +2,9 @@
These are small lesson programs for the first hour of learning Claro. These are small lesson programs for the first hour of learning Claro.
These lessons use short `ASK` and `SET`, `TEACH` with `DO`, and `END` for
choices, loops, and functions. See [the syntax guide](../docs/SYNTAX.md).
Recommended order: Recommended order:
1. `01_hello.claro` - show text with `SAY` 1. `01_hello.claro` - show text with `SAY`
+7934 -669
View File
File diff suppressed because one or more lines are too long
+21
View File
@@ -0,0 +1,21 @@
@echo off
rem Run only in a fresh extracted Claro test folder.
if exist native.ok del native.ok
if not exist osprobe.exe goto wrongos
osprobe.exe --require-win98se >oscheck.txt
if errorlevel 1 goto wrongos
claro98.exe --version >nver.txt
if errorlevel 1 goto failed
claro98.exe test >ntests.txt
if errorlevel 1 goto failed
claro98.exe validate >ncheck.txt
if errorlevel 1 goto failed
echo Automated native checks passed. Manual console and file checks are still required.
echo PASS>native.ok
goto end
:wrongos
echo Native target not verified. Check oscheck.txt and the probe message.
goto end
:failed
echo Claro checks failed. Keep the logs; do not report acceptance.
:end
+6
View File
@@ -0,0 +1,6 @@
IMPORT "lib/random.claro" AS random
CALL random.seed WITH 17
CALL random.int WITH -2147483648, 2147483647
SET value RESULT
SAY value >= -2147483648
SAY value <= 2147483647
+2
View File
@@ -0,0 +1,2 @@
YES
YES
+56
View File
@@ -0,0 +1,56 @@
[
{
"name": "quote",
"filename": "quote.claro",
"mode": "run",
"code": "SAY \"Start\"\nSAY \"unfinished\n",
"diagnostic": "quote.claro:2: Text is missing a closing quote. Add a double quote at the end.",
"repair": "SAY \"Start\"\nSAY \"finished\"\n",
"output": "Start\nfinished\n"
},
{
"name": "operand",
"filename": "operand.claro",
"mode": "run",
"code": "SET score 1\nSAY score +\n",
"diagnostic": "operand.claro:2: This expression needs a value after the operator.",
"repair": "SET score 1\nSAY score + 1\n",
"output": "2\n"
},
{
"name": "command",
"filename": "command.claro",
"mode": "check",
"code": "PRNT \"Hello\"\n",
"diagnostic": "command.claro:1: I do not know the command \"PRNT\". Did you mean \"SAY\"?",
"repair": "SAY \"Hello\"\n",
"output": "Hello\n"
},
{
"name": "assignment",
"filename": "assignment.claro",
"mode": "check",
"code": "SET name\n",
"diagnostic": "assignment.claro:1: SET needs a name and a value. Try: SET name \"Alex\"",
"repair": "SET name \"Alex\"\nSAY name\n",
"output": "Alex\n"
},
{
"name": "choice",
"filename": "choice.claro",
"mode": "check",
"code": "IF YES\n SAY \"Yes\"\n",
"diagnostic": "choice.claro:1: IF block opened here needs END",
"repair": "IF YES\n SAY \"Yes\"\nEND\n",
"output": "Yes\n"
},
{
"name": "variable",
"filename": "variable.claro",
"mode": "run",
"code": "SET score 10\nSAY socre\n",
"diagnostic": "variable.claro:2: I do not know the variable \"socre\" yet.",
"repair": "SET score 10\nSAY score\n",
"output": "10\n"
}
]
+44
View File
@@ -0,0 +1,44 @@
[
{
"name": "Say hello",
"code": "SAY \"Hello!\"\n",
"input": "",
"output": "Hello!\n",
"task": "Run this once. Change the message to something you choose and run it again."
},
{
"name": "Ask a name",
"code": "ASK \"Name? \" name\nSAY \"Hello \" + name\n",
"input": "Sam\n",
"output": "Name? \nHello Sam\n",
"task": "Run it and type a pretend name. Explain where that name is remembered."
},
{
"name": "Change a number",
"code": "SET score 2\nSAY score + 1\n",
"input": "",
"output": "3\n",
"task": "Predict the result, run it, then change 2 to another small number."
},
{
"name": "Make a choice",
"code": "SET score 2\nIF score > 1\n SAY \"More than one\"\nELSE\n SAY \"One or less\"\nEND\n",
"input": "",
"output": "More than one\n",
"task": "Optional: change score to 0. Predict which message appears."
},
{
"name": "Repeat",
"code": "DO 3 TIMES\n SAY \"Again\"\nEND\n",
"input": "",
"output": "Again\nAgain\nAgain\n",
"task": "Optional: change how many times the message appears. Ask how to stop a stuck run."
},
{
"name": "Save a file",
"code": "WRITE FILE \"practice.txt\" WITH \"My first file\"\nREAD FILE \"practice.txt\" AS text\nSAY text\n",
"input": "",
"output": "My first file\n",
"task": "Optional: use a new writable practice folder. Find the file after running it."
}
]
+42
View File
@@ -0,0 +1,42 @@
{
"hello.claro": [
"",
"Hello from Claro RC2!\n"
],
"name_input.claro": [
"Ada\n",
"Name Input Example\nWhat is your name?\nHello Ada!\n"
],
"quiz.claro": [
"Ada\n4\n10\n",
"Welcome to the Claro quiz!\nWhat is your name?\nHello Ada!\nWhat is 2 + 2?\nCorrect!\nWhat is 5 + 5?\nCorrect!\nYour final score is:\n2\nGreat job!\n"
],
"calculator.claro": [
"2\n3\n",
"Claro Calculator\nFirst number?\nSecond number?\nSum:\n5\n"
],
"guessing_game.claro": [
"7\n",
"Guess the Number Game\nGuess a number from 1 to 10:\nYou got it!\n"
],
"shopping_list.claro": [
"",
"Shopping List Example\nItems to buy:\nmilk\nbread\napples\n"
],
"save_and_load_file.claro": [
"",
"Save and Load Example\nThe file says:\nClaro saved this file.\n"
],
"simple_functions.claro": [
"",
"Simple functions in Claro\nHello Jon\nHello Friend\n"
],
"text_and_lists.claro": [
"",
"Jon\nJON\n"
],
"counting.claro": [
"",
"Counting with DO TIMES:\n1\n2\n3\n4\n5\n\nCounting down with REPEAT:\n5\n4\n3\n2\n1\nBlastoff!\n"
]
}
+21
View File
@@ -0,0 +1,21 @@
@echo off
rem Run only in a fresh extracted Claro test folder.
if exist native.ok del native.ok
if not exist osprobe.exe goto wrongos
osprobe.exe --require-native >oscheck.txt
if errorlevel 1 goto wrongos
claro.exe --version >nver.txt
if errorlevel 1 goto failed
claro.exe test >ntests.txt
if errorlevel 1 goto failed
claro.exe validate >ncheck.txt
if errorlevel 1 goto failed
echo Automated native checks passed. Manual console and file checks are still required.
echo PASS>native.ok
goto end
:wrongos
echo Native target not verified. Check oscheck.txt and the probe message.
goto end
:failed
echo Claro checks failed. Keep the logs; do not report acceptance.
:end
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""Build clean source/Linux-musl/Windows-x64 archives from the current tree."""
import argparse
import hashlib
import json
import os
from pathlib import Path
import platform
import re
import shutil
import subprocess
import tempfile
import zipfile
ROOT = Path(__file__).resolve().parents[1]
DIRECTORIES = ['.forgejo', 'src', 'docs', 'lessons', 'examples', 'lib', 'tests', 'tools', 'assets', 'support']
FILES = ['README.md', 'LICENSE', 'CHANGELOG.md', 'CONTRIBUTING.md', 'Makefile', 'build.bat', 'build.ps1', 'build98.bat', 'build98.rsp', 'test98.bat', 'testwin.bat', 'run_tests.bat']
FLAGS = ['-std=c89', '-pedantic-errors', '-Werror=implicit-function-declaration', '-O2', '-static']
def sha256(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--target', required=True, choices=['source', 'linux-x86_64', 'windows-x86_64'])
parser.add_argument('--output', required=True, type=Path)
args = parser.parse_args()
match = re.search(r'#define CLARO_VERSION "Claro (v[^"]+)"', (ROOT / 'src/claro.c').read_text())
if match is None:
raise SystemExit('Cannot read the runtime version from src/claro.c')
version = match[1]
args.output.mkdir(parents=True, exist_ok=True)
name = f'Claro-{version}-{args.target}'
destination = args.output / (name + '.zip')
if destination.exists():
raise SystemExit(f'Refusing to replace existing archive: {destination}')
with tempfile.TemporaryDirectory(prefix='claro-package-', dir=os.environ.get('TMPDIR')) as tmp:
stage = Path(tmp) / name
stage.mkdir()
for directory in DIRECTORIES:
for source in sorted((ROOT / directory).rglob('*')):
if not source.is_file() or source.is_symlink() or '__pycache__' in source.parts or source.suffix in ['.pyc', '.o']:
continue
target = stage / source.relative_to(ROOT)
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, target)
for filename in FILES:
shutil.copy2(ROOT / filename, stage / filename)
command = []
compiler = None
if args.target != 'source':
if args.target == 'linux-x86_64':
if platform.machine().lower() not in ['x86_64', 'amd64']:
raise SystemExit('Linux x64 package requires an x86-64 musl toolchain')
compiler, executable = 'musl-gcc', 'claro'
notices = {'musl.txt': '/usr/share/doc/musl/copyright', 'GCC-runtime.txt': '/usr/share/doc/gcc-15-base/copyright'}
else:
compiler, executable = 'x86_64-w64-mingw32-gcc', 'claro.exe'
notices = {'MinGW-runtime.txt': '/usr/share/doc/mingw-w64-common/copyright', 'GCC-runtime.txt': '/usr/share/doc/gcc-mingw-w64-x86-64-win32/copyright'}
if not shutil.which(compiler):
raise SystemExit(f'Missing distribution compiler: {compiler}')
command = [compiler] + FLAGS + ['src/claro.c', '-o', executable, '-lm']
if args.target == 'windows-x86_64':
command += ['-Wl,--stack,16777216']
subprocess.run(command, cwd=stage, check=True)
third_party = stage / 'THIRD_PARTY_NOTICES'
third_party.mkdir()
for filename, source in notices.items():
if not Path(source).is_file():
raise SystemExit(f'Missing required toolchain notice: {source}')
shutil.copy2(source, third_party / filename)
metadata = {'version': version, 'target': args.target, 'source_sha256': sha256(stage / 'src/claro.c'), 'build_command': command}
if compiler:
metadata['compiler'] = subprocess.check_output([compiler, '--version'], text=True).splitlines()[0]
(stage / 'DISTRIBUTION.json').write_text(json.dumps(metadata, indent=2) + '\n')
start = 'Extract this entire archive before using Claro. Open a terminal in the extracted folder.\n\n'
if args.target == 'windows-x86_64':
start += 'Run: .\\claro.exe lessons\\01_hello.claro\nThis is a genuine Windows x64 executable, not the Linux build with an .exe suffix.\n'
elif args.target == 'linux-x86_64':
start += 'Run: ./claro lessons/01_hello.claro\nIf extraction removed execute permission: chmod +x claro\nThis is a static musl build; it does not require the builder host glibc.\n'
else:
start += 'Build: make\nOr compile src/claro.c directly with the strict C89 command in README.md.\nOn Windows, use build.bat/build.ps1; the executable needs the documented stack reserve.\n'
start += '\nRead docs/QUICK_START.md and docs/PORTABILITY.md. Run only trusted scripts.\nReal HTTP requires curl; the basic lessons and offline networking need no network.\n'
(stage / 'START_HERE.txt').write_text(start)
entries = {str(path.relative_to(stage)).replace(os.sep, '/'): sha256(path) for path in sorted(stage.rglob('*')) if path.is_file()}
(stage / 'MANIFEST.json').write_text(json.dumps({'sha256': entries}, indent=2) + '\n')
with zipfile.ZipFile(destination, 'w', compression=zipfile.ZIP_DEFLATED) as archive:
for path in sorted(stage.rglob('*')):
if path.is_file():
archive.write(path, path.relative_to(stage.parent))
print(json.dumps({'archive': str(destination.resolve()), 'sha256': sha256(destination), 'target': args.target}, indent=2))
if __name__ == '__main__':
main()
+35
View File
@@ -0,0 +1,35 @@
/* Native-OS evidence helper, not part of the Claro interpreter. */
#include <windows.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
OSVERSIONINFOA info;
HMODULE module;
int wine, want98, match98;
memset(&info, 0, sizeof(info));
info.dwOSVersionInfoSize = sizeof(info);
if (!GetVersionExA(&info)) {
fprintf(stderr, "Could not identify the reported Windows API version.\n");
return 3;
}
module = GetModuleHandleA("ntdll.dll");
wine = module && GetProcAddress(module, "wine_get_version") != NULL;
want98 = argc > 1 && strcmp(argv[1], "--require-win98se") == 0;
match98 = info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
info.dwMajorVersion == 4 && info.dwMinorVersion == 10 &&
(info.dwBuildNumber & 0xffffUL) == 2222;
printf("{\"wine\":%s,\"reported_api_major\":%lu,\"reported_api_minor\":%lu,\"reported_build\":%lu,\"platform_id\":%lu,\"matches_win98se\":%s}\n",
wine ? "true" : "false", (unsigned long)info.dwMajorVersion,
(unsigned long)info.dwMinorVersion,
(unsigned long)(info.dwBuildNumber & 0xffffUL),
(unsigned long)info.dwPlatformId, match98 ? "true" : "false");
if (wine) {
fprintf(stderr, "Wine detected: this is NOT native Windows acceptance.\n");
return 2;
}
if (want98 && !match98) {
fprintf(stderr, "This is not the required Windows 98 SE 4.10.2222 target.\n");
return 2;
}
return 0;
}
+1 -1
View File
@@ -9,7 +9,7 @@ ROOT = pathlib.Path(__file__).resolve().parent.parent
CLARO = ROOT / ("claro.exe" if os.name == "nt" else "claro") CLARO = ROOT / ("claro.exe" if os.name == "nt" else "claro")
if not CLARO.exists(): if not CLARO.exists():
print("Build first: gcc -std=c99 src/claro.c -O0 -o " + ("claro.exe" if os.name == "nt" else "claro") + " -lm") print("Build first: gcc -std=c89 -pedantic-errors src/claro.c -O0 -o " + ("claro.exe" if os.name == "nt" else "claro") + " -lm")
raise SystemExit(2) raise SystemExit(2)
raise SystemExit(subprocess.call([str(CLARO), "test"], cwd=str(ROOT))) raise SystemExit(subprocess.call([str(CLARO), "test"], cwd=str(ROOT)))
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
"""Stamp a development/release run once, using its current UTC date and time.
Run before building: python3 tools/stamp_version.py && make
Ordinary rebuilds and validation retain the stamp for reproducibility.
Historical release notes and changelog entries are deliberately not rewritten.
"""
from datetime import datetime, timezone
from pathlib import Path
import re
ROOT = Path(__file__).resolve().parents[1]
CURRENT_DOCS = (
"CURRENT_STATUS.md", "CLASSROOM_READINESS.md", "CLI.md", "SPEC.md",
"FINAL_RELEASE_CHECKLIST.md", "WEB_SERVER_PLAN.md", "GRAPHICS.md",
"PERFORMANCE.md", "COMPLETE_PLATFORM_ROADMAP.md", "SDL12.md",
"V1_16_PACKAGES_PROJECTS.md",
)
def main():
source = ROOT / "src/claro.c"
match = re.search(r'#define CLARO_VERSION "Claro ([^"\n]+)"', source.read_text())
if not match:
raise SystemExit("Cannot find CLARO_VERSION; no files changed")
previous = match.group(1)
version = datetime.now(timezone.utc).strftime("v%Y.%m.%d.%H%M%S")
paths = [source, ROOT / "README.md"]
paths += sorted((ROOT / "tools").glob("*.py"))
paths += [ROOT / "docs" / name for name in CURRENT_DOCS]
updates = []
for path in paths:
if path == Path(__file__).resolve() or not path.exists():
continue
old = path.read_text(encoding="utf-8")
new = old.replace(previous, version)
if new != old:
updates.append((path, new))
for path, content in updates:
path.write_text(content, encoding="utf-8")
print(f"Claro {version} (UTC); updated {len(updates)} current source/docs/validator files")
if __name__ == "__main__":
main()
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env python3
"""Check that ADD releases its evaluated item under LeakSanitizer."""
from pathlib import Path
import os
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
if os.name == "nt":
print("SKIP: LeakSanitizer check is POSIX-only")
return 0
with tempfile.TemporaryDirectory(prefix="claro-add-cleanup-") as tmp:
tmp_path = Path(tmp)
binary = tmp_path / "claro-lsan"
script = tmp_path / "add_expressions.claro"
script.write_text(
'SET items TO LIST\n'
+ 'ADD "transient" + " item" TO items\n' * 2000,
encoding="utf-8",
)
build = subprocess.run(
["gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-g", "-fsanitize=address,undefined",
"src/claro.c", "-o", str(binary), "-lm"],
cwd=ROOT, text=True, capture_output=True,
)
if build.returncode:
print(build.stdout, end="")
print(build.stderr, end="")
return build.returncode
run = subprocess.run(
[str(binary), str(script)], cwd=ROOT, text=True, capture_output=True,
env={**os.environ, "ASAN_OPTIONS": "detect_leaks=1"},
)
if run.returncode or "LeakSanitizer" in run.stderr or "SUMMARY:" in run.stderr:
print("FAIL: ADD expression values still leak")
print(run.stdout, end="")
print(run.stderr, end="")
return 1
print("PASS: ADD expression values are released")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python3
"""Check ASK prompt expression values are released under LeakSanitizer."""
from pathlib import Path
import os
import subprocess
import sys
import tempfile
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
if os.name == "nt":
print("SKIP: LeakSanitizer check is POSIX-only")
return 0
with tempfile.TemporaryDirectory(prefix="claro-ask-prompt-cleanup-") as tmp:
tmp_path = Path(tmp)
binary = tmp_path / "claro-lsan"
script = tmp_path / "ask_prompts.claro"
script.write_text(
''.join('ASK "question" + " value" AS answer\n' for _ in range(2000)),
encoding="utf-8",
)
build = subprocess.run(
["gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-g", "-fsanitize=address,undefined",
"src/claro.c", "-o", str(binary), "-lm"],
cwd=ROOT, text=True, capture_output=True,
)
if build.returncode:
print(build.stdout, end="")
print(build.stderr, end="", file=sys.stderr)
return build.returncode
run = subprocess.run(
[str(binary), str(script)], cwd=ROOT, input="\n" * 2000,
text=True, capture_output=True,
env={**os.environ, "ASAN_OPTIONS": "detect_leaks=1"},
)
if run.returncode:
print("FAIL: ASK prompt cleanup probe failed")
print(run.stdout, end="")
print(run.stderr, end="", file=sys.stderr)
return 1
if "LeakSanitizer" in run.stderr or "SUMMARY:" in run.stderr:
print("FAIL: ASK prompt expression values still leak")
print(run.stderr, end="", file=sys.stderr)
return 1
if run.stdout.count("question value\n") != 2000:
print("FAIL: ASK prompt cleanup probe produced the wrong output")
return 1
print("PASS: ASK prompt expression values are released")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env python3
"""Compile the single source in isolation and exercise C89 portability boundaries."""
import os
from pathlib import Path
import shlex
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parents[1]
FLAGS = ['-std=c89', '-pedantic-errors', '-Werror=implicit-function-declaration']
def main():
cc = shlex.split(os.environ.get('CLARO_CC', 'gcc'))
with tempfile.TemporaryDirectory(prefix='claro-c89-', dir=os.environ.get('TMPDIR')) as directory:
work = Path(directory)
source = work / 'claro.c'
source.write_bytes((ROOT / 'src/claro.c').read_bytes())
exe = work / ('claro.exe' if os.name == 'nt' else 'claro')
subprocess.run(cc + FLAGS + [str(source), '-o', str(exe), '-lm'], check=True)
unit = work / 'format_probe.c'
unit.write_text(r'''#define main claro_application_main
#include "claro.c"
#undef main
#include <assert.h>
int main(void)
{
char guard[8] = "abcdefg";
char buffer[400];
char *number;
int result;
result=claro_snprintf(guard+1,4,"%s","123456");
assert(result==6 && guard[0]=='a' && guard[5]=='f');
assert(strcmp(guard+1,"123")==0);
assert(claro_snprintf(NULL,0,"%s:%d","hello",-12)==9);
assert(claro_snprintf(guard,1,"%s","hello")==5 && guard[0]==0);
assert(claro_snprintf(buffer,sizeof(buffer),"%.*s",3,"abcdef")==3);
assert(strcmp(buffer,"abc")==0);
assert(claro_snprintf(buffer,sizeof(buffer),"%.*s",-1,"abc")==3);
assert(strcmp(buffer,"abc")==0);
claro_snprintf(buffer,sizeof(buffer),"%08lx %.15g %.0f %%",(unsigned long)42,1.25,3.0);
assert(strcmp(buffer,"0000002a 1.25 3 %")==0);
claro_snprintf(buffer,sizeof(buffer),"%.0f",DBL_MAX);
assert(strlen(buffer)<sizeof(buffer));
number=num_to_string(-0.0); assert(strcmp(number,"0")==0); free(number);
return 0;
}
''')
unit_exe = work / ('format_probe.exe' if os.name == 'nt' else 'format_probe')
subprocess.run(cc + FLAGS + [str(unit), '-o', str(unit_exe), '-lm'], check=True)
subprocess.run([str(unit_exe)], cwd=work, check=True)
invalid = work / 'invalid.c'
invalid.write_text('int main(void) { int n = 0; n++; int late = 1; return late; }\n')
bad = subprocess.run(cc + FLAGS + ['-fsyntax-only', str(invalid)], capture_output=True, text=True)
assert bad.returncode != 0 and 'error' in bad.stderr.lower(), 'C89 compiler gate accepted invalid C99 syntax'
script = work / 'probe.claro'
script.write_text('''TEACH add a b
RETURN a + b
END
DO add 2 3
SAY RESULT
SAY 2147483648
SAY -2147483649
SAY 9007199254740991
SAY -2.5
IMPORT "lib/random.claro" AS random
DO random.seed 1
DO random.int -2147483648 2147483647
SET sample RESULT
IF sample >= -2147483648
IF sample <= 2147483647
SAY "range OK"
END
END
''')
run = subprocess.run([str(exe), str(script)], cwd=work, text=True, capture_output=True)
expected = '5\n2147483648\n-2147483649\n9007199254740991\n-2.5\nrange OK\n'
assert run.returncode == 0 and run.stdout == expected, (run.returncode, run.stdout, run.stderr)
print('PASS: standalone strict C89 build, rejected C99 fixture, bounded formatting, functions, numbers, extreme random bounds')
if __name__ == '__main__':
main()
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env python3
"""Verify recursive Claro calls stop with the documented runtime diagnostic."""
from pathlib import Path
import subprocess
import sys
import tempfile
ROOT = Path(__file__).resolve().parent.parent
EXPECTED = (
"Claro function call depth exceeded the safe limit of 256. "
"Simplify the recursion or add a stopping condition."
)
SCRIPT = """TEACH loop
CALL loop
ENDTEACH
TRY
CALL loop
CATCH
SAY LASTERROR
ENDTRY
"""
def main() -> int:
binary = ROOT / ("claro.exe" if sys.platform == "win32" else "claro")
if not binary.exists():
print(f"FAIL: build Claro first; missing {binary}")
return 1
with tempfile.TemporaryDirectory(prefix="claro-call-depth-") as tmp:
program = Path(tmp) / "recursive.claro"
program.write_text(SCRIPT, encoding="utf-8")
run = subprocess.run([str(binary), str(program)], cwd=ROOT, text=True,
capture_output=True, timeout=30)
output = run.stdout + run.stderr
if EXPECTED not in output:
print("FAIL: recursion did not produce the documented call-depth error")
print(output, end="")
return 1
if run.returncode == 0:
print("FAIL: runtime error unexpectedly exited successfully")
return 1
print("PASS: unbounded recursion stops with the documented call-depth error")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env python3
"""Check that check-mode releases tokens allocated for each expression."""
from pathlib import Path
import os
import subprocess
import sys
import tempfile
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
if os.name == "nt":
print("SKIP: LeakSanitizer check is POSIX-only")
return 0
with tempfile.TemporaryDirectory(prefix="check-token-cleanup-") as tmp:
tmp_path = Path(tmp)
binary = tmp_path / "claro-lsan"
script = tmp_path / "tokens.claro"
script.write_text('SAY "a repeated string literal"\n' * 2000, encoding="utf-8")
build = subprocess.run(
["gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-g", "-fsanitize=address,undefined",
"src/claro.c", "-o", str(binary), "-lm"],
cwd=ROOT, text=True, capture_output=True,
)
if build.returncode:
print(build.stdout, end="")
print(build.stderr, end="", file=sys.stderr)
return build.returncode
run = subprocess.run(
[str(binary), "check", str(script)], cwd=ROOT, text=True,
capture_output=True, env={**os.environ, "ASAN_OPTIONS": "detect_leaks=1"},
)
if run.returncode:
print("FAIL: check-mode token cleanup probe failed")
print(run.stdout, end="")
print(run.stderr, end="", file=sys.stderr)
return 1
if "LeakSanitizer" in run.stderr or "SUMMARY:" in run.stderr:
print("FAIL: check-mode token allocations still leak")
print(run.stderr, end="", file=sys.stderr)
return 1
print("PASS: check-mode expression tokens are released")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+9
View File
@@ -12,6 +12,15 @@ REQUIRED_COMMANDS = [
"python3 tools/validate_version_convention.py", "python3 tools/validate_version_convention.py",
"python3 tools/validate_package_security.py", "python3 tools/validate_package_security.py",
"python3 tools/validate_compiler_warnings.py", "python3 tools/validate_compiler_warnings.py",
"python3 tools/validate_reliability.py",
"python3 tools/validate_learning_syntax.py",
"python3 tools/validate_c89.py",
"python3 tools/validate_msvc6_source.py",
"python3 tools/validate_documentation_status.py",
"python3 tools/validate_error_locations.py",
"python3 tools/validate_error_guide.py",
"python3 tools/validate_learner_pilot.py",
"python3 tools/validate_resource_guards.py",
"python3 tools/validate_ci_workflow.py", "python3 tools/validate_ci_workflow.py",
] ]
+1 -1
View File
@@ -10,7 +10,7 @@ ROOT = Path(__file__).resolve().parents[1]
def main(): def main():
with tempfile.TemporaryDirectory() as tmp: with tempfile.TemporaryDirectory() as tmp:
result = subprocess.run( result = subprocess.run(
["gcc", "-std=c99", "-O0", "-c", "src/claro.c", "-o", str(Path(tmp) / "claro.o")], ["gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-c", "src/claro.c", "-o", str(Path(tmp) / "claro.o")],
cwd=ROOT, cwd=ROOT,
text=True, text=True,
capture_output=True, capture_output=True,
@@ -0,0 +1,70 @@
#!/usr/bin/env python3
"""Regression check for discarded control-flow expression Values."""
from pathlib import Path
import os
import subprocess
import sys
import tempfile
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
if os.name == "nt":
print("SKIP: LeakSanitizer check is POSIX-only")
return 0
with tempfile.TemporaryDirectory(prefix="claro-control-expression-cleanup-") as tmp:
tmp_path = Path(tmp)
binary = tmp_path / "claro-lsan"
script = tmp_path / "control_expressions.claro"
script.write_text(
''.join(
'IF "condition" + " value"\n'
' SET seen TO YES\n'
'ENDIF\n'
for _ in range(2000)
)
+ ''.join(
'DO "1" TIMES\n'
' SET seen TO YES\n'
'DONE\n'
for _ in range(2000)
)
+ 'SAY seen\n',
encoding="utf-8",
)
build = subprocess.run(
[
"gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-g",
"-fsanitize=address,undefined",
"src/claro.c", "-o", str(binary), "-lm",
], cwd=ROOT, text=True, capture_output=True,
)
if build.returncode:
print(build.stdout, end="")
print(build.stderr, end="", file=sys.stderr)
return build.returncode
run = subprocess.run(
[str(binary), str(script)], cwd=ROOT, text=True,
capture_output=True,
env={**os.environ, "ASAN_OPTIONS": "detect_leaks=1"},
)
if run.returncode:
print("FAIL: control-expression cleanup probe failed")
print(run.stdout, end="")
print(run.stderr, end="", file=sys.stderr)
return 1
if "LeakSanitizer" in run.stderr or "SUMMARY:" in run.stderr:
print("FAIL: discarded control-flow expression values still leak")
print(run.stderr, end="", file=sys.stderr)
return 1
if run.stdout != "YES\n":
print("FAIL: control-expression cleanup probe produced the wrong output")
print(run.stdout, end="", file=sys.stderr)
return 1
print("PASS: discarded control-flow expression values are released")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+61
View File
@@ -0,0 +1,61 @@
#!/usr/bin/env python3
"""Regression check for control-flow expression Value cleanup under LeakSanitizer."""
from pathlib import Path
import os
import subprocess
import sys
import tempfile
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
if os.name == "nt":
print("SKIP: LeakSanitizer check is POSIX-only")
return 0
with tempfile.TemporaryDirectory(prefix="claro-control-flow-cleanup-") as tmp:
tmp_path = Path(tmp)
binary = tmp_path / "claro-lsan"
script = tmp_path / "control_flow.claro"
script.write_text(
'SET items TO LIST\n'
'FOR EACH item IN items\n'
' SAY item\n'
'DONE\n',
encoding="utf-8",
)
build = subprocess.run(
[
"gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-g",
"-fsanitize=address,undefined",
"src/claro.c", "-o", str(binary), "-lm",
], cwd=ROOT, text=True, capture_output=True,
)
if build.returncode:
print(build.stdout, end="")
print(build.stderr, end="", file=sys.stderr)
return build.returncode
run = subprocess.run(
[str(binary), str(script)], cwd=ROOT, text=True,
capture_output=True,
env={**os.environ, "ASAN_OPTIONS": "detect_leaks=1"},
)
if run.returncode:
print("FAIL: control-flow cleanup probe failed")
print(run.stdout, end="")
print(run.stderr, end="", file=sys.stderr)
return 1
if "LeakSanitizer" in run.stderr or "SUMMARY:" in run.stderr:
print("FAIL: control-flow expression values still leak")
print(run.stderr, end="", file=sys.stderr)
return 1
if run.stdout != "":
print("FAIL: control-flow cleanup probe produced the wrong output")
print(run.stdout, end="", file=sys.stderr)
return 1
print("PASS: control-flow expression values are released")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,48 @@
#!/usr/bin/env python3
"""Regression check for COPY/MOVE FILE expression temporary ownership."""
from pathlib import Path
import os
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
if os.name == "nt":
print("SKIP: LeakSanitizer check is POSIX-only")
return 0
with tempfile.TemporaryDirectory(prefix="claro-copy-move-cleanup-") as tmp:
tmp_path = Path(tmp)
binary = tmp_path / "claro-lsan"
script = tmp_path / "copy_move_expressions.claro"
(tmp_path / "copy-source.txt").write_text("sample", encoding="utf-8")
script.write_text(
('COPY FILE "copy-source.txt" TO "copy-destination.txt"\n'
'MOVE FILE "copy-destination.txt" TO "copy-source.txt"\n') * 1000,
encoding="utf-8",
)
build = subprocess.run(
["gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-g", "-fsanitize=address,undefined",
"src/claro.c", "-o", str(binary), "-lm"],
cwd=ROOT, text=True, capture_output=True,
)
if build.returncode:
print(build.stdout, end="")
print(build.stderr, end="")
return build.returncode
run = subprocess.run(
[str(binary), str(script)], cwd=tmp_path, text=True, capture_output=True,
env={**os.environ, "ASAN_OPTIONS": "detect_leaks=1"},
)
if run.returncode or "LeakSanitizer" in run.stderr or "SUMMARY:" in run.stderr:
print("FAIL: COPY/MOVE FILE expression values still leak")
print(run.stdout, end="")
print(run.stderr, end="")
return 1
print("PASS: COPY/MOVE FILE expression values are released")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""Regression check for discarded COUNT expression Values."""
from pathlib import Path
import os
import subprocess
import sys
import tempfile
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
if os.name == "nt":
print("SKIP: LeakSanitizer check is POSIX-only")
return 0
with tempfile.TemporaryDirectory(prefix="claro-count-cleanup-") as tmp:
tmp_path = Path(tmp)
binary = tmp_path / "claro-lsan"
script = tmp_path / "count_expressions.claro"
script.write_text(
'SET items TO LIST\n'
+ ''.join('COUNT items AS item_count\n' for _ in range(2000))
+ 'SAY item_count\n', encoding="utf-8"
)
build = subprocess.run(
["gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-g", "-fsanitize=address,undefined",
"src/claro.c", "-o", str(binary), "-lm"],
cwd=ROOT, text=True, capture_output=True,
)
if build.returncode:
print(build.stdout, end="")
print(build.stderr, end="", file=sys.stderr)
return build.returncode
run = subprocess.run(
[str(binary), str(script)], cwd=ROOT, text=True, capture_output=True,
env={**os.environ, "ASAN_OPTIONS": "detect_leaks=1"},
)
if run.returncode or "LeakSanitizer" in run.stderr or "SUMMARY:" in run.stderr:
print("FAIL: discarded COUNT expression values still leak")
print(run.stdout, end="")
print(run.stderr, end="", file=sys.stderr)
return 1
if run.stdout != "0\n":
print("FAIL: COUNT cleanup probe produced the wrong output")
print(run.stdout, end="", file=sys.stderr)
return 1
print("PASS: discarded COUNT expression values are released")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,47 @@
#!/usr/bin/env python3
"""Regression check for CREATE FOLDER expression temporary ownership."""
from pathlib import Path
import os
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
if os.name == "nt":
print("SKIP: LeakSanitizer check is POSIX-only")
return 0
with tempfile.TemporaryDirectory(prefix="claro-create-folder-cleanup-") as tmp:
tmp_path = Path(tmp)
binary = tmp_path / "claro-lsan"
script = tmp_path / "create_folder_expressions.claro"
script.write_text(
'SET path TO "' + str(tmp_path / "new-folder") + '"\n'
+ "CREATE FOLDER path\n" * 2000,
encoding="utf-8",
)
build = subprocess.run(
["gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-g", "-fsanitize=address,undefined",
"src/claro.c", "-o", str(binary), "-lm"],
cwd=ROOT, text=True, capture_output=True,
)
if build.returncode:
print(build.stdout, end="")
print(build.stderr, end="")
return build.returncode
run = subprocess.run(
[str(binary), str(script)], cwd=ROOT, text=True, capture_output=True,
env={**os.environ, "ASAN_OPTIONS": "detect_leaks=1"},
)
if run.returncode or "LeakSanitizer" in run.stderr or "SUMMARY:" in run.stderr:
print("FAIL: CREATE FOLDER expression values still leak")
print(run.stdout, end="")
print(run.stderr, end="")
return 1
print("PASS: CREATE FOLDER expression values are released")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,47 @@
#!/usr/bin/env python3
"""Regression check for DELETE FILE expression temporary ownership."""
from pathlib import Path
import os
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
if os.name == "nt":
print("SKIP: LeakSanitizer check is POSIX-only")
return 0
with tempfile.TemporaryDirectory(prefix="claro-delete-cleanup-") as tmp:
tmp_path = Path(tmp)
binary = tmp_path / "claro-lsan"
script = tmp_path / "delete_expressions.claro"
script.write_text(
'SET path TO "' + str(tmp_path / "unused") + '"\n'
+ 'DELETE FILE path + ""\n' * 2000,
encoding="utf-8",
)
build = subprocess.run(
["gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-g", "-fsanitize=address,undefined",
"src/claro.c", "-o", str(binary), "-lm"],
cwd=ROOT, text=True, capture_output=True,
)
if build.returncode:
print(build.stdout, end="")
print(build.stderr, end="")
return build.returncode
run = subprocess.run(
[str(binary), str(script)], cwd=ROOT, text=True, capture_output=True,
env={**os.environ, "ASAN_OPTIONS": "detect_leaks=1"},
)
if run.returncode or "LeakSanitizer" in run.stderr or "SUMMARY:" in run.stderr:
print("FAIL: DELETE FILE expression values still leak")
print(run.stdout, end="")
print(run.stderr, end="")
return 1
print("PASS: DELETE FILE expression values are released")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+147
View File
@@ -0,0 +1,147 @@
#!/usr/bin/env python3
"""Extract a distribution, verify hashes, and execute its actual payload."""
import argparse
import hashlib
import json
import os
from pathlib import Path, PurePosixPath
import re
import shlex
import shutil
import stat
import struct
import subprocess
import sys
import tempfile
import zipfile
def digest(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('archive', type=Path)
parser.add_argument('--wine-prefix', type=Path)
parser.add_argument('--report', type=Path, required=True)
args = parser.parse_args()
if args.report.exists():
raise SystemExit('Refusing to replace existing verification report')
results = {'archive': str(args.archive.resolve()), 'sha256': digest(args.archive), 'checks': []}
logs = args.report.parent / (args.report.stem + '-logs')
logs.mkdir(parents=True, exist_ok=False)
env = dict(os.environ)
def run(label, command, cwd, timeout=180):
result = subprocess.run(command, cwd=cwd, env=env, capture_output=True, text=True, timeout=timeout)
log = logs / (label + '.log')
log.write_text(result.stdout + result.stderr, encoding='utf-8')
results['checks'].append({'name': label, 'exit': result.returncode, 'log': str(log.resolve())})
assert result.returncode == 0, (label, result.returncode, (result.stdout + result.stderr)[-3000:])
print('PASS:', label)
return result.stdout
try:
with tempfile.TemporaryDirectory(prefix='claro-unpack-', dir=os.environ.get('TMPDIR')) as tmp:
work = Path(tmp) / 'unpacked package with spaces'
work.mkdir()
with zipfile.ZipFile(args.archive) as archive:
names = archive.namelist()
assert len(names) == len(set(names)), 'Duplicate archive entries'
assert len({PurePosixPath(name).parts[0] for name in names}) == 1, 'Expected one archive root'
for info in archive.infolist():
path = PurePosixPath(info.filename)
assert not path.is_absolute() and '..' not in path.parts and '\\' not in info.filename, 'Unsafe archive path'
mode = info.external_attr >> 16
assert not stat.S_ISLNK(mode), 'Archive must not contain symlinks'
target = work.joinpath(*path.parts)
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(archive.read(info))
if os.name != 'nt' and mode:
target.chmod(mode & 0o777)
root = next(work.iterdir())
manifest = json.loads((root / 'MANIFEST.json').read_text())['sha256']
actual = {str(p.relative_to(root)).replace(os.sep, '/'): digest(p) for p in root.rglob('*') if p.is_file() and p.name != 'MANIFEST.json'}
assert actual == manifest, 'Package manifest mismatch'
assert not any('__pycache__' in name or name.endswith('.pyc') or 'claro-asan' in name for name in actual), 'Development leftovers in package'
metadata = json.loads((root / 'DISTRIBUTION.json').read_text())
assert metadata['source_sha256'] == digest(root / 'src/claro.c')
results.update(target=metadata['target'], version=metadata['version'], manifest_files=len(manifest), source_sha256=metadata['source_sha256'])
print('PASS: archive inventory, source hash and manifest')
target = metadata['target']
if target == 'source':
assert not (root / 'claro').exists() and not (root / 'claro.exe').exists()
isolated = work / 'standalone'
isolated.mkdir()
shutil.copy2(root / 'src/claro.c', isolated / 'claro.c')
run('isolated-c89-build', ['gcc', '-std=c89', '-pedantic-errors', '-Werror=implicit-function-declaration', 'claro.c', '-o', 'claro', '-lm'], isolated)
run('isolated-version', [str(isolated / 'claro'), '--version'], isolated)
run('source-make', ['make', '-s', 'all'], root)
executable = root / ('claro.exe' if os.name == 'nt' else 'claro')
runner = [str(executable)]
elif target == 'linux-x86_64':
executable = root / 'claro'
assert not (root / 'claro.exe').exists(), 'Linux archive contains misleading .exe'
data = executable.read_bytes()
assert data[:5] == b'\x7fELF\x02' and struct.unpack_from('<H', data, 18)[0] == 62
headers = run('elf-inspection', ['readelf', '-l', str(executable)], root)
assert 'INTERP' not in headers, 'Linux release is not static'
runner = [str(executable)]
elif target == 'windows-x86_64':
executable = root / 'claro.exe'
assert not (root / 'claro').exists()
data = executable.read_bytes()
assert data[:2] == b'MZ'
pe = struct.unpack_from('<I', data, 60)[0]
assert data[pe:pe+4] == b'PE\0\0' and struct.unpack_from('<H', data, pe+4)[0] == 0x8664
reserve = struct.unpack_from('<Q', data, pe+24+72)[0]
assert reserve >= 16777216, 'Windows recursion guard requires a sufficient stack reserve'
results['windows_stack_reserve'] = reserve
pe_text = run('pe-inspection', ['x86_64-w64-mingw32-objdump', '-p', str(executable)], root)
results['windows_dll_imports'] = re.findall(r'DLL Name:\s*(\S+)', pe_text)
if os.name == 'nt':
runner = [str(executable)]
else:
assert args.wine_prefix and args.wine_prefix.is_dir(), 'Supply an initialized isolated --wine-prefix'
env.update(WINEPREFIX=str(args.wine_prefix.resolve()), WINEDEBUG='-all')
runner = ['wine', str(executable)]
else:
raise AssertionError('Unknown distribution target')
version = run('version', runner + ['--version'], root)
assert metadata['version'] in version
standalone = work / 'single executable with spaces'
standalone.mkdir()
isolated_binary = standalone / executable.name
shutil.copy2(executable, isolated_binary)
(standalone / 'hello.claro').write_text('SAY "Standalone Claro works"\n')
isolated_runner = (['wine', str(isolated_binary)] if runner[0] == 'wine' else [str(isolated_binary)])
output = run('standalone-runtime', isolated_runner + ['hello.claro'], standalone)
assert output == 'Standalone Claro works\n'
run('doctor', runner + ['doctor'], root)
golden = run('golden-tests', runner + ['test'], root)
results['golden_pass_count'] = len(re.findall(r'^PASS tests/', golden, re.M))
assert results['golden_pass_count'] == len(list((root / 'tests').glob('*.out'))), 'Golden runner did not cover all packaged fixtures'
run('builtin-validation', runner + ['validate'], root)
if runner[0] == 'wine':
launcher = work / 'wine-launcher'
launcher.write_text('#!/bin/sh\nexec wine ' + shlex.quote(str(executable)) + ' "$@"\n')
launcher.chmod(0o755)
env['CLARO_EXE'] = str(launcher)
else:
env['CLARO_EXE'] = str(executable)
run('learning-path', [sys.executable, str(root / 'tools/validate_learning_syntax.py')], root, timeout=600 if runner[0] == 'wine' else 180)
run('first-hour', [sys.executable, str(root / 'tools/validate_first_hour.py')], root)
if target == 'source':
run('full-source-validation', [sys.executable, str(root / 'tools/validate_v1_17.py')], root, timeout=360)
results['status'] = 'passed'
except Exception as error:
results.update(status='failed', error=str(error))
raise
finally:
args.report.write_text(json.dumps(results, indent=2) + '\n', encoding='utf-8')
print('Verified:', args.report)
if __name__ == '__main__':
main()
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env python3
"""Check active status/navigation without treating historical claims as current."""
import ast
from pathlib import Path
import re
from urllib.parse import unquote, urlsplit
ROOT = Path(__file__).resolve().parents[1]
ACTIVE = (
'README.md', 'docs/README.md', 'docs/CURRENT_STATUS.md',
'docs/RELEASE_STATUS.md', 'docs/FINAL_RELEASE_CHECKLIST.md',
'docs/CLASSROOM_READINESS.md', 'docs/TESTING.md', 'docs/HARDENING.md',
'docs/PORTABILITY.md', 'docs/RELIABILITY.md',
'docs/ERRORS.md', 'docs/RUNTIME_LIMITS.md', 'docs/LEARNER_PILOT.md',
'docs/LEARNER_FEEDBACK_TEMPLATE.md', 'docs/NATIVE_WINDOWS_TESTING.md',
'docs/SIX_TRACK_STATUS.md',
'docs/history/CURRENT_STATUS_BEFORE_STATUS_CLEANUP.md',
'docs/history/HARDENING_BEFORE_STATUS_CLEANUP.md',
)
def validate():
match = re.search(r'#define CLARO_VERSION "Claro ([^"]+)"',
(ROOT / 'src/claro.c').read_text())
assert match, 'Missing runtime version'
version = match.group(1)
readme = (ROOT / 'README.md').read_text()
assert 'Current release:' not in readme, 'A development stamp is not a published release'
assert f'Development version: **{version}**' in readme, 'README must match the runtime development stamp'
status = (ROOT / 'docs/CURRENT_STATUS.md').read_text()
assert version in status and 'RELEASE_STATUS.md' in status
assert len(status.splitlines()) <= 100, 'Keep the current status concise; archive detailed history'
assert 'not a sandbox' in status.lower()
release = (ROOT / 'docs/RELEASE_STATUS.md').read_text()
assert 'not published' in release.lower() and 'wine' in release.lower()
assert 'unverified' in release.lower() and 'MSVC600' in release
checklist = (ROOT / 'docs/FINAL_RELEASE_CHECKLIST.md').read_text()
assert 'v1.xx.yy' not in checklist and 'vYYYY.MM.DD.HHMMSS' in checklist
assert '[ ]' in checklist, 'Release actions must be explicit, unchecked gates'
classroom = (ROOT / 'docs/CLASSROOM_READINESS.md').read_text().lower()
assert 'learner testing' in classroom and 'not yet validated' in classroom
hardening = (ROOT / 'docs/HARDENING.md').read_text()
assert 'historical' in hardening.lower() and 'RELIABILITY.md' in hardening
for name in ['CURRENT_STATUS_BEFORE_STATUS_CLEANUP.md', 'HARDENING_BEFORE_STATUS_CLEANUP.md']:
assert (ROOT / 'docs/history' / name).is_file(), ('Missing historical record', name)
tree = ast.parse((ROOT / 'tools/stamp_version.py').read_text())
current_docs = next(ast.literal_eval(n.value) for n in tree.body
if isinstance(n, ast.Assign) and any(isinstance(t, ast.Name) and t.id == 'CURRENT_DOCS' for t in n.targets))
assert 'RELEASE_STATUS.md' not in current_docs, 'Do not re-stamp immutable verification checkpoints'
checked = 0
for name in ACTIVE:
path = ROOT / name
text = re.sub(r'```.*?```', '', path.read_text(), flags=re.S)
for target in re.findall(r'!?\[[^\]]*\]\(([^\s)]+)\)', text):
url = urlsplit(target)
if url.scheme or url.netloc or not url.path:
continue
destination = (path.parent / unquote(url.path)).resolve()
assert destination.is_relative_to(ROOT), (name, 'Link leaves the repository', target)
assert destination.exists(), (name, 'Broken local link', target)
checked += 1
print(f'PASS: development/release separation, archived history, claim scope, and {checked} local documentation links')
if __name__ == '__main__':
validate()
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
"""Execute every error-guide failure and repair against the actual interpreter."""
from pathlib import Path
import json, os, re, shlex, subprocess, tempfile
ROOT = Path(__file__).resolve().parents[1]
def main():
cases = json.loads((ROOT/'tests/error_examples.json').read_text())
guide = (ROOT/'docs/ERRORS.md').read_text()
blocks = re.findall(r'```claro\n(.*?)```',guide,re.S)
expected = [c[k] for c in cases for k in ['code','repair']]
assert blocks == expected, 'Every error and repair block must match executable coverage'
with tempfile.TemporaryDirectory(prefix='claro-error-guide-',dir=os.environ.get('TMPDIR')) as tmp:
work=Path(tmp); override=os.environ.get('CLARO_EXE'); exe=Path(override).resolve() if override else work/'claro'
if not override:
subprocess.run(shlex.split(os.environ.get('CLARO_CC','gcc'))+['-std=c89','-pedantic-errors',str(ROOT/'src/claro.c'),'-o',str(exe),'-lm'],check=True)
for case in cases:
path=work/case['filename'];path.write_text(case['code'])
cmd=[str(exe)]+(['check'] if case['mode']=='check' else [])+[path.name]
r=subprocess.run(cmd,cwd=work,capture_output=True,text=True,timeout=10)
assert r.returncode==1 and case['diagnostic'] in r.stdout+r.stderr,(case['name'],r.stdout,r.stderr)
assert case['diagnostic'] in guide
path.write_text(case['repair']);r=subprocess.run([str(exe),path.name],cwd=work,capture_output=True,text=True,timeout=10)
assert r.returncode==0 and r.stdout==case['output'],(case['name'],r.stdout,r.stderr)
print('PASS error guide:',case['name'])
if __name__=='__main__':main()
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env python3
"""Verify real runtime diagnostic origins, including functions/imports/TRY."""
from pathlib import Path
import os
import shlex
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parents[1]
def main():
with tempfile.TemporaryDirectory(prefix='claro-error-locations-', dir=os.environ.get('TMPDIR')) as temp:
work = Path(temp)
override = os.environ.get('CLARO_EXE')
exe = Path(override).resolve() if override else work / 'claro'
if not override:
compiler = shlex.split(os.environ.get('CLARO_CC', 'gcc'))
subprocess.run(compiler + ['-std=c89', '-pedantic-errors', str(ROOT / 'src/claro.c'), '-o', str(exe), '-lm'], check=True)
cases = [
('quote.claro', 'SAY "Start"\nSAY "unfinished\n', 2, 'Text is missing a closing quote.'),
('operand.claro', 'SET score 1\nSAY score +\n', 2, 'needs a value after the operator'),
('parenthesis.claro', 'SAY "Start"\nSAY (1 + 2\n', 2, 'needs a closing parenthesis'),
('function.claro', 'TEACH broken\n SAY 1 +\nEND\nDO broken\n', 2, 'needs a value after the operator'),
('repeat.claro', 'REPEAT\n SAY "Once"\nUNTIL (\n', 3, 'needs a value after the operator'),
]
for name, text, line, message in cases:
(work / name).write_text(text)
result = subprocess.run([str(exe), name], cwd=work, capture_output=True, text=True, timeout=10)
output = result.stdout + result.stderr
assert not any(t in output for t in ["AddressSanitizer", "LeakSanitizer", "runtime error:"]), output
assert result.returncode == 1, (name, result.returncode, output)
assert f'{name}:{line}:' in output and ':0:' not in output and message in output, (name, output)
print('PASS origin:', name, line)
(work / 'broken-module.claro').write_text('TEACH broken\n SAY 1 +\nEND\n')
(work / 'caller.claro').write_text('IMPORT "broken-module.claro" AS problem\nDO problem.broken\n')
result = subprocess.run([str(exe), 'caller.claro'], cwd=work, capture_output=True, text=True, timeout=10)
assert result.returncode == 1 and 'broken-module.claro:2:' in result.stderr, result.stderr
print('PASS origin: imported function')
(work / 'caught.claro').write_text('TRY\n DO problem.broken\nCATCH\n SAY LASTERRORFILE\n SAY LASTERRORLINE\nENDTRY\n')
(work / 'caller.claro').write_text('IMPORT "broken-module.claro" AS problem\nTRY\n DO problem.broken\nCATCH\n SAY LASTERRORFILE\n SAY LASTERRORLINE\nENDTRY\n')
result = subprocess.run([str(exe), 'caller.claro'], cwd=work, capture_output=True, text=True, timeout=10)
assert result.returncode == 0 and result.stdout.replace('\\', '/').endswith('broken-module.claro\n2\n'), (result.stdout, result.stderr)
print('PASS origin: caught error metadata')
if __name__ == '__main__':
main()
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""Regression check for EXISTS FILE expression temporary ownership."""
from pathlib import Path
import os
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
if os.name == "nt":
print("SKIP: LeakSanitizer check is POSIX-only")
return 0
with tempfile.TemporaryDirectory(prefix="claro-exists-cleanup-") as tmp:
tmp_path = Path(tmp)
binary = tmp_path / "claro-lsan"
script = tmp_path / "exists_expressions.claro"
script.write_text(
'SET path TO "missing-file-for-cleanup-probe"\n'
+ 'EXISTS FILE path AS present\n' * 2000
+ "SAY present\n",
encoding="utf-8",
)
build = subprocess.run(
["gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-g", "-fsanitize=address,undefined",
"src/claro.c", "-o", str(binary), "-lm"],
cwd=ROOT, text=True, capture_output=True,
)
if build.returncode:
print(build.stdout, end="")
print(build.stderr, end="")
return build.returncode
run = subprocess.run(
[str(binary), str(script)], cwd=ROOT, text=True, capture_output=True,
env={**os.environ, "ASAN_OPTIONS": "detect_leaks=1"},
)
if run.returncode or "LeakSanitizer" in run.stderr or "SUMMARY:" in run.stderr:
print("FAIL: EXISTS FILE expression values still leak")
print(run.stdout, end="")
print(run.stderr, end="")
return 1
if run.stdout != "NO\n":
print(f"FAIL: unexpected EXISTS FILE probe output: {run.stdout!r}")
return 1
print("PASS: EXISTS FILE expression values are released")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+64
View File
@@ -0,0 +1,64 @@
#!/usr/bin/env python3
"""Regression check for discarded expression Value cleanup under LeakSanitizer."""
from pathlib import Path
import os
import subprocess
import sys
import tempfile
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
if os.name == "nt":
print("SKIP: LeakSanitizer check is POSIX-only")
return 0
with tempfile.TemporaryDirectory(prefix="claro-expression-cleanup-") as tmp:
tmp_path = Path(tmp)
binary = tmp_path / "claro-lsan"
script = tmp_path / "expressions.claro"
script.write_text(
'SET value TO "seed"\n'
+ ''.join(
'SET value TO "a" + "b"\n'
'SET answer TO 1 + 2 * 3\n'
for _ in range(2000)
)
+ 'SAY value\n',
encoding="utf-8",
)
build = subprocess.run(
[
"gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-g",
"-fsanitize=address,undefined",
"src/claro.c", "-o", str(binary), "-lm",
], cwd=ROOT, text=True, capture_output=True,
)
if build.returncode:
print(build.stdout, end="")
print(build.stderr, end="", file=sys.stderr)
return build.returncode
run = subprocess.run(
[str(binary), str(script)], cwd=ROOT, text=True,
capture_output=True,
env={**os.environ, "ASAN_OPTIONS": "detect_leaks=1"},
)
if run.returncode:
print("FAIL: expression temporary cleanup probe failed")
print(run.stdout, end="")
print(run.stderr, end="", file=sys.stderr)
return 1
if "LeakSanitizer" in run.stderr or "SUMMARY:" in run.stderr:
print("FAIL: discarded expression values still leak")
print(run.stderr, end="", file=sys.stderr)
return 1
if run.stdout != "ab\n":
print("FAIL: expression cleanup probe produced the wrong output")
print(run.stdout, end="", file=sys.stderr)
return 1
print("PASS: discarded expression values are released")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""Regression check for FIND expression temporaries."""
from pathlib import Path
import os
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
if os.name == "nt":
print("SKIP: LeakSanitizer check is POSIX-only")
return 0
with tempfile.TemporaryDirectory(prefix="claro-find-cleanup-") as tmp:
tmp_path = Path(tmp)
binary = tmp_path / "claro-lsan"
script = tmp_path / "find_expressions.claro"
script.write_text(
"SET items TO LIST\n"
+ 'FIND "Missing" IN items AS position\n' * 2000
+ "SAY position\n",
encoding="utf-8",
)
build = subprocess.run(
["gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-g", "-fsanitize=address,undefined",
"src/claro.c", "-o", str(binary), "-lm"],
cwd=ROOT, text=True, capture_output=True,
)
if build.returncode:
print(build.stdout, end="")
print(build.stderr, end="")
return build.returncode
run = subprocess.run(
[str(binary), str(script)], cwd=ROOT, text=True, capture_output=True,
env={**os.environ, "ASAN_OPTIONS": "detect_leaks=1"},
)
if run.returncode or "LeakSanitizer" in run.stderr or "SUMMARY:" in run.stderr:
print("FAIL: FIND expression values still leak")
print(run.stdout, end="")
print(run.stderr, end="")
return 1
if run.stdout != "0\n":
print(f"FAIL: unexpected FIND probe output: {run.stdout!r}")
return 1
print("PASS: FIND expression values are released")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
"""Execute every Claro code block in docs/FIRST_HOUR.md and check its output."""
from pathlib import Path
import os
import re
import subprocess
import sys
import tempfile
ROOT = Path(__file__).resolve().parents[1]
EXE = Path(os.environ.get("CLARO_EXE", str(ROOT / ("claro.exe" if os.name == "nt" else "claro")))).resolve()
INPUTS = {2: "Ada\n", 5: "4\n"}
EXPECTED = {
1: "Hello!\n",
2: "What is your name?\nHello Ada\n",
3: "0\n",
4: "1\n",
5: "What is 2 + 2?\nCorrect!\n",
6: "Practice helps\nPractice helps\nPractice helps\n",
7: "milk\nbread\n",
8: "5\n",
9: "Hello\n",
}
def main() -> int:
text = (ROOT / "docs/FIRST_HOUR.md").read_text(encoding="utf-8")
blocks = re.findall(r"```claro\s*\n(.*?)\n```", text, flags=re.DOTALL)
if len(blocks) != len(EXPECTED):
print(f"FAIL: expected {len(EXPECTED)} Claro blocks, found {len(blocks)}")
return 1
with tempfile.TemporaryDirectory(prefix="claro-first-hour-") as tmp:
for number, block in enumerate(blocks, 1):
script = Path(tmp) / f"block-{number}.claro"
script.write_text(block + "\n", encoding="utf-8")
run = subprocess.run([str(EXE), str(script)], cwd=tmp,
input=INPUTS.get(number, ""), text=True,
capture_output=True, timeout=10)
if run.returncode or run.stdout != EXPECTED[number]:
print(f"FAIL: First Hour block {number}: exit={run.returncode}")
print(f"expected stdout: {EXPECTED[number]!r}")
print(f"actual stdout: {run.stdout!r}")
print(run.stderr, end="", file=sys.stderr)
return 1
print(f"PASS: First Hour block {number}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""Regression check for GET ARGUMENT index-expression ownership."""
from pathlib import Path
import os
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
if os.name == "nt":
print("SKIP: LeakSanitizer check is POSIX-only")
return 0
with tempfile.TemporaryDirectory(prefix="claro-get-argument-cleanup-") as tmp:
tmp_path = Path(tmp)
binary = tmp_path / "claro-lsan"
script = tmp_path / "get_argument_expressions.claro"
script.write_text(
'GET ARGUMENT "1" AS selected\n' * 2000 + "SAY selected\n",
encoding="utf-8",
)
build = subprocess.run(
["gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-g", "-fsanitize=address,undefined",
"src/claro.c", "-o", str(binary), "-lm"],
cwd=ROOT, text=True, capture_output=True,
)
if build.returncode:
print(build.stdout, end="")
print(build.stderr, end="")
return build.returncode
run = subprocess.run(
[str(binary), str(script)], cwd=ROOT, text=True, capture_output=True,
env={**os.environ, "ASAN_OPTIONS": "detect_leaks=1"},
)
if run.returncode or "LeakSanitizer" in run.stderr or "SUMMARY:" in run.stderr:
print("FAIL: GET ARGUMENT index expression values still leak")
print(run.stdout, end="")
print(run.stderr, end="")
return 1
if run.stdout != "\n":
print(f"FAIL: unexpected GET ARGUMENT probe output: {run.stdout!r}")
return 1
print("PASS: GET ARGUMENT index expression values are released")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
"""Check that GET ... AT ... releases its evaluated index value."""
from pathlib import Path
import os
import subprocess
import sys
import tempfile
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
if os.name == "nt":
print("SKIP: LeakSanitizer check is POSIX-only")
return 0
with tempfile.TemporaryDirectory(prefix="get-at-cleanup-") as tmp:
binary = Path(tmp) / "claro-asan"
build = subprocess.run(
["gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-g", "-fsanitize=address,undefined",
"src/claro.c", "-o", str(binary), "-lm"],
cwd=ROOT, text=True, capture_output=True,
)
if build.returncode:
print(build.stdout, end="")
print(build.stderr, end="", file=sys.stderr)
return build.returncode
script = Path(tmp) / "get-at.claro"
script.write_text(
'SET items TO LIST\n'
'ADD "hello" TO items\n'
'DO 2000 TIMES\n'
'GET items AT "1" AS item\n'
'END\n'
'SAY item\n', encoding="utf-8"
)
run = subprocess.run(
[str(binary), str(script)], cwd=ROOT, text=True, capture_output=True,
env={**os.environ, "ASAN_OPTIONS": "detect_leaks=1"},
)
if run.returncode or "LeakSanitizer" in run.stderr or "SUMMARY:" in run.stderr:
print("FAIL: GET ... AT ... retains expression allocations")
print(run.stdout, end="")
print(run.stderr, end="", file=sys.stderr)
return 1
if run.stdout != "hello\n":
print(f"FAIL: unexpected output: {run.stdout!r}")
return 1
print("PASS: repeated GET ... AT ... is leak-free and preserves the selected value")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""Regression check for GET ENV expression temporary cleanup."""
from pathlib import Path
import os
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
if os.name == "nt":
print("SKIP: LeakSanitizer check is POSIX-only")
return 0
with tempfile.TemporaryDirectory(prefix="claro-get-env-cleanup-") as tmp:
tmp_path = Path(tmp)
binary = tmp_path / "claro-lsan"
script = tmp_path / "get_env_expressions.claro"
script.write_text(
'GET ENV "PATH" AS path\n' * 2000 + "SAY path\n",
encoding="utf-8",
)
build = subprocess.run(
["gcc", "-std=c89", "-pedantic-errors", "-Werror=implicit-function-declaration", "-O0", "-g", "-fsanitize=address,undefined",
"src/claro.c", "-o", str(binary), "-lm"],
cwd=ROOT, text=True, capture_output=True,
)
if build.returncode:
print(build.stdout, end="")
print(build.stderr, end="")
return build.returncode
run = subprocess.run(
[str(binary), str(script)], cwd=ROOT, text=True, capture_output=True,
env={**os.environ, "ASAN_OPTIONS": "detect_leaks=1"},
)
if run.returncode or "LeakSanitizer" in run.stderr or "SUMMARY:" in run.stderr:
print("FAIL: GET ENV expression values still leak")
print(run.stdout, end="")
print(run.stderr, end="")
return 1
if not run.stdout.endswith("\n") or len(run.stdout.splitlines()) != 1:
print("FAIL: GET ENV probe did not produce one output line")
return 1
print("PASS: GET ENV expression values are released")
return 0
if __name__ == "__main__":
raise SystemExit(main())

Some files were not shown because too many files have changed in this diff Show More