Skip to content

feat(solo/stdlib): string-keyed Map/dict builtins#49

Merged
hyperpolymath merged 2 commits into
mainfrom
feat/solo-stdlib-map-builtins
May 18, 2026
Merged

feat(solo/stdlib): string-keyed Map/dict builtins#49
hyperpolymath merged 2 commits into
mainfrom
feat/solo-stdlib-map-builtins

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

What

Implements the highest-leverage remaining Solo Phase-2 stdlib gap: string-keyed maps.

Adds 7 builtins backed by the existing Value::Record(HashMap<String, Value>) representation (no new Value variant, no checker type-table change — same approach as the already-landed fs_*/format/env_args builtins):

builtin signature notes
map_new() -> Record empty map
map_set(m, k, v) -> Record immutable; returns a fresh map (overwrites existing key)
map_get(m, k) -> value errors (FieldNotFound) if absent
map_has(m, k) -> Bool membership test
map_keys(m) -> Array<String> sorted for deterministic tooling output
map_remove(m, k) -> Record immutable; no-op if absent
map_len(m) -> Int entry count

Mutation builtins follow the same immutable convention as push (clone + return new).

Why

Value::Record was only reachable through static, syntax-level field identifiers (eval_field). Tooling needs dynamic / computed string keys for config tables, lookup tables, and JSON-shaped data. This is also the natural object substrate for the follow-up json_parse/json_stringify work (#47), per the tracker's suggested order (Map → JSON → date).

Validation

  • examples/map.my runs and produces exactly its documented expected output.
  • New integration regression test test_eval_map_builtins (exercises new/set/overwrite/get/remove/len + immutability of the source map).
  • Full integration suite: 16 passed, 0 failed.
  • Touched files are rustfmt-clean. (Pre-existing fmt drift elsewhere in stdlib.rs left untouched — out of scope.)

Scope notes

vscode-extension/package-lock.json (pre-existing untracked file) deliberately not included.

Refs #45
Refs #46

🤖 Generated with Claude Code

Add map_new/map_set/map_get/map_has/map_keys/map_remove/map_len to the
Solo runtime stdlib, backed by the existing Value::Record(HashMap<String,
Value>) representation. Previously Record was only reachable through
static, syntax-level field identifiers (eval_field), so tooling had no
way to use dynamic / computed string keys for config tables, lookup
tables, or JSON-shaped data.

Mutation builtins (map_set, map_remove) follow the same immutable
convention as `push`: they clone and return a fresh Record. map_keys
returns keys sorted for deterministic tooling output.

Registered in register_stdlib() and stdlib_functions(); checker uses the
existing Ty::Unknown fallback, consistent with the fs_*/format/env_args
builtins. Adds examples/map.my and an integration regression test.

Highest-leverage Phase-2 gap and the natural representation substrate for
the follow-up json_parse/json_stringify work (#47).

Refs #45
Refs #46

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The status table conflated "Solo dialect Complete" (true: lexer/parser/
checker/interpreter/REPL) with the runtime stdlib (still Phase 2). Split
the row, add an explicit "runtime standard library | In Progress" row,
and a NOTE pointing at the GitHub Phase-2 tracker as live status so the
table cannot drift into a lie.

Refs #45

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hyperpolymath hyperpolymath merged commit 6fdaafc into main May 18, 2026
12 of 13 checks passed
@hyperpolymath hyperpolymath deleted the feat/solo-stdlib-map-builtins branch May 18, 2026 09:51
hyperpolymath added a commit that referenced this pull request May 18, 2026
Adds json_parse(s)->Value and json_stringify(v)->String, backed by
serde_json (promoted to a non-optional dependency of my-lang; serde
derive stays behind the `serde` feature). Mapping pairs with the Map
builtins (#46): object<->Record, array<->Array, integral number->Int
else Float, null<->Unit. Object keys serialize sorted (serde_json's
default BTreeMap), matching map_keys' deterministic ordering.

Also fixes a prerequisite latent bug: the lexer stored raw string-literal
slices and escape sequences were never decoded anywhere, so `"a\"b"`
yielded the four chars a \ " b and json_parse("{\"k\":1}") — issue #45's
own repro — was unparseable. parse_string_literal now decodes the
standard escapes (\" \\ \/ \n \t \r \0, \uXXXX / \u{...}); unknown
escapes pass through leniently so no previously-valid program breaks.

Adds examples/json.my and integration regression tests for both the JSON
round-trip (deterministic sorted keys) and string-escape decoding.

Rebased onto main after #49 (Map) landed; coexists with map_* builtins.

Refs #45
Refs #47

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 18, 2026
)

Adds json_parse(s)->Value and json_stringify(v)->String, backed by
serde_json (promoted to a non-optional dependency of my-lang; serde
derive stays behind the `serde` feature). Mapping pairs with the Map
builtins (#46): object<->Record, array<->Array, integral number->Int
else Float, null<->Unit. Object keys serialize sorted (serde_json's
default BTreeMap), matching map_keys' deterministic ordering.

Also fixes a prerequisite latent bug: the lexer stored raw string-literal
slices and escape sequences were never decoded anywhere, so `"a\"b"`
yielded the four chars a \ " b and json_parse("{\"k\":1}") — issue #45's
own repro — was unparseable. parse_string_literal now decodes the
standard escapes (\" \\ \/ \n \t \r \0, \uXXXX / \u{...}); unknown
escapes pass through leniently so no previously-valid program breaks.

Adds examples/json.my and integration regression tests for both the JSON
round-trip (deterministic sorted keys) and string-escape decoding.

Rebased onto main after #49 (Map) landed; coexists with map_* builtins.

Refs #45
Refs #47

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 18, 2026
time() only yields a Unix timestamp float; tooling that stamps generated
artifacts (changelogs, scaffolds) needs a calendar date. date_today()
returns the current UTC date as an ISO `YYYY-MM-DD` string.

Civil date derived from SystemTime epoch seconds via Howard Hinnant's
civil_from_days algorithm (public domain) — no date-crate dependency,
correct for all Gregorian dates. Verified: produces 2026-05-18 today.

Adds examples/date.my and an integration test asserting format/shape and
a sane range (exact value depends on the wall clock).

Rebased onto main after #49 (Map) and #53 (JSON) landed; coexists with
the map_* / json_* builtins.

Refs #45
Refs #48

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 18, 2026
time() only yields a Unix timestamp float; tooling that stamps generated
artifacts (changelogs, scaffolds) needs a calendar date. date_today()
returns the current UTC date as an ISO `YYYY-MM-DD` string.

Civil date derived from SystemTime epoch seconds via Howard Hinnant's
civil_from_days algorithm (public domain) — no date-crate dependency,
correct for all Gregorian dates. Verified: produces 2026-05-18 today.

Adds examples/date.my and an integration test asserting format/shape and
a sane range (exact value depends on the wall clock).

Rebased onto main after #49 (Map) and #53 (JSON) landed; coexists with
the map_* / json_* builtins.

Refs #45
Refs #48

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 18, 2026
#46/#47/#48 closed and merged via PR #49/#53/#54. The roadmap table
still showed them as Planned / In flight; sync to reality. IMPLEMENTATION.md
already lists them as builtins; this aligns the human-facing roadmap.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 35 issues detected

Severity Count
🔴 Critical 6
🟠 High 11
🟡 Medium 18

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Issue in quality.yml",
    "type": "missing_workflow",
    "file": "quality.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in security-policy.yml",
    "type": "missing_workflow",
    "file": "security-policy.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "unwrap_or(0) with dangerous default (1 occurrences, CWE-754)",
    "type": "unwrap_dangerous_default",
    "file": "/home/runner/work/my-lang/my-lang/_exploratory/me-scaffolding/crates/parser/src/lib.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "expect() in hot path (80 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/my-lang/my-lang/_exploratory/me-scaffolding/crates/parser/src/lib.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "unwrap() without prior check -- DoS via panic (1 occurrences, CWE-754)",
    "type": "unwrap_without_check",
    "file": "/home/runner/work/my-lang/my-lang/my-ssg/src/generator.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "high"
  },
  {
    "reason": "expect() in hot path (5 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/my-lang/my-lang/crates/my-mir/src/lib.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "unwrap() without prior check -- DoS via panic (26 occurrences, CWE-754)",
    "type": "unwrap_without_check",
    "file": "/home/runner/work/my-lang/my-lang/crates/my-fmt/src/lib.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "high"
  },
  {
    "reason": "unwrap() without prior check -- DoS via panic (1 occurrences, CWE-754)",
    "type": "unwrap_without_check",
    "file": "/home/runner/work/my-lang/my-lang/crates/my-hir/src/lib.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "high"
  },
  {
    "reason": "unwrap() without prior check -- DoS via panic (3 occurrences, CWE-754)",
    "type": "unwrap_without_check",
    "file": "/home/runner/work/my-lang/my-lang/crates/my-llvm/src/lib.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "high"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

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