Skip to content

feat(stdlib): add fs_*/env_args/format builtins + my-cli argv forwarding#2

Merged
hyperpolymath merged 1 commit into
mainfrom
feat/stdlib-fs-env-format
May 2, 2026
Merged

feat(stdlib): add fs_*/env_args/format builtins + my-cli argv forwarding#2
hyperpolymath merged 1 commit into
mainfrom
feat/stdlib-fs-env-format

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Summary

Adds six new stdlib builtins to enable My (Solo dialect) for tooling workloads — file I/O, CLI argv, and string formatting — and a small my-cli extension to forward trailing argv to the program.

Motivation

Pre-this-PR, Solo's runtime stdlib was rich for in-memory + terminal-I/O code (71 builtins) but explicitly not tooling-class — IMPLEMENTATION.md lists std::fs, std::io, std::collections::map etc. as Phase-2 roadmap items. That meant scaffold tools / code generators / audit utilities couldn't be written in My; they had to fall back to Python.

This PR ships the smallest set of additions that lets a real Solo tooling program be written end-to-end. Validated by an out-of-tree scaffold/audit tool (scaffold-component.my for the idaptik-port polyrepo work) running clean against six component repos.

What's added

crates/my-lang/src/stdlib.rs

Builtin Signature Notes
fs_write_file (path: String, content: String) -> Unit Auto-creates parent dirs
fs_read_file (path: String) -> String
fs_create_dir_all (path: String) -> Unit Explicit dir creation
fs_exists (path: String) -> Bool
env_args () -> [String] Reads host-supplied args, falls back to std::env::args().skip(1)
format (template: String, args: [Any]) -> String Positional {} substitution; {{ and }} escape

Plus a static PROGRAM_ARGS: OnceLock<Vec<String>> and pub fn set_program_args(args: Vec<String>) so embedders can supply argv to programs without my-cli's clap consuming flags.

All six are stateless fn(Vec<Value>) -> Result<Value, RuntimeError> pointers consistent with existing builtins. Errors route through RuntimeError::Custom(String) for I/O failures; RuntimeError::TypeError for type mismatches.

crates/my-cli/src/main.rs

Run subcommand grows a args: Vec<String> field with trailing_var_arg = true, allow_hyphen_values = true, so:

my run scaffold.my -- --audit /path/to/repo

forwards --audit /path/to/repo to env_args() rather than clap rejecting --audit. The match arm calls my_lang::stdlib::set_program_args(args) before invoking the interpreter.

Test plan

  • cargo build --release --bin my -p my-cli clean (3 pre-existing dead-code warnings unchanged)
  • Smoke test exercising every new builtin (/tmp/smoke.my):
    hello foo world 42
    ok: fs_exists returned true
    literal { and middle and }
    argv length: 0
    smoke OK
    Result: Int(0)
    
  • End-to-end run of an out-of-tree scaffold tool (scaffold-audit.my) against six idaptik-port component repos — all six OK audits, exit 0.
  • Unit tests in crates/my-lang/tests/ for each new builtin (success + error paths). Not added in this PR; happy to follow up.

Open design questions

  1. Path semantics. fs_write_file auto-creates parent dirs; fs_create_dir_all is exported separately because some scaffolds want explicit empty dirs. Acceptable, or split write into two funcs?
  2. Error type. All I/O errors route through RuntimeError::Custom(String). A new RuntimeError::IoError { path, kind, message } variant would be cleaner long-term but would touch every error-handling path; deferred.
  3. format placeholder syntax. Positional {} only. No {name} named placeholders, no {0}/{1} indexed. Aligned with Rust's format! minimum surface; can extend later.
  4. env_args semantics. Falls back to std::env::args().skip(1) when set_program_args hasn't been called. Skipping arg[0] matches typical CLI ergonomics but could be debated.

Related

Why merge this

Six builtins, ~290 LOC of Rust, no breaking changes. Unblocks the entire tooling category; concretely unblocks the idaptik-port polyrepo scaffolding pipeline that produced this work.

🤖 Generated with Claude Code

Six new stdlib builtins for tooling workloads:
  - fs_write_file / fs_read_file / fs_create_dir_all / fs_exists
  - env_args (host-supplied with std::env::args().skip(1) fallback)
  - format (positional {} substitution, {{ }} escapes)

Plus PROGRAM_ARGS OnceLock + pub fn set_program_args, and my-cli's Run
subcommand grows a trailing_var_arg field forwarded to set_program_args.

Validated by an end-to-end run of an out-of-tree scaffold tool against
six idaptik-port component repos.

See PR body for details and open design questions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 1, 2026
Two more CI infra fixes uncovered while sorting PR #2:

* hypatia-scan.yml: setup-beam@v1.18.2 doesn't recognise ImageOS
  ubuntu24, so every Hypatia run aborts with "Tried to map a target
  OS from env. variable 'ImageOS' (got ubuntu24), but failed."
  Bumped to v1.24.0 (8d44588995...), which lists ubuntu-24.04
  support in its README.

* workflow-linter.yml: the SHA-pinned-actions check ran
  `grep -rn "uses:" .github/workflows/`, which matches the linter's
  own explanatory comment `# Find any uses: lines that don't have
  @sha format`. The comment is then flagged as unpinned and the
  check fails on itself. Anchor the regex on leading-whitespace +
  the directive form `^[[:space:]]+uses:[[:space:]]` so comments
  no longer match.
@hyperpolymath
hyperpolymath merged commit 459ee23 into main May 2, 2026
14 of 16 checks passed
hyperpolymath added a commit that referenced this pull request May 2, 2026
…p-beam ubuntu24, workflow-linter self-match) (#3)

* ci(hypatia): fix unresolvable upload-artifact SHA pin

The pinned SHA 65c79d7f54e76e4e3c7a8f34db0f4ac8b515c478 doesn't exist
in actions/upload-artifact, so the runner errors with "Unable to
resolve action ... unable to find version" and Hypatia scans never
start.

Pinned to v4.6.2 (ea165f8d65b6e75b540449e92b4886f43607fa02), which
is the latest v4 series matching the original `# v4` comment.

* ci: bump setup-beam to v1.24.0; tighten workflow-linter uses: regex

Two more CI infra fixes uncovered while sorting PR #2:

* hypatia-scan.yml: setup-beam@v1.18.2 doesn't recognise ImageOS
  ubuntu24, so every Hypatia run aborts with "Tried to map a target
  OS from env. variable 'ImageOS' (got ubuntu24), but failed."
  Bumped to v1.24.0 (8d44588995...), which lists ubuntu-24.04
  support in its README.

* workflow-linter.yml: the SHA-pinned-actions check ran
  `grep -rn "uses:" .github/workflows/`, which matches the linter's
  own explanatory comment `# Find any uses: lines that don't have
  @sha format`. The comment is then flagged as unpinned and the
  check fails on itself. Anchor the regex on leading-whitespace +
  the directive form `^[[:space:]]+uses:[[:space:]]` so comments
  no longer match.

* ci(hypatia): cd into $HOME/hypatia in shell instead of via working-directory

\${{ env.HOME }} resolves the workflow's `env:` block, not the runner's
$HOME, so the previous `working-directory: \${{ env.HOME }}/hypatia`
expanded to /hypatia and the build step failed with
"working directory '/hypatia'. No such file or directory".

Use a shell `cd "$HOME/hypatia"` instead, matching how the Clone and
Run steps already address the path.
hyperpolymath added a commit that referenced this pull request May 4, 2026
* ci(hypatia): fix unresolvable upload-artifact SHA pin

The pinned SHA 65c79d7f54e76e4e3c7a8f34db0f4ac8b515c478 doesn't exist
in actions/upload-artifact, so the runner errors with "Unable to
resolve action ... unable to find version" and Hypatia scans never
start.

Pinned to v4.6.2 (ea165f8d65b6e75b540449e92b4886f43607fa02), which
is the latest v4 series matching the original `# v4` comment.

* ci: bump setup-beam to v1.24.0; tighten workflow-linter uses: regex

Two more CI infra fixes uncovered while sorting PR #2:

* hypatia-scan.yml: setup-beam@v1.18.2 doesn't recognise ImageOS
  ubuntu24, so every Hypatia run aborts with "Tried to map a target
  OS from env. variable 'ImageOS' (got ubuntu24), but failed."
  Bumped to v1.24.0 (8d44588995...), which lists ubuntu-24.04
  support in its README.

* workflow-linter.yml: the SHA-pinned-actions check ran
  `grep -rn "uses:" .github/workflows/`, which matches the linter's
  own explanatory comment `# Find any uses: lines that don't have
  @sha format`. The comment is then flagged as unpinned and the
  check fails on itself. Anchor the regex on leading-whitespace +
  the directive form `^[[:space:]]+uses:[[:space:]]` so comments
  no longer match.

* ci(hypatia): cd into $HOME/hypatia in shell instead of via working-directory

\${{ env.HOME }} resolves the workflow's `env:` block, not the runner's
$HOME, so the previous `working-directory: \${{ env.HOME }}/hypatia`
expanded to /hypatia and the build step failed with
"working directory '/hypatia'. No such file or directory".

Use a shell `cd "$HOME/hypatia"` instead, matching how the Clone and
Run steps already address the path.
@hyperpolymath
hyperpolymath deleted the feat/stdlib-fs-env-format branch May 4, 2026 22:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant