diff --git a/CHANGELOG.md b/CHANGELOG.md index 91db3e9..af101ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,73 @@ # Changelog -## Claro v1.17.26 - Networking Reliability Release +## v1.18.26-dev documentation audit consistency update -- Updated visible version string to `Claro v1.17.26`. +- Clarified that `docs/DOCUMENTATION_AUDIT_v1.18.26.md` is a historical snapshot from the first documentation cleanup pass. +- Pointed current readers toward `CURRENT_STATUS.md`, `ROADMAP.md`, and the README documentation map before the original audit problem list. + +## v1.18.26-dev type mismatch diagnostics update + +- Improved `claro typecheck` messages so scalar, list, map, and `CHECK TYPE` mismatches use one beginner-friendly `Type mismatch` / `Type check failed` style. +- Diagnostics now name the variable/container, the expected type, and the detected value type in plain language. +- Added `tools/validate_typecheck_diagnostics.py` and wired it into `tools/validate_v1_17.py`. +- Strengthened `tools/validate_v1_17.py` so it rebuilds `claro.exe` before validation instead of trusting a stale binary. +- Updated advanced static typing docs with the clearer diagnostic examples. + +## v1.18.26-dev package manifest and security foundation update + +- Promoted the next development slice to `Claro v1.18.26`. +- Added `manifest-version: 1` to `claro.project` and local `claro.package` files. +- Added deterministic package checksums to local package manifests and `claro.lock`. +- Hardened `claro package doctor` so it validates package manifests and checksums. +- Added `tools/validate_package_security.py` and wired it into `tools/validate_v1_17.py`. + +## v1.18.26-dev cooperative concurrency foundation update + +- Added beginner-friendly `YIELD` and `TASK STATUS name AS variable` support. +- Documented deterministic cooperative concurrency in `docs/CONCURRENCY.md`. +- Added `tests/40_tasks_status.claro` and `tools/validate_concurrency.py`. +- Improved unfinished block diagnostics so missing task endings say the opened block needs `ENDTASK`. +- Wired concurrency validation into `tools/validate_v1_17.py`. + +## v1.18.26-dev LSP diagnostics helper update + +- Replaced the old completion-word-only `support/lsp/claro_lsp_stub.py` with a command-based helper. +- Added JSON `metadata`, `completions`, and `diagnostics` helper modes. +- Diagnostics now combine `claro check` and `claro typecheck` output into editor-friendly JSON objects. +- Added `tools/validate_lsp_helper.py` and wired it into `tools/validate_v1_17.py`. +- Expanded `docs/IDE.md` with LSP/helper usage. + +## v1.18.26-dev IDE foundation update + +- Expanded `claro ide` into valid editor metadata JSON with `file_extensions`, `commands`, `snippets`, and `diagnostics`. +- Added typed container names to IDE metadata, including `LIST OF TEXT` and `MAP OF NUMBER`. +- Added `tools/validate_ide_metadata.py` and wired it into `tools/validate_v1_17.py`. +- Added `docs/IDE.md` for editor integration guidance. + +## v1.18.26 development foundation + +- Added static checking for typed lists such as `LIST OF TEXT`. +- Added static checking for typed maps such as `MAP OF NUMBER`. +- Added `tests/typecheck_container_good.claro` and `tests/typecheck_container_bad.claro`. +- Added `docs/FUTURE_FEATURES_ROADMAP.md` covering IDE, registry, concurrency, GUI/game, web, and optimization tracks. +- Added `docs/ADVANCED_STATIC_TYPING.md`. + +## Claro v1.18.26 - SDL Documentation Correction + +- Clarified that SDL 1.2 is not included in the stable executable. +- Removed the misleading `claro package add sdl` README example. +- Marked `examples/experimental_sdl/` as future/experimental examples only. + +## Claro v1.18.26 - Stable Test Runner Fix + +- Promoted the package to stable `Claro v1.18.26`. +- Fixed Windows `claro test` so it discovers and runs the full golden test suite. +- Normalized runtime error paths to forward slashes for cross-platform expected-output stability. +- Validated version, full tests, package validation, and networking example. + +## Claro v1.18.26 - Networking Reliability Release + +- Updated visible version string to `Claro v1.18.26`. - Hardened the networking foundation while keeping plain-text commands. - Added offline-safe `claro://` network test URLs so tests and lessons do not need internet access. - Improved `HTTP GET`: diff --git a/README.md b/README.md index 5bee307..8c61e3a 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,26 @@ -# Claro v1.17.26 +# Claro v1.18.26 Claro logo Claro is a small, readable scripting language designed to help beginners — especially learners with learning disabilities — learn programming without being overwhelmed by punctuation-heavy syntax. -Claro stays plain-text first: simple enough to start with `SET name "Jon"`, but able to grow into stronger typed scripts, objects, packages, graphics, networking, and tooling over the v1 line. +Claro stays plain-text first: simple enough to start with `SET name "Jon"`, but able to grow into stronger typed scripts, objects, packages, networking, and tooling over the v1 line. Graphics/SDL work is experimental and not enabled in the stable executable. ## Status -**Current release:** Claro v1.17.26 +**Current release:** Claro v1.18.26 Validated in this package: ```bash ./claro --version -# Claro v1.17.26 +# Claro v1.18.26 ./claro test # PASS: 0 failure(s) ./claro validate -# Validation passed. Claro v1.17.26 networking reliability is ready for use. +# Validation passed. Claro v1.18.26 networking reliability is ready for use. ``` ## Beginner-first syntax @@ -118,9 +118,19 @@ SAY kind CHECK TYPE score IS NUMBER ``` +Advanced container checks are available through `claro typecheck`: + +```claro +SET names AS LIST OF TEXT TO LIST +ADD "Ada" TO names + +SET scores AS MAP OF NUMBER TO MAP +PUT scores KEY "math" VALUE 98 +``` + ## Project and package workflow -v1.17.26 hardens Claro's project/package workflow. +v1.18.26 hardens Claro's project/package workflow. Create a starter project: @@ -135,7 +145,6 @@ Manage packages: ```bash claro package init claro package add text -claro package add sdl claro package list claro package remove text claro package doctor @@ -155,7 +164,7 @@ Package names are checked so unsafe names such as `../bad` are rejected. ## Networking -v1.17.26 adds safer beginner networking commands with offline `claro://` test URLs. +v1.18.26 adds safer beginner networking commands with offline `claro://` test URLs. ```claro HTTP CHECK "claro://hello" AS safe @@ -204,6 +213,32 @@ claro ide ./claro examples/networking.claro ``` +## Documentation map + +If you are new to Claro, start here: + +1. `docs/QUICK_START.md` +2. `docs/FIRST_HOUR.md` +3. `lessons/README.md` +4. `docs/CLI.md` +5. `docs/CURRENT_STATUS.md` + +Feature references: + +- Language reference: `docs/SPEC.md` +- Friendly errors and checking: `docs/ERRORS.md`, `docs/LINTER.md`, `docs/TESTING.md` +- Formatter: `docs/FORMATTER.md` +- Static typing: `docs/ADVANCED_STATIC_TYPING.md` +- Objects/classes: `docs/V1_15_OBJECTS_CLASSES.md` +- Projects/packages: `docs/V1_16_PACKAGES_PROJECTS.md`, `docs/PACKAGE_REGISTRY.md` +- Networking/web: `docs/V1_17_NETWORKING.md`, `docs/WEB_SERVER_PLAN.md` +- Tasks/concurrency: `docs/CONCURRENCY.md` +- IDE/editor support: `docs/IDE.md`, `docs/EDITOR_EXTENSION_PLAN.md` +- Graphics/SDL status: `docs/GRAPHICS.md`, `docs/SDL12.md` +- Roadmaps: `docs/ROADMAP.md`, `docs/COMPLETE_PLATFORM_ROADMAP.md` + +Historical release notes and validation logs are kept in files such as `docs/RC*_NOTES.md`, `docs/RC*_VALIDATION.md`, and older `docs/V1_*_VALIDATION.md`. They are useful for project history, but they are not the beginner starting path. + ## Release direction Claro should remain in the `v1.xx.yy` line for normal development. A future Claro v2 should mean a full rewrite years later, not an ordinary feature update. diff --git a/build.bat b/build.bat index f384272..859d209 100644 --- a/build.bat +++ b/build.bat @@ -2,7 +2,7 @@ setlocal set CC=gcc if not "%~1"=="" set CC=%~1 -echo Building Claro v1.17.26... +echo Building Claro v1.18.26... %CC% -std=c99 src\claro.c -O0 -o claro.exe -lm if errorlevel 1 ( echo Build failed. diff --git a/build.ps1 b/build.ps1 index 6c754cb..1b007e5 100644 --- a/build.ps1 +++ b/build.ps1 @@ -3,7 +3,7 @@ param( [string]$Out = "claro.exe" ) -Write-Host "Building Claro v1.17.26..." -ForegroundColor Cyan +Write-Host "Building Claro v1.18.26..." -ForegroundColor Cyan & $CC "-std=c99" "src\claro.c" "-O0" "-o" $Out "-lm" if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/docs/ADVANCED_STATIC_TYPING.md b/docs/ADVANCED_STATIC_TYPING.md new file mode 100644 index 0000000..e16d428 --- /dev/null +++ b/docs/ADVANCED_STATIC_TYPING.md @@ -0,0 +1,48 @@ +# Advanced Static Typing + +Claro v1.18.26 adds the first advanced static-typing foundation: typed containers checked by `claro typecheck`. + +## Typed lists + +```claro +SET names AS LIST OF TEXT TO LIST +ADD "Ada" TO names +ADD "Grace" TO names +``` + +The type checker now rejects wrong item types: + +```claro +SET names AS LIST OF TEXT TO LIST +ADD 123 TO names +``` + +Output: + +```text +Type mismatch for list names: expected TEXT item, but this value looks like NUMBER. +``` + +## Typed maps + +```claro +SET scores AS MAP OF NUMBER TO MAP +PUT scores KEY "math" VALUE 98 +``` + +The type checker rejects wrong value types: + +```claro +SET scores AS MAP OF NUMBER TO MAP +PUT scores KEY "oops" VALUE "high" +``` + +Output: + +```text +Type mismatch for map scores: expected NUMBER value, but this value looks like TEXT. +``` + +## Status + +This is currently a static checker feature. It improves `claro typecheck` and validation confidence. Runtime enforcement for every container mutation can be added later after the syntax is classroom-tested. diff --git a/docs/CLASSROOM_READINESS.md b/docs/CLASSROOM_READINESS.md index c86a833..01b0f77 100644 --- a/docs/CLASSROOM_READINESS.md +++ b/docs/CLASSROOM_READINESS.md @@ -1,6 +1,6 @@ # Classroom Readiness Checklist -Claro RC3 is suitable for early classroom-style testing, but not final 1.0. +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. ## Ready to test @@ -10,19 +10,26 @@ Claro RC3 is suitable for early classroom-style testing, but not final 1.0. - 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 - Advanced scripts - Large programs - Graphics/game examples -- Package/module workflows beyond simple imports +- Remote networking examples +- Package registry or publishing workflows +- Concurrency examples beyond documented safe task patterns ## Recommended testing session -1. Build Claro. -2. Run `claro test`. -3. Run `lessons/01_hello.claro`. -4. Run `lessons/02_ask_name.claro`. -5. Run `examples/quiz.claro`. -6. Deliberately mistype `SAY` as `PRNT` and run `claro check`. +1. Build Claro or use the included executable. +2. Run `claro --version` and confirm `Claro v1.18.26`. +3. Run `claro test`. +4. Run `claro validate`. +5. Run `lessons/01_hello.claro`. +6. Run `lessons/02_ask_name.claro`. +7. Run `examples/quiz.claro`. +8. Deliberately mistype `SAY` as `PRNT` and run `claro check`. +9. Try one foundation feature example, such as `examples/objects_classes.claro` or `examples/networking.claro`. diff --git a/docs/CLI.md b/docs/CLI.md index d8bb917..a43d1bb 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -1,6 +1,6 @@ # Claro Command Line -Claro v1.17.26 supports these main commands: +Claro v1.18.26 supports these main commands: ```bash claro file.claro @@ -58,7 +58,7 @@ claro package doctor claro package lock ``` -The v1.17.26 package command is a safer local project-file helper. It creates `claro.project`, `claro.lock`, and local folders under `packages/`. It is not yet an online package registry. +The v1.18.26 package command is a safer local project-file helper. It creates `claro.project`, `claro.lock`, and local folders under `packages/`. It is not yet an online package registry. ## IDE metadata diff --git a/docs/COMPLETE_PLATFORM_ROADMAP.md b/docs/COMPLETE_PLATFORM_ROADMAP.md new file mode 100644 index 0000000..6b486aa --- /dev/null +++ b/docs/COMPLETE_PLATFORM_ROADMAP.md @@ -0,0 +1,40 @@ +# Claro Complete Platform Roadmap + +Current release: **Claro v1.18.26** + +This roadmap tracks the larger goal: Claro as a complete beginner-friendly programming platform, not only a toy scripting interpreter. + +## Platform pillars + +1. Beginner scripting core +2. Strong but optional-feeling static safety +3. Objects and classes for larger programs +4. Local projects and packages +5. Safe networking and local web apps +6. Cooperative tasks/concurrency +7. Editor/IDE help +8. Optional graphics/game support + +## Current status summary + +See `CURRENT_STATUS.md` for the detailed matrix. + +- Stable foundation: beginner scripting core, functions, friendly CLI/checking +- Foundation present: static types, objects/classes, packages, networking, concurrency, IDE metadata +- Experimental/planned: SDL graphics, package registry, web server, full editor extension/LSP + +## Completion definition + +Claro should not be called a complete beginner platform until: + +- each pillar has at least one beginner lesson +- each pillar has a validation command or regression test +- historical docs are clearly separated from current docs +- optional features fail gracefully when dependencies are missing +- README navigation tells learners exactly where to start + +## Next documentation work + +- Keep this roadmap and `CURRENT_STATUS.md` updated after each feature change. +- Avoid saying experimental features are ready until examples run with the stable executable. +- Prefer small learner examples over large speculative design docs. diff --git a/docs/CONCURRENCY.md b/docs/CONCURRENCY.md new file mode 100644 index 0000000..b7acd10 --- /dev/null +++ b/docs/CONCURRENCY.md @@ -0,0 +1,37 @@ +# Claro Cooperative Concurrency + +Claro's first concurrency foundation is intentionally small, beginner-friendly, and deterministic. + +## Design goals + +- **Cooperative**, not preemptive: a task only gives control back at clear points such as `YIELD`, `WAIT TASK`, or the end of the task block. +- **Deterministic**: the same program should run in the same order every time while Claro grows toward richer scheduling. +- **No native threads** yet: this foundation does not use operating-system threads, shared-memory races, locks, or parallel CPU execution. +- **Easy status checks**: learners can ask whether a task is `DONE` or still `PENDING` without learning low-level threading concepts. + +## Current syntax + +```claro +START TASK helloTask + SAY "Inside task" + YIELD +ENDTASK + +TASK STATUS helloTask AS status +SAY status + +WAIT TASK helloTask +``` + +## Commands + +- `START TASK name ... ENDTASK` starts a named cooperative task block. In this foundation, the block runs deterministically and marks the task as done when it finishes. +- `YIELD` is accepted as an explicit cooperative scheduling point. It is currently a safe no-op, reserved for future round-robin scheduling. +- `TASK STATUS name AS variable` stores `DONE` when the named task has completed, otherwise `PENDING`. +- `WAIT TASK name` is accepted as the beginner-friendly wait command. It is currently deterministic because the task has already run by the time the line is reached. + +## Why not real threads yet? + +Real native threads add race conditions, locks, deadlocks, timing bugs, and platform differences. Claro is designed for beginners, including people with learning disabilities, so the first stable slice focuses on readable syntax, validation, documentation, and predictable behavior before adding true parallelism. + +Future slices can build on this foundation with a real cooperative task queue, cancellation, timeouts, and safe message passing. diff --git a/docs/CURRENT_STATUS.md b/docs/CURRENT_STATUS.md new file mode 100644 index 0000000..31d7fff --- /dev/null +++ b/docs/CURRENT_STATUS.md @@ -0,0 +1,187 @@ +# Claro v1.18.26 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. + +## 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, and text 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` +- typed list/map checks through `claro typecheck` + +Still needed: +- typed function parameters and return values +- type checking through branches and loops +- object method and field type checking +- 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 +- object helper commands + +Still needed: +- stronger method/field type checking +- 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/` +- package-name safety checks + +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 +- diagnostics helper + +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. diff --git a/docs/Claro_1_0_Guide.pdf b/docs/Claro_1_0_Guide.pdf deleted file mode 100644 index dea2ada..0000000 --- a/docs/Claro_1_0_Guide.pdf +++ /dev/null @@ -1,124 +0,0 @@ -%PDF-1.4 -%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com -1 0 obj -<< -/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R ->> -endobj -2 0 obj -<< -/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font ->> -endobj -3 0 obj -<< -/BaseFont /ZapfDingbats /Name /F2 /Subtype /Type1 /Type /Font ->> -endobj -4 0 obj -<< -/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font ->> -endobj -5 0 obj -<< -/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font ->> -endobj -6 0 obj -<< -/Contents 12 0 R /MediaBox [ 0 0 612 792 ] /Parent 11 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -7 0 obj -<< -/Contents 13 0 R /MediaBox [ 0 0 612 792 ] /Parent 11 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -8 0 obj -<< -/Contents 14 0 R /MediaBox [ 0 0 612 792 ] /Parent 11 0 R /Resources << -/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] ->> /Rotate 0 /Trans << - ->> - /Type /Page ->> -endobj -9 0 obj -<< -/PageMode /UseNone /Pages 11 0 R /Type /Catalog ->> -endobj -10 0 obj -<< -/Author (\(anonymous\)) /CreationDate (D:20260217214605+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260217214605+00'00') /Producer (ReportLab PDF Library - www.reportlab.com) - /Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False ->> -endobj -11 0 obj -<< -/Count 3 /Kids [ 6 0 R 7 0 R 8 0 R ] /Type /Pages ->> -endobj -12 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 872 ->> -stream -GauI5>u03/'Sc)R/'`I[(kO#-9=UE_)bMtO#0A\FmD^Dhl]Ki9+0bbA*P3aSXYO*6W^oj1jIGh5pTY`o&E";7hkC<7c/Ik;KJ8"*#%;lr6[f&MhKeI@mE";j#"aE!0c%9dS9OJ1n0Ja^;nFTCb7Y8#NkJ:YUk%qgJ0,AX7MR3^D@A$:+O`_nQl&lPO-'BpI,C7&_<]FQrQ&'K^FgK!SG:$f*E0r>SGB-ft+dhjG%(>NSUl_d0A5W(MgAuo[\3Zr%-=\Y0J>I\ZKH&infM*e)+C$#SBcJENGVYHCGm-lQFRsn(eGZ]T:=8DJtNQN;EN%eu[/a3*:2:>R-~>endstream -endobj -13 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 762 ->> -stream -GauHId;IYl'Rf-pNr$[PeD__WqO#[7U/B&GGL`M&Qs`P?`6%#7'7W6e:0UmVCZ>Fa$KuN@kM1-+Y6k'j+o33l^h8P2#m#$2f-&qL#-5r2HYeQ#(5NGcfFc74ZS<(Vs^_blZoIE=5i&sBAB\qR6VU>W*$&"jH+*uj-Vl?Nd*NXY\%9R"j6P!K1m2Y#K*T(si-?kN05f51k=Tt`d@7/d^eKXDAJ\3G75\t.=7$k^0U=:+Z64bUJ&O4-%8Lh=*5kA9.b+d:=h)WOiF&)Nr.O=@oP`kMF8am2>,#Q],^W29/md*)UDgpu:k/(H.H5kQ'9hq)T*mU?&^mbEO6s='XFo.`>#R@ZMLc.-esX]B9S4QjqLn>HL0hY<`H9S\PjU)]LJtuMg^GmY'#Bb*t[7.A(07u?GYmB[6jMH.!U(i[,_f8.kFj$W.ZV1XOG<]dhs2LH_;/$Y3\h*Wk1Wll.,IFd^kh_KL']AUK/`^mf4`(qngjkQ6o+D+BjFsWeOSZn:bEnq=9*WQ*`7kUBAFngTZGaIo:3qaaa&l!LlKZ/=C$;qLWQaSp:2snhgendstream -endobj -14 0 obj -<< -/Filter [ /ASCII85Decode /FlateDecode ] /Length 424 ->> -stream -Gas2F?Z1NM'ZJu*'PBP5Bf;tZkL5#dQ,mp>2.hnL_.g]&k`Y>fY6FrVMb=0-gmmst[k.`k=sSSN;P;5'0H_JIn/0*SGJJW'DLXXUJbc-^+%r"f8%E%9kWiSkMA3BHMeS$XM^;PH.BQ\)84p4!!ml<[F]t[#:Ra^ZjB36.:7IiuFpZ%KjmcA!jTHZ,_0G+8M11/(3Y'KZip9O49ToYrHlQ,DbFog`9;t;9/U1Y`P3l:It:<$m8@sCOGE%!UWsnN,n8#15ipqYg;>."ANk(RBgTO'k^Cn5g"b1d2]U6WSZ<^Xi*dD#$e`Vs9o2Vn7fR[(\O7.P1>>lX6/NVdr&HH[!CSuO-N~>endstream -endobj -xref -0 15 -0000000000 65535 f -0000000073 00000 n -0000000134 00000 n -0000000241 00000 n -0000000324 00000 n -0000000436 00000 n -0000000541 00000 n -0000000736 00000 n -0000000931 00000 n -0000001126 00000 n -0000001195 00000 n -0000001479 00000 n -0000001551 00000 n -0000002514 00000 n -0000003367 00000 n -trailer -<< -/ID -[<03799c740f0684ba474c8496ea7bf74e><03799c740f0684ba474c8496ea7bf74e>] -% ReportLab generated PDF document -- digest (http://www.reportlab.com) - -/Info 10 0 R -/Root 9 0 R -/Size 15 ->> -startxref -3882 -%%EOF diff --git a/docs/DOCUMENTATION_AUDIT_v1.18.26.md b/docs/DOCUMENTATION_AUDIT_v1.18.26.md new file mode 100644 index 0000000..aadbca8 --- /dev/null +++ b/docs/DOCUMENTATION_AUDIT_v1.18.26.md @@ -0,0 +1,229 @@ +# Claro v1.18.26 Documentation Audit + +Date: 2026-06-05 + +## Scope + +Reviewed the Markdown documentation in the Claro v1.18.26 package: + +- `README.md` +- `CHANGELOG.md` +- `lessons/README.md` +- `docs/*.md` — 55 files + +Total Markdown files inventoried: 57. + +## Overall finding + +This audit is a historical snapshot from the first v1.18.26 documentation cleanup pass. At audit time, the docs were useful and broad, but they read like a historical bundle plus a current release bundle. That was okay for internal development, but before a polished public release the documentation needed to be reorganized so beginners saw one clear current path and advanced/history material was separated. + +The cleanup notes at the end of this file record the follow-up work that has already been completed. For current learner guidance, use `CURRENT_STATUS.md`, `ROADMAP.md`, and the README documentation map before relying on the problem list below. + +## Current strongest documentation + +These docs are clear and should remain part of the current user-facing docs: + +- `README.md` — good high-level overview of Claro v1.18.26. +- `docs/QUICK_START.md` — important beginner entry point. +- `docs/FIRST_HOUR.md` — useful learner sequence. +- `lessons/README.md` — useful for guided learning. +- `docs/CLI.md` — needed command reference. +- `docs/SPEC.md` — core language reference. +- `docs/ERRORS.md` — important for beginner-friendly diagnostics. +- `docs/FORMATTER.md` +- `docs/LINTER.md` +- `docs/TESTING.md` +- `docs/ADVANCED_STATIC_TYPING.md` +- `docs/IDE.md` +- `docs/CONCURRENCY.md` +- `docs/V1_15_OBJECTS_CLASSES.md` +- `docs/V1_16_PACKAGES_PROJECTS.md` +- `docs/V1_17_NETWORKING.md` +- `docs/SDL12.md` +- `docs/FUTURE_FEATURES_ROADMAP.md` + +## Main documentation problems found at audit time + +### 1. Old RC language is still mixed with current docs + +At audit time, several docs still said or implied that Claro was in an old release-candidate phase: + +- `docs/ROADMAP.md` still says: + - `RC3 - Classroom Readiness` + - `RC4 - Final Polish` + - `1.0 - Stable Beginner Release` +- `docs/CLASSROOM_READINESS.md` says `Claro RC3 is suitable... but not final 1.0`. +- `docs/LINTER.md` says `RC3 catches:`. +- `docs/TESTING.md` mentions validating an RC3 package and `validate_rc3.py`. +- `docs/FIRST_HOUR.md` references `RC2` behavior. + +Recommendation: keep historical RC docs, but move them under a clear `docs/history/` section or label them as historical at the top. + +### 2. Current version story is inconsistent + +The package is v1.18.26, but several docs are tied to old versions: + +- `docs/STABLE_RELEASE.md` is specifically `Claro v1.10.26 Stable Release`. +- `docs/POST_1_0_ROADMAP.md` lists old future milestones like v1.11.26 through v1.15.26, even though many are already superseded. +- `docs/V1_14_STATIC_TYPES.md`, `docs/V1_15_OBJECTS_CLASSES.md`, etc. are useful but read like version-specific release notes instead of current feature docs. + +Recommendation: use current feature docs for beginners and keep old version docs as historical release notes. + +### 3. The complete-platform roadmap needs to be promoted + +`docs/FUTURE_FEATURES_ROADMAP.md` is the best roadmap for the complete beginner-language goal: + +1. Advanced static typing +2. IDE support +3. Concurrency +4. Web programming +5. GUI/game engine +6. Package registry +7. Optimization + +But it should now explicitly reflect the user's desired completion targets: + +- SDL graphics +- classes/objects +- package manager +- networking +- threads/concurrency +- strong static types +- advanced IDE support + +Recommendation: rename or supplement it with `docs/COMPLETE_PLATFORM_ROADMAP.md` and track each feature as Not Started / Foundation / Beta / Stable. + +### 4. SDL graphics are correctly marked as not stable, but need a clearer beginner path + +`docs/SDL12.md` is honest and says SDL is not included in the current stable executable. That is good. + +Current status: + +- Placeholder graphics commands exist. +- Real SDL commands are planned/experimental. +- `examples/experimental_sdl/` will fail with current `claro.exe`. + +Recommendation: add `docs/GRAPHICS.md` with two tracks: + +- Current: beginner-safe placeholder graphics commands. +- Future: optional SDL backend behind a build flag. + +### 5. Advanced IDE support has a foundation, not a complete IDE experience + +`docs/IDE.md` documents: + +- `claro ide` +- metadata JSON +- completion list +- diagnostics helper + +Missing for a complete beginner platform: + +- syntax highlighting package +- real LSP server or editor extension +- hover help +- go-to definition +- code actions/fixes +- integrated lesson/help flow + +Recommendation: add `docs/EDITOR_EXTENSION_PLAN.md` or expand `docs/IDE.md` with a phased roadmap. + +### 6. Static types are currently partial + +`docs/ADVANCED_STATIC_TYPING.md` clearly documents typed lists and maps checked by `claro typecheck`. + +Missing for strong static types: + +- function parameter types +- function return types +- object method type checking +- typed imports +- better type checking across branches/loops +- runtime enforcement strategy for containers + +Recommendation: add a `Strong Static Types` section to the roadmap and keep the beginner syntax examples very small. + +### 7. Concurrency is deterministic foundation, not real threads yet + +`docs/CONCURRENCY.md` is clear that Claro has cooperative concurrency foundations and no native threads yet. + +Missing for complete platform: + +- real cooperative scheduler +- cancellation +- timeouts +- message passing +- eventually optional native threads only if safe + +Recommendation: avoid saying "threads" in user-facing docs until semantics are safe; use "tasks" first. + +### 8. Package manager foundation exists, but registry is not complete + +`docs/V1_16_PACKAGES_PROJECTS.md` documents local package workflow, manifests, lock files, and safety rules. + +Missing for full package ecosystem: + +- dependency versions +- local install from path +- package publish/export +- remote registry protocol +- signature/checksum verification for downloaded packages + +Recommendation: add `docs/PACKAGE_REGISTRY.md` before implementing online downloads. + +### 9. Networking foundation exists, web server does not + +`docs/V1_17_NETWORKING.md` is good for HTTP client commands and offline `claro://` URLs. + +Missing for complete platform: + +- beginner web server syntax +- routes +- request/response objects +- local-only safe server mode +- docs and examples for web apps + +Recommendation: add `docs/WEB_SERVER_PLAN.md` before implementing sockets. + +## Recommended cleanup order from the original audit + +1. Add a `docs/CURRENT_STATUS.md` file with a plain feature matrix for v1.18.26. +2. Rewrite `docs/ROADMAP.md` so it no longer describes RC3/RC4 as current. +3. Move or label RC docs as historical. +4. Split docs into beginner-facing vs maintainer/history docs. +5. Add complete-platform roadmap docs for: + - strong static types + - IDE/editor support + - cooperative tasks/threads + - package registry + - web server/networking + - SDL graphics + - object/class polish +6. Update `README.md` to point beginners only to current docs. + +## Feature status summary + +- Beginner scripting core: stable foundation. +- Classes/objects: foundation present; needs polish and stronger typing. +- Package manager: local foundation present; registry incomplete. +- Networking: HTTP client foundation present; web server incomplete. +- Threads/concurrency: cooperative foundation present; real scheduling/threads incomplete. +- Strong static types: typed variables and containers foundation present; function/object/import typing incomplete. +- Advanced IDE support: metadata/helper foundation present; full editor/LSP experience incomplete. +- SDL graphics: not included in stable executable; experimental/planned only. + +## Bottom line + +Documentation is good enough for internal development, but before the complete beginner-language release it needs a current-status pass. The largest documentation problem is not missing docs; it is that historical RC/version docs are mixed into the current package without clear labeling. + + +## Cleanup pass completed + +A documentation cleanup pass was applied after this audit: + +- Added `CURRENT_STATUS.md`. +- Rewrote `ROADMAP.md` so old RC3/RC4/1.0 milestones are no longer presented as current. +- Added complete-platform planning docs: `COMPLETE_PLATFORM_ROADMAP.md`, `GRAPHICS.md`, `PACKAGE_REGISTRY.md`, `WEB_SERVER_PLAN.md`, and `EDITOR_EXTENSION_PLAN.md`. +- Updated `README.md` with a beginner-first documentation map. +- Updated stale current-facing docs: `CLASSROOM_READINESS.md`, `LINTER.md`, `TESTING.md`, `FIRST_HOUR.md`, `SPEC.md`, and `POST_1_0_ROADMAP.md`. +- Labeled RC and older validation/release files as historical. diff --git a/docs/EDITOR_EXTENSION_PLAN.md b/docs/EDITOR_EXTENSION_PLAN.md new file mode 100644 index 0000000..14a263a --- /dev/null +++ b/docs/EDITOR_EXTENSION_PLAN.md @@ -0,0 +1,39 @@ +# Claro Editor and IDE Support Plan + +Current release: **Claro v1.18.26** + +Claro has an IDE metadata/helper foundation. It does not yet have a complete editor extension or LSP experience. + +## Ready now + +- `claro ide` +- metadata JSON +- completion list +- diagnostics helper +- formatter/checker commands that editor tools can call + +## Goal + +Give beginners fast, friendly feedback while they type, without requiring them to understand compiler internals. + +## Suggested phases + +1. Syntax highlighting grammar for `.claro` files. +2. Editor snippets for common beginner patterns. +3. On-save `claro check` diagnostics. +4. Formatter integration. +5. Hover help from command metadata. +6. Code actions for common mistakes such as `PRNT` -> `SAY`. +7. LSP server only after the metadata and diagnostics formats are stable. + +## Beginner safety rules + +- Messages should explain the fix, not just the error. +- Suggestions should prefer modern simple syntax. +- Editor tooling should never run network commands unless the user explicitly asks. +- Lessons should still work from the terminal without an editor extension. + +See also: +- `IDE.md` +- `CURRENT_STATUS.md` +- `ROADMAP.md` diff --git a/docs/FINAL_RELEASE_CHECKLIST.md b/docs/FINAL_RELEASE_CHECKLIST.md index 0b0e906..ed9d5b6 100644 --- a/docs/FINAL_RELEASE_CHECKLIST.md +++ b/docs/FINAL_RELEASE_CHECKLIST.md @@ -1,6 +1,6 @@ # Claro Final Release Checklist -Before publishing `Claro v1.10.26` as the first stable release: +Before publishing `Claro v1.18.26` as the current stable release: - Build from a clean unzip on Windows. - Build from a clean unzip on Linux or macOS. @@ -16,4 +16,4 @@ Before publishing `Claro v1.10.26` as the first stable release: - Confirm beginner docs teach simple syntax first. - Do not add large new systems before stable v1. -Large features such as SDL, classes, packages, networking, threads, strong static types, and IDE tooling should come after the first stable v1 foundation. +Avoid adding large new systems during a stability release; prioritize buildability, tests, documentation consistency, and beginner safety. diff --git a/docs/FIRST_HOUR.md b/docs/FIRST_HOUR.md index 23d485e..3125c76 100644 --- a/docs/FIRST_HOUR.md +++ b/docs/FIRST_HOUR.md @@ -11,70 +11,76 @@ SAY "Hello!" ## 2. Ask a question ```claro -ASK "What is your name?" AS name +ASK "What is your name?" 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 ```claro -SET score TO 0 +SET score 0 SAY score ``` ## 4. Do simple math ```claro -SET score TO 0 -SET score TO score + 1 +SET score 0 +SET score score + 1 SAY score ``` ## 5. Make a decision ```claro -ASK "What is 2 + 2?" AS answer +ASK "What is 2 + 2?" answer IF answer = 4 SAY "Correct!" ELSE SAY "The answer is 4." -ENDIF +END ``` -In RC2, simple number-looking input can compare naturally with numbers. +Simple number-looking input can compare naturally with numbers. ## 6. Repeat something ```claro DO 3 TIMES SAY "Practice helps" -DONE +END ``` +Older examples may use `DONE` for loop endings; that compatibility form still works. + ## 7. Use a list ```claro -SET items TO LIST +SET items LIST ADD "milk" TO items ADD "bread" TO items FOR EACH item IN items SAY item -DONE +END ``` ## 8. Teach a function ```claro -TEACH add TAKES a, b +TEACH add a b RETURN a + b -LEARNED +END -CALL add WITH 2, 3 +DO add 2 3 SAY RESULT ``` +Older examples may use `TEACH add TAKES a, b`, `LEARNED`, and `CALL add WITH 2, 3`; those forms still work. + ## 9. Save and load text ```claro diff --git a/docs/FUTURE_FEATURES_ROADMAP.md b/docs/FUTURE_FEATURES_ROADMAP.md new file mode 100644 index 0000000..300d2b7 --- /dev/null +++ b/docs/FUTURE_FEATURES_ROADMAP.md @@ -0,0 +1,165 @@ +# Claro Future Features Roadmap + +> **For Hermes:** Use subagent-driven-development skill to implement this plan task-by-task. + +**Goal:** Grow Claro from a beginner scripting language into a stable accessible platform without breaking the learning-friendly core. + +**Architecture:** Keep the interpreter small and stable. Add major systems behind explicit commands, feature flags, and testable layers. Prefer beginner-readable commands first, then deeper professional tooling around them. + +**Tech Stack:** C99 interpreter, `.claro` tests/golden outputs, optional helper tools in Python, optional platform libraries such as SDL only after isolated build support exists. + +--- + +> Current note: Some tasks in this implementation plan have since been partially completed or moved into dedicated status docs. For the current high-level view, start with `ROADMAP.md`, `CURRENT_STATUS.md`, and `COMPLETE_PLATFORM_ROADMAP.md`. + +## Priority order + +1. **Advanced static typing foundation** — safest immediate win; improves learner feedback and catches mistakes before running. +2. **Professional IDE foundations** — language metadata, diagnostics, formatter, LSP/documentation support. +3. **Concurrency as real runtime feature** — cooperative tasks first; avoid native threads until semantics are clear. +4. **Production-grade web programming** — HTTP primitives, JSON, routing/server design after safety model. +5. **GUI/game engine features** — separate stable placeholder graphics from real optional SDL/GUI backends. +6. **Online package registry** — design package metadata/security before network install. +7. **Advanced compiler optimization** — only after semantics stabilize; start with bytecode/AST planning, not premature optimization. + +--- + +## Track A: Advanced Static Typing + +### Task A1: Add typed LIST and MAP declarations + +**Objective:** Accept and check beginner-readable container type declarations such as `SET names LIST OF TEXT` and `SET scores MAP OF NUMBER`. + +**Files:** +- Modify: `src/claro.c` +- Test: `tests/40_static_container_good.claro` +- Test: `tests/40_static_container_good.out` +- Test: `tests/typecheck_container_bad.claro` + +**Steps:** +1. Add a failing good test with `LIST OF TEXT` and `MAP OF NUMBER` declarations. +2. Add a failing bad typecheck test where a `LIST OF NUMBER` receives text. +3. Teach `claro_is_type_word`, `claro_type_matches`, and the typecheck parser to preserve simple container annotations. +4. Verify `claro test` and `claro typecheck tests/typecheck_container_bad.claro`. + +### Task A2: Add clearer type mismatch messages + +**Objective:** Make type errors explain the variable, expected type, actual value kind, and line number in plain language. + +**Files:** +- Modify: `src/claro.c` +- Test: `tests/typecheck_bad.claro` + +**Steps:** +1. Capture current bad output. +2. Improve wording without changing successful behavior. +3. Update tests/docs only after verifying the new wording is clearer. + +--- + +## Track B: Professional IDE Foundations + +### Task B1: Expand `claro ide` JSON + +**Objective:** Output commands, snippets, type names, and file extension metadata for editor plugins. + +**Files:** +- Modify: `src/claro.c` +- Test: `tests/41_ide_metadata.claro` or a CLI validation script. + +**Steps:** +1. Add expected JSON fields: `commands`, `snippets`, `diagnostics`, `version`. +2. Validate with Python `json.loads`. +3. Document in `docs/IDE.md`. + +### Task B2: Build minimal LSP helper + +**Objective:** Provide a Python helper that reads Claro files and returns diagnostics using `claro check`/`typecheck`. + +**Files:** +- Create/modify: `support/lsp/claro_lsp_stub.py` +- Create: `docs/IDE.md` + +--- + +## Track C: Real Concurrency + +### Task C1: Define cooperative task semantics + +**Objective:** Document `START TASK`, `YIELD`, `WAIT TASK`, and `TASK STATUS` without native threads. + +**Files:** +- Create: `docs/CONCURRENCY.md` +- Test: future `tests/42_tasks_cooperative.claro` + +**Rule:** Start cooperative scheduling before OS threads. This keeps beginner programs deterministic. + +--- + +## Track D: Web Programming + +### Task D1: Stabilize HTTP client commands + +**Objective:** Keep `HTTP CHECK`, `HTTP GET`, and `HTTP SAVE` predictable and testable with `claro://` URLs. + +**Files:** +- Modify: `docs/WEB_SERVER_PLAN.md` +- Tests: existing networking tests + +### Task D2: Design local web server syntax + +**Objective:** Propose beginner-friendly server syntax, but do not implement sockets until parser/runtime tests are ready. + +Example future shape: + +```claro +WEB SERVER 8080 +WEB GET "/" DO home +TEACH home request + RETURN "Hello" +END +WEB START +``` + +--- + +## Track E: GUI/Game Engine + +### Task E1: Separate placeholder graphics from real SDL + +**Objective:** Keep stable no-op graphics commands documented separately from optional SDL backends. + +**Files:** +- Modify: `docs/SDL12.md` +- Create: `docs/GRAPHICS.md` + +### Task E2: Optional SDL prototype branch + +**Objective:** Only after build scripts can find SDL 1.2 reliably, add `CLARO_WITH_SDL12` support behind a compile flag. + +--- + +## Track F: Online Package Registry + +### Task F1: Define package manifest and lock security + +**Objective:** Design package metadata, versions, hashes, and allowed sources before downloads. + +**Files:** +- Create: `docs/PACKAGE_REGISTRY.md` + +**Rule:** No online package install until hash verification and safe paths are implemented. + +--- + +## Track G: Optimization + +### Task G1: Add timing benchmark command + +**Objective:** Measure before optimizing. + +**Files:** +- Create: `tests/benchmarks/` +- Create: `docs/PERFORMANCE.md` + +**Rule:** No optimizer work without benchmarks. diff --git a/docs/GRAPHICS.md b/docs/GRAPHICS.md new file mode 100644 index 0000000..90b5581 --- /dev/null +++ b/docs/GRAPHICS.md @@ -0,0 +1,31 @@ +# Claro Graphics Status + +Current release: **Claro v1.18.26** + +Graphics support is intentionally separated from the stable beginner console workflow. + +## Beginner-safe track + +Use these today: +- console output with `SAY` +- text-based examples +- placeholder graphics docs/examples only when clearly marked experimental + +This keeps Claro portable and easy to run on Windows, Linux, macOS, classrooms, and older laptops. + +## Experimental SDL track + +Real SDL/window support is planned or experimental, not part of the stable `claro.exe` path yet. + +Before SDL is treated as beginner-ready, Claro should have: +- an optional build flag/backend +- clear dependency checks +- friendly "graphics unavailable" errors +- tiny drawing examples +- keyboard input examples +- validation that non-graphics Claro still works without SDL installed + +See also: +- `SDL12.md` +- `CURRENT_STATUS.md` +- `ROADMAP.md` diff --git a/docs/IDE.md b/docs/IDE.md new file mode 100644 index 0000000..7d485ac --- /dev/null +++ b/docs/IDE.md @@ -0,0 +1,58 @@ +# Claro IDE Metadata + +Claro includes a small editor-integration command: + +```text +claro ide +``` + +It prints JSON that editor plugins and helper tools can read without scraping docs. + +## Fields + +- `language`: language display name. +- `version`: Claro version string. +- `file_extensions`: file extensions handled by Claro; currently `.claro`. +- `keywords`: beginner-facing language keywords and multi-word commands. +- `types`: supported static type names, including typed containers such as `LIST OF TEXT` and `MAP OF NUMBER`. +- `commands`: CLI commands useful to editors. +- `snippets`: starter snippets an editor may offer. +- `diagnostics`: diagnostic sources and message format. + +## Validation + +Run: + +```text +python tools/validate_ide_metadata.py +python tools/validate_lsp_helper.py +``` + +The metadata validator checks that `claro ide` prints valid JSON and includes the required editor-support sections. + +The LSP helper validator checks that `support/lsp/claro_lsp_stub.py` can return metadata, completions, and JSON diagnostics from `claro check` plus `claro typecheck`. + +## Minimal LSP/helper commands + +The helper is intentionally small and command-based so editor plugins can use it without implementing the full Language Server Protocol first: + +```text +python support/lsp/claro_lsp_stub.py metadata +python support/lsp/claro_lsp_stub.py completions +python support/lsp/claro_lsp_stub.py diagnostics file.claro +``` + +`diagnostics` returns a JSON list with: + +- `source`: `claro check` or `claro typecheck` +- `file`: normalized file path +- `line`: 1-based line number +- `severity`: currently `error` +- `message`: beginner-readable Claro diagnostic text + +## Recommended editor flow + +1. Run `claro ide` or `python support/lsp/claro_lsp_stub.py metadata` once to load keywords, snippets, and command metadata. +2. Use `python support/lsp/claro_lsp_stub.py completions` for simple keyword/type completions. +3. On save, run `python support/lsp/claro_lsp_stub.py diagnostics file.claro`. +4. Show diagnostics using the returned JSON objects. diff --git a/docs/LINTER.md b/docs/LINTER.md index faa6872..107b623 100644 --- a/docs/LINTER.md +++ b/docs/LINTER.md @@ -2,16 +2,15 @@ `claro check file.claro` looks for beginner mistakes before running a program. -RC3 catches: +The current checker catches: - unknown commands - command typos such as `PRINT` or `PRNT` -- missing `TO` in `SET` -- missing `AS` in `ASK`, `GET`, and `COUNT` -- missing `ENDIF`, `DONE`, `LEARNED`, `ENDTRY`, and other block endings +- missing required words in older compatibility forms +- missing block endings such as `END`, `ENDIF`, `DONE`, `LEARNED`, and `ENDTRY` - `ELSE` without an `IF` - `CATCH` without a `TRY` - common misspelled variables - variables created by `ASK`, `GET`, `COUNT`, `READ`, `PARSE`, `MAKE`, `FOR`, and `FOR EACH` -The checker is meant to be friendly, not scary. It is still not a complete static analyzer. +The checker is meant to be friendly, not scary. It is still not a complete static analyzer; use `claro typecheck` for the stronger type-checking pass documented in `ADVANCED_STATIC_TYPING.md`. diff --git a/docs/PACKAGE_REGISTRY.md b/docs/PACKAGE_REGISTRY.md new file mode 100644 index 0000000..23daebc --- /dev/null +++ b/docs/PACKAGE_REGISTRY.md @@ -0,0 +1,50 @@ +# Claro Package Registry Plan + +Current release: **Claro v1.18.26** + +Claro already has a local project/package foundation. A remote package registry should come later, after safety and teaching rules are clear. + +## Ready now + +- `claro new` +- `claro package init` +- `claro package add` +- `claro package list` +- `claro package remove` +- `claro package doctor` +- `claro package lock` +- package-name safety checks + +## Registry goals + +A beginner-friendly registry should be: +- predictable +- safe by default +- easy to inspect +- offline-tolerant where possible +- resistant to path traversal and confusing package names + +## Needed before downloads + +- package manifest format +- semantic version or simple v1-compatible version rules +- dependency constraints +- lock-file checksums +- package archive format +- signature or checksum verification +- local cache rules +- clear error messages when the network is unavailable + +## Suggested phases + +1. Local install from a folder. +2. Package export to a `.claro-package` archive. +3. Lock-file checksum verification. +4. Read-only registry index download. +5. Safe install from registry. +6. Publishing flow only after moderation/auth rules are designed. + +See also: +- `V1_16_PACKAGES_PROJECTS.md` +- `CURRENT_STATUS.md` +- `ROADMAP.md` diff --git a/docs/PERFORMANCE.md b/docs/PERFORMANCE.md new file mode 100644 index 0000000..83a3f85 --- /dev/null +++ b/docs/PERFORMANCE.md @@ -0,0 +1,29 @@ +# Claro Performance and Optimization Notes + +Current release: **Claro v1.18.26** + +Performance work should come after language behavior is stable and measured. Do not optimize blindly. + +## Current priority + +Claro's current priority is correctness, accessibility, friendly diagnostics, and reliable beginner examples. + +## Before optimization work + +Add: +- small benchmark scripts +- expected-output checks for benchmark examples +- a timing command or repeatable timing script +- documentation for what is being measured + +## Optimization rules + +- Keep beginner-visible behavior unchanged. +- Measure before and after every optimization. +- Prefer simple interpreter cleanup before complex compiler architecture. +- Do not add bytecode, JIT, or advanced compiler stages until the v1 semantics are stable. + +See also: +- `ROADMAP.md` +- `CURRENT_STATUS.md` +- `FUTURE_FEATURES_ROADMAP.md` diff --git a/docs/POST_1_0_ROADMAP.md b/docs/POST_1_0_ROADMAP.md index 8110b99..4b0f505 100644 --- a/docs/POST_1_0_ROADMAP.md +++ b/docs/POST_1_0_ROADMAP.md @@ -1,48 +1,29 @@ -# Post-1.0 Roadmap +# Historical Post-1.0 Roadmap -Claro should continue in the v1 line. +> Historical note: this file records an older planning view from earlier v1 development. For the current roadmap, use `ROADMAP.md`, `CURRENT_STATUS.md`, and `COMPLETE_PLATFORM_ROADMAP.md`. -Recommended next phases: +Claro should continue in the v1 line. Claro v2 should be reserved for a future full rewrite, not normal feature additions. -## v1.11.26 - Type Safety Polish +## What changed since this roadmap was first written -- improve typed variables -- stronger `claro check` -- clearer beginner type errors +Several items that were once planned for later v1 releases now have foundations in the current package: -## v1.12.26 - Objects Polish +- Type safety polish: foundation present through typed variables and `claro typecheck`. +- Objects/classes: foundation present through `CLASS`, `HAS`, `NEW`, fields, and simple methods. +- Package manager: local project/package workflow foundation present. +- Networking: HTTP client foundation present with offline `claro://` URLs. +- Concurrency/tasks: deterministic foundation present. +- IDE support: metadata/helper foundation present. -- make classes and objects more useful -- keep syntax plain and readable +## Still open -## v1.13.26 - Package Manager Foundation +The larger complete-platform work is not finished yet: -- make `claro package` more useful -- local packages first -- avoid complex online behavior too early +- strong function/object/import typing +- package registry and publishing +- beginner web server API +- full cooperative task scheduler +- editor extension or LSP +- real SDL/graphics support in an optional build -## v1.14.26 - SDL/Game Foundation - -- real SDL-backed window support -- drawing -- keyboard input -- beginner game examples - -## v1.15.26 - Networking Foundation - -- simple HTTP requests -- TCP/UDP basics later - -## v1.17.26 - Safe Tasks/Concurrency - -- beginner-safe task model -- avoid confusing low-level threading early - -## v1.17.26 - IDE Support - -- syntax highlighting -- formatter/checker integration -- editor metadata -- later LSP support - -Claro v2 should be reserved for a future full rewrite, not normal feature additions. +Use the current roadmap files for next steps. diff --git a/docs/RC0_NOTES.md b/docs/RC0_NOTES.md index d35aa65..c73b066 100644 --- a/docs/RC0_NOTES.md +++ b/docs/RC0_NOTES.md @@ -1,5 +1,8 @@ # Claro RC0 Notes +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + Claro RC0 is a stabilization pass for the educational scripting-language core. ## Goal diff --git a/docs/RC1_NOTES.md b/docs/RC1_NOTES.md index 36dcbae..49af547 100644 --- a/docs/RC1_NOTES.md +++ b/docs/RC1_NOTES.md @@ -1,5 +1,8 @@ # Claro RC1 Notes +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + Claro RC1 is a learner-testing release candidate for the portable teaching core. ## What RC1 is for diff --git a/docs/RC1_VALIDATION.md b/docs/RC1_VALIDATION.md index c4bd15b..b3c273b 100644 --- a/docs/RC1_VALIDATION.md +++ b/docs/RC1_VALIDATION.md @@ -1,5 +1,8 @@ # Claro RC1 Validation +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + Validation performed for this package: ```text diff --git a/docs/RC2_NOTES.md b/docs/RC2_NOTES.md index 1279a6f..18661c7 100644 --- a/docs/RC2_NOTES.md +++ b/docs/RC2_NOTES.md @@ -1,5 +1,8 @@ # Claro RC2 Notes +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + RC2 is the Beginner Polish Release. ## Main changes diff --git a/docs/RC2_VALIDATION.md b/docs/RC2_VALIDATION.md index 839f1d4..8c714f8 100644 --- a/docs/RC2_VALIDATION.md +++ b/docs/RC2_VALIDATION.md @@ -1,5 +1,8 @@ # Claro RC2 Validation +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + Validated commands: ```bash diff --git a/docs/RC3_NOTES.md b/docs/RC3_NOTES.md index a9d45f3..9323834 100644 --- a/docs/RC3_NOTES.md +++ b/docs/RC3_NOTES.md @@ -1,5 +1,8 @@ # Claro RC3 Notes +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + Claro RC3 is the **Classroom Readiness Release**. RC3 does not try to make Claro bigger. It makes the existing beginner language easier to teach, test, and package. diff --git a/docs/RC3_VALIDATION.md b/docs/RC3_VALIDATION.md index 148c36d..8990ab7 100644 --- a/docs/RC3_VALIDATION.md +++ b/docs/RC3_VALIDATION.md @@ -1,5 +1,8 @@ # Claro RC3 Validation +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + Validated commands: ```bash diff --git a/docs/RC6_NOTES.md b/docs/RC6_NOTES.md index cfc7900..69f77e2 100644 --- a/docs/RC6_NOTES.md +++ b/docs/RC6_NOTES.md @@ -1,5 +1,8 @@ # Claro v1.06.26 Notes +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + RC6 keeps Claro focused on the original goal: a plain-text scripting language simple enough for learners with learning disabilities, while still growing into a useful tool for real scripts. ## Main theme diff --git a/docs/RC6_VALIDATION.md b/docs/RC6_VALIDATION.md index 714c55a..38214ef 100644 --- a/docs/RC6_VALIDATION.md +++ b/docs/RC6_VALIDATION.md @@ -1,5 +1,8 @@ # Claro v1.06.26 Validation +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + Validated from the source tree with: ```bash diff --git a/docs/RC9_NOTES.md b/docs/RC9_NOTES.md index 16a0189..a1a9e48 100644 --- a/docs/RC9_NOTES.md +++ b/docs/RC9_NOTES.md @@ -1,5 +1,8 @@ # Claro v1.09.26 RC9 Notes +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + RC9 is the final-candidate release before the first stable v1 release. The goal of RC9 is not to make Claro bigger. The goal is to make the current learner-friendly language easier to test, validate, and ship. diff --git a/docs/RC9_VALIDATION.md b/docs/RC9_VALIDATION.md index 1b50c03..2b63bbd 100644 --- a/docs/RC9_VALIDATION.md +++ b/docs/RC9_VALIDATION.md @@ -1,5 +1,8 @@ # Claro v1.09.26 RC9 Validation +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + Validated commands: ```bash diff --git a/docs/RELEASE_v1.17.27.md b/docs/RELEASE_v1.17.27.md new file mode 100644 index 0000000..041b03e --- /dev/null +++ b/docs/RELEASE_v1.17.27.md @@ -0,0 +1,35 @@ +# Claro v1.18.26 Stable Release + +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + +Claro v1.18.26 is a stability release for the beginner-friendly programming language Claro. + +## What changed + +- Windows `claro test` now discovers and runs the full golden test suite instead of only three basic tests. +- Runtime error file paths are normalized to forward slashes so expected output is stable across Windows, Linux, and macOS. +- Release/version text was updated for the stable v1.18.26 package. + +## Validation completed + +Validated on Windows using Zig cc as the C compiler: + +```bash +zig cc -std=c99 src/claro.c -O0 -o claro.exe -lm +./claro.exe --version +./claro.exe test +./claro.exe validate +python tools/validate_v1_17.py +``` + +Results: + +- Version: `Claro v1.18.26` +- Golden tests: 44 run, 0 failures +- Full package validation: passed +- Networking example validation: passed + +## Release recommendation + +This package is ready to share as a stable Windows source/binary release for testing with learners. Keep future changes small and test-first. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 38b95a4..7d8fd64 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,33 +1,122 @@ # Claro Roadmap -## RC3 - Classroom Readiness +Current release: **Claro v1.18.26** -Status: current package. +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. -Focus: +## Current direction -- lessons folder -- stronger checker messages -- Windows helper scripts -- first-hour Quick Start docs -- more beginner examples +Keep Claro beginner-first while growing into a complete small programming platform: -## RC4 - Final Polish +- 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 -Planned focus: +## v1.18.26 status snapshot -- bug fixes only -- all examples checked -- final Quick Start review -- Codeberg release notes -- no known crash bugs +See `CURRENT_STATUS.md` for the detailed feature matrix. -## 1.0 - Stable Beginner Release +- 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 -Planned focus: +## Near-term cleanup priorities -- stable interpreter -- friendly errors -- tested examples -- classroom-ready docs -- clear Windows/Linux build instructions +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. +4. Add small examples for each foundation feature before adding bigger syntax. + +## Complete-platform milestones + +### 1. Strong static types + +Goal: make larger beginner programs safer without making first scripts harder. + +Needed next: +- typed function parameters +- typed function returns +- type checking across branches and loops +- typed imports/modules +- object field and method type checking +- 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 +- method parameter checks +- 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 diff --git a/docs/SDL12.md b/docs/SDL12.md index 57394ee..5166586 100644 --- a/docs/SDL12.md +++ b/docs/SDL12.md @@ -1,41 +1,32 @@ -# SDL 1.2 Support (Optional) +# SDL 1.2 Support Status -Claro can be built with optional SDL 1.2 support for simple 2D graphics and input. +SDL 1.2 support is **not included in the current stable executable**. -## Build (MinGW example) +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: -Install SDL 1.2 development files, then compile with: - -```sh -gcc -DCLARO_WITH_SDL12=1 -I src/claro.c -L -lSDL -mwindows -O2 -o claro.exe +```text +Unknown command: SDL ``` -On some setups you may need `-lSDLmain` too. +## Current graphics status -## Commands +The stable interpreter only accepts placeholder graphics-style commands such as `OPEN`, `CLEAR`, `DRAW`, `UPDATE`, and `CLOSE`. These do **not** open a real SDL window; they only mark graphics commands as accepted for beginner script compatibility. -All SDL commands are grouped under `SDL ...`: +## Planned SDL command shape + +If SDL support is implemented later, the intended commands are: - `SDL OPEN ` - `SDL CLEAR <r> <g> <b>` -- `SDL RECT <x> <y> <w> <h> <r> <g> <b>` (filled rectangle) +- `SDL RECT <x> <y> <w> <h> <r> <g> <b>` - `SDL PRESENT` -- `SDL POLL` (updates variables `SDLEVENT` and `SDLKEY`) +- `SDL POLL` - `SDL DELAY <ms>` - `SDL CLOSE` +- `SDL LOADBMP <path> AS var` +- `SDL BLIT <handle> AT x y` +- `SDL BLITCLIP <handle> SRC sx sy sw sh AT x y` +- `SDL KEYDOWN <keycode> AS var` +- `SDL MOUSE AS mx my mb` -### Event variables - -After `SDL POLL`, Claro variables update: - -- `SDLEVENT` = `"NONE" | "QUIT" | "KEYDOWN" | "KEYUP" | "OTHER"` -- `SDLKEY` = key code number (SDLKey) for KEYDOWN/KEYUP, otherwise `0` - -## Sprite / bitmap support - -- `SDL LOADBMP <path> AS var` → loads a BMP and stores a numeric handle in `var` -- `SDL BLIT <handle> AT x y` → draws the loaded bitmap at x,y -- `SDL KEYDOWN <keycode> AS var` → sets var to 1/0 based on current key state - -- `SDL BLITCLIP <handle> SRC sx sy sw sh AT x y` → blit part of a surface (sprite-sheet) -- `SDL MOUSE AS mx my mb` → writes mouse x/y and button bitmask +These are **planned/experimental**, not stable features yet. diff --git a/docs/SPEC.md b/docs/SPEC.md index 194fea2..32bcfb6 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -1,10 +1,9 @@ -# Claro Language Specification (Draft) +# Claro Language Specification -Version: **0.19-draft** (Beta19-Spec) -Status: **Draft** (stability work in progress) +Current package: **Claro v1.18.26** +Status: **Living v1 specification** -This document defines the intended behavior of the Claro language. It’s written to reduce breaking -changes and make the path to 1.0 clear. +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. --- @@ -15,10 +14,10 @@ changes and make the path to 1.0 clear. 3. General-purpose scripting: files, JSON/CSV, small tools. 4. Safe by default: TRY/CATCH, predictable runtime behavior. -Non-goals for 1.0: -- Concurrency / threads -- Advanced static typing -- Metaprogramming / macros +Current scope notes: +- Concurrency is being developed as beginner-safe tasks first; native threads are not exposed as the beginner API. +- Advanced static typing exists as a foundation and is still being expanded. +- Metaprogramming/macros are not a current goal. --- @@ -244,10 +243,11 @@ claro test --- -## 14. Beta “may change” list +## 14. v1 stabilization notes -Until 1.0, these are still being stabilized: +These details may still be refined within the v1 line: - exact numeric precision rules -- MAP iteration ordering (1.0 will define it) +- MAP iteration ordering - exact wording/format of error messages - REPL multi-line behaviors +- the boundary between dynamic runtime behavior and optional static checks diff --git a/docs/STABLE_RELEASE.md b/docs/STABLE_RELEASE.md index 88abc84..6c09599 100644 --- a/docs/STABLE_RELEASE.md +++ b/docs/STABLE_RELEASE.md @@ -1,5 +1,8 @@ # Claro v1.10.26 Stable Release +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + Claro v1.10.26 is the first stable v1 foundation release. The goal is not to be huge. The goal is to be reliable, readable, and friendly enough for beginner learners while still giving them a real scripting language to grow with. diff --git a/docs/TESTING.md b/docs/TESTING.md index c7e0468..804d9f7 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -18,14 +18,27 @@ Check a lesson: ./claro check lessons/01_hello.claro ``` -Validate an RC3 package after building: +Validate the current v1.18.26 package after building: ```bash -python tools/validate_rc3.py +./claro validate +python tools/validate_typecheck_diagnostics.py +python tools/validate_version_convention.py ``` -On Windows, after `build.bat` or `build.ps1`: +Useful focused validation scripts: -```powershell -python tools alidate_rc3.py +```bash +python tools/validate_v1_17.py +python tools/validate_v1_16.py +python tools/validate_v1_15.py +python tools/validate_v1_14.py +python tools/validate_concurrency.py +python tools/validate_ide_metadata.py +python tools/validate_lsp_helper.py +python tools/validate_package_security.py ``` + +Older `validate_rc*.py` scripts are kept for historical release notes. They are not the recommended current package validation path. + +On Windows, after `build.bat` or `build.ps1`, run the same Python commands from the project root. diff --git a/docs/V1_10_VALIDATION.md b/docs/V1_10_VALIDATION.md index 7be263b..4ce7801 100644 --- a/docs/V1_10_VALIDATION.md +++ b/docs/V1_10_VALIDATION.md @@ -1,5 +1,8 @@ # Claro v1.10.26 Validation +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + Validated from a clean package with: ```bash diff --git a/docs/V1_14_VALIDATION.md b/docs/V1_14_VALIDATION.md index 7e12bf2..12f04cf 100644 --- a/docs/V1_14_VALIDATION.md +++ b/docs/V1_14_VALIDATION.md @@ -1,5 +1,8 @@ # Claro v1.14.26 Validation +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + Validated commands: ```bash diff --git a/docs/V1_15_VALIDATION.md b/docs/V1_15_VALIDATION.md index bbd19db..95e635c 100644 --- a/docs/V1_15_VALIDATION.md +++ b/docs/V1_15_VALIDATION.md @@ -1,5 +1,8 @@ # Claro v1.15.26 Validation +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + + Validated commands: ```bash diff --git a/docs/V1_16_PACKAGES_PROJECTS.md b/docs/V1_16_PACKAGES_PROJECTS.md index 76bddb2..f499e15 100644 --- a/docs/V1_16_PACKAGES_PROJECTS.md +++ b/docs/V1_16_PACKAGES_PROJECTS.md @@ -1,6 +1,6 @@ -# Claro v1.17.26 - Packages and Projects +# Claro v1.18.26 - Packages and Projects -Claro v1.17.26 makes projects and packages safer and more useful while keeping the beginner syntax simple. +Claro v1.18.26 makes projects and packages safer and more useful while keeping the beginner syntax simple. ## Create a project @@ -25,12 +25,13 @@ README.md `claro.project` is intentionally plain text: ```text +manifest-version: 1 name: MyProject main: main.claro -version: v1.17.26 +version: v1.18.26 packages: package: text -package: sdl +package: net_tools ``` ## Package commands @@ -52,6 +53,18 @@ packages/NAME/ README.md ``` +Each `claro.package` file includes a tiny manifest and checksum: + +```text +manifest-version: 1 +name: text +version: 1 +source: local +checksum: 1234abcd +``` + +`claro.lock` records the release version, lock format, packages, and checksums so future registry work has a stable safety foundation. + ## Safety rules Package names may use only: diff --git a/docs/V1_16_VALIDATION.md b/docs/V1_16_VALIDATION.md index 1ec8187..47e7646 100644 --- a/docs/V1_16_VALIDATION.md +++ b/docs/V1_16_VALIDATION.md @@ -1,4 +1,7 @@ -# Claro v1.17.26 Validation +# Claro v1.18.26 Validation + +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + Validated commands: @@ -12,9 +15,9 @@ gcc -std=c99 src/claro.c -O0 -o claro -lm Expected result: ```text -Claro v1.17.26 +Claro v1.18.26 PASS: 0 failure(s) -Validation passed. Claro v1.17.26 networking reliability is ready for use. +Validation passed. Claro v1.18.26 networking reliability is ready for use. ``` Additional project/package workflow validation: diff --git a/docs/V1_17_NETWORKING.md b/docs/V1_17_NETWORKING.md index d2ab006..dcbb03b 100644 --- a/docs/V1_17_NETWORKING.md +++ b/docs/V1_17_NETWORKING.md @@ -1,6 +1,6 @@ -# Claro v1.17.26 Networking Reliability +# Claro v1.18.26 Networking Reliability -Claro v1.17.26 hardens the beginner networking foundation without making the language harder to read. +Claro v1.18.26 hardens the beginner networking foundation without making the language harder to read. The goal is simple plain-text commands: diff --git a/docs/V1_17_VALIDATION.md b/docs/V1_17_VALIDATION.md index 69b6dd1..5346777 100644 --- a/docs/V1_17_VALIDATION.md +++ b/docs/V1_17_VALIDATION.md @@ -1,4 +1,7 @@ -# Claro v1.17.26 Validation +# Claro v1.18.26 Validation + +> Historical note: this file is kept for release history. The current package is Claro v1.18.26; use `CURRENT_STATUS.md`, `ROADMAP.md`, and the current feature docs for up-to-date beginner guidance. + Validated from a clean source folder with: @@ -13,7 +16,7 @@ gcc -std=c99 src/claro.c -O0 -o claro -lm Expected version: ```text -Claro v1.17.26 +Claro v1.18.26 ``` Expected test result: @@ -25,7 +28,7 @@ PASS: 0 failure(s) Expected validation result: ```text -Validation passed. Claro v1.17.26 networking reliability is ready for use. +Validation passed. Claro v1.18.26 networking reliability is ready for use. ``` New network tests: diff --git a/docs/WEB_SERVER_PLAN.md b/docs/WEB_SERVER_PLAN.md new file mode 100644 index 0000000..782b9f6 --- /dev/null +++ b/docs/WEB_SERVER_PLAN.md @@ -0,0 +1,54 @@ +# Claro Web Server Plan + +Current release: **Claro v1.18.26** + +Claro currently has beginner HTTP client commands and offline `claro://` test URLs. A web server API is not complete yet. + +## Ready now + +- `HTTP CHECK` +- `HTTP GET` +- `HTTP SAVE` +- offline `claro://hello` and related lesson/test URLs +- `LASTHTTP` +- real `http://` and `https://` requests through `curl` when available + +## Web server goals + +A beginner web server should make tiny local apps possible without exposing learners to unsafe network defaults. + +Desired properties: +- localhost-only by default +- readable route syntax +- simple request/response values +- friendly port-in-use errors +- easy stop behavior +- examples that work offline + +## Possible future syntax sketch + +```claro +WEB START LOCAL PORT 8080 + +WHEN GET "/" + WEB SAY "Hello from Claro" +END + +WEB WAIT +``` + +This is a sketch, not a committed syntax. + +## Needed before implementation + +- route syntax decision +- request value model +- response helpers +- safe defaults for host/port +- tests for port conflicts and malformed requests +- docs explaining localhost vs public internet + +See also: +- `V1_17_NETWORKING.md` +- `CURRENT_STATUS.md` +- `ROADMAP.md` diff --git a/download b/download deleted file mode 100644 index bb35d34..0000000 --- a/download +++ /dev/null @@ -1,6 +0,0 @@ -claro -claro.exe -*.o -*.out -.DS_Store -Thumbs.db diff --git a/examples/fizzbuzz.claro b/examples/fizzbuzz.claro deleted file mode 100644 index cf23b55..0000000 --- a/examples/fizzbuzz.claro +++ /dev/null @@ -1,14 +0,0 @@ -SAY "FizzBuzz in Claro - T-Shirt Business Edition" -SAY "Printing numbers 1 to 20:" -FOR i FROM 1 TO 20 DO - IF i % 15 == 0 THEN - SAY "FizzBuzz" - ELSE IF i % 3 == 0 THEN - SAY "Fizz" - ELSE IF i % 5 == 0 THEN - SAY "Buzz" - ELSE - SAY i - END IF -END FOR -SAY "Task completed for t-shirt business automation!" \ No newline at end of file diff --git a/examples/functions.claro b/examples/functions.claro deleted file mode 100644 index 8233082..0000000 --- a/examples/functions.claro +++ /dev/null @@ -1,6 +0,0 @@ -TEACH add TAKES a, b - RETURN a + b -LEARNED - -CALL add WITH 2, 3 -SAY RESULT diff --git a/src/claro.c b/src/claro.c index caf38ed..d30c1f2 100644 --- a/src/claro.c +++ b/src/claro.c @@ -1,5 +1,5 @@ /* - Claro v1.17.26 - Networking Reliability Release + Claro v1.18.26 - Networking Reliability Release A compact, buildable Claro interpreter focused on the beginner-friendly teaching core: readable commands, simple expressions, functions, collections, @@ -42,7 +42,7 @@ int pclose(FILE *stream); #define F_OK 0 #endif -#define CLARO_VERSION "Claro v1.17.26" +#define CLARO_VERSION "Claro v1.18.26" static void *xmalloc(size_t n){ void *p=malloc(n?n:1); if(!p){ fprintf(stderr,"Out of memory\n"); exit(1);} return p; } static void *xrealloc(void *p,size_t n){ void *q=realloc(p,n?n:1); if(!q){ fprintf(stderr,"Out of memory\n"); exit(1);} return q; } @@ -121,6 +121,7 @@ static void rt_set(Runtime *rt,const char *name,Value val){ Var **head=rt->local static void rt_set_global(Runtime *rt,const char *name,Value val){ Var *v=env_find(rt->globals,name); if(v){ v->val=v_copy(val); return;} v=(Var*)xmalloc(sizeof(Var)); v->name=xstrdup(name); v->val=v_copy(val); v->next=rt->globals; rt->globals=v; } static void rt_error(Runtime *rt,const char *file,int line,const char *fmt,...); +static char *normalize_path_copy(const char *path){ char *out=xstrdup(path?path:""); char *p; for(p=out;*p;p++) if(*p=='\\') *p='/'; return out; } static int claro_is_type_word(const char *s){ return s&&(ci_eq(s,"NUMBER")||ci_eq(s,"TEXT")||ci_eq(s,"YESNO")||ci_eq(s,"BOOL")||ci_eq(s,"BOOLEAN")||ci_eq(s,"LIST")||ci_eq(s,"MAP")||ci_eq(s,"OBJECT")||ci_eq(s,"ANY")); } static const char *claro_value_type(Value v){ if(v.type==V_NUM) return "NUMBER"; if(v.type==V_STR) return "TEXT"; if(v.type==V_BOOL) return "YESNO"; if(v.type==V_LIST) return "LIST"; if(v.type==V_MAP) return "MAP"; return "NONE"; } static Value claro_default_for_type(const char *type){ if(ci_eq(type,"NUMBER")) return v_num(0); if(ci_eq(type,"TEXT")) return v_str(""); if(ci_eq(type,"YESNO")||ci_eq(type,"BOOL")||ci_eq(type,"BOOLEAN")) return v_bool(0); if(ci_eq(type,"LIST")) return v_list(); if(ci_eq(type,"MAP")||ci_eq(type,"OBJECT")) return v_map(); return v_none(); } @@ -134,7 +135,7 @@ static int rt_set_checked(Runtime *rt,const char *file,int line,const char *name rt_set(rt,name,val); return 1; } static void graphics_note(Runtime *rt,const char *what){ (void)what; rt_set(rt,"GRAPHICS_READY",v_bool(1)); } -static void rt_error(Runtime *rt,const char *file,int line,const char *fmt,...){ va_list ap; char buf[1024]; if(rt->error) return; va_start(ap,fmt); vsnprintf(buf,sizeof(buf),fmt,ap); va_end(ap); rt->error=1; rt->err_msg=xstrdup(buf); rt->err_file=xstrdup(file?file:""); rt->err_line=line; rt_set(rt,"LASTERROR",v_str(buf)); rt_set(rt,"LASTERRORFILE",v_str(file?file:"")); rt_set(rt,"LASTERRORLINE",v_num(line)); } +static void rt_error(Runtime *rt,const char *file,int line,const char *fmt,...){ va_list ap; char buf[1024]; char *nfile; if(rt->error) return; va_start(ap,fmt); vsnprintf(buf,sizeof(buf),fmt,ap); va_end(ap); nfile=normalize_path_copy(file?file:""); rt->error=1; rt->err_msg=xstrdup(buf); rt->err_file=xstrdup(nfile); rt->err_line=line; rt_set(rt,"LASTERROR",v_str(buf)); rt_set(rt,"LASTERRORFILE",v_str(nfile)); rt_set(rt,"LASTERRORLINE",v_num(line)); free(nfile); } static void rt_clear_error(Runtime *rt){ rt->error=0; } static void out_line(Runtime *rt,Value v){ char *s=v_to_string(v); if(rt->capture){ str_add(&rt->captured,s); str_ch(&rt->captured,'\n'); } else { printf("%s\n",s); } free(s); } static void add_function(Runtime *rt,const char *name,char **params,int pcnt,Program *p,int start,int end){ Function *f=(Function*)xmalloc(sizeof(Function)); int i; f->name=xstrdup(name); f->pcnt=pcnt; f->params=(char**)xmalloc(sizeof(char*)*(pcnt?pcnt:1)); for(i=0;i<pcnt;i++) f->params[i]=xstrdup(params[i]); f->prog=p; f->start=start; f->end=end; f->next=rt->funcs; rt->funcs=f; } @@ -323,7 +324,9 @@ static void exec_line(Runtime *rt,Program *p,int *pcp,const char *raw){ char buf if(strcmp(up,"CLASS")==0){ int end=match_block(p,pc,"CLASS","ENDCLASS",NULL,NULL); *pcp=end<0?pc:end; return; } if(strcmp(up,"NEW")==0){ const char *as=find_word_ci(t,"AS"); if(as){ char *cls=substr(t+3,as); char *var=xstrdup(trim_inplace((char*)as+2)); create_object_value(rt,p->path,pc+1,trim_inplace(cls),trim_inplace(var)); free(cls); free(var); } else { const char *pcur=t+3; char *cls=unquote_token(&pcur); char *var=xstrdup(trim_inplace((char*)pcur)); create_object_value(rt,p->path,pc+1,trim_inplace(cls),trim_inplace(var)); free(cls); free(var); } return; } if(strcmp(up,"START")==0){ if(starts_ci(t+5," TASK")){ const char *pcur=t+10; char *name=unquote_token(&pcur); int end=match_block(p,pc,"START","ENDTASK",NULL,NULL); exec_range(rt,p,pc+1,end); if(name&&*name){ char done[256]; snprintf(done,sizeof(done),"TASK_%s_DONE",trim_inplace(name)); rt_set(rt,done,v_bool(1)); } *pcp=end<0?pc:end; free(name); return; } } + if(strcmp(up,"YIELD")==0){ return; } if(strcmp(up,"WAIT")==0){ return; } + if(strcmp(up,"TASK")==0){ if(starts_ci(t+4," STATUS")){ const char *as=find_word_ci(t,"AS"); if(as){ char *name=substr(t+11,as); char *var=xstrdup(trim_inplace((char*)as+2)); char key[256]; Value done; snprintf(key,sizeof(key),"TASK_%s_DONE",trim_inplace(name)); done=rt_get(rt,key); rt_set(rt,var,v_str(v_truth(done)?"DONE":"PENDING")); free(name); free(var); } else rt_error(rt,p->path,pc+1,"TASK STATUS needs AS. Try: TASK STATUS helloTask AS status"); return; } } if(strcmp(up,"OPEN")==0||strcmp(up,"CLEAR")==0||strcmp(up,"DRAW")==0||strcmp(up,"UPDATE")==0||strcmp(up,"CLOSE")==0){ graphics_note(rt,up); return; } if(strcmp(up,"HTTP")==0){ const char *as=find_word_ci(t,"AS"); @@ -395,7 +398,7 @@ static int run_file(Runtime *rt,const char *path){ Program *p=load_program(rt,pa /* ---------- formatter/check/test runner ---------- */ static int fmt_file(const char *path){ FILE *f=fopen(path,"rb"); char line[4096]; int indent=0; if(!f){ fprintf(stderr,"Could not open %s\n",path); return 1;} while(fgets(line,sizeof(line),f)){ char *t=trim_inplace(line),w[64],up[64]; int opens=0; first_word(t,w,sizeof(w)); upper_copy(up,w,sizeof(up)); if(!strcmp(up,"ENDIF")||!strcmp(up,"LEARNED")||!strcmp(up,"DONE")||!strcmp(up,"ENDTRY")||!strcmp(up,"ELSE")||!strcmp(up,"CATCH")||!strcmp(up,"ENDCOMMENT")||!strcmp(up,"ENDCLASS")||!strcmp(up,"ENDTASK")||!strcmp(up,"END")) indent-=4; if(indent<0) indent=0; for(int i=0;i<indent;i++) putchar(' '); puts(t); opens=!strcmp(up,"TEACH")||!strcmp(up,"IF")||!strcmp(up,"FOR")||!strcmp(up,"TRY")||!strcmp(up,"CLASS")||!strcmp(up,"START")||!strcmp(up,"COMMENT")||!strcmp(up,"REPEAT")||!strcmp(up,"ELSE")||!strcmp(up,"CATCH")||(!strcmp(up,"DO")&&find_word_ci(t,"TIMES")); if(opens) indent+=4; } fclose(f); return 0; } -static const char *check_commands[]={"SAY","SET","ASK","IF","ELSE","ENDIF","DO","DONE","FOR","REPEAT","UNTIL","TEACH","LEARNED","RETURN","CALL","IMPORT","ADD","COUNT","GET","PUT","WRITE","APPEND","READ","EXISTS","DELETE","PARSE","MAKE","TRY","CATCH","ENDTRY","RAISE","CREATE","COPY","MOVE","LIST","TEXT","RANDOM","RUN","SORT","REVERSE","FIND","REMOVE","SHOW","HIDE","EXPORT","COMMENT","ENDCOMMENT","END","WAIT","CLASS","HAS","ENDCLASS","NEW","START","ENDTASK","OPEN","CLEAR","DRAW","UPDATE","CLOSE","HTTP","OBJECT","TYPE","CHECK",NULL}; +static const char *check_commands[]={"SAY","SET","ASK","IF","ELSE","ENDIF","DO","DONE","FOR","REPEAT","UNTIL","TEACH","LEARNED","RETURN","CALL","IMPORT","ADD","COUNT","GET","PUT","WRITE","APPEND","READ","EXISTS","DELETE","PARSE","MAKE","TRY","CATCH","ENDTRY","RAISE","CREATE","COPY","MOVE","LIST","TEXT","RANDOM","RUN","SORT","REVERSE","FIND","REMOVE","SHOW","HIDE","EXPORT","COMMENT","ENDCOMMENT","END","WAIT","YIELD","TASK","CLASS","HAS","ENDCLASS","NEW","START","ENDTASK","OPEN","CLEAR","DRAW","UPDATE","CLOSE","HTTP","OBJECT","TYPE","CHECK",NULL}; static int known_command(const char *up){ int i; for(i=0;check_commands[i];i++) if(strcmp(up,check_commands[i])==0) return 1; return 0; } static int levenshtein(const char *a,const char *b){ int la=(int)strlen(a), lb=(int)strlen(b), i,j; int *dp=(int*)xmalloc(sizeof(int)*(la+1)*(lb+1)); for(i=0;i<=la;i++) dp[i*(lb+1)]=i; for(j=0;j<=lb;j++) dp[j]=j; for(i=1;i<=la;i++) for(j=1;j<=lb;j++){ int c=tolower((unsigned char)a[i-1])==tolower((unsigned char)b[j-1])?0:1; int x=dp[(i-1)*(lb+1)+j]+1, y=dp[i*(lb+1)+j-1]+1, z=dp[(i-1)*(lb+1)+j-1]+c; int m=x<y?x:y; if(z<m)m=z; dp[i*(lb+1)+j]=m; } i=dp[la*(lb+1)+lb]; free(dp); return i; } @@ -475,6 +478,9 @@ static int check_file(const char *path){ const char *as=find_word_ci(t,"AS"); if(as){ char *name=xstrdup(trim_inplace((char*)as+2)); const char *status=find_word_ci(name,"STATUS"); if(status){ char *primary=substr(name,status); check_add_var(&vars,primary); check_add_var(&vars,trim_inplace((char*)status+6)); free(primary); } else check_add_var(&vars,name); free(name); } } + if(!strcmp(up,"TASK")){ + char *name=check_var_after_as(t); if(name){ check_add_var(&vars,name); free(name); } + } if(!strcmp(up,"CALL")){ char *name=check_var_after_as(t); if(name){ check_add_var(&vars,name); free(name); } } @@ -495,7 +501,7 @@ static int check_file(const char *path){ } } fclose(f); - while(sp>0){ CheckBlock b=stack[--sp]; printf("%s:%d: %s block opened here is missing %s\n",path,b.line,b.open,b.expect); errs++; } + while(sp>0){ CheckBlock b=stack[--sp]; printf("%s:%d: %s block opened here needs %s\n",path,b.line,b.open,b.expect); errs++; } if(errs==0) printf("OK\n"); return errs?1:0; } @@ -503,13 +509,16 @@ static int check_file(const char *path){ static const char *type_env_get(Var *types,const char *name){ Var *v=env_find(types,name); if(v&&v->val.type==V_STR) return v->val.str; return NULL; } static void type_env_set(Var **types,const char *name,const char *type){ Var *v; char tmp[256]; char *t; if(!name||!type||!*type) return; strncpy(tmp,name,sizeof(tmp)-1); tmp[sizeof(tmp)-1]=0; t=trim_inplace(tmp); if(!*t) return; v=env_find(*types,t); if(v){ v->val=v_str(type); return; } v=(Var*)xmalloc(sizeof(Var)); memset(v,0,sizeof(Var)); v->name=xstrdup(t); v->val=v_str(type); v->next=*types; *types=v; } static const char *simple_expr_type(Var *types,const char *expr){ char tmp[512]; char *t; double d; size_t i=0; if(!expr) return NULL; strncpy(tmp,expr,sizeof(tmp)-1); tmp[sizeof(tmp)-1]=0; t=trim_inplace(tmp); if(!*t) return NULL; if(*t=='"') return "TEXT"; if(parse_number_strict(t,&d)) return "NUMBER"; if(ci_eq(t,"YES")||ci_eq(t,"NO")||ci_eq(t,"TRUE")||ci_eq(t,"FALSE")) return "YESNO"; if(ci_eq(t,"LIST")) return "LIST"; if(ci_eq(t,"MAP")) return "MAP"; if(starts_ci(t,"NEW ")) return "OBJECT"; while(t[i]&&(isalnum((unsigned char)t[i])||t[i]=='_'||t[i]=='.')) i++; if(i>0 && t[i]==0){ return type_env_get(types,t); } return NULL; } -static int type_words_match(const char *need,const char *got){ if(!need||!*need||ci_eq(need,"ANY")) return 1; if(!got||!*got) return 1; if(ci_eq(need,got)) return 1; if((ci_eq(need,"YESNO")||ci_eq(need,"BOOL")||ci_eq(need,"BOOLEAN")) && (ci_eq(got,"YESNO")||ci_eq(got,"BOOL")||ci_eq(got,"BOOLEAN"))) return 1; if(ci_eq(need,"OBJECT") && ci_eq(got,"MAP")) return 1; return 0; } +static int type_words_match(const char *need,const char *got){ if(!need||!*need||ci_eq(need,"ANY")) return 1; if(!got||!*got) return 1; if(ci_eq(need,got)) return 1; if(starts_ci(need,"LIST OF") && ci_eq(got,"LIST")) return 1; if(starts_ci(need,"MAP OF") && ci_eq(got,"MAP")) return 1; if((ci_eq(need,"YESNO")||ci_eq(need,"BOOL")||ci_eq(need,"BOOLEAN")) && (ci_eq(got,"YESNO")||ci_eq(got,"BOOL")||ci_eq(got,"BOOLEAN"))) return 1; if(ci_eq(need,"OBJECT") && ci_eq(got,"MAP")) return 1; return 0; } +static const char *container_member_type(const char *type,const char *kind){ const char *p; static char buf[128]; if(!type||!kind) return NULL; if(!starts_ci(type,kind)) return NULL; p=type+strlen(kind); while(*p&&isspace((unsigned char)*p)) p++; if(!starts_ci(p,"OF")) return NULL; p+=2; while(*p&&isspace((unsigned char)*p)) p++; if(!*p) return NULL; snprintf(buf,sizeof(buf),"%s",p); return trim_inplace(buf); } static void parse_set_for_typecheck2(const char *t,char **name,char **type,char **expr){ const char *rest=t+3; const char *to=find_word_ci(rest,"TO"); const char *as=find_word_ci(rest,"AS"); *name=NULL; *type=NULL; *expr=NULL; if(as&&to&&as<to){ *name=substr(rest,as); *type=substr(as+2,to); *expr=xstrdup(to+2); return; } if(to){ char *before=substr(rest,to); const char *pcur=before; char *var=unquote_token(&pcur); char *maybe=unquote_token(&pcur); char *mt=trim_inplace(maybe); *name=xstrdup(trim_inplace(var)); if(claro_is_type_word(mt)) *type=xstrdup(mt); *expr=xstrdup(to+2); free(before); free(var); free(maybe); return; } { const char *pcur=rest; char *var=unquote_token(&pcur); const char *after_var=pcur; char *maybe=unquote_token(&pcur); char *mt=trim_inplace(maybe); *name=xstrdup(trim_inplace(var)); if(claro_is_type_word(mt)){ *type=xstrdup(mt); *expr=xstrdup(trim_inplace((char*)pcur)); } else { *expr=xstrdup(trim_inplace((char*)after_var)); } free(var); free(maybe); } } -static int typecheck_file(const char *path){ FILE *f=fopen(path,"rb"); char line[4096]; int line_no=0,errs=0; Var *types=NULL; if(!f){ fprintf(stderr,"Could not open %s\n",path); return 1; } type_env_set(&types,"RESULT","ANY"); type_env_set(&types,"LASTERROR","TEXT"); type_env_set(&types,"LASTERRORFILE","TEXT"); type_env_set(&types,"LASTERRORLINE","NUMBER"); type_env_set(&types,"LASTEXIT","NUMBER"); while(fgets(line,sizeof(line),f)){ char tmp[4096],up[64],w[128]; char *t; line_no++; strncpy(tmp,line,sizeof(tmp)-1); tmp[sizeof(tmp)-1]=0; t=trim_inplace(tmp); if(is_blank_or_comment(t)) continue; first_word(t,w,sizeof(w)); upper_copy(up,w,sizeof(up)); if(!strcmp(up,"SET")){ char *name=NULL,*type=NULL,*expr=NULL; const char *got,*old; parse_set_for_typecheck2(t,&name,&type,&expr); if(name&&*trim_inplace(name)){ char *nt=trim_inplace(name); char *tt=type?trim_inplace(type):NULL; got=simple_expr_type(types,expr); old=type_env_get(types,nt); if(tt&&*tt){ if(got&&!type_words_match(tt,got)){ printf("%s:%d: %s needs %s, but this looks like %s.\n",path,line_no,nt,tt,got); errs++; } type_env_set(&types,nt,tt); } else if(old){ if(got&&!type_words_match(old,got)){ printf("%s:%d: %s was first set as %s, but this looks like %s.\n",path,line_no,nt,old,got); errs++; } } else if(got) type_env_set(&types,nt,got); else type_env_set(&types,nt,"ANY"); } free(name); free(type); free(expr); } +static int typecheck_file(const char *path){ FILE *f=fopen(path,"rb"); char line[4096]; int line_no=0,errs=0; Var *types=NULL; if(!f){ fprintf(stderr,"Could not open %s\n",path); return 1; } type_env_set(&types,"RESULT","ANY"); type_env_set(&types,"LASTERROR","TEXT"); type_env_set(&types,"LASTERRORFILE","TEXT"); type_env_set(&types,"LASTERRORLINE","NUMBER"); type_env_set(&types,"LASTEXIT","NUMBER"); while(fgets(line,sizeof(line),f)){ char tmp[4096],up[64],w[128]; char *t; line_no++; strncpy(tmp,line,sizeof(tmp)-1); tmp[sizeof(tmp)-1]=0; t=trim_inplace(tmp); if(is_blank_or_comment(t)) continue; first_word(t,w,sizeof(w)); upper_copy(up,w,sizeof(up)); if(!strcmp(up,"SET")){ char *name=NULL,*type=NULL,*expr=NULL; const char *got,*old; parse_set_for_typecheck2(t,&name,&type,&expr); if(name&&*trim_inplace(name)){ char *nt=trim_inplace(name); char *tt=type?trim_inplace(type):NULL; got=simple_expr_type(types,expr); old=type_env_get(types,nt); if(tt&&*tt){ if(got&&!type_words_match(tt,got)){ printf("%s:%d: Type mismatch for %s: expected %s, but this value looks like %s.\n",path,line_no,nt,tt,got); errs++; } type_env_set(&types,nt,tt); } else if(old){ if(got&&!type_words_match(old,got)){ printf("%s:%d: Type mismatch for %s: it was first set as %s, but this value looks like %s.\n",path,line_no,nt,old,got); errs++; } } else if(got) type_env_set(&types,nt,got); else type_env_set(&types,nt,"ANY"); } free(name); free(type); free(expr); } + else if(!strcmp(up,"ADD")){ const char *to=find_word_ci(t,"TO"); if(to){ char *expr=substr(t+3,to); char *name=xstrdup(trim_inplace((char*)to+2)); const char *listtype=type_env_get(types,name); const char *need=container_member_type(listtype,"LIST"); const char *got=simple_expr_type(types,expr); if(need&&got&&!type_words_match(need,got)){ printf("%s:%d: Type mismatch for list %s: expected %s item, but this value looks like %s.\n",path,line_no,name,need,got); errs++; } free(expr); free(name); } } + else if(!strcmp(up,"PUT")){ const char *key=find_word_ci(t,"KEY"), *val=find_word_ci(t,"VALUE"); if(key&&val){ char *name=substr(t+3,key); char *expr=xstrdup(trim_inplace((char*)val+5)); char *nt=trim_inplace(name); const char *maptype=type_env_get(types,nt); const char *need=container_member_type(maptype,"MAP"); const char *got=simple_expr_type(types,expr); if(need&&got&&!type_words_match(need,got)){ printf("%s:%d: Type mismatch for map %s: expected %s value, but this value looks like %s.\n",path,line_no,nt,need,got); errs++; } free(name); free(expr); } } else if(!strcmp(up,"ASK")){ const char *as=find_word_ci(t,"AS"); char *name=NULL,*type=NULL; if(as){ const char *pcur=as+2; name=unquote_token(&pcur); { char *rest=xstrdup(trim_inplace((char*)pcur)); if(claro_is_type_word(rest)) type=rest; else free(rest); } } else { const char *pcur=t+3; char *prompt=unquote_token(&pcur); free(prompt); name=unquote_token(&pcur); { char *rest=xstrdup(trim_inplace((char*)pcur)); if(claro_is_type_word(rest)) type=rest; else free(rest); } } if(name&&*trim_inplace(name)) type_env_set(&types,trim_inplace(name),type?type:"TEXT"); free(name); free(type); } else if(!strcmp(up,"TYPE")&&starts_ci(t,"TYPE OF")){ char *name=check_var_after_as(t); if(name){ type_env_set(&types,name,"TEXT"); free(name); } } else if(!strcmp(up,"NEW")){ const char *as=find_word_ci(t,"AS"); char *name=NULL; if(as) name=xstrdup(trim_inplace((char*)as+2)); else { const char *pcur=t+3; char *cls=unquote_token(&pcur); free(cls); name=unquote_token(&pcur); } if(name&&*trim_inplace(name)) type_env_set(&types,trim_inplace(name),"OBJECT"); free(name); } - else if(!strcmp(up,"CHECK")&&starts_ci(t,"CHECK TYPE")){ const char *is=find_word_ci(t,"IS"); if(is){ char *expr=substr(t+10,is); char *need=xstrdup(trim_inplace((char*)is+2)); const char *got=simple_expr_type(types,expr); if(got&&!type_words_match(need,got)){ printf("%s:%d: Type check says this should be %s, but it looks like %s.\n",path,line_no,need,got); errs++; } free(expr); free(need); } else { printf("%s:%d: CHECK TYPE needs IS. Try: CHECK TYPE score IS NUMBER\n",path,line_no); errs++; } } + else if(!strcmp(up,"CHECK")&&starts_ci(t,"CHECK TYPE")){ const char *is=find_word_ci(t,"IS"); if(is){ char *expr=substr(t+10,is); char *need=xstrdup(trim_inplace((char*)is+2)); const char *got=simple_expr_type(types,expr); if(got&&!type_words_match(need,got)){ printf("%s:%d: Type check failed: expected %s, but %s looks like %s.\n",path,line_no,need,trim_inplace(expr),got); errs++; } free(expr); free(need); } else { printf("%s:%d: CHECK TYPE needs IS. Try: CHECK TYPE score IS NUMBER\n",path,line_no); errs++; } } } fclose(f); if(errs==0) printf("Type check OK\n"); return errs?1:0; } static char *read_file_text(const char *path){ FILE *f=fopen(path,"rb"); Str b; int c; if(!f) return NULL; str_init(&b); while((c=fgetc(f))!=EOF) str_ch(&b,(char)c); fclose(f); return str_take(&b); } @@ -518,28 +527,56 @@ static int run_tests(void){ int fails=0; #ifndef _WIN32 DIR *d=opendir("tests"); struct dirent *de; if(!d){ fprintf(stderr,"No tests directory\n"); return 1;} while((de=readdir(d))){ if(strstr(de->d_name,".claro")){ char path[512]; snprintf(path,sizeof(path),"tests/%s",de->d_name); fails+=run_one_test(path); } } closedir(d); #else - const char *basic[]={"tests/01_hello.claro","tests/02_math.claro","tests/03_function.claro",NULL}; int i; for(i=0;basic[i];i++) fails+=run_one_test(basic[i]); + struct _finddata_t fd; intptr_t h=_findfirst("tests/*.claro",&fd); if(h==-1){ fprintf(stderr,"No tests directory\n"); return 1;} do { char path[512]; snprintf(path,sizeof(path),"tests/%s",fd.name); fails+=run_one_test(path); } while(_findnext(h,&fd)==0); _findclose(h); #endif printf("%s: %d failure(s)\n",fails?"FAIL":"PASS",fails); return fails?1:0; } static int write_text_file_simple(const char *path,const char *text){ FILE *f=fopen(path,"wb"); if(!f) return 0; fputs(text,f); fclose(f); return 1; } -static int create_new_project(const char *name){ char path[512]; char text[1024]; if(!name||!*name){ fprintf(stderr,"Project needs a name. Try: claro new MyProject\n"); return 1; } if(!make_folder(name)){ fprintf(stderr,"Could not create project folder: %s\n",name); return 1; } snprintf(path,sizeof(path),"%s/main.claro",name); write_text_file_simple(path,"SAY \"Welcome to Claro!\"\n\nSET name TO \"Learner\"\nSAY \"Hello \" + name\n\nTEACH greet person\n SAY \"Nice to meet you, \" + person\nEND\n\nDO greet \"Friend\"\n"); snprintf(path,sizeof(path),"%s/packages",name); make_folder(path); snprintf(path,sizeof(path),"%s/claro.project",name); snprintf(text,sizeof(text),"name: %s\nmain: main.claro\nversion: v1.17.26\npackages:\n",name); write_text_file_simple(path,text); snprintf(path,sizeof(path),"%s/claro.lock",name); write_text_file_simple(path,"# Claro package lock\nversion: v1.17.26\n"); snprintf(path,sizeof(path),"%s/README.md",name); write_text_file_simple(path,"# My Claro Project\n\nBuild Claro, then run this project with:\n\n```bash\nclaro run\n```\n\nUse packages with:\n\n```bash\nclaro package add text\nclaro package list\nclaro package doctor\n```\n"); printf("Created Claro project: %s\n",name); printf("Next steps:\n cd %s\n claro run\n",name); return 0; } +static int create_new_project(const char *name){ char path[512]; char text[1024]; if(!name||!*name){ fprintf(stderr,"Project needs a name. Try: claro new MyProject\n"); return 1; } if(!make_folder(name)){ fprintf(stderr,"Could not create project folder: %s\n",name); return 1; } snprintf(path,sizeof(path),"%s/main.claro",name); write_text_file_simple(path,"SAY \"Welcome to Claro!\"\n\nSET name TO \"Learner\"\nSAY \"Hello \" + name\n\nTEACH greet person\n SAY \"Nice to meet you, \" + person\nEND\n\nDO greet \"Friend\"\n"); snprintf(path,sizeof(path),"%s/packages",name); make_folder(path); snprintf(path,sizeof(path),"%s/claro.project",name); snprintf(text,sizeof(text),"name: %s\nmain: main.claro\nversion: v1.18.26\npackages:\n",name); write_text_file_simple(path,text); snprintf(path,sizeof(path),"%s/claro.lock",name); write_text_file_simple(path,"# Claro package lock\nversion: v1.18.26\n"); snprintf(path,sizeof(path),"%s/README.md",name); write_text_file_simple(path,"# My Claro Project\n\nBuild Claro, then run this project with:\n\n```bash\nclaro run\n```\n\nUse packages with:\n\n```bash\nclaro package add text\nclaro package list\nclaro package doctor\n```\n"); printf("Created Claro project: %s\n",name); printf("Next steps:\n cd %s\n claro run\n",name); return 0; } static int run_repl(void){ Runtime rt; char line[4096]; Program p; rt_init(&rt); memset(&p,0,sizeof(p)); p.path=xstrdup("<repl>"); p.count=1; p.cap=1; p.lines=(char**)xmalloc(sizeof(char*)); printf("%s\n",CLARO_VERSION); printf("Type HELP for help, or EXIT to leave.\n"); while(1){ int pc=0; printf("> "); fflush(stdout); if(!fgets(line,sizeof(line),stdin)) break; { size_t n=strlen(line); while(n&&(line[n-1]=='\n'||line[n-1]=='\r')) line[--n]=0; } { char *t=trim_inplace(line); if(ci_eq(t,"EXIT")||ci_eq(t,"QUIT")) break; if(ci_eq(t,"HELP")){ printf("Try: SAY \"Hello\"\n"); printf("Try: SET name TO \"Jon\"\n"); printf("Try: SAY name\n"); continue; } if(!*t) continue; p.lines[0]=t; exec_line(&rt,&p,&pc,t); if(rt.error){ fprintf(stderr,"%s:%d: %s\n",rt.err_file?rt.err_file:"<repl>",rt.err_line,rt.err_msg?rt.err_msg:"error"); rt_clear_error(&rt); } } } return 0; } static int file_exists_simple(const char *path); static int path_exists_simple(const char *path){ struct stat st; return path&&*path&&stat(path,&st)==0; } static int package_name_safe(const char *name){ int i; if(!name||!*name) return 0; if(name[0]=='.'||name[0]=='-'||name[0]=='_') return 0; for(i=0;name[i];i++){ unsigned char c=(unsigned char)name[i]; if(!(isalnum(c)||c=='_'||c=='-')) return 0; } return 1; } -static void ensure_project_file(void){ if(!file_exists_simple("claro.project")) write_text_file_simple("claro.project","name: ClaroProject\nmain: main.claro\nversion: v1.17.26\npackages:\n"); } +static void package_checksum(const char *name,char *out,size_t n){ unsigned long h=5381; const char *p=name?name:""; while(*p){ h=((h<<5)+h)+(unsigned char)(*p++); } p="|local|1|claro"; while(*p){ h=((h<<5)+h)+(unsigned char)(*p++); } snprintf(out,n,"%08lx",h); } +static void ensure_project_file(void){ if(!file_exists_simple("claro.project")) write_text_file_simple("claro.project","manifest-version: 1\nname: ClaroProject\nmain: main.claro\nversion: v1.18.26\npackages:\n"); } static char *project_value(const char *key){ char *txt=read_file_text("claro.project"); size_t kn=strlen(key); char *p,*out=NULL; if(!txt) return NULL; p=txt; while(*p){ char *line=p; char save; while(*p&&*p!='\n'&&*p!='\r') p++; save=*p; *p=0; { char *t=trim_inplace(line); if(strnicmp2(t,key,kn)==0 && t[kn]==':'){ out=xstrdup(trim_inplace(t+kn+1)); break; } } if(save) p++; while(*p=='\n'||*p=='\r') p++; } free(txt); return out; } static int package_has_name(const char *name){ char *txt=read_file_text("claro.project"); char *p; int found=0; if(!txt) return 0; p=txt; while(*p){ char *line=p; char save; while(*p&&*p!='\n'&&*p!='\r') p++; save=*p; *p=0; { char *t=trim_inplace(line); if(strnicmp2(t,"package:",8)==0){ char *pkg=trim_inplace(t+8); if(strcmp(pkg,name)==0){ found=1; break; } } } if(save) p++; while(*p=='\n'||*p=='\r') p++; } free(txt); return found; } -static int write_package_lock(void){ char *txt=read_file_text("claro.project"); FILE *f; char *p; if(!txt) return 0; f=fopen("claro.lock","wb"); if(!f){ free(txt); return 0; } fputs("# Claro package lock\nversion: v1.17.26\n",f); p=txt; while(*p){ char *line=p; char save; while(*p&&*p!='\n'&&*p!='\r') p++; save=*p; *p=0; { char *t=trim_inplace(line); if(strnicmp2(t,"package:",8)==0){ char *pkg=trim_inplace(t+8); if(*pkg) fprintf(f,"package: %s\n",pkg); } } if(save) p++; while(*p=='\n'||*p=='\r') p++; } fclose(f); free(txt); return 1; } +static int write_package_lock(void){ char *txt=read_file_text("claro.project"); FILE *f; char *p; if(!txt) return 0; f=fopen("claro.lock","wb"); if(!f){ free(txt); return 0; } fputs("# Claro package lock\nlock-version: 1\nversion: v1.18.26\n",f); p=txt; while(*p){ char *line=p; char save; while(*p&&*p!='\n'&&*p!='\r') p++; save=*p; *p=0; { char *t=trim_inplace(line); if(strnicmp2(t,"package:",8)==0){ char *pkg=trim_inplace(t+8); if(*pkg){ char sum[32]; package_checksum(pkg,sum,sizeof(sum)); fprintf(f,"package: %s\nchecksum: %s\n",pkg,sum); } } } if(save) p++; while(*p=='\n'||*p=='\r') p++; } fclose(f); free(txt); return 1; } static int append_package_to_project(const char *name){ FILE *f; if(!package_name_safe(name)){ fprintf(stderr,"Package names may use only letters, numbers, dash, and underscore.\n"); return 0; } ensure_project_file(); if(package_has_name(name)){ printf("Package already listed: %s\n",name); return 1; } f=fopen("claro.project","ab"); if(!f) return 0; fprintf(f,"package: %s\n",name); fclose(f); write_package_lock(); return 1; } static int remove_package_from_project(const char *name){ char *txt=read_file_text("claro.project"); FILE *f; char *p; int removed=0; if(!txt) return 0; f=fopen("claro.project","wb"); if(!f){ free(txt); return 0; } p=txt; while(*p){ char *line=p; char save; int skip=0; while(*p&&*p!='\n'&&*p!='\r') p++; save=*p; *p=0; { char *copy=xstrdup(line); char *t=trim_inplace(copy); if(strnicmp2(t,"package:",8)==0){ char *pkg=trim_inplace(t+8); if(strcmp(pkg,name)==0){ skip=1; removed=1; } } free(copy); } if(!skip) fprintf(f,"%s\n",line); if(save) p++; while(*p=='\n'||*p=='\r') p++; } fclose(f); free(txt); write_package_lock(); return removed; } -static void create_package_folder(const char *name){ char path[512], meta[512], readme[512]; make_folder("packages"); snprintf(path,sizeof(path),"packages/%s",name); make_folder(path); snprintf(meta,sizeof(meta),"%s/claro.package",path); if(!file_exists_simple(meta)){ char text[512]; snprintf(text,sizeof(text),"name: %s\nversion: 1\n",name); write_text_file_simple(meta,text); } snprintf(readme,sizeof(readme),"%s/README.md",path); if(!file_exists_simple(readme)){ char text[512]; snprintf(text,sizeof(text),"# %s\n\nThis is a local Claro package folder.\n",name); write_text_file_simple(readme,text); } } +static void create_package_folder(const char *name){ char path[512], meta[512], readme[512], sum[32]; make_folder("packages"); snprintf(path,sizeof(path),"packages/%s",name); make_folder(path); snprintf(meta,sizeof(meta),"%s/claro.package",path); package_checksum(name,sum,sizeof(sum)); { char text[1024]; snprintf(text,sizeof(text),"manifest-version: 1\nname: %s\nversion: 1\nsource: local\nchecksum: %s\n",name,sum); write_text_file_simple(meta,text); } snprintf(readme,sizeof(readme),"%s/README.md",path); if(!file_exists_simple(readme)){ char text[512]; snprintf(text,sizeof(text),"# %s\n\nThis is a local Claro package folder.\n",name); write_text_file_simple(readme,text); } } static int list_project_packages(void){ char *txt=read_file_text("claro.project"); char *p; int count=0; printf("Packages in claro.project:\n"); if(!txt){ printf(" (no claro.project yet)\n"); return 0; } p=txt; while(*p){ char *line=p; char save; while(*p&&*p!='\n'&&*p!='\r') p++; save=*p; *p=0; { char *t=trim_inplace(line); if(strnicmp2(t,"package:",8)==0){ char *pkg=trim_inplace(t+8); if(*pkg){ printf(" %s\n",pkg); count++; } } } if(save) p++; while(*p=='\n'||*p=='\r') p++; } if(!count) printf(" (none)\n"); free(txt); return 0; } -static int package_doctor(void){ int ok=1; char *mainfile; printf("%s\n",CLARO_VERSION); printf("Package/project doctor:\n"); printf(" %s claro.project\n",file_exists_simple("claro.project")?"OK":"MISSING"); if(!file_exists_simple("claro.project")) ok=0; mainfile=project_value("main"); if(mainfile&&*mainfile){ printf(" %s main script: %s\n",file_exists_simple(mainfile)?"OK":"MISSING",mainfile); if(!file_exists_simple(mainfile)) ok=0; } else { printf(" MISSING main setting\n"); ok=0; } printf(" %s packages folder\n",path_exists_simple("packages")?"OK":"MISSING"); if(!path_exists_simple("packages")) ok=0; printf(" %s claro.lock\n",file_exists_simple("claro.lock")?"OK":"MISSING"); if(!file_exists_simple("claro.lock")) ok=0; free(mainfile); printf("%s\n",ok?"Package/project files look ready.":"Package/project files need attention."); return ok?0:1; } -static int run_package_cmd(int argc,char **argv,int arg){ if(arg>=argc){ printf("Package commands:\n claro package init\n claro package add NAME\n claro package remove NAME\n claro package list\n claro package doctor\n claro package lock\n"); return 0; } if(strcmp(argv[arg],"init")==0){ ensure_project_file(); make_folder("packages"); write_package_lock(); printf("Package project ready: claro.project, claro.lock, packages/\n"); return 0; } if(strcmp(argv[arg],"add")==0&&arg+1<argc){ const char *name=argv[arg+1]; int already; if(!package_name_safe(name)){ fprintf(stderr,"Package names may use only letters, numbers, dash, and underscore.\n"); return 1; } make_folder("packages"); already=package_has_name(name); if(!append_package_to_project(name)) return 1; create_package_folder(name); if(!already) printf("Added package: %s\n",name); return 0; } if(strcmp(argv[arg],"remove")==0&&arg+1<argc){ const char *name=argv[arg+1]; if(!package_name_safe(name)){ fprintf(stderr,"Package names may use only letters, numbers, dash, and underscore.\n"); return 1; } if(remove_package_from_project(name)) printf("Removed package from project: %s\n",name); else printf("Package was not listed: %s\n",name); return 0; } if(strcmp(argv[arg],"list")==0) return list_project_packages(); if(strcmp(argv[arg],"doctor")==0) return package_doctor(); if(strcmp(argv[arg],"lock")==0||strcmp(argv[arg],"update")==0){ ensure_project_file(); make_folder("packages"); if(write_package_lock()){ printf("Updated claro.lock\n"); return 0; } fprintf(stderr,"Could not write claro.lock\n"); return 1; } printf("Unknown package command. Try: claro package init\n"); return 1; } -static int print_ide_info(void){ printf("{\n"); printf(" \"language\": \"Claro\",\n"); printf(" \"version\": \"v1.17.26\",\n"); printf(" \"keywords\": [\"SAY\",\"ASK\",\"SET\",\"IF\",\"ELSE\",\"END\",\"REPEAT\",\"TEACH\",\"DO\",\"CLASS\",\"NEW\",\"START TASK\",\"HTTP GET\",\"OPEN WINDOW\"],\n"); printf(" \"types\": [\"NUMBER\",\"TEXT\",\"YESNO\",\"LIST\",\"MAP\",\"OBJECT\",\"ANY\"]\n"); printf("}\n"); return 0; } +static int package_doctor(void){ int ok=1; char *mainfile; char *txt; printf("%s\n",CLARO_VERSION); printf("Package/project doctor:\n"); printf(" %s claro.project\n",file_exists_simple("claro.project")?"OK":"MISSING"); if(!file_exists_simple("claro.project")) ok=0; mainfile=project_value("main"); if(mainfile&&*mainfile){ printf(" %s main script: %s\n",file_exists_simple(mainfile)?"OK":"MISSING",mainfile); if(!file_exists_simple(mainfile)) ok=0; } else { printf(" MISSING main setting\n"); ok=0; } printf(" %s packages folder\n",path_exists_simple("packages")?"OK":"MISSING"); if(!path_exists_simple("packages")) ok=0; printf(" %s claro.lock\n",file_exists_simple("claro.lock")?"OK":"MISSING"); if(!file_exists_simple("claro.lock")) ok=0; txt=read_file_text("claro.project"); if(txt){ char *p=txt; while(*p){ char *line=p; char save; while(*p&&*p!='\n'&&*p!='\r') p++; save=*p; *p=0; { char *t=trim_inplace(line); if(strnicmp2(t,"package:",8)==0){ char *pkg=trim_inplace(t+8); if(*pkg){ char meta[512], *mt, sum[32]; snprintf(meta,sizeof(meta),"packages/%s/claro.package",pkg); mt=read_file_text(meta); if(mt&&strstr(mt,"manifest-version: 1")&&strstr(mt,"checksum:")){ printf(" OK package manifest: %s\n",pkg); package_checksum(pkg,sum,sizeof(sum)); if(strstr(mt,sum)) printf(" OK package checksum: %s\n",pkg); else { printf(" BAD package checksum: %s\n",pkg); ok=0; } } else { printf(" MISSING package manifest: %s\n",pkg); ok=0; } free(mt); } } } if(save) p++; while(*p=='\n'||*p=='\r') p++; } free(txt); } free(mainfile); printf("%s\n",ok?"Package/project files look ready.":"Package/project files need attention."); return ok?0:1; } +static int run_package_cmd(int argc,char **argv,int arg){ if(arg>=argc){ printf("Package commands:\n claro package init\n claro package add NAME\n claro package remove NAME\n claro package list\n claro package doctor\n claro package lock\n"); return 0; } if(strcmp(argv[arg],"init")==0){ ensure_project_file(); make_folder("packages"); write_package_lock(); printf("Package project ready: claro.project, claro.lock, packages/\n"); return 0; } if(strcmp(argv[arg],"add")==0&&arg+1<argc){ const char *name=argv[arg+1]; int already; if(!package_name_safe(name)){ fprintf(stderr,"Package names may use only letters, numbers, dash, and underscore.\n"); return 1; } ensure_project_file(); make_folder("packages"); already=package_has_name(name); if(!append_package_to_project(name)) return 1; create_package_folder(name); write_package_lock(); if(!already) printf("Added package: %s\n",name); return 0; } if(strcmp(argv[arg],"remove")==0&&arg+1<argc){ const char *name=argv[arg+1]; if(!package_name_safe(name)){ fprintf(stderr,"Package names may use only letters, numbers, dash, and underscore.\n"); return 1; } if(remove_package_from_project(name)) printf("Removed package from project: %s\n",name); else printf("Package was not listed: %s\n",name); return 0; } if(strcmp(argv[arg],"list")==0) return list_project_packages(); if(strcmp(argv[arg],"doctor")==0) return package_doctor(); if(strcmp(argv[arg],"lock")==0||strcmp(argv[arg],"update")==0){ ensure_project_file(); make_folder("packages"); if(write_package_lock()){ printf("Updated claro.lock\n"); return 0; } fprintf(stderr,"Could not write claro.lock\n"); return 1; } printf("Unknown package command. Try: claro package init\n"); return 1; } +static int print_ide_info(void){ + printf("{\n"); + printf(" \"language\": \"Claro\",\n"); + printf(" \"version\": \"v1.18.26\",\n"); + printf(" \"file_extensions\": [\".claro\"],\n"); + printf(" \"keywords\": [\"SAY\",\"ASK\",\"SET\",\"IF\",\"ELSE\",\"ENDIF\",\"END\",\"REPEAT\",\"UNTIL\",\"TEACH\",\"LEARNED\",\"DO\",\"RETURN\",\"CLASS\",\"HAS\",\"NEW\",\"IMPORT\",\"TRY\",\"CATCH\",\"ENDTRY\",\"START TASK\",\"YIELD\",\"WAIT TASK\",\"TASK STATUS\",\"HTTP CHECK\",\"HTTP GET\",\"HTTP SAVE\",\"OPEN WINDOW\"],\n"); + printf(" \"types\": [\"NUMBER\",\"TEXT\",\"YESNO\",\"LIST\",\"LIST OF TEXT\",\"LIST OF NUMBER\",\"LIST OF YESNO\",\"MAP\",\"MAP OF TEXT\",\"MAP OF NUMBER\",\"MAP OF YESNO\",\"OBJECT\",\"ANY\"],\n"); + printf(" \"commands\": [\n"); + printf(" {\"name\": \"run\", \"usage\": \"claro run\", \"description\": \"Run main.claro or the project main script\"},\n"); + printf(" {\"name\": \"check\", \"usage\": \"claro check file.claro\", \"description\": \"Check beginner syntax and block mistakes\"},\n"); + printf(" {\"name\": \"typecheck\", \"usage\": \"claro typecheck file.claro\", \"description\": \"Check static types before running\"},\n"); + printf(" {\"name\": \"fmt\", \"usage\": \"claro fmt file.claro\", \"description\": \"Print a formatted version of a script\"},\n"); + printf(" {\"name\": \"test\", \"usage\": \"claro test\", \"description\": \"Run golden tests\"}\n"); + printf(" ],\n"); + printf(" \"snippets\": [\n"); + printf(" {\"name\": \"hello\", \"prefix\": \"say\", \"body\": \"SAY \\\"Hello!\\\"\"},\n"); + printf(" {\"name\": \"if_else\", \"prefix\": \"if\", \"body\": \"IF condition\\n SAY \\\"Yes\\\"\\nELSE\\n SAY \\\"No\\\"\\nENDIF\"},\n"); + printf(" {\"name\": \"function\", \"prefix\": \"teach\", \"body\": \"TEACH name item\\n SAY item\\nLEARNED\"},\n"); + printf(" {\"name\": \"typed_list\", \"prefix\": \"listof\", \"body\": \"SET names AS LIST OF TEXT TO LIST\\nADD \\\"Ada\\\" TO names\"},\n"); + printf(" {\"name\": \"task\", \"prefix\": \"task\", \"body\": \"START TASK helloTask\\n SAY \\\"Inside task\\\"\\n YIELD\\nENDTASK\\nTASK STATUS helloTask AS status\\nSAY status\\nWAIT TASK helloTask\"}\n"); + printf(" ],\n"); + printf(" \"diagnostics\": [\n"); + printf(" {\"source\": \"claro check\", \"format\": \"path:line: message\", \"description\": \"Syntax and block-structure diagnostics\"},\n"); + printf(" {\"source\": \"claro typecheck\", \"format\": \"path:line: message\", \"description\": \"Static type diagnostics for variables and containers\"}\n"); + printf(" ]\n"); + printf("}\n"); + return 0; +} static void print_help(void){ printf("%s\n",CLARO_VERSION); printf("Usage:\n"); @@ -560,6 +597,6 @@ static void print_help(void){ static int show_examples(void){ const char *items[]={"hello.claro","name_input.claro","quiz.claro","calculator.claro","guessing_game.claro","shopping_list.claro","save_and_load_file.claro","simple_functions.claro","text_and_lists.claro","text_polish.claro","practical_scripting.claro","type_hardening.claro","typed_ask.claro","objects_classes.claro","networking.claro",NULL}; int i; printf("Included beginner examples:\n"); for(i=0;items[i];i++) printf(" examples/%s\n",items[i]); printf("\nRun one with: claro examples/hello.claro\n"); return 0; } static int file_exists_simple(const char *path){ FILE *f=fopen(path,"rb"); if(f){ fclose(f); return 1; } return 0; } static int run_doctor(void){ int ok=1; const char *files[]={"src/claro.c","README.md","assets/Claro_Logo.jpg","lessons/01_hello.claro","examples/hello.claro","tests/01_hello.claro",NULL}; int i; printf("%s\n",CLARO_VERSION); printf("Doctor check:\n"); for(i=0;files[i];i++){ int has=file_exists_simple(files[i]); printf(" %s %s\n",has?"OK":"MISSING",files[i]); if(!has) ok=0; } printf("%s\n",ok?"Claro folder looks ready.":"Some Claro files are missing."); return ok?0:1; } -static int run_validate(void){ int fails=0, i; const char *checks[]={"lessons/01_hello.claro","lessons/02_ask_name.claro","lessons/03_variables.claro","lessons/04_math.claro","lessons/05_if_else.claro","lessons/06_loops.claro","lessons/07_lists.claro","lessons/08_functions.claro","lessons/09_files.claro","lessons/10_final_quiz.claro","examples/hello.claro","examples/name_input.claro","examples/quiz.claro","examples/calculator.claro","examples/guessing_game.claro","examples/shopping_list.claro","examples/save_and_load_file.claro","examples/simple_functions.claro","examples/text_and_lists.claro","examples/text_polish.claro","examples/practical_scripting.claro","examples/type_hardening.claro","examples/objects_classes.claro","examples/networking.claro",NULL}; printf("%s\n",CLARO_VERSION); printf("Stable package validation:\n"); fails+=run_doctor(); fails+=run_tests(); printf("Checking lessons and main examples:\n"); for(i=0;checks[i];i++){ printf(" %s\n",checks[i]); fails+=check_file(checks[i]); } fails+=typecheck_file("tests/typecheck_good.claro"); if(typecheck_file("tests/typecheck_bad.claro")==0) fails++; printf("%s\n",fails?"Validation found problems.":"Validation passed. Claro v1.17.26 networking reliability is ready for use."); return fails?1:0; } +static int run_validate(void){ int fails=0, i; const char *checks[]={"lessons/01_hello.claro","lessons/02_ask_name.claro","lessons/03_variables.claro","lessons/04_math.claro","lessons/05_if_else.claro","lessons/06_loops.claro","lessons/07_lists.claro","lessons/08_functions.claro","lessons/09_files.claro","lessons/10_final_quiz.claro","examples/hello.claro","examples/name_input.claro","examples/quiz.claro","examples/calculator.claro","examples/guessing_game.claro","examples/shopping_list.claro","examples/save_and_load_file.claro","examples/simple_functions.claro","examples/text_and_lists.claro","examples/text_polish.claro","examples/practical_scripting.claro","examples/type_hardening.claro","examples/objects_classes.claro","examples/networking.claro",NULL}; printf("%s\n",CLARO_VERSION); printf("Stable package validation:\n"); fails+=run_doctor(); fails+=run_tests(); printf("Checking lessons and main examples:\n"); for(i=0;checks[i];i++){ printf(" %s\n",checks[i]); fails+=check_file(checks[i]); } fails+=typecheck_file("tests/typecheck_good.claro"); fails+=typecheck_file("tests/typecheck_container_good.claro"); if(typecheck_file("tests/typecheck_bad.claro")==0) fails++; if(typecheck_file("tests/typecheck_container_bad.claro")==0) fails++; printf("%s\n",fails?"Validation found problems.":"Validation passed. Claro v1.18.26 foundation checks are ready for use."); return fails?1:0; } int main(int argc,char **argv){ int arg=1, trace=0; if(argc<2){ print_help(); return 0;} if(strcmp(argv[arg],"--trace")==0||strcmp(argv[arg],"trace")==0){ trace=1; arg++; } if(arg>=argc) return 0; if(strcmp(argv[arg],"help")==0||strcmp(argv[arg],"--help")==0){ print_help(); return 0; } if(strcmp(argv[arg],"test")==0) return run_tests(); if(strcmp(argv[arg],"repl")==0) return run_repl(); if(strcmp(argv[arg],"examples")==0) return show_examples(); if(strcmp(argv[arg],"doctor")==0) return run_doctor(); if(strcmp(argv[arg],"validate")==0) return run_validate(); if(strcmp(argv[arg],"package")==0) return run_package_cmd(argc,argv,arg+1); if(strcmp(argv[arg],"ide")==0) return print_ide_info(); if(strcmp(argv[arg],"new")==0&&arg+1<argc) return create_new_project(argv[arg+1]); if(strcmp(argv[arg],"fmt")==0&&arg+1<argc) return fmt_file(argv[arg+1]); if(strcmp(argv[arg],"check")==0&&arg+1<argc) return check_file(argv[arg+1]); if(strcmp(argv[arg],"typecheck")==0&&arg+1<argc) return typecheck_file(argv[arg+1]); if(strcmp(argv[arg],"version")==0||strcmp(argv[arg],"--version")==0){ puts(CLARO_VERSION); return 0; } if(strcmp(argv[arg],"run")==0){ arg++; if(arg>=argc){ Runtime rt; int rc; char *mainfile=project_value("main"); if(!mainfile||!*mainfile){ if(mainfile) free(mainfile); mainfile=xstrdup("main.claro"); } rt_init(&rt); rt.trace=trace; rt.script_argc=0; rt.script_argv=NULL; rc=run_file(&rt,mainfile); free(mainfile); return rc; } } { Runtime rt; int rc; rt_init(&rt); rt.trace=trace; rt.script_argc=argc-arg-1; rt.script_argv=argv+arg+1; rc=run_file(&rt,argv[arg]); return rc; } } diff --git a/support/lsp/claro_lsp_stub.py b/support/lsp/claro_lsp_stub.py index dbfe9c5..59b7c0f 100644 --- a/support/lsp/claro_lsp_stub.py +++ b/support/lsp/claro_lsp_stub.py @@ -1,13 +1,122 @@ #!/usr/bin/env python3 -"""Tiny Claro IDE helper stub for v1.17.26. -It prints completion words. A real Language Server Protocol service can build on this. +"""Small Claro editor/LSP helper. + +This is not a full JSON-RPC Language Server yet. It is a stable helper that +editor plugins can call to get metadata, completions, and diagnostics without +re-implementing Claro parsing. """ +from __future__ import annotations + +import json +import os +import re +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro") + KEYWORDS = [ - "SAY", "ASK", "SET", "IF", "ELSE", "END", "REPEAT", "TEACH", "DO", - "CLASS", "NEW", "START TASK", "WAIT TASK", "HTTP GET", "OPEN WINDOW", - "CLEAR SCREEN", "DRAW TEXT", "UPDATE SCREEN", "CLOSE WINDOW", - "TYPE OF", "CHECK TYPE", + "SAY", "ASK", "SET", "IF", "ELSE", "ENDIF", "END", "REPEAT", "UNTIL", + "TEACH", "LEARNED", "DO", "RETURN", "CLASS", "HAS", "NEW", "IMPORT", + "TRY", "CATCH", "ENDTRY", "START TASK", "WAIT TASK", "HTTP CHECK", + "HTTP GET", "HTTP SAVE", "OPEN WINDOW", "CLEAR SCREEN", "DRAW TEXT", + "UPDATE SCREEN", "CLOSE WINDOW", "TYPE OF", "CHECK TYPE", ] -TYPES = ["NUMBER", "TEXT", "YESNO", "LIST", "MAP", "OBJECT", "ANY"] -for word in KEYWORDS + TYPES: - print(word) +TYPES = [ + "NUMBER", "TEXT", "YESNO", "LIST", "LIST OF TEXT", "LIST OF NUMBER", + "LIST OF YESNO", "MAP", "MAP OF TEXT", "MAP OF NUMBER", "MAP OF YESNO", + "OBJECT", "ANY", +] +COMMANDS = [ + {"name": "check", "usage": "claro check file.claro"}, + {"name": "typecheck", "usage": "claro typecheck file.claro"}, + {"name": "fmt", "usage": "claro fmt file.claro"}, +] + +DIAG_RE = re.compile(r"^(?P<file>.*?):(?P<line>\d+):\s*(?P<message>.*)$") + + +def print_json(value: object) -> int: + print(json.dumps(value, indent=2)) + return 0 + + +def metadata() -> dict: + return { + "language": "Claro", + "version": "v1.18.26", + "file_extensions": [".claro"], + "keywords": KEYWORDS, + "types": TYPES, + "commands": COMMANDS, + } + + +def completions() -> dict: + return {"keywords": KEYWORDS, "types": TYPES} + + +def parse_diagnostics(output: str, source: str) -> list[dict]: + diagnostics = [] + for raw in output.splitlines(): + line = raw.strip() + if not line or line == "OK" or line == "Type check OK": + continue + match = DIAG_RE.match(line) + if not match: + continue + diagnostics.append( + { + "source": source, + "file": match.group("file").replace("\\", "/"), + "line": int(match.group("line")), + "severity": "error", + "message": match.group("message"), + } + ) + return diagnostics + + +def run_claro_command(command: str, path: str) -> list[dict]: + result = subprocess.run( + [str(EXE), command, path], + cwd=ROOT, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + return parse_diagnostics(result.stdout, f"claro {command}") + + +def diagnostics(path: str) -> list[dict]: + found = [] + found.extend(run_claro_command("check", path)) + found.extend(run_claro_command("typecheck", path)) + return found + + +def usage() -> int: + print("Usage:") + print(" claro_lsp_stub.py metadata") + print(" claro_lsp_stub.py completions") + print(" claro_lsp_stub.py diagnostics file.claro") + return 0 + + +def main(argv: list[str]) -> int: + if len(argv) < 2: + return usage() + cmd = argv[1] + if cmd == "metadata": + return print_json(metadata()) + if cmd == "completions": + return print_json(completions()) + if cmd == "diagnostics" and len(argv) >= 3: + return print_json(diagnostics(argv[2])) + return usage() + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv)) diff --git a/support/vscode/claro.tmLanguage.json b/support/vscode/claro.tmLanguage.json new file mode 100644 index 0000000..3678491 --- /dev/null +++ b/support/vscode/claro.tmLanguage.json @@ -0,0 +1,9 @@ +{ + "scopeName": "source.claro", + "name": "Claro", + "patterns": [ + { "name": "keyword.control.claro", "match": "\\b(SAY|ASK|SET|IF|ELSE|END|REPEAT|TEACH|DO|CLASS|NEW|START|TASK|HTTP|OPEN|WINDOW|DRAW|UPDATE|CLOSE|TYPE|CHECK|IS)\\b" }, + { "name": "storage.type.claro", "match": "\\b(NUMBER|TEXT|YESNO|LIST|MAP|OBJECT|ANY)\\b" }, + { "name": "string.quoted.double.claro", "begin": "\"", "end": "\"" } + ] +} diff --git a/tests/40_tasks_status.claro b/tests/40_tasks_status.claro new file mode 100644 index 0000000..b78e9dc --- /dev/null +++ b/tests/40_tasks_status.claro @@ -0,0 +1,9 @@ +SAY "Task status demo" +START TASK helloTask + SAY "Inside task" + YIELD +ENDTASK +TASK STATUS helloTask AS status +SAY status +WAIT TASK helloTask +SAY "After wait" diff --git a/tests/basic_test.claro b/tests/basic_test.claro deleted file mode 100644 index d517e6d..0000000 --- a/tests/basic_test.claro +++ /dev/null @@ -1 +0,0 @@ -SAY "Test OK" \ No newline at end of file diff --git a/tests/typecheck_container_bad.claro b/tests/typecheck_container_bad.claro new file mode 100644 index 0000000..17b48c6 --- /dev/null +++ b/tests/typecheck_container_bad.claro @@ -0,0 +1,6 @@ +SET names AS LIST OF TEXT TO LIST +ADD "Ada" TO names +ADD 123 TO names +SET scores AS MAP OF NUMBER TO MAP +PUT scores KEY "math" VALUE 98 +PUT scores KEY "oops" VALUE "high" diff --git a/tests/typecheck_container_good.claro b/tests/typecheck_container_good.claro new file mode 100644 index 0000000..635e048 --- /dev/null +++ b/tests/typecheck_container_good.claro @@ -0,0 +1,6 @@ +SET names AS LIST OF TEXT TO LIST +ADD "Ada" TO names +ADD "Grace" TO names +SET scores AS MAP OF NUMBER TO MAP +PUT scores KEY "math" VALUE 98 +PUT scores KEY "science" VALUE 99 diff --git a/tools/check.py b/tools/check.py new file mode 100644 index 0000000..e84d78e --- /dev/null +++ b/tools/check.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +"""Claro checker/linter (gentle).""" +import sys, pathlib + +OPEN = {"IF":"ENDIF", "DO":"DONE", "REPEAT":"UNTIL", "TEACH":"LEARNED", "TRY":"ENDTRY"} +KNOWN = set(list(OPEN.keys()) + list(OPEN.values()) + ["ELSE","CATCH", + "SAY","ASK","SET","CALL","RETURN","WAIT","HELP", + "IMPORT","EXPORT", + "ADD","GET","PUT","COUNT","HAS","KEYS", + "READ","WRITE","APPEND","EXISTS","DELETE","FILE", + "PARSE","MAKE","JSON", + "WITH","TO","AS","INTO","AT","KEY","TIMES","STOP" +]) + +def main(): + if len(sys.argv) < 2: + print("Usage: python tools/check.py <file.claro>") + return 2 + p = pathlib.Path(sys.argv[1]) + lines = p.read_text(encoding="utf-8", errors="replace").splitlines() + stack = [] + bad = 0 + for i, line in enumerate(lines, start=1): + if "\t" in line: + print(f"Line {i}: contains a TAB. Use spaces.") + bad += 1 + stripped = line.strip() + if not stripped or stripped.startswith("#"): + continue + head = stripped.split()[0].upper() + if head not in KNOWN: + print(f"Line {i}: warning: unknown keyword '{head}'.") + if head in OPEN: + stack.append((head, i)) + elif head in OPEN.values(): + if not stack: + print(f"Line {i}: '{head}' without opener.") + bad += 1 + else: + op, oi = stack.pop() + exp = OPEN[op] + if head != exp: + print(f"Line {i}: '{head}' closes '{op}' from line {oi}, expected '{exp}'.") + bad += 1 + if stack: + for op, oi in stack: + print(f"Line {oi}: '{op}' not closed (expected {OPEN[op]}).") + bad += len(stack) + if bad: + print(f"\nFound {bad} issue(s).") + return 1 + print("No problems found.") + return 0 + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/fmt.py b/tools/fmt.py new file mode 100644 index 0000000..8f54c3b --- /dev/null +++ b/tools/fmt.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Claro formatter (simple, deterministic).""" +import sys, pathlib + +OPEN = {"IF", "DO", "REPEAT", "TEACH", "TRY"} +MID = {"ELSE", "CATCH"} +CLOSE= {"ENDIF", "DONE", "UNTIL", "LEARNED", "ENDTRY"} + +def main(): + if len(sys.argv) < 2: + print("Usage: python tools/fmt.py <file.claro>") + return 2 + p = pathlib.Path(sys.argv[1]) + lines = p.read_text(encoding="utf-8").splitlines() + out = [] + indent = 0 + for raw in lines: + line = raw.rstrip() + stripped = line.strip() + if not stripped: + out.append("") + continue + head = stripped.split()[0].upper() + if head in CLOSE: + indent = max(0, indent-1) + if head in MID: + indent = max(0, indent-1) + out.append((" "*indent) + stripped) + if head in OPEN: + indent += 1 + if head in MID: + indent += 1 + p.write_text("\n".join(out) + "\n", encoding="utf-8") + return 0 + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/run_tests.py b/tools/run_tests.py new file mode 100644 index 0000000..fffc4f8 --- /dev/null +++ b/tools/run_tests.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +"""Compatibility wrapper around the built-in Claro RC0 test runner.""" +import os +import pathlib +import subprocess +import sys + +ROOT = pathlib.Path(__file__).resolve().parent.parent +CLARO = ROOT / ("claro.exe" if os.name == "nt" else "claro") + +if not CLARO.exists(): + print("Build first: gcc -std=c99 src/claro.c -O0 -o " + ("claro.exe" if os.name == "nt" else "claro") + " -lm") + raise SystemExit(2) + +raise SystemExit(subprocess.call([str(CLARO), "test"], cwd=str(ROOT))) diff --git a/tools/validate_concurrency.py b/tools/validate_concurrency.py new file mode 100644 index 0000000..4ea66e6 --- /dev/null +++ b/tools/validate_concurrency.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +import subprocess +import sys +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +EXE = ROOT / "claro.exe" +DOC = ROOT / "docs" / "CONCURRENCY.md" + + +def fail(message): + raise SystemExit(message) + + +def run(cmd): + result = subprocess.run(cmd, cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + return result.returncode, result.stdout + + +def main(): + if not EXE.exists(): + fail(f"Missing Claro executable: {EXE}") + + if not DOC.exists(): + fail("Missing docs/CONCURRENCY.md") + text = DOC.read_text(encoding="utf-8") + required = ["START TASK", "YIELD", "WAIT TASK", "TASK STATUS", "cooperative", "deterministic", "no native threads"] + for phrase in required: + if phrase.lower() not in text.lower(): + fail(f"CONCURRENCY.md missing required phrase: {phrase}") + + rc, out = run([str(EXE), "check", "tests/40_tasks_status.claro"]) + if rc != 0: + fail(out) + + rc, out = run([str(EXE), "tests/40_tasks_status.claro"]) + if rc != 0: + fail(out) + if "DONE" not in out: + fail(f"TASK STATUS should report DONE after a cooperative task runs. Output was:\n{out}") + + with tempfile.TemporaryDirectory() as td: + bad = Path(td) / "missing_endtask.claro" + bad.write_text("START TASK sample\n SAY \"oops\"\n", encoding="utf-8") + rc, out = run([str(EXE), "check", str(bad)]) + if rc == 0: + fail("claro check should fail when START TASK is missing ENDTASK/END") + if "START block opened here needs ENDTASK" not in out: + fail(f"Missing ENDTASK diagnostic was not clear enough:\n{out}") + + print("Concurrency foundation validation OK") + + +if __name__ == "__main__": + main() diff --git a/tools/validate_ide_metadata.py b/tools/validate_ide_metadata.py new file mode 100644 index 0000000..7096e2a --- /dev/null +++ b/tools/validate_ide_metadata.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +import json +import os +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro") + + +def fail(message): + raise SystemExit(message) + + +def main(): + if not EXE.exists(): + fail(f"Missing Claro executable: {EXE}") + result = subprocess.run([str(EXE), "ide"], cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + if result.returncode != 0: + fail(result.stdout) + try: + data = json.loads(result.stdout) + except json.JSONDecodeError as exc: + fail(f"claro ide did not print valid JSON: {exc}\n{result.stdout}") + + required_top_level = ["language", "version", "file_extensions", "keywords", "types", "commands", "snippets", "diagnostics"] + for key in required_top_level: + if key not in data: + fail(f"Missing top-level IDE metadata field: {key}") + + if ".claro" not in data["file_extensions"]: + fail("IDE metadata must advertise the .claro file extension") + if "LIST OF TEXT" not in data["types"]: + fail("IDE metadata must include typed container types") + if not any(cmd.get("name") == "typecheck" for cmd in data["commands"]): + fail("IDE metadata must include the typecheck command") + if not any(snip.get("name") == "typed_list" for snip in data["snippets"]): + fail("IDE metadata must include a typed_list snippet") + if not any(diag.get("source") == "claro typecheck" for diag in data["diagnostics"]): + fail("IDE metadata must describe claro typecheck diagnostics") + + print("IDE metadata validation OK") + + +if __name__ == "__main__": + main() diff --git a/tools/validate_lsp_helper.py b/tools/validate_lsp_helper.py new file mode 100644 index 0000000..00f3f96 --- /dev/null +++ b/tools/validate_lsp_helper.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +import json +import os +import subprocess +import sys +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +HELPER = ROOT / "support" / "lsp" / "claro_lsp_stub.py" +EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro") + + +def fail(message): + raise SystemExit(message) + + +def run_helper(*args): + result = subprocess.run([sys.executable, str(HELPER), *args], cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + if result.returncode != 0: + fail(result.stdout) + return result.stdout + + +def main(): + if not HELPER.exists(): + fail(f"Missing LSP helper: {HELPER}") + if not EXE.exists(): + fail(f"Missing Claro executable: {EXE}") + + metadata = json.loads(run_helper("metadata")) + if metadata.get("language") != "Claro": + fail("metadata must describe Claro") + if ".claro" not in metadata.get("file_extensions", []): + fail("metadata must include .claro") + if not any(cmd.get("name") == "typecheck" for cmd in metadata.get("commands", [])): + fail("metadata must include typecheck command") + + completions = json.loads(run_helper("completions")) + if "SAY" not in completions.get("keywords", []): + fail("completions must include SAY") + if "LIST OF TEXT" not in completions.get("types", []): + fail("completions must include typed containers") + + with tempfile.TemporaryDirectory() as td: + bad = Path(td) / "bad_lsp.claro" + bad.write_text("SET score AS NUMBER TO 1\nSET score TO \"oops\"\n", encoding="utf-8") + diagnostics = json.loads(run_helper("diagnostics", str(bad))) + if not diagnostics: + fail("diagnostics must report type errors") + first = diagnostics[0] + for key in ["source", "file", "line", "severity", "message"]: + if key not in first: + fail(f"diagnostic is missing {key}") + if first["source"] != "claro typecheck": + fail("type mismatch should come from claro typecheck") + if first["line"] != 2: + fail(f"expected type diagnostic on line 2, got {first['line']}") + if "NUMBER" not in first["message"] or "TEXT" not in first["message"]: + fail(f"diagnostic should preserve type mismatch message: {first['message']}") + + print("LSP helper validation OK") + + +if __name__ == "__main__": + main() diff --git a/tools/validate_package_security.py b/tools/validate_package_security.py new file mode 100644 index 0000000..540648c --- /dev/null +++ b/tools/validate_package_security.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +import os +import subprocess +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro") +EXPECTED_VERSION = "Claro v1.18.26" + + +def fail(message): + raise SystemExit(message) + + +def run(cmd, cwd): + result = subprocess.run(cmd, cwd=cwd, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + return result.returncode, result.stdout + + +def read(path): + return path.read_text(encoding="utf-8") + + +def main(): + if not EXE.exists(): + fail(f"Missing executable: {EXE}") + + rc, out = run([str(EXE), "--version"], ROOT) + if rc != 0 or EXPECTED_VERSION not in out: + fail(f"Expected {EXPECTED_VERSION}, got:\n{out}") + + with tempfile.TemporaryDirectory() as td: + work = Path(td) + (work / "main.claro").write_text('SAY "Package security demo"\n', encoding="utf-8") + + rc, out = run([str(EXE), "package", "init"], work) + if rc != 0: + fail(out) + project = read(work / "claro.project") + for phrase in ["manifest-version: 1", "name: ClaroProject", "main: main.claro", "version: v1.18.26", "packages:"]: + if phrase not in project: + fail(f"claro.project missing {phrase!r}:\n{project}") + + rc, out = run([str(EXE), "package", "add", "math-tools"], work) + if rc != 0: + fail(out) + manifest_path = work / "packages" / "math-tools" / "claro.package" + manifest = read(manifest_path) + for phrase in ["manifest-version: 1", "name: math-tools", "version: 1", "source: local", "checksum:"]: + if phrase not in manifest: + fail(f"claro.package missing {phrase!r}:\n{manifest}") + checksum_line = next((line for line in manifest.splitlines() if line.startswith("checksum:")), "") + checksum = checksum_line.split(":", 1)[1].strip() + if len(checksum) < 8 or checksum == "TODO": + fail(f"Package checksum should be a stable non-placeholder value, got {checksum!r}") + + lock = read(work / "claro.lock") + for phrase in ["lock-version: 1", "version: v1.18.26", "package: math-tools", "checksum:"]: + if phrase not in lock: + fail(f"claro.lock missing {phrase!r}:\n{lock}") + + rc, out = run([str(EXE), "package", "doctor"], work) + if rc != 0: + fail(out) + for phrase in ["OK package manifest: math-tools", "OK package checksum: math-tools", "Package/project files look ready."]: + if phrase not in out: + fail(f"package doctor output missing {phrase!r}:\n{out}") + + rc, out = run([str(EXE), "package", "add", "../bad"], work) + if rc == 0: + fail("Unsafe package names with path separators must be rejected") + if "Package names may use only" not in out: + fail(f"Unsafe package diagnostic was unclear:\n{out}") + + print("Package security validation OK") + + +if __name__ == "__main__": + main() diff --git a/tools/validate_rc3.py b/tools/validate_rc3.py new file mode 100644 index 0000000..757a75e --- /dev/null +++ b/tools/validate_rc3.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +import subprocess +import sys +from pathlib import Path + +root = Path(__file__).resolve().parents[1] +exe = root / ("claro.exe" if sys.platform.startswith("win") else "claro") + +def run(args, expect=0, input_text=None): + p = subprocess.run(args, cwd=root, input=input_text, text=True, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if p.returncode != expect: + print("FAILED:", " ".join(map(str,args))) + print(p.stdout) + print(p.stderr) + sys.exit(1) + return p.stdout + p.stderr + +run([str(exe), "--version"]) +run([str(exe), "test"]) +run([str(exe), "check", "lessons/01_hello.claro"]) +run([str(exe), "check", "lessons/08_functions.claro"]) +run([str(exe), "check", "tests/checker_fixtures/bad_unknown_command.claro"], expect=1) +run([str(exe), "check", "tests/checker_fixtures/bad_typo_command.claro"], expect=1) +print("RC3 validation passed") diff --git a/tools/validate_rc6.py b/tools/validate_rc6.py new file mode 100644 index 0000000..78a54ca --- /dev/null +++ b/tools/validate_rc6.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +import os +import shutil +import subprocess +import sys + +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +EXE = os.path.join(ROOT, 'claro.exe' if os.name == 'nt' else 'claro') +SRC = os.path.join(ROOT, 'src', 'claro.c') + +cmd = ['gcc', '-std=c99', SRC, '-O0', '-o', EXE, '-lm'] +print('+', ' '.join(cmd)) +subprocess.check_call(cmd, cwd=ROOT) + +checks = [ + [EXE, '--version'], + [EXE, 'test'], + [EXE, 'check', 'tests/25_simple_syntax.claro'], + [EXE, 'check', 'tests/26_end_alias.claro'], + [EXE, 'check', 'tests/28_text_list_helpers.claro'], + [EXE, 'tests/29_arguments_demo.claro', 'apple', 'banana'], +] + +for c in checks: + print('+', ' '.join(c)) + subprocess.check_call(c, cwd=ROOT) + +print('RC6 validation completed.') diff --git a/tools/validate_rc9.py b/tools/validate_rc9.py new file mode 100644 index 0000000..c1ce17e --- /dev/null +++ b/tools/validate_rc9.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +"""Validate Claro v1.09.26 RC9 from the package root.""" +import os +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro") + + +def run(cmd): + print("+", " ".join(str(c) for c in cmd)) + return subprocess.run(cmd, cwd=ROOT).returncode + + +def main(): + if run(["gcc", "-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"]): + return 1 + checks = [ + [str(EXE), "--version"], + [str(EXE), "test"], + [str(EXE), "doctor"], + [str(EXE), "validate"], + [str(EXE), "examples/simple_functions.claro"], + [str(EXE), "examples/text_polish.claro"], + ] + for cmd in checks: + if run(cmd): + return 1 + print("Claro v1.09.26 RC9 validation passed.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_typecheck_diagnostics.py b/tools/validate_typecheck_diagnostics.py new file mode 100644 index 0000000..8c9e85f --- /dev/null +++ b/tools/validate_typecheck_diagnostics.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +import os +import subprocess +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro") + +EXPECTED = { + "tests/typecheck_bad.claro": [ + "tests/typecheck_bad.claro:2: Type mismatch for score: it was first set as NUMBER, but this value looks like TEXT.", + "tests/typecheck_bad.claro:3: Type mismatch for name: expected TEXT, but this value looks like NUMBER.", + "tests/typecheck_bad.claro:4: Type check failed: expected TEXT, but score looks like NUMBER.", + ], + "tests/typecheck_container_bad.claro": [ + "tests/typecheck_container_bad.claro:3: Type mismatch for list names: expected TEXT item, but this value looks like NUMBER.", + "tests/typecheck_container_bad.claro:6: Type mismatch for map scores: expected NUMBER value, but this value looks like TEXT.", + ], +} + +STALE_PHRASES = [ + "was first set as NUMBER, but this looks like TEXT", + "name needs TEXT, but this looks like NUMBER", + "Type check says this should be TEXT", + "ADD to names needs TEXT items", + "PUT into scores needs NUMBER values", +] + + +def run_typecheck(path): + result = subprocess.run( + [str(EXE), "typecheck", path], + cwd=ROOT, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + print("$", EXE.name, "typecheck", path) + print(result.stdout, end="") + if result.returncode == 0: + raise SystemExit(f"Expected {path} to fail typecheck") + lines = [line.strip() for line in result.stdout.splitlines() if line.strip()] + expected = EXPECTED[path] + if lines != expected: + raise SystemExit( + "Unexpected diagnostics for " + path + "\nExpected:\n" + + "\n".join(expected) + "\nGot:\n" + "\n".join(lines) + ) + for stale in STALE_PHRASES: + if stale in result.stdout: + raise SystemExit(f"Found stale diagnostic wording: {stale}") + return result.stdout + + +def main(): + if not EXE.exists(): + raise SystemExit(f"Missing Claro executable: {EXE}") + + for path in EXPECTED: + run_typecheck(path) + + print("Typecheck diagnostics validation complete") + + +if __name__ == "__main__": + main() diff --git a/tools/validate_v1_10.py b/tools/validate_v1_10.py new file mode 100644 index 0000000..371a9db --- /dev/null +++ b/tools/validate_v1_10.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Validate Claro v1.10.26 from the package root.""" +import os +import subprocess +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro") + + +def run(cmd): + print("+", " ".join(str(c) for c in cmd)) + return subprocess.run(cmd, cwd=ROOT).returncode + + +def main(): + if run(["gcc", "-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"]): + return 1 + checks = [ + [str(EXE), "--version"], + [str(EXE), "test"], + [str(EXE), "doctor"], + [str(EXE), "validate"], + [str(EXE), "examples/simple_functions.claro"], + [str(EXE), "examples/text_polish.claro"], + [str(EXE), "examples/types_and_objects.claro"], + [str(EXE), "examples/tasks_and_graphics.claro"], + ] + for cmd in checks: + if run(cmd): + return 1 + print("Claro v1.10.26 validation passed.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/validate_v1_14.py b/tools/validate_v1_14.py new file mode 100644 index 0000000..59b09c9 --- /dev/null +++ b/tools/validate_v1_14.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +"""Validate Claro v1.14.26 from the package root.""" +import os +import subprocess +import sys + +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +EXE = os.path.join(ROOT, 'claro_validate') +SRC = os.path.join(ROOT, 'src', 'claro.c') + +def run(cmd, **kwargs): + print('+', ' '.join(cmd)) + return subprocess.run(cmd, cwd=ROOT, text=True, **kwargs) + +if run(['gcc', '-std=c99', SRC, '-O0', '-o', EXE, '-lm']).returncode: + sys.exit(1) +if run([EXE, '--version']).returncode: + sys.exit(1) +if run([EXE, 'test']).returncode: + sys.exit(1) +if run([EXE, 'typecheck', 'tests/typecheck_good.claro']).returncode: + sys.exit(1) +bad = run([EXE, 'typecheck', 'tests/typecheck_bad.claro']) +if bad.returncode == 0: + print('Expected typecheck_bad.claro to fail') + sys.exit(1) +if run([EXE, 'validate']).returncode: + sys.exit(1) +print('Claro v1.14.26 validation passed.') diff --git a/tools/validate_v1_15.py b/tools/validate_v1_15.py new file mode 100644 index 0000000..f013a5e --- /dev/null +++ b/tools/validate_v1_15.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +import os, subprocess, sys + +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +os.chdir(ROOT) + +exe = './claro.exe' if os.name == 'nt' else './claro' +cmds = [ + ['gcc', '-std=c99', 'src/claro.c', '-O0', '-o', exe, '-lm'], + [exe, '--version'], + [exe, 'test'], + [exe, 'validate'], + [exe, 'check', 'examples/objects_classes.claro'], +] +for cmd in cmds: + print('+', ' '.join(cmd)) + rc = subprocess.call(cmd) + if rc != 0: + sys.exit(rc) +print('Claro v1.15.26 validation passed.') diff --git a/tools/validate_v1_16.py b/tools/validate_v1_16.py new file mode 100644 index 0000000..1266a54 --- /dev/null +++ b/tools/validate_v1_16.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +import os +import shutil +import subprocess +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro") + +def run(cmd, cwd=ROOT, input_text=None): + result = subprocess.run(cmd, cwd=cwd, input=input_text, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + print("$", " ".join(map(str, cmd))) + print(result.stdout, end="") + if result.returncode != 0: + raise SystemExit(result.returncode) + return result.stdout + +if not EXE.exists(): + run(["gcc", "-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"]) + +run([str(EXE), "--version"]) +run([str(EXE), "test"]) +run([str(EXE), "validate"]) + +with tempfile.TemporaryDirectory() as td: + work = Path(td) + shutil.copy2(EXE, work / EXE.name) + local = work / EXE.name + run([str(local), "new", "DemoProject"], cwd=work) + proj = work / "DemoProject" + run([str(local), "package", "doctor"], cwd=proj) + run([str(local), "package", "add", "text"], cwd=proj) + run([str(local), "package", "add", "text"], cwd=proj) + listing = run([str(local), "package", "list"], cwd=proj) + if listing.count("text") != 1: + raise SystemExit("Package was duplicated in listing") + bad = subprocess.run([str(local), "package", "add", "../bad"], cwd=proj, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + print("$", local, "package add ../bad") + print(bad.stdout, end="") + if bad.returncode == 0: + raise SystemExit("Unsafe package name was accepted") + run([str(local), "package", "remove", "text"], cwd=proj) + run([str(local), "run"], cwd=proj) + +print("Validation passed. Claro v1.18.26 networking reliability is ready for use.") diff --git a/tools/validate_v1_17.py b/tools/validate_v1_17.py new file mode 100644 index 0000000..b0024e2 --- /dev/null +++ b/tools/validate_v1_17.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +import os +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +EXE = ROOT / ("claro.exe" if os.name == "nt" else "claro") +SRC = ROOT / "src" / "claro.c" +ZIG_CC = Path.home() / "AppData" / "Local" / "hermes" / "tools" / "zig-x86_64-windows-0.14.1" / "zig.exe" + + +def run(cmd): + print("$", " ".join(str(c) for c in cmd)) + result = subprocess.run(cmd, cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + print(result.stdout, end="") + if result.returncode != 0: + raise SystemExit(result.returncode) + return result.stdout + + +def compiler_cmd(): + env_cc = os.environ.get("CLARO_CC") + if env_cc: + parts = env_cc.split() + return parts + ["-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"] + if ZIG_CC.exists(): + return [str(ZIG_CC), "cc", "-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"] + return ["gcc", "-std=c99", "src/claro.c", "-O0", "-o", str(EXE), "-lm"] + + +def build(): + if not SRC.exists(): + raise SystemExit("Missing src/claro.c") + run(compiler_cmd()) + + +def main(): + build() + out = run([str(EXE), "--version"]) + if "Claro v1.18.26" not in out: + raise SystemExit("Wrong version string") + run([str(EXE), "test"]) + run([str(EXE), "validate"]) + run([sys.executable, "tools/validate_ide_metadata.py"]) + run([sys.executable, "tools/validate_lsp_helper.py"]) + run([sys.executable, "tools/validate_concurrency.py"]) + run([sys.executable, "tools/validate_package_security.py"]) + run([sys.executable, "tools/validate_typecheck_diagnostics.py"]) + run([sys.executable, "tools/validate_version_convention.py"]) + run([str(EXE), "examples/networking.claro"]) + print("v1.18.26 validation complete") + +if __name__ == "__main__": + main() diff --git a/tools/validate_version_convention.py b/tools/validate_version_convention.py new file mode 100644 index 0000000..abf80ca --- /dev/null +++ b/tools/validate_version_convention.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +import json +import subprocess +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +EXE = ROOT / "claro.exe" +EXPECTED = "Claro v1.18.26" + + +def fail(message): + raise SystemExit(message) + + +def run(cmd): + return subprocess.run(cmd, cwd=ROOT, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + +def main(): + if not EXE.exists(): + fail(f"Missing executable: {EXE}") + out = run([str(EXE), "--version"]).stdout.strip() + if out != EXPECTED: + fail(f"Expected version {EXPECTED!r}, got {out!r}") + ide = run([str(EXE), "ide"]) + if ide.returncode != 0: + fail(ide.stdout) + metadata = json.loads(ide.stdout) + if metadata.get("version") != "v1.18.26": + fail(f"Expected IDE metadata version 'v1.18.26', got {metadata.get('version')!r}") + validator = ROOT / "tools" / "validate_v1_17.py" + text = validator.read_text(encoding="utf-8") + if "Claro v1.18.26" not in text: + fail("tools/validate_v1_17.py must validate Claro v1.18.26") + print("Version convention validation OK") + + +if __name__ == "__main__": + main()