Claude-skill-registry gem-port

Port Ruby gems with native C extensions to Fil-C. Use when fixing gem compilation errors, VALUE/int type mismatches, rb_attr/rb_protect issues, or using ast-grep for Ruby C extensions.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/gem-port" ~/.claude/skills/majiayu000-claude-skill-registry-gem-port && rm -rf "$T"
manifest: skills/data/gem-port/SKILL.md
source content

Fil-C Ruby Gem Porting

Context

Fil-C is a memory-safe C compiler. Ruby's VALUE type becomes a pointer (

struct rb_value_unit_struct *
) instead of an integer. This breaks code that:

  • Passes int literals where VALUE expected (Qfalse, Qtrue, ERROR_TOKEN)
  • Stores VALUE in int variables
  • Does bitwise ops on VALUE
  • Mixes VALUE and ID types
  • Uses
    switch
    on VALUE (case labels must be integer constants)

Workflow

Each command runs independently (no persistent shell). Use

ruby_3_3
not
ruby
.

1. Unpack gem

rm -rf /tmp/gem-port && mkdir -p /tmp/gem-port
nix develop .#pkgsFilc.ruby_3_3.gems.<name> --command bash -c 'gem unpack $src'
# Move to /tmp if unpacked in current dir
mv *<name>* /tmp/gem-port/ 2>/dev/null || true

2. Find ext structure

ls /tmp/gem-port/*/ext/

Note: Some gems have

ext/<name>/
, others have files directly in
ext/
.

3. Run extconf.rb

nix develop .#pkgsFilc.ruby_3_3.gems.<name> --command bash -c \
  'cd /tmp/gem-port/*/ext && ruby extconf.rb'
# Or if nested: cd /tmp/gem-port/*/ext/<name>

4. Build and capture errors

nix develop .#pkgsFilc.ruby_3_3.gems.<name> --command bash -c \
  'cd /tmp/gem-port/*/ext && make 2>&1' > /tmp/build.log
cat /tmp/build.log | head -100

5. Fix and iterate

Edit files in

/tmp/gem-port/*/ext/
directly, then re-run step 4. No need to re-unpack or re-run extconf.

6. Add to rubyports.nix and verify

# Always capture stderr and check exit code
nix build .#pkgsFilc.ruby_3_3.gems.<name> 2>&1; echo "EXIT: $?"
ls result/ 2>&1

ast-grep Usage

ALWAYS use ast-grep for pattern-based code fixes. It handles whitespace variations that break string replacement.

Selectors

Different C constructs need different selectors:

  • call_expression
    - function calls like
    rb_attr($A, $B, Qfalse)
  • switch_statement
    - switch blocks with case labels
  • declaration
    - variable declarations

Examples

Function call replacement:

ast-grep run -p 'rb_attr($A, $B, $C, $D, Qfalse)' \
             -r 'rb_attr($A, $B, $C, $D, 0)' \
             -l c --selector call_expression -U .

Switch to if-else (VALUE can't be case label):

ast-grep run \
  -p 'switch (rb_range_beg_len($A, $B, $L, $S, $F)) { case Qfalse: break; case Qnil: return Qnil; default: return subseq($X, $Y, $Z); }' \
  -r '{ VALUE r = rb_range_beg_len($A, $B, $L, $S, $F); if (r == Qfalse) { } else if (r == Qnil) { return Qnil; } else { return subseq($X, $Y, $Z); } }' \
  -l c --selector switch_statement -U .

Finding patterns (no -U):

ast-grep run -p 'static VALUE $NAME;' -l c .
ast-grep run -p 'case Q$CONST:' -l c .

rubyports.nix Helpers

# String replacement (fragile - prefer ast-grep)
(replace "path/to/file.c" "old string" "new string")

# ast-grep with call_expression selector
(astGrep "pattern($A)" "replacement($A)")

# ast-grep with custom selector
(astGrepSel "switch_statement" "switch(...)" "if-else...")

Common Patterns

IssuePatternFix
VALUE→ID for rb_intern
static VALUE id = rb_intern(...)
static ID id = ...
int→VALUE in switch
case Qfalse: case Qnil:
Convert to if-else
VALUE reused for int
arg = ... (bitwise op)
Use separate int variable
int return from VALUE func
return 1;
in VALUE function
return Qtrue;

See REFERENCE.md for full patterns, FIXES.md for per-gem solutions, and AST_GREP.md for comprehensive ast-grep documentation.