Skip to content

feat(res-to-affine): partial-port mode — --partial fn skeletons + switch→match (Refs #488)#494

Merged
hyperpolymath merged 1 commit into
mainfrom
claude/pensive-fermi-JVCzu
May 31, 2026
Merged

feat(res-to-affine): partial-port mode — --partial fn skeletons + switch→match (Refs #488)#494
hyperpolymath merged 1 commit into
mainfrom
claude/pensive-fermi-JVCzu

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

What

First slice of #488 — a --partial flag implementing the partial-port model: a distinct mode from --translate whose output is a skeleton a human finishes and deliberately does not type-check (un-annotated ReScript functions can't yield a compilable AffineScript fn) — but it parses.

Renders each module-top-level let f = (params) => body into fn f(params: _) -> _ { … }:

ReScript AffineScript (--partial)
let area = (w, h) => w *. h fn area(w: _, h: _) -> _ { w * h }
let classify = x => switch x { | Some(n) => n + 1 | None => 0 } fn classify(x: _) -> _ { match x { Some(n) => n + 1, None => 0, } }
let greet = name => "hi " ++ name fn greet(name: _) -> _ { "hi " ++ name }
let piped = x => x->doStuff(1) fn piped(x: _) -> _ { () /* TODO: x->doStuff(1) */ }

How

  • switchmatch with variant / tuple / literal pattern translation; arm bodies translated (unwrapping sequence_expression/expression_statement).
  • Expression translation: literals, identifiers, calls, binary operators (normalising ReScript float ops +./*.+/* and ===/!====/!=), ++ concat, member + module-qualified access, ternary → if/else. The binary operator is an anonymous token in the ReScript grammar, so it's sliced from source between the operands.
  • Holes: un-inferable types → _; un-translatable expr/pattern → () /* TODO */ / _ /* TODO */, so the output still parses.
  • Walker: translate_expr/translate_switch (mutually recursive) + translate_pattern + partial_function + collect_partial + Walker.translate_partial; emitter gains emit_partial; main gains --partial (precedence over --translate, walker-only). --translate and its declaration model are untouched.

Verification (local, apt-bootstrapped toolchain)

Check Result
full dune build ✅ exit 0
dune runtest tools/res-to-affine/ ✅ 27/27 walker (5 new) against pinned grammar 990214a
parse checkmain.exe check on the generated skeleton ✅ reaches resolution/type-checking with no parse error (the partial-port bar; type/resolution errors are expected and fine)
doc-truthing + no-extension-ts guards ✅ pass

Scope / next under #488

First slice. Deferred: pipe desugaring (a->f(b)f(a, b)), if/block-bodied functions, combining --partial with --translate, and module-qualified-reference resolution (module-mapping). Documented in the README's updated scope-boundary + new --partial section.

Refs #488

https://claude.ai/code/session_017T8SzHr2yXav8hm4Ho76Uw


Generated by Claude Code

…tch->match (Refs #488)

First slice of #488: a NEW --partial flag, a distinct model from --translate.
Its output is a partial port a human finishes and DELIBERATELY does not
type-check (un-annotated ReScript fns can't yield a compilable AffineScript
fn) — but it parses.

Renders each module-top-level `let f = (params) => body` into
`fn f(params: _) -> _ { <translated body> }`:

- switch -> match with variant / tuple / literal pattern translation
- expression translation: literals, identifiers, calls, binary operators
  (normalising float ops +. *. -> + * and === !== -> == !=), ++ concat,
  member + module-qualified access, ternary -> if/else
- un-inferable types -> `_` holes; un-translatable expr/pattern ->
  `() /* TODO */` / `_ /* TODO */` islands, so the result still parses

The binary operator is an anonymous token in the ReScript grammar, so it is
sliced from source between the operands. Walker: translate_expr / translate_switch
(mutually recursive) + translate_pattern + partial_function + collect_partial +
Walker.translate_partial; emitter gains emit_partial; main gains --partial
(precedence over --translate, walker-only).

Verified locally (apt-bootstrapped toolchain): full dune build exit 0; 27
res-to-affine walker tests green (5 new, against pinned grammar 990214a); and
`main.exe check` on the generated skeletons reaches resolution/type-checking
WITHOUT a parse error (the partial-port bar).

Refs #488

https://claude.ai/code/session_017T8SzHr2yXav8hm4Ho76Uw
@hyperpolymath hyperpolymath marked this pull request as ready for review May 31, 2026 06:41
@hyperpolymath hyperpolymath merged commit c157a0f into main May 31, 2026
15 of 24 checks passed
@hyperpolymath hyperpolymath deleted the claude/pensive-fermi-JVCzu branch May 31, 2026 06:41
hyperpolymath added a commit that referenced this pull request May 31, 2026
…le (Refs #488) (#499)

STATE.a2ml: dated note documenting res-to-affine --partial partial-port slices 1-3 (#494/#495/#496) + remaining #488 work. .claude/settings.json: allow the _build/default/bin/main.exe compiler oracle. Docs/config only, no code. Refs #488.
@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 83 issues detected

Severity Count
🔴 Critical 2
🟠 High 13
🟡 Medium 68

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Action perpolymath/standards/.github/workflows/governance-reusable.yml@main\n needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action ons/checkout@v6\n    needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action land/setup-deno@v2\n    needs attention",
    "type": "unpinned_action",
    "file": "publish-jsr.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in affine-vscode-publish.yml",
    "type": "missing_timeout_minutes",
    "file": "affine-vscode-publish.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  }
]

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.

2 participants