konpeito 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +44 -0
- data/lib/konpeito/codegen/inliner.rb +34 -0
- data/lib/konpeito/codegen/llvm_generator.rb +61 -3
- data/lib/konpeito/codegen/mruby_backend.rb +101 -3
- data/lib/konpeito/codegen/mruby_helpers.c +125 -18
- data/lib/konpeito/compiler.rb +14 -2
- data/lib/konpeito/hir/builder.rb +26 -0
- data/lib/konpeito/stdlib/clay/clay_native.c +24 -2
- data/lib/konpeito/stdlib/clay_tui/clay_tui.rb +112 -0
- data/lib/konpeito/stdlib/clay_tui/clay_tui.rbs +258 -0
- data/lib/konpeito/stdlib/clay_tui/clay_tui_native.c +452 -0
- data/lib/konpeito/stdlib/kui/kui.rb +345 -0
- data/lib/konpeito/stdlib/kui/kui.rbs +53 -0
- data/lib/konpeito/stdlib/kui/kui_events.rb +17 -0
- data/lib/konpeito/stdlib/kui/kui_events.rbs +1 -0
- data/lib/konpeito/stdlib/kui/kui_gui.rb +317 -0
- data/lib/konpeito/stdlib/kui/kui_gui.rbs +2 -0
- data/lib/konpeito/stdlib/kui/kui_theme.rb +143 -0
- data/lib/konpeito/stdlib/kui/kui_theme.rbs +9 -0
- data/lib/konpeito/stdlib/kui/kui_tui.rb +443 -0
- data/lib/konpeito/stdlib/kui/kui_tui.rbs +5 -0
- data/lib/konpeito/stdlib/raylib/raylib.rb +112 -0
- data/lib/konpeito/stdlib/raylib/raylib.rbs +288 -0
- data/lib/konpeito/stdlib/raylib/raylib_native.c +510 -0
- data/lib/konpeito/stdlib/rpg_framework/rpg_framework.rb +668 -0
- data/lib/konpeito/stdlib/shell/shell.rb +20 -0
- data/lib/konpeito/stdlib/shell/shell.rbs +59 -0
- data/lib/konpeito/stdlib/shell/shell_native.c +195 -0
- data/lib/konpeito/type_checker/rbs_loader.rb +17 -2
- data/lib/konpeito/version.rb +1 -1
- data/vendor/clay/clay_renderer_termbox2.c +215 -0
- data/vendor/termbox2/LICENSE +19 -0
- data/vendor/termbox2/termbox2.h +4307 -0
- data/vendor/termbox2/termbox2_impl.c +3 -0
- metadata +22 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 50ad4b5d35d1b8abd26ee9d941a901b99e281eb9cc7d2deed777f3ebf12b2308
|
|
4
|
+
data.tar.gz: fc91a16a60718a8a910776ab56f01a09d49ec2ee78e71082965392f1dee332e1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c2efa11a609aba145beef6d2b722028f657a6a4d5469864241cbdde1b43dc494b9cf814379788a8ede8d4715d0aceab76df3d64a1c6b14913a3e2f84d0787f1f
|
|
7
|
+
data.tar.gz: 77860386e45ac9a5b78ead9b0f02dfc3d759668fe5ffc3d97c72c4932ca297778d27192c6a698ad264f7641a988b07905dbf6a15c32f10320678999e89a604dc
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.0] - 2026-03-14
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **KUI declarative UI framework**: Pure Ruby DSL for building GUI/TUI apps with a single codebase. Wraps Clay+Raylib (GUI) or ClayTUI (TUI). Widgets: `vpanel`, `hpanel`, `fixed_panel`, `label`, `label_num`, `button`, `menu_item`, `spacer`, `divider`, `progress_bar`. Theme system and unified key event abstraction.
|
|
14
|
+
- **ClayTUI stdlib**: Terminal UI backend using Clay layout engine + termbox2 rendering. Auto-detected via `ClayTUI` module reference.
|
|
15
|
+
- **KonpeitoShell stdlib**: Shell execution (`exec`, `system`), environment variables (`getenv`, `setenv`), and file I/O (`read_file`, `write_file`, `append_file`, `file_exists`) for mruby backend.
|
|
16
|
+
- **RPG framework stdlib**: Reusable RPG game components (tilemap, sprites, camera, battle system) for raylib-based games.
|
|
17
|
+
- **Stdlib auto-detection expansion**: `KonpeitoJSON`, `KonpeitoHTTP`, `KonpeitoCrypto`, `KonpeitoCompression` added to `STDLIB_MODULE_MAP` for automatic RBS injection when referenced in source code.
|
|
18
|
+
- **KUI auto-path resolution**: `require "kui_gui"` / `require "kui_tui"` works without `-I` flags — KUI stdlib directory is automatically included in compiler search paths.
|
|
19
|
+
- **KUI example apps**: Counter (GUI + TUI), minimal hello, multi-page dashboard with sidebar navigation.
|
|
20
|
+
- **DQ RPG demo with Clay UI**: Battle scenes, shop menus, and status displays using Clay layout integration.
|
|
21
|
+
- Keyword arguments documented as supported in mruby backend.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- **mruby GC crash in native callbacks**: `rb_block_call` stored raw C stack pointer (`data2`) in proc env — GC tried to dereference it as `RBasic*` causing crash in `gc_mark_children`. Fixed by storing `mrb_nil_value()` instead; the native callback path reads `data2` from a global, not from proc env.
|
|
25
|
+
- **TUI rendering flicker**: Added per-frame string pool to `clay_tui_native.c` (64KB static buffer) to prevent Clay from holding pointers to GC-managed mruby heap memory that could be invalidated between frames.
|
|
26
|
+
- **Documentation references**: Fixed CLAUDE.md referencing non-existent `docs/architecture.md`.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- Tutorials (EN/JA) updated with KUI framework sections and all stdlib module documentation (Shell, JSON, HTTP, Crypto, Compression).
|
|
30
|
+
|
|
31
|
+
## [0.6.0] - 2026-03-11
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- **Raylib stdlib expansion**: 119 new cfunc bindings (87 → 206 total) for 2D RPG/SLG game development
|
|
35
|
+
- Texture/Sprite management (10): load, unload, draw, draw_rec, draw_pro, draw_scaled, dimensions, validity check
|
|
36
|
+
- Audio — Sound (10): load, unload, play, stop, pause, resume, playing?, volume, pitch
|
|
37
|
+
- Audio — Music (13): load, unload, play, stop, pause, resume, update, playing?, volume, pitch, time_length, time_played, seek
|
|
38
|
+
- Audio — Device (4): init, close, ready?, master volume
|
|
39
|
+
- Camera2D (4): begin/end mode, world-to-screen coordinate conversion (x/y)
|
|
40
|
+
- File I/O (4): save/load text files, file/directory existence checks
|
|
41
|
+
- Font management (6): load, load_ex, unload, draw_text_ex, measure_text_ex (x/y)
|
|
42
|
+
- Gamepad input (7 + 21 constants): button pressed/down/released/up, axis movement/count, D-pad/face/trigger/middle button and axis constants
|
|
43
|
+
- Extended shapes (5): draw_rectangle_pro, draw_rectangle_rounded, gradient v/h, circle sector
|
|
44
|
+
- Collision detection (5): recs, circles, circle-rec, point-rec, point-circle
|
|
45
|
+
- ID table pattern for resource management: textures (256), sounds (128), music (32), fonts (32)
|
|
46
|
+
- **RPG tilemap demo**: `examples/mruby_rpg_demo/rpg_demo.rb` — 40×40 tilemap, smooth camera scrolling, 4-direction player movement with animation, sign interaction, HUD with step counter
|
|
47
|
+
- macOS audio framework linker flags (CoreAudio, AudioToolbox, CoreFoundation)
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
- **Inliner CFuncCall bug**: `clone_and_rename()` now handles `CFuncCall`, `ExternConstructorCall`, and `ExternMethodCall` — parameters are correctly remapped when user functions calling `@cfunc` are inlined (previously fell through to default case, returning instructions with unmapped parameters)
|
|
51
|
+
- **CI lint**: use anonymous block forwarding (`&`) in `rbs_loader.rb`
|
|
52
|
+
- **CI test isolation**: use unique symbol names in `module_native_array_test` to avoid ELF symbol interposition on Linux
|
|
53
|
+
|
|
10
54
|
## [0.5.0] - 2026-03-10
|
|
11
55
|
|
|
12
56
|
### Added
|
|
@@ -542,6 +542,40 @@ module Konpeito
|
|
|
542
542
|
result_var: new_result
|
|
543
543
|
)
|
|
544
544
|
|
|
545
|
+
when HIR::CFuncCall
|
|
546
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
547
|
+
new_args = inst.args.map { |a| transform_value(a, prefix, param_map) }
|
|
548
|
+
HIR::CFuncCall.new(
|
|
549
|
+
c_func_name: inst.c_func_name,
|
|
550
|
+
args: new_args,
|
|
551
|
+
cfunc_type: inst.cfunc_type,
|
|
552
|
+
result_var: new_result
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
when HIR::ExternConstructorCall
|
|
556
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
557
|
+
new_args = inst.args.map { |a| transform_value(a, prefix, param_map) }
|
|
558
|
+
HIR::ExternConstructorCall.new(
|
|
559
|
+
extern_type: inst.extern_type,
|
|
560
|
+
c_func_name: inst.c_func_name,
|
|
561
|
+
args: new_args,
|
|
562
|
+
method_sig: inst.method_sig,
|
|
563
|
+
result_var: new_result
|
|
564
|
+
)
|
|
565
|
+
|
|
566
|
+
when HIR::ExternMethodCall
|
|
567
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
568
|
+
new_receiver = transform_value(inst.receiver, prefix, param_map)
|
|
569
|
+
new_args = inst.args.map { |a| transform_value(a, prefix, param_map) }
|
|
570
|
+
HIR::ExternMethodCall.new(
|
|
571
|
+
receiver: new_receiver,
|
|
572
|
+
c_func_name: inst.c_func_name,
|
|
573
|
+
args: new_args,
|
|
574
|
+
extern_type: inst.extern_type,
|
|
575
|
+
method_sig: inst.method_sig,
|
|
576
|
+
result_var: new_result
|
|
577
|
+
)
|
|
578
|
+
|
|
545
579
|
else
|
|
546
580
|
# For other instructions, just return as-is with renamed result
|
|
547
581
|
inst
|
|
@@ -69,7 +69,7 @@ module Konpeito
|
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
attr_reader :profiler, :variadic_functions, :alias_renamed_methods
|
|
72
|
+
attr_reader :profiler, :variadic_functions, :keyword_param_functions, :alias_renamed_methods
|
|
73
73
|
|
|
74
74
|
def generate(hir_program)
|
|
75
75
|
@hir_program = hir_program
|
|
@@ -1126,6 +1126,17 @@ module Konpeito
|
|
|
1126
1126
|
init_builder.store(init_builder.call(mrb_undef_fn), @mrb_qundef_global)
|
|
1127
1127
|
init_builder.ret_void
|
|
1128
1128
|
|
|
1129
|
+
# GC arena management functions (prevent arena overflow in loops)
|
|
1130
|
+
@konpeito_gc_arena_save = @mod.functions.add(
|
|
1131
|
+
"konpeito_gc_arena_save", [], LLVM::Int32
|
|
1132
|
+
)
|
|
1133
|
+
@konpeito_gc_arena_restore = @mod.functions.add(
|
|
1134
|
+
"konpeito_gc_arena_restore", [LLVM::Int32], LLVM.Void
|
|
1135
|
+
)
|
|
1136
|
+
@konpeito_gc_protect_value = @mod.functions.add(
|
|
1137
|
+
"konpeito_gc_protect_value", [value_type], LLVM.Void
|
|
1138
|
+
)
|
|
1139
|
+
|
|
1129
1140
|
# Clear @qnil etc. so the accessor methods know to load from globals
|
|
1130
1141
|
@qnil = nil
|
|
1131
1142
|
@qtrue = nil
|
|
@@ -1728,6 +1739,16 @@ module Konpeito
|
|
|
1728
1739
|
end
|
|
1729
1740
|
end
|
|
1730
1741
|
|
|
1742
|
+
# For mruby: save GC arena index so we can restore before return.
|
|
1743
|
+
# This prevents arena overflow when LLVM functions call each other
|
|
1744
|
+
# directly (bypassing mruby dispatch's arena management).
|
|
1745
|
+
@mruby_gc_arena_alloca = nil
|
|
1746
|
+
if @runtime == :mruby
|
|
1747
|
+
@mruby_gc_arena_alloca = @builder.alloca(LLVM::Int32, "_gc_arena")
|
|
1748
|
+
arena_idx = @builder.call(@konpeito_gc_arena_save)
|
|
1749
|
+
@builder.store(arena_idx, @mruby_gc_arena_alloca)
|
|
1750
|
+
end
|
|
1751
|
+
|
|
1731
1752
|
# Insert profiling entry probe after parameter setup
|
|
1732
1753
|
insert_profile_entry_probe(hir_func)
|
|
1733
1754
|
|
|
@@ -1753,6 +1774,21 @@ module Konpeito
|
|
|
1753
1774
|
func
|
|
1754
1775
|
end
|
|
1755
1776
|
|
|
1777
|
+
# Emit a return instruction with mruby GC arena management.
|
|
1778
|
+
# For the mruby backend, restores the arena to the saved index and
|
|
1779
|
+
# protects the return value from GC before returning.
|
|
1780
|
+
def emit_ret(value)
|
|
1781
|
+
if @mruby_gc_arena_alloca
|
|
1782
|
+
idx = @builder.load2(LLVM::Int32, @mruby_gc_arena_alloca, "_arena_r")
|
|
1783
|
+
@builder.call(@konpeito_gc_arena_restore, idx)
|
|
1784
|
+
# Protect the return value so it survives the arena restore
|
|
1785
|
+
if value.type == value_type
|
|
1786
|
+
@builder.call(@konpeito_gc_protect_value, value)
|
|
1787
|
+
end
|
|
1788
|
+
end
|
|
1789
|
+
@builder.ret(value)
|
|
1790
|
+
end
|
|
1791
|
+
|
|
1756
1792
|
# Topologically sort blocks based on phi dependencies
|
|
1757
1793
|
# Ensures blocks are generated after the blocks their phi nodes reference
|
|
1758
1794
|
def sort_blocks_by_phi_dependencies(blocks)
|
|
@@ -4843,6 +4879,9 @@ module Konpeito
|
|
|
4843
4879
|
saved_allocas = @variable_allocas.dup
|
|
4844
4880
|
saved_in_block_callback = @in_block_callback
|
|
4845
4881
|
saved_block_callback_self = @block_callback_self
|
|
4882
|
+
saved_mrb_cache = save_mrb_constant_cache
|
|
4883
|
+
saved_current_function = @current_function
|
|
4884
|
+
saved_gc_arena_alloca = @mruby_gc_arena_alloca
|
|
4846
4885
|
|
|
4847
4886
|
# Create entry block for callback
|
|
4848
4887
|
entry = callback_func.basic_blocks.append("entry")
|
|
@@ -4850,10 +4889,16 @@ module Konpeito
|
|
|
4850
4889
|
|
|
4851
4890
|
# Reset variable tracking for callback scope.
|
|
4852
4891
|
# Set @in_block_callback so nested proc creation uses GC-safe escape-cells mode.
|
|
4892
|
+
# Reset mruby constant cache — LLVM values are scoped per function, so
|
|
4893
|
+
# cached %qnil/%qtrue from the parent function are invalid here.
|
|
4894
|
+
# Clear GC arena alloca — callbacks don't own their own arena scope.
|
|
4853
4895
|
@in_block_callback = true
|
|
4896
|
+
@mruby_gc_arena_alloca = nil
|
|
4854
4897
|
@variables = {}
|
|
4855
4898
|
@variable_types = {}
|
|
4856
4899
|
@variable_allocas = {}
|
|
4900
|
+
reset_mrb_constant_cache
|
|
4901
|
+
@current_function = callback_func
|
|
4857
4902
|
|
|
4858
4903
|
# Setup captured variable access through data2 pointer
|
|
4859
4904
|
unless captures.empty?
|
|
@@ -5132,6 +5177,9 @@ module Konpeito
|
|
|
5132
5177
|
@variable_allocas = saved_allocas
|
|
5133
5178
|
@in_block_callback = saved_in_block_callback
|
|
5134
5179
|
@block_callback_self = saved_block_callback_self
|
|
5180
|
+
restore_mrb_constant_cache(saved_mrb_cache)
|
|
5181
|
+
@current_function = saved_current_function
|
|
5182
|
+
@mruby_gc_arena_alloca = saved_gc_arena_alloca
|
|
5135
5183
|
|
|
5136
5184
|
callback_func
|
|
5137
5185
|
end
|
|
@@ -5583,6 +5631,7 @@ module Konpeito
|
|
|
5583
5631
|
saved_block_callback_self = @block_callback_self
|
|
5584
5632
|
saved_in_thread_callback = @in_thread_callback
|
|
5585
5633
|
saved_thread_boxed_vars = @thread_boxed_vars
|
|
5634
|
+
saved_gc_arena_alloca = @mruby_gc_arena_alloca
|
|
5586
5635
|
|
|
5587
5636
|
# Create entry block for callback
|
|
5588
5637
|
entry = callback_func.basic_blocks.append("entry")
|
|
@@ -5599,6 +5648,7 @@ module Konpeito
|
|
|
5599
5648
|
@in_block_callback = false
|
|
5600
5649
|
@block_callback_self = nil
|
|
5601
5650
|
@in_thread_callback = true
|
|
5651
|
+
@mruby_gc_arena_alloca = nil
|
|
5602
5652
|
|
|
5603
5653
|
unless captures.empty?
|
|
5604
5654
|
declare_free
|
|
@@ -5735,6 +5785,7 @@ module Konpeito
|
|
|
5735
5785
|
@block_callback_self = saved_block_callback_self
|
|
5736
5786
|
@in_thread_callback = saved_in_thread_callback
|
|
5737
5787
|
@thread_boxed_vars = saved_thread_boxed_vars
|
|
5788
|
+
@mruby_gc_arena_alloca = saved_gc_arena_alloca
|
|
5738
5789
|
|
|
5739
5790
|
callback_func
|
|
5740
5791
|
end
|
|
@@ -8134,11 +8185,13 @@ module Konpeito
|
|
|
8134
8185
|
saved_block_callback_self = @block_callback_self
|
|
8135
8186
|
saved_rescue_escape_array = @rescue_escape_array
|
|
8136
8187
|
saved_rescue_escape_indices = @rescue_escape_indices
|
|
8188
|
+
saved_gc_arena_alloca = @mruby_gc_arena_alloca
|
|
8137
8189
|
|
|
8138
8190
|
# Reset variable tracking for callback scope
|
|
8139
8191
|
@variables = {}
|
|
8140
8192
|
@variable_types = {}
|
|
8141
8193
|
@variable_allocas = {}
|
|
8194
|
+
@mruby_gc_arena_alloca = nil
|
|
8142
8195
|
|
|
8143
8196
|
if try_hir_blocks && !try_hir_blocks.empty?
|
|
8144
8197
|
# Structured try body: process HIR BasicBlock list (handles control flow inside try).
|
|
@@ -8231,6 +8284,7 @@ module Konpeito
|
|
|
8231
8284
|
@block_callback_self = saved_block_callback_self
|
|
8232
8285
|
@rescue_escape_array = saved_rescue_escape_array
|
|
8233
8286
|
@rescue_escape_indices = saved_rescue_escape_indices
|
|
8287
|
+
@mruby_gc_arena_alloca = saved_gc_arena_alloca
|
|
8234
8288
|
|
|
8235
8289
|
callback_func
|
|
8236
8290
|
end
|
|
@@ -8252,6 +8306,7 @@ module Konpeito
|
|
|
8252
8306
|
saved_block_callback_self = @block_callback_self
|
|
8253
8307
|
saved_rescue_escape_array = @rescue_escape_array
|
|
8254
8308
|
saved_rescue_escape_indices = @rescue_escape_indices
|
|
8309
|
+
saved_gc_arena_alloca = @mruby_gc_arena_alloca
|
|
8255
8310
|
|
|
8256
8311
|
# In normal mode: params[0] = self. In escape mode, set @block_callback_self after unpacking.
|
|
8257
8312
|
@block_callback_self = callback_func.params[0] unless escape_var_names
|
|
@@ -8264,6 +8319,7 @@ module Konpeito
|
|
|
8264
8319
|
@variables = {}
|
|
8265
8320
|
@variable_types = {}
|
|
8266
8321
|
@variable_allocas = {}
|
|
8322
|
+
@mruby_gc_arena_alloca = nil
|
|
8267
8323
|
|
|
8268
8324
|
# CRuby rescue callback: (VALUE data2, VALUE exception)
|
|
8269
8325
|
exception_val = callback_func.params[1]
|
|
@@ -8393,6 +8449,7 @@ module Konpeito
|
|
|
8393
8449
|
@block_callback_self = saved_block_callback_self
|
|
8394
8450
|
@rescue_escape_array = saved_rescue_escape_array
|
|
8395
8451
|
@rescue_escape_indices = saved_rescue_escape_indices
|
|
8452
|
+
@mruby_gc_arena_alloca = saved_gc_arena_alloca
|
|
8396
8453
|
|
|
8397
8454
|
callback_func
|
|
8398
8455
|
end
|
|
@@ -8532,6 +8589,7 @@ module Konpeito
|
|
|
8532
8589
|
@block_callback_self = saved_block_callback_self
|
|
8533
8590
|
@rescue_escape_array = saved_rescue_escape_array
|
|
8534
8591
|
@rescue_escape_indices = saved_rescue_escape_indices
|
|
8592
|
+
@mruby_gc_arena_alloca = saved_gc_arena_alloca
|
|
8535
8593
|
|
|
8536
8594
|
callback_func
|
|
8537
8595
|
end
|
|
@@ -9593,9 +9651,9 @@ module Konpeito
|
|
|
9593
9651
|
value, type_tag = get_value_with_type(term.value)
|
|
9594
9652
|
# Box the value before returning to Ruby
|
|
9595
9653
|
boxed = convert_value(value, type_tag, :value)
|
|
9596
|
-
|
|
9654
|
+
emit_ret(boxed)
|
|
9597
9655
|
else
|
|
9598
|
-
|
|
9656
|
+
emit_ret(qnil)
|
|
9599
9657
|
end
|
|
9600
9658
|
when HIR::Branch
|
|
9601
9659
|
condition, cond_type = get_value_with_type(term.condition)
|
|
@@ -8,6 +8,10 @@ module Konpeito
|
|
|
8
8
|
module Codegen
|
|
9
9
|
# Generates standalone executable from LLVM module using mruby runtime
|
|
10
10
|
class MRubyBackend
|
|
11
|
+
# Stdlib modules that should be auto-defined in mruby init code
|
|
12
|
+
# even when no ModuleDef exists in HIR (cfunc-only modules).
|
|
13
|
+
STDLIB_MODULES = %w[Raylib Clay ClayTUI KonpeitoShell].freeze
|
|
14
|
+
|
|
11
15
|
attr_reader :llvm_generator, :output_file, :module_name, :rbs_loader, :debug
|
|
12
16
|
|
|
13
17
|
def initialize(llvm_generator, output_file:, module_name: nil, rbs_loader: nil, debug: false, extra_c_files: [],
|
|
@@ -62,6 +66,10 @@ module Konpeito
|
|
|
62
66
|
clay_objs = ensure_clay_compiled
|
|
63
67
|
extra_obj_files.concat(clay_objs)
|
|
64
68
|
|
|
69
|
+
# Compile vendored termbox2 library if ClayTUI is used
|
|
70
|
+
tb2_objs = ensure_termbox_compiled
|
|
71
|
+
extra_obj_files.concat(tb2_objs)
|
|
72
|
+
|
|
65
73
|
obj_files = [obj_file, init_obj_file, helpers_obj_file] + extra_obj_files
|
|
66
74
|
|
|
67
75
|
# Link into standalone executable
|
|
@@ -278,7 +286,9 @@ module Konpeito
|
|
|
278
286
|
lines << ""
|
|
279
287
|
|
|
280
288
|
# Define modules
|
|
289
|
+
defined_module_names = []
|
|
281
290
|
hir.modules.each do |module_def|
|
|
291
|
+
defined_module_names << module_def.name.to_s
|
|
282
292
|
module_var = "m#{module_def.name}"
|
|
283
293
|
lines << " struct RClass *#{module_var} = mrb_define_module(mrb, \"#{module_def.name}\");"
|
|
284
294
|
|
|
@@ -319,6 +329,18 @@ module Konpeito
|
|
|
319
329
|
end
|
|
320
330
|
end
|
|
321
331
|
|
|
332
|
+
# Auto-define stdlib modules that have cfunc methods but no ModuleDef in HIR.
|
|
333
|
+
# This ensures that even if a dynamic dispatch fallback occurs (e.g., due to
|
|
334
|
+
# a method name mismatch), the module constant exists at runtime.
|
|
335
|
+
if @rbs_loader
|
|
336
|
+
STDLIB_MODULES.each do |mod_name|
|
|
337
|
+
next if defined_module_names.include?(mod_name)
|
|
338
|
+
next unless @rbs_loader.has_cfunc_methods?(mod_name.to_sym)
|
|
339
|
+
|
|
340
|
+
lines << " mrb_define_module(mrb, \"#{mod_name}\");"
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
322
344
|
# Define NativeClasses with mrb_data_type
|
|
323
345
|
native_classes.each do |class_name, class_type|
|
|
324
346
|
lines.concat(generate_native_class_init(class_name, class_type))
|
|
@@ -434,8 +456,21 @@ module Konpeito
|
|
|
434
456
|
else
|
|
435
457
|
arity = llvm_func.params.size - 1 # Subtract self
|
|
436
458
|
|
|
459
|
+
# Check if this function has only keyword args (no positional params).
|
|
460
|
+
# If so, the kwargs hash arg is optional — caller may pass 0 args
|
|
461
|
+
# when no keywords are specified (e.g., `footer do ... end`).
|
|
462
|
+
func_base_name = mangled_name.sub(/\Arn_/, "")
|
|
463
|
+
kw_info = llvm_generator.keyword_param_functions[func_base_name] ||
|
|
464
|
+
llvm_generator.keyword_param_functions[func_base_name.to_sym]
|
|
465
|
+
kwargs_only = kw_info && kw_info[:regular_count] == 0 && arity == 1
|
|
466
|
+
|
|
437
467
|
lines << "static mrb_value #{wrapper_name}(mrb_state *mrb, mrb_value self) {"
|
|
438
|
-
if
|
|
468
|
+
if kwargs_only
|
|
469
|
+
# Single optional kwargs hash arg — default to empty hash
|
|
470
|
+
lines << " mrb_value a0 = mrb_nil_value(), _block = mrb_nil_value();"
|
|
471
|
+
lines << " mrb_get_args(mrb, \"|o&\", &a0, &_block);"
|
|
472
|
+
lines << " if (mrb_nil_p(a0)) a0 = mrb_hash_new(mrb);"
|
|
473
|
+
elsif arity > 0
|
|
439
474
|
lines << " mrb_value #{(0...arity).map { |i| "a#{i}" }.join(', ')}, _block;"
|
|
440
475
|
format_str = "o" * arity + "&"
|
|
441
476
|
args_list = (0...arity).map { |i| "&a#{i}" }.join(", ") + ", &_block"
|
|
@@ -933,6 +968,36 @@ module Konpeito
|
|
|
933
968
|
)
|
|
934
969
|
end
|
|
935
970
|
|
|
971
|
+
# Include termbox2 if ClayTUI is used
|
|
972
|
+
clay_tui_used = @extra_c_files.any? { |f| File.basename(f).include?("clay_tui") }
|
|
973
|
+
if clay_tui_used
|
|
974
|
+
sections << license_section(
|
|
975
|
+
"termbox2",
|
|
976
|
+
"MIT",
|
|
977
|
+
"Copyright (c) 2021 termbox developers",
|
|
978
|
+
"https://github.com/termbox/termbox2",
|
|
979
|
+
<<~MIT
|
|
980
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
981
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
982
|
+
in the Software without restriction, including without limitation the rights
|
|
983
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
984
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
985
|
+
furnished to do so, subject to the following conditions:
|
|
986
|
+
|
|
987
|
+
The above copyright notice and this permission notice shall be included in all
|
|
988
|
+
copies or substantial portions of the Software.
|
|
989
|
+
|
|
990
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
991
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
992
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
993
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
994
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
995
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
996
|
+
SOFTWARE.
|
|
997
|
+
MIT
|
|
998
|
+
)
|
|
999
|
+
end
|
|
1000
|
+
|
|
936
1001
|
# Include raylib if linked
|
|
937
1002
|
ffi_libs = @rbs_loader&.all_ffi_libraries || []
|
|
938
1003
|
if ffi_libs.any? { |lib| lib.to_s.include?("raylib") }
|
|
@@ -1171,7 +1236,9 @@ module Konpeito
|
|
|
1171
1236
|
# Known framework dependencies for popular libraries
|
|
1172
1237
|
case lib_name
|
|
1173
1238
|
when "raylib"
|
|
1174
|
-
["-framework", "IOKit", "-framework", "Cocoa", "-framework", "OpenGL"
|
|
1239
|
+
["-framework", "IOKit", "-framework", "Cocoa", "-framework", "OpenGL",
|
|
1240
|
+
"-framework", "CoreAudio", "-framework", "AudioToolbox",
|
|
1241
|
+
"-framework", "CoreFoundation"]
|
|
1175
1242
|
when "SDL2", "sdl2"
|
|
1176
1243
|
["-framework", "IOKit", "-framework", "Cocoa", "-framework", "Carbon",
|
|
1177
1244
|
"-framework", "CoreAudio", "-framework", "AudioToolbox",
|
|
@@ -1218,15 +1285,46 @@ module Konpeito
|
|
|
1218
1285
|
[clay_impl_obj]
|
|
1219
1286
|
end
|
|
1220
1287
|
|
|
1288
|
+
# Compile vendored termbox2 implementation if ClayTUI stdlib is used
|
|
1289
|
+
# Returns array of object file paths
|
|
1290
|
+
def ensure_termbox_compiled
|
|
1291
|
+
clay_tui_used = @extra_c_files.any? { |f| File.basename(f).include?("clay_tui") }
|
|
1292
|
+
return [] unless clay_tui_used
|
|
1293
|
+
|
|
1294
|
+
tb2_dir = File.expand_path("../../../vendor/termbox2", __dir__)
|
|
1295
|
+
tb2_impl_c = File.join(tb2_dir, "termbox2_impl.c")
|
|
1296
|
+
tb2_impl_obj = File.join(tb2_dir, "termbox2_impl.o")
|
|
1297
|
+
|
|
1298
|
+
return [] unless File.exist?(tb2_impl_c)
|
|
1299
|
+
|
|
1300
|
+
cc, cc_flags = cross_cc_with_flags
|
|
1301
|
+
|
|
1302
|
+
# Compile termbox2_impl.c (only if stale)
|
|
1303
|
+
unless File.exist?(tb2_impl_obj) && File.mtime(tb2_impl_obj) > File.mtime(tb2_impl_c)
|
|
1304
|
+
cmd = [*cc, "-c", "-O2"]
|
|
1305
|
+
cmd.concat(cc_flags)
|
|
1306
|
+
cmd += ["-o", tb2_impl_obj, tb2_impl_c]
|
|
1307
|
+
system(*cmd) or return []
|
|
1308
|
+
end
|
|
1309
|
+
|
|
1310
|
+
[tb2_impl_obj]
|
|
1311
|
+
end
|
|
1312
|
+
|
|
1221
1313
|
def ffi_include_flags
|
|
1222
1314
|
flags = []
|
|
1223
1315
|
|
|
1224
|
-
# Always add vendored Clay include path if Clay stdlib is used
|
|
1316
|
+
# Always add vendored Clay include path if Clay/ClayTUI stdlib is used
|
|
1225
1317
|
clay_dir = File.expand_path("../../../vendor/clay", __dir__)
|
|
1226
1318
|
if @extra_c_files.any? { |f| File.basename(f).include?("clay") } && Dir.exist?(clay_dir)
|
|
1227
1319
|
flags << "-I#{clay_dir}"
|
|
1228
1320
|
end
|
|
1229
1321
|
|
|
1322
|
+
# Add vendored termbox2 include path if ClayTUI stdlib is used
|
|
1323
|
+
tb2_dir = File.expand_path("../../../vendor/termbox2", __dir__)
|
|
1324
|
+
if @extra_c_files.any? { |f| File.basename(f).include?("clay_tui") } && Dir.exist?(tb2_dir)
|
|
1325
|
+
flags << "-I#{tb2_dir}"
|
|
1326
|
+
end
|
|
1327
|
+
|
|
1230
1328
|
if cross_compiling?
|
|
1231
1329
|
# When cross-compiling, use cross library include paths
|
|
1232
1330
|
flags << "-I#{@cross_libs_dir}/../include" if @cross_libs_dir && Dir.exist?("#{@cross_libs_dir}/../include")
|