[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-74734":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":8,"htmlUrl":8,"language":9,"languages":8,"totalLinesOfCode":8,"stars":10,"forks":11,"watchers":12,"openIssues":13,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":15,"stars7d":16,"stars30d":17,"stars90d":14,"forks30d":14,"starsTrendScore":18,"compositeScore":19,"rankGlobal":8,"rankLanguage":8,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":21,"hasPages":21,"topics":23,"createdAt":8,"pushedAt":8,"updatedAt":24,"readmeContent":25,"aiSummary":26,"trendingCount":14,"starSnapshotCount":14,"syncStatus":27,"lastSyncTime":28,"discoverSource":29},74734,"spinel","matz\u002Fspinel","matz",null,"Ruby",1622,69,9,31,0,6,23,124,18,18.54,"MIT License",false,"master",[],"2026-06-12 02:03:27","# Spinel -- Ruby AOT Compiler\n\nSpinel compiles Ruby source code into standalone native executables.\nIt performs whole-program type inference and generates optimized C code,\nachieving significant speedups over CRuby.\n\nSpinel is **self-hosting**: the compiler backend is written in Ruby and\ncompiles itself into a native binary.\n\n## How It Works\n\n```\nRuby (.rb)\n    |\n    v\nspinel_parse           Parse with Prism (libprism), serialize AST\n    |                  (C binary, or CRuby + Prism gem as fallback)\n    v\nAST text file (.ast)\n    |\n    v\nspinel_analyze         Whole-program type inference (self-hosted)\n    |                  Walks AST to fixpoint: param \u002F return \u002F ivar\n    |                  types, value-type detection, DCE markers,\n    |                  per-node inferred-type cache.\n    v\nIR text file (.ir)\n    |\n    v\nspinel_codegen         C code generation (self-hosted)\n    |                  Consumes .ast + .ir, emits one C file.\n    v\nC source (.c)\n    |\n    v\ncc -O2 -Ilib -lm      Standard C compiler + runtime header\n    |\n    v\nNative binary           Standalone, no runtime dependencies\n```\n\nThe analyze \u002F codegen split is by design -- they live in separate\nbinaries (`spinel_analyze`, `spinel_codegen`) that share no in-memory\nstate. Everything analyze decides is serialized through the IR file;\ncodegen reconstructs its inferred view from that contract. See\n[docs\u002FANALYZE-IR.md](docs\u002FANALYZE-IR.md) for the per-record format\nand [docs\u002FAST.md](docs\u002FAST.md) for the AST format the analyze stage\nconsumes.\n\n## Quick Start\n\n```bash\n# Fetch libprism sources (from the prism gem on rubygems.org):\nmake deps\n\n# Build everything:\nmake\n\n# Write a Ruby program:\ncat > hello.rb \u003C\u003C'RUBY'\ndef fib(n)\n  if n \u003C 2\n    n\n  else\n    fib(n - 1) + fib(n - 2)\n  end\nend\n\nputs fib(34)\nRUBY\n\n# Compile and run:\n.\u002Fspinel hello.rb\n.\u002Fhello               # prints 5702887 (instantly)\n```\n\n### Options\n\n```bash\n.\u002Fspinel app.rb              # compiles to .\u002Fapp\n.\u002Fspinel app.rb -o myapp     # compiles to .\u002Fmyapp\n.\u002Fspinel app.rb -c           # generates app.c only\n.\u002Fspinel app.rb -S           # prints C to stdout\n```\n\n## Self-Hosting\n\nSpinel compiles its own backend. Both `spinel_analyze.rb` and\n`spinel_codegen.rb` are written in a Ruby subset that compiles through\nthe same pipeline that compiles user code. The bootstrap chain\nexercises each side in both dimensions (IR fixpoint, C fixpoint):\n\n```\nCRuby + spinel_parse(.rb)     → analyze.ast \u002F codegen.ast\nCRuby + spinel_analyze.rb     → analyze1.ir, codegen1.ir\nCRuby + spinel_codegen.rb     → analyze1.c, codegen1.c    → bin1 (analyze + codegen)\n\nbin1 + analyze.ast            → analyze2.ir\nbin1 + analyze.ast + .ir      → analyze2.c                → bin2 (analyze)\nbin1 + codegen.ast            → codegen2.ir\nbin1 + codegen.ast + .ir      → codegen2.c                → bin2 (codegen)\n\nbin2 + analyze.ast            → analyze3.ir\nbin2 + analyze.ast + .ir      → analyze3.c\nbin2 + codegen.ast            → codegen3.ir\nbin2 + codegen.ast + .ir      → codegen3.c\n\nanalyze2.ir == analyze3.ir    (analyze.rb: IR fixpoint OK)\nanalyze2.c  == analyze3.c     (analyze.rb: C  fixpoint OK)\ncodegen2.ir == codegen3.ir    (codegen.rb: IR fixpoint OK)\ncodegen2.c  == codegen3.c     (codegen.rb: C  fixpoint OK)\n```\n\nAll four `==` checks have to hold for `make bootstrap` to declare\nsuccess. Any change that affects deterministic output -- record order,\ndefault-value handling, hash iteration -- breaks one of them, and the\nbootstrap stops with a clear `analyze.rb: IR fixpoint FAIL` (or the\nmatching codegen \u002F C variant) so the regression surfaces immediately.\n\n## Benchmarks\n\n384 tests pass. 52 benchmarks pass.\nGeometric mean: **~11.6x faster** than miniruby (Ruby 4.1.0dev) across\nthe 28 benchmarks below. Baseline is the latest CRuby `miniruby` build\n(without bundled gems), which is considerably faster than the system\n`ruby` (3.2.3); Spinel's advantage is correspondingly smaller but still\nsubstantial on computation-heavy workloads.\n\n### Computation\n\n| Benchmark | Spinel | miniruby | Speedup |\n|---|---|---|---|\n| life (Conway's GoL) | 20 ms | 1_733 ms | 86.7x |\n| ackermann | 5 ms | 374 ms | 74.8x |\n| mandelbrot | 25 ms | 1_453 ms | 58.1x |\n| fib (recursive) | 17 ms | 581 ms | 34.2x |\n| nqueens | 10 ms | 304 ms | 30.4x |\n| tarai | 16 ms | 461 ms | 28.8x |\n| tak | 22 ms | 532 ms | 24.2x |\n| matmul | 13 ms | 313 ms | 24.1x |\n| sudoku | 6 ms | 102 ms | 17.0x |\n| partial_sums | 93 ms | 1_498 ms | 16.1x |\n| fannkuch | 2 ms | 19 ms | 9.5x |\n| sieve | 39 ms | 332 ms | 8.5x |\n| fasta (DNA seq gen) | 3 ms | 21 ms | 7.0x |\n\n### Data Structures & GC\n\n| Benchmark | Spinel | miniruby | Speedup |\n|---|---|---|---|\n| rbtree (red-black tree) | 24 ms | 543 ms | 22.6x |\n| splay tree | 14 ms | 195 ms | 13.9x |\n| huffman (encoding) | 6 ms | 59 ms | 9.8x |\n| so_lists | 76 ms | 410 ms | 5.4x |\n| binary_trees | 11 ms | 40 ms | 3.6x |\n| linked_list | 136 ms | 388 ms | 2.9x |\n| gcbench | 1_845 ms | 3_641 ms | 2.0x |\n\n### Real-World Programs\n\n| Benchmark | Spinel | miniruby | Speedup |\n|---|---|---|---|\n| json_parse | 39 ms | 394 ms | 10.1x |\n| bigint_fib (1000 digits) | 2 ms | 16 ms | 8.0x |\n| ao_render (ray tracer) | 417 ms | 3_334 ms | 8.0x |\n| pidigits (bigint) | 2 ms | 13 ms | 6.5x |\n| str_concat | 2 ms | 13 ms | 6.5x |\n| template engine | 152 ms | 936 ms | 6.2x |\n| csv_process | 234 ms | 860 ms | 3.7x |\n| io_wordcount | 33 ms | 97 ms | 2.9x |\n\n## Supported Ruby Features\n\n**Core**: Classes, inheritance, `super`, `include` (mixin), `attr_accessor`,\n`Struct.new`, `alias`, module constants, open classes for built-in types.\n\n**Control Flow**: `if`\u002F`elsif`\u002F`else`, `unless`, `case`\u002F`when`,\n`case`\u002F`in` (pattern matching), `while`, `until`, `loop`, `for..in`\n(range and array), `break`, `next`, `return`, `catch`\u002F`throw`,\n`&.` (safe navigation).\n\n**Blocks**: `yield`, `block_given?`, `&block`, `proc {}`, `Proc.new`,\nlambda `-> x { }`, `method(:name)`. Block methods: `each`,\n`each_with_index`, `map`, `select`, `reject`, `reduce`, `sort_by`,\n`any?`, `all?`, `none?`, `times`, `upto`, `downto`.\n\n**Exceptions**: `begin`\u002F`rescue`\u002F`ensure`\u002F`retry`, `raise`,\ncustom exception classes.\n\n**Types**: Integer, Float, String (immutable + mutable), Array, Hash,\nRange, Time, StringIO, File, Regexp, Bigint (auto-promoted), Fiber.\nPolymorphic values via tagged unions. Nullable object types (`T?`)\nfor self-referential data structures (linked lists, trees).\n\n**Inspect \u002F `p`**: `Object#inspect` is implemented for all primitive\ntypes (Integer, Float, String, Symbol, Boolean, nil), for typed\narrays (`int_array`, `float_array`, `str_array`, `sym_array`), and\nfor heterogeneous arrays (`poly_array`, e.g. `[1, \"x\", :y]`).\nScalar polymorphic values (the tagged-union values from the `Types`\nsection above) also inspect correctly.\n`Array#to_s` is aliased to `Array#inspect`, matching CRuby.\n`Kernel#p` dispatches through `compile_inspect_for` so `p obj`,\n`obj.inspect`, `obj.to_s`, and `\"#{obj.inspect}\"` interpolation\nall produce CRuby-byte-identical output. User-class instances\ninside a polymorphic value currently render as the placeholder\n`\"#\u003CObject>\"` (the runtime has no class-name table yet); Hash,\nRange, and Struct inspect are not yet implemented.\n\n**Global Variables**: `$name` compiled to static C variables with\ntype-mismatch detection at compile time.\n\n**Strings**: `\u003C\u003C` automatically promotes to mutable strings (`sp_String`)\nfor O(n) in-place append. `+`, interpolation, `tr`, `ljust`\u002F`rjust`\u002F`center`,\nand all standard methods work on both. Character comparisons like\n`s[i] == \"c\"` are optimized to direct char array access (zero allocation).\nChained concatenation (`a + b + c + d`) collapses to a single malloc\nvia `sp_str_concat4` \u002F `sp_str_concat_arr` -- N-1 fewer allocations.\nLoop-local `str.split(sep)` reuses the same `sp_StrArray` across\niterations (csv_process: 4 M allocations eliminated).\n\n**Regexp**: Built-in NFA regexp engine (no external dependency).\n`=~`, `$1`-`$9`, `match?`, `gsub(\u002Fre\u002F, str)`, `sub(\u002Fre\u002F, str)`,\n`scan(\u002Fre\u002F)`, `split(\u002Fre\u002F)`.\n\n**Bigint**: Arbitrary precision integers via mruby-bigint. Auto-promoted\nfrom loop multiplication patterns (e.g. `q = q * k`). Linked as static\nlibrary -- only included when used.\n\n**Fiber**: Cooperative concurrency via `ucontext_t`. `Fiber.new`,\n`Fiber#resume`, `Fiber.yield` with value passing. Captures free\nvariables via heap-promoted cells. Per-fiber storage via `Fiber[:k]`\n\u002F `Fiber[:k] = v` (and the `Fiber.current[:k]` aliases) — symbol-keyed\npoly-valued, lazily allocated, shallow-snapshot inherited from the\nparent at `Fiber.new` time.\n\n**Memory**: Mark-and-sweep GC with size-segregated free lists, non-recursive\nmarking, and sticky mark bits. Small classes (≤8 scalar fields, no\ninheritance, no mutation through parameters) are automatically\nstack-allocated as **value types** -- 1M allocations of a 5-field class\ndrop from 85 ms to 2 ms. Programs using only value types emit no GC\nruntime at all.\n\n**Symbols**: Separate `sp_sym` type, distinct from strings (`:a != \"a\"`).\nSymbol literals are interned at compile time (`SPS_name` constants);\n`String#to_sym` uses a dynamic pool only when needed. Symbol-keyed\nhashes (`{a: 1}`) use a dedicated `sp_SymIntHash` that stores\n`sp_sym` (integer) keys directly rather than strings -- no strcmp, no\ndynamic string allocation.\n\n**I\u002FO**: `puts`, `print`, `printf`, `p`, `gets`, `ARGV`, `ENV[]`,\n`File.read\u002Fwrite\u002Fopen` (with blocks), `system()`, backtick.\n\n**FFI**: Direct C calls without an extension compiler. Declarations\n(`ffi_func`, `ffi_lib`, `ffi_const`, `ffi_buffer`, `ffi_read_*`) live\ninside a `module` body; the codegen emits externs and the `spinel`\nwrapper picks up `-l` flags from marker comments. Scalars, strings,\nopaque `:ptr`, integer constants, raw byte buffers, and struct-field\nreads are covered. See [docs\u002FFFI.md](docs\u002FFFI.md) for the full spec\nand [examples\u002Fffi\u002F](examples\u002Fffi\u002F) for runnable demos against libc\u002F\nlibm and sqlite3.\n\n## Optimizations\n\nWhole-program type inference drives several compile-time optimizations:\n\n- **Value-type promotion**: small immutable classes (≤8 scalar fields)\n  become C structs on the stack, eliminating GC overhead entirely.\n- **Constant propagation**: simple literal constants (`N = 100`) are\n  inlined at use sites instead of going through `cst_N` runtime lookup.\n- **Loop-invariant length hoisting**: `while i \u003C arr.length` evaluates\n  `arr.length` once before the loop; `while i \u003C str.length` hoists\n  `strlen`. Mutation of the receiver inside the body (e.g. `arr.push`)\n  correctly disables the hoist.\n- **Method inlining**: short methods (≤3 statements, non-recursive)\n  get `static inline` so gcc can inline them at call sites.\n- **String concat chain flattening**: `a + b + c + d` compiles to a\n  single `sp_str_concat4` \u002F `sp_str_concat_arr` call -- one malloc\n  instead of N-1 intermediate strings.\n- **Bigint auto-promotion**: loops with `x = x * y` or fibonacci-style\n  `c = a + b` self-referential addition auto-promote to bigint.\n- **Bigint `to_s`**: divide-and-conquer O(n log²n) via mruby-bigint's\n  `mpz_get_str` instead of naive O(n²).\n- **Static symbol interning**: `\"literal\".to_sym` resolves to a\n  compile-time `SPS_\u003Cname>` constant; the runtime dynamic pool is\n  only emitted when dynamic interning is actually used.\n- **`strlen` caching in sub_range**: when a string's length is\n  hoisted, `str[i]` accesses use `sp_str_sub_range_len` to skip the\n  internal strlen call.\n- **split reuse**: `fields = line.split(\",\")` inside a loop reuses\n  the existing `sp_StrArray` rather than allocating a new one.\n- **Dead-code elimination**: compiled with `-ffunction-sections\n  -fdata-sections` and linked with `--gc-sections`; each unused\n  runtime function is stripped from the final binary.\n- **Iterative inference early exit**: the param\u002Freturn\u002Fivar fixed-point\n  loop stops as soon as a signature of the three refined arrays stops\n  changing. Most programs converge in 1-2 iterations instead of the\n  full 4, cutting bootstrap time by ~14%.\n- **`parse_id_list` byte walk**: the AST-field list parser (called\n  ~120 K times during self-compile) walks bytes manually via\n  `s.bytes[i]` instead of `s.split(\",\")`, dropping N+1 allocations\n  per call to 2.\n- **Warning-free build**: generated C compiles cleanly at the default\n  warning level across every test and benchmark; the harness uses\n  `-Werror` so regressions surface immediately.\n\n## Architecture\n\n```\nspinel                One-command wrapper script (POSIX shell)\nspinel_parse.c        C frontend: libprism → text AST (1_608 lines)\nspinel_analyze.rb     Type inference: AST → IR (21_162 lines, self-hosted)\nspinel_codegen.rb     C emission: AST + IR → C (30_411 lines, self-hosted)\nlib\u002Fsp_runtime.h      Runtime library header (1_537 lines)\nlib\u002Fsp_bigint.c       Arbitrary precision integers (5_400 lines)\nlib\u002Fregexp\u002F           Built-in regexp engine\ntest\u002F                 384 feature tests\nbenchmark\u002F            52 benchmarks\ndocs\u002F                 Format specs (AST, IR, FFI, sp_Class design)\nMakefile              Build automation\n```\n\nThe two backend stages -- `spinel_analyze.rb` and `spinel_codegen.rb`\n-- are both written in the Ruby subset that Spinel itself can compile:\nclasses, `def`, `attr_accessor`, `if`\u002F`case`\u002F`while`,\n`each`\u002F`map`\u002F`select`, `yield`, `begin`\u002F`rescue`, String\u002FArray\u002FHash\noperations, File I\u002FO.\n\nNo metaprogramming, no `eval`, no `require` in either backend.\n\n### What spinel_analyze does\n\nThe analyze stage owns whole-program type inference. It's a sequence\nof passes over the AST, each one filling in or refining one piece of\nthe static model:\n\n1. **`collect_all`** -- single walk that registers every class,\n   module, top-level method, instance method, class method, ivar\n   declaration, FFI declaration, regexp literal, and constant. After\n   this pass the parallel tables (`@cls_names`, `@meth_names`,\n   `@cls_ivar_names`, ...) carry every name the program defines.\n\n2. **Per-scope call-site widening** -- `infer_main_call_types`,\n   `infer_function_body_call_types`, `infer_class_body_call_types`,\n   `infer_ieval_body_call_types`. Walks each scope's call sites and\n   feeds the arg types into the callee's param-type slots via\n   `unify_call_types`. The unifier widens to `poly` only when two\n   call sites disagree -- the conservative direction.\n\n3. **Iterative refinement loop (≤ 4 rounds)** --\n   `infer_all_returns`, `infer_function_body_call_types`,\n   `infer_class_body_call_types`, `infer_ivar_types_from_writers`,\n   `infer_param_array_type_from_body`,\n   `narrow_param_types_from_body_method_calls`,\n   `narrow_param_hash_types_from_body_writes`,\n   `widen_cmeths_via_hash_each_blocks` (#424),\n   `detect_poly_params`. The loop terminates when\n   `inference_signature` -- a fingerprint over return types, ivar\n   types, param types, cmeth ptypes -- stops changing. Most programs\n   converge in 1-2 rounds; the cap at 4 catches pathological cases\n   without exploding compile time.\n\n4. **Post-loop fixups** -- `fix_nil_ivar_self_refs` (e.g. `@left =\n   nil` on an attr_accessor inside `class Node` resolves to\n   `obj_Node?`), `fix_lambda_return_types`, then re-run the inference\n   passes so dependent types pick up the corrections.\n\n5. **`refine_all_module_ivar_types`** -- with stable param types in\n   hand, module-level `@@h[k] = v` ivar writes can now refine the\n   hash variant from the placeholder `str_int_hash` default to the\n   actual key\u002Fvalue shape.\n\n6. **Feature detection** -- `pre_detect_bigint`, `detect_features`,\n   `detect_value_types`, `recalc_needs_gc`, `collect_sym_names`,\n   `scan_toplevel_ivars`, `compute_live_cls_methods`,\n   `compute_live_instance_methods`. Sets the `@needs_*` flags that\n   gate runtime helper emission and marks classes \u002F methods for DCE.\n\n7. **`precompute_all_scope_decls`** -- runs the multi-pass local-decl\n   refinement per method \u002F cmeth \u002F ieval \u002F main scope and stores the\n   result in `@nd_scope_names[bid]` \u002F `@nd_scope_types[bid]` so\n   codegen doesn't have to re-run `scan_locals`.\n\n8. **`annotate_all_node_types`** -- post-order walks every reachable\n   AST node, calls `infer_type`, fills `@nd_inferred_type[nid]`.\n   Codegen's own `infer_type` hits this cache > 99 % of the time at\n   emit; only block-body expressions (whose scope is\n   iterator-specific) and a few `@current_class_idx`-dependent arms\n   fall through.\n\n9. **`dump_analysis_buf`** -- serializes the result. See\n   [docs\u002FANALYZE-IR.md](docs\u002FANALYZE-IR.md) for the line-oriented\n   text format that lands in the `.ir` file.\n\n### What spinel_codegen does\n\nThe codegen stage reads `.ast` + `.ir`, then emits one C file:\n\n- `load_analysis_buf` reconstructs every analysis-derived ivar from\n  the IR. After this, `@cls_names`, `@meth_return_types`,\n  `@nd_inferred_type`, `@nd_scope_names`, etc. are populated as if\n  the analyze passes had just run.\n\n- `generate_code` emits the standard preamble (`#include\n  \"sp_runtime.h\"`, the per-program runtime helpers gated on\n  `@needs_*` flags, the sp_Class tables for hierarchy-using\n  programs, the symbol intern table, class structs and constructors,\n  forward declarations), then walks every reachable method \u002F cmeth\n  body to emit its `static inline` definition, then emits\n  `int main()`.\n\n- Per-program runtime helpers are gated. A `puts \"hi\"` program emits\n  ~10 lines of C; a program that touches Method instances, hash\n  literals, the class hierarchy, etc. gets the matching runtime\n  blocks. The gating ladder is set by pre-scan passes in\n  `generate_code` so the gates have the right value before the\n  emission decisions land.\n\nThe runtime (`lib\u002Fsp_runtime.h`) contains GC, array\u002Fhash\u002Fstring\nimplementations, and all runtime support as a single header file.\nGenerated C includes this header, and the linker pulls only the\nneeded parts from `libspinel_rt.a` (bigint + regexp engine).\n\nThe parser has two implementations:\n- **spinel_parse.c** links libprism directly (no CRuby needed)\n- **spinel_parse.rb** uses the Prism gem (CRuby fallback)\n\nBoth produce identical AST output. The `spinel` wrapper prefers the\nC binary if available. `require_relative` is resolved at parse time\nby inlining the referenced file.\n\n## Building\n\n```bash\nmake deps         # fetch libprism into vendor\u002Fprism (one-time)\nmake              # build parser + regexp library + bootstrap analyze + codegen\nmake test         # run 384 feature tests (requires bootstrap)\nmake bench        # run 52 benchmarks (requires bootstrap)\nmake bootstrap    # rebuild analyze + codegen from source (4-way fixpoint check)\nsudo make install # install to \u002Fusr\u002Flocal (spinel in PATH)\nmake clean        # remove build artifacts\n```\n\nOverride install prefix: `make install PREFIX=$HOME\u002F.local`\n\n[Prism](https:\u002F\u002Fgithub.com\u002Fruby\u002Fprism) is the Ruby parser used by\n`spinel_parse`. `make deps` downloads the prism gem tarball from\nrubygems.org and extracts its C sources to `vendor\u002Fprism`. If you\nalready have the prism gem installed, the build auto-detects it; you\ncan also point at a custom location with `PRISM_DIR=\u002Fpath\u002Fto\u002Fprism`.\n\nCRuby is needed only for the initial bootstrap. After `make`, the\nentire pipeline runs without Ruby.\n\n## Portability\n\nSpinel can emit C without invoking the C compiler — useful when you\nwant to build the Ruby program on one machine and ship the generated\nsources to another:\n\n```sh\nspinel app.rb -c            # writes app.c next to the source\nspinel app.rb -c -o app.c   # specify output path\nspinel app.rb -S            # print the C to stdout\n```\n\nThe output is one self-contained `.c` file that compiles against\n`lib\u002Fsp_runtime.h`. The two together are everything a downstream\nconsumer needs — no link to `libspinel`.\n\nThe runtime is POSIX-flavoured but covers every platform CI exercises:\n\n| Platform | Status | Compiler |\n|---|---|---|\n| Linux (x86-64, arm64) | Supported | gcc, clang |\n| macOS (Intel, Apple Silicon) | Supported | clang |\n| *BSD | Expected to work; not in CI | clang |\n| Windows | Supported via [MSYS2](https:\u002F\u002Fwww.msys2.org\u002F) \u002F MinGW | gcc |\n| Windows native (MSVC) | Not supported | -- |\n\nEvery PR runs `ubuntu-latest \u002F gcc`, `ubuntu-latest \u002F clang`,\n`macos-latest \u002F clang`, and `windows-mingw` jobs end-to-end (parser\nbuild, codegen build, fixed-point bootstrap, full test + benchmark\nsuites). MSVC isn't supported because the runtime relies on POSIX\nassumptions (`\u003Cucontext.h>` for `Fiber`, `\u003Csys\u002Fmman.h>` for the\nregexp engine's executable buffers, GCC's `__attribute__((cleanup))`\nfor the GC root stack); a port would either replace those or guard\nthem behind compile-time switches.\n\n## Limitations\n\n- **No eval**: `eval`, `instance_eval`, `class_eval`\n- **No metaprogramming**: `send`, `method_missing`, `define_method` (dynamic)\n- **No threads**: `Thread`, `Mutex` (Fiber is supported)\n- **No encoding**: assumes UTF-8\u002FASCII\n- **No general lambda calculus**: deeply nested `-> x { }` with `[]` calls\n\n## Dependencies\n\n- **Build time**: [libprism](https:\u002F\u002Fgithub.com\u002Fruby\u002Fprism) (C library),\n  CRuby (bootstrap only)\n- **Run time**: None. Generated binaries need only libc + libm.\n- **Regexp**: Built-in engine, no external library needed.\n- **Bigint**: Built-in (from mruby-bigint), linked only when used.\n\n## Contributing\n\nContributions are welcome. The issue tracker doubles as the roadmap —\nanything open is fair game; the most useful entry points are\nreproducer-shaped bug reports (a 5-line Ruby that fails in Spinel but\npasses in CRuby) and codegen fixes that close one such report.\n\nWorkflow:\n\n- Open a focused PR. Small and contained merges faster than sweeping\n  refactors.\n- Make `make` close (`gen2.c == gen3.c`) and `make test` \u002F `make bench`\n  pass before pushing.\n- Add a regression test under `test\u002F` for any fix or new feature; the\n  harness compares Spinel's output against CRuby on the same source,\n  so the test usually doesn't need to assert anything beyond `puts`.\n- Reference issues with `Closes #N` \u002F `Fixes #N` \u002F `Refs #N` trailers\n  in the commit message.\n- If the work was assisted by an AI, add a `Co-Authored-By:` trailer\n  for the assistant alongside any human co-authors. The maintainer's\n  own AI-assisted commits use `Co-Authored-By: Claude Opus 4.7`.\n\nAdjacent ecosystem (community-built, not part of this repo):\n\n- [rubocop_spinel](https:\u002F\u002Fgithub.com\u002Fgurgeous\u002Frubocop_spinel) —\n  a RuboCop custom cop that flags Ruby code Spinel doesn't yet\n  support.\n\n## History\n\nSpinel was originally implemented in C (18K lines, branch `c-version`),\nthen rewritten in Ruby (branch `ruby-v1`), and finally rewritten in a\nself-hosting Ruby subset (current `master`).\n\n## License\n\nMIT License. See [LICENSE](LICENSE).\n","Spinel 是一个将 Ruby 源代码编译成独立的本地可执行文件的 AOT（Ahead-of-Time）编译器。它通过全程序类型推断生成优化后的 C 代码，相比 CRuby 可以实现显著的速度提升。Spinel 的核心特点包括使用 Prism 进行解析和序列化抽象语法树、进行全程序类型推断以及自托管的编译后端。这使得 Spinel 非常适合需要高性能 Ruby 应用的场景，如计算密集型任务或希望减少启动时间的应用。此外，由于生成的二进制文件是独立且无运行时依赖的，因此也适用于嵌入式系统或其他资源受限环境。",2,"2026-06-11 03:50:33","high_star"]