Skip to content

Macro-generate repetitive Luax getter/setter functions#1

Merged
hellerve merged 3 commits into
masterfrom
claude/luax-macro-gen
May 21, 2026
Merged

Macro-generate repetitive Luax getter/setter functions#1
hellerve merged 3 commits into
masterfrom
claude/luax-macro-gen

Conversation

@carpentry-agent
Copy link
Copy Markdown

Summary

Replace 19 hand-written getter/setter functions in the Luax module with 5 defndynamic generators (luax--def-maybe-get, luax--def-set-global, luax--def-get-global, luax--def-set-field, luax--def-get-field) that produce identical code from compact type specs.

Before: each type variant (int, float, bool, string) × each operation group was a separate doc/defn pair — 150 lines of near-identical code.

After: 5 generators (68 lines) + 19 one-liner invocations (32 lines). Net reduction of ~50 lines. Adding a new type (e.g. light-user-data) now requires one line per group instead of a full function per group.

What changed

  • Added 5 defndynamic generators alongside the existing luax--make-push-calls / luax--make-field-stmts helpers
  • Replaced 19 hand-written functions in defmodule Luax with generator calls
  • get-carp-str, do-in, call-fn, and make-table remain hand-written (they aren't part of the repetitive pattern)

What didn't change

  • Public API: every function name, signature, and doc string is preserved
  • Behavior: generated code is structurally identical to the originals
  • Tests: no test changes needed — the existing suite covers all generated functions

Not verified on this machine

The Carp compiler (carp), carp-fmt, and angler are not available on the build host. The macro code follows the same defndynamic + eval pattern used by Lua.setup and should work correctly, but compilation has not been verified.


Opened by the carpentry-org heartbeat agent (Claude). Veit has not reviewed this yet.

Replace 19 hand-written getter/setter functions across 5 groups
(maybe-get-*, set-*-global, get-*-global, set-*-field, get-*-field)
with defndynamic generators that produce identical code from type
specs. Reduces boilerplate by ~50 lines while preserving the same
public API, behavior, and documentation.
@carpentry-agent
Copy link
Copy Markdown
Author

Build & Tests

CI failing on both ubuntu and macOS:

Can't find symbol 'int' at lua.carp:505:24

Findings

  1. defndynamic vs defmacro — this is the CI failure. All 5 generators (luax--def-maybe-get, luax--def-set-global, luax--def-get-global, luax--def-set-field, luax--def-get-field) are defined as defndynamic, but they're called at the top level of defmodule Luax with bare symbol arguments like int, Lua.TYPE_NUMBER, Lua.get-int. In Carp, defndynamic evaluates its arguments — so the evaluator tries to resolve int as a dynamic variable and fails.

    The fix is to change all 5 from defndynamic to defmacro. Macros receive arguments unevaluated, so int would be passed as a symbol (which is what Symbol.concat expects). The existing luax--make-push-calls and luax--make-field-stmts work with defndynamic because they're only called from within other defmacro bodies (call-fn, make-table), where arguments are already captured as quoted forms.

  2. API coverage is correct (assuming the fix above). The 5 generator families produce exactly 19 functions matching the original set: 4 maybe-get + 3 set-global + 4 get-global + 4 set-field + 4 get-field. The missing set-string-global in Luax matches the original (it lives in the Lua module).

  3. Generated code is structurally identical to the originals. I compared each generator template against the hand-written function it replaces. The logic, stack management, and type checking all match.

  4. Doc strings lose line breaks. The originals had multi-line doc strings (e.g. "Read the value at \index` as an integer, returning\n`Nothing` if..."); the generated versions use Dynamic.String.concat` which produces single-line strings. Minor cosmetic difference in generated docs.

  5. The approach is sound. The defndynamic + eval pattern (once fixed to defmacro) is idiomatic Carp metaprogramming and matches the style of Lua.setup. The net reduction is meaningful and adding new Lua types becomes a one-liner-per-group operation.

Verdict: revise

CI is failing due to defndynamic vs defmacro. Change all 5 generators from defndynamic to defmacro and verify that CI passes. The rest of the change looks correct.

Veit added 2 commits May 21, 2026 12:41
defndynamic evaluates arguments, so bare symbols like `int` and
`Lua.TYPE_NUMBER` fail with "Can't find symbol". defmacro receives
arguments unevaluated, which is what these code generators need.
@carpentry-agent
Copy link
Copy Markdown
Author

Review

Build & Tests

CI passing on both ubuntu-latest and macos-latest. No local Carp compiler available; relying on CI.

Prior feedback

The previous review (2026-05-21) identified that all 5 generators needed defmacro instead of defndynamic because defndynamic evaluates arguments, causing "Can't find symbol 'int'". This has been fixed. All 5 generators now use defmacro.

Findings

Verified correct:

  • All 5 generators (luax--def-maybe-get, luax--def-set-global, luax--def-get-global, luax--def-set-field, luax--def-get-field) use defmacro.
  • Exactly 19 functions are generated: 4 maybe-get + 3 set-global + 4 get-global + 4 set-field + 4 get-field. set-string-global is correctly absent (it lives in the Lua module).
  • Each generated function body is structurally identical to its hand-written original. Verified all 19.
  • The maybe-get-string getter uses get-carp-str, which is defined earlier in the module as (String.from-cstr-or (Lua.to-string lua index) @"") — identical to the original inline expression.
  • Doc strings are semantically preserved (minor formatting differences from Dynamic.String.concat vs hand-written multi-line strings).
  • No CHANGELOG exists in this repo — nothing to update.

Minor note: The PR description body still says "5 defndynamic generators" — it wasn't updated after the fix commit. Cosmetic only.

Verdict: merge

The defndynamicdefmacro fix is correctly applied. All 19 generated functions match their originals. CI is green.

@hellerve hellerve marked this pull request as ready for review May 21, 2026 11:20
@hellerve hellerve merged commit 03e67be into master May 21, 2026
2 checks passed
@hellerve hellerve deleted the claude/luax-macro-gen branch May 21, 2026 11:20
@carpentry-agent carpentry-agent Bot mentioned this pull request May 23, 2026
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