Module: Kapusta::Compiler::Runtime
- Defined in:
- lib/kapusta/compiler/runtime.rb
Constant Summary collapse
- HELPER_DEPENDENCIES =
{ print_values: %i[stringify], concat: %i[stringify], method_path_value: %i[kebab_to_snake], set_method_path: %i[kebab_to_snake], get_ivar: %i[kebab_to_snake], set_ivar: %i[kebab_to_snake], get_cvar: %i[current_class_scope kebab_to_snake], set_cvar: %i[current_class_scope kebab_to_snake], get_gvar: %i[kebab_to_snake], set_gvar: %i[kebab_to_snake], destructure: %i[destructure_into], match_pattern: %i[match_pattern_into] }.freeze
- HELPER_SOURCES =
{ kebab_to_snake: <<~RUBY.chomp, call: <<~'RUBY'.chomp, send_call: <<~RUBY.chomp, invoke_self: <<~RUBY.chomp, stringify: <<~RUBY.chomp, print_values: <<~'RUBY'.chomp, concat: <<~RUBY.chomp, get_path: <<~RUBY.chomp, qget_path: <<~RUBY.chomp, set_path: <<~RUBY.chomp, method_path_value: <<~RUBY.chomp, set_method_path: <<~'RUBY'.chomp, current_class_scope: <<~RUBY.chomp, get_ivar: <<~'RUBY'.chomp, set_ivar: <<~'RUBY'.chomp, get_cvar: <<~'RUBY'.chomp, set_cvar: <<~'RUBY'.chomp, get_gvar: <<~'RUBY'.chomp, set_gvar: <<~'RUBY'.chomp, ensure_module: <<~RUBY.chomp, ensure_class: <<~RUBY.chomp, destructure: <<~RUBY.chomp, destructure_into: <<~'RUBY'.chomp, match_pattern: <<~RUBY.chomp, match_pattern_into: <<~'RUBY'.chomp def __kap_match_pattern_into(pattern, value, bindings) case pattern[0] when :sym name = pattern[1] bindings[name] = value unless name == '_' true when :vec return false unless value.is_a?(Array) || value.respond_to?(:to_ary) array = value.is_a?(Array) ? value : value.to_ary items = pattern[1] rest_idx = items.index { |item| item.is_a?(Array) && item[0] == :rest } if rest_idx before = items[0...rest_idx] rest_pattern = items[rest_idx][1] return false if array.length < before.length before.each_with_index do |item, i| return false unless __kap_match_pattern_into(item, array[i], bindings) end __kap_match_pattern_into(rest_pattern, array[rest_idx..], bindings) else return false unless array.length == items.length items.each_with_index do |item, i| return false unless __kap_match_pattern_into(item, array[i], bindings) end true end when :hash return false unless value.is_a?(Hash) pattern[1].each do |key, subpattern| return false unless value.key?(key) return false unless __kap_match_pattern_into(subpattern, value[key], bindings) end true when :lit value == pattern[1] when :nil value.nil? else raise "bad pattern: #{pattern.inspect}" end end RUBY }.transform_values(&:freeze).freeze
Class Method Summary collapse
- .append_helper_source(name, ordered, seen) ⇒ Object
- .call(callee, positional, kwargs = nil, block = nil) ⇒ Object
- .concat(values) ⇒ Object
- .current_class_scope(receiver) ⇒ Object
- .destructure(pattern, value) ⇒ Object
- .destructure_into(pattern, value, bindings) ⇒ Object
- .ensure_class(holder, path, super_class) ⇒ Object
- .ensure_module(holder, path) ⇒ Object
- .get_cvar(receiver, name) ⇒ Object
- .get_gvar(name) ⇒ Object
- .get_ivar(receiver, name) ⇒ Object
- .get_path(obj, keys) ⇒ Object
- .helper_name(name) ⇒ Object
- .helper_source(helpers) ⇒ Object
- .invoke_self(receiver, method_name, positional, kwargs = nil, block = nil) ⇒ Object
- .match_pattern(pattern, value) ⇒ Object
- .match_pattern_into(pattern, value, bindings) ⇒ Object
- .method_path_value(base, segments) ⇒ Object
- .print_values(*values) ⇒ Object
- .qget_path(obj, keys) ⇒ Object
- .send_call(receiver, method_name, positional, kwargs = nil, block = nil) ⇒ Object
- .set_cvar(receiver, name, value) ⇒ Object
- .set_gvar(name, value) ⇒ Object
- .set_ivar(receiver, name, value) ⇒ Object
- .set_method_path(base, segments, value) ⇒ Object
- .set_path(obj, keys, value) ⇒ Object
- .stringify(value) ⇒ Object
Class Method Details
.append_helper_source(name, ordered, seen) ⇒ Object
320 321 322 323 324 325 326 327 328 |
# File 'lib/kapusta/compiler/runtime.rb', line 320 def append_helper_source(name, ordered, seen) return if seen[name] HELPER_DEPENDENCIES.fetch(name, []).each do |dependency| append_helper_source(dependency, ordered, seen) end ordered << name seen[name] = true end |
.call(callee, positional, kwargs = nil, block = nil) ⇒ Object
330 331 332 333 334 335 336 337 338 |
# File 'lib/kapusta/compiler/runtime.rb', line 330 def call(callee, positional, kwargs = nil, block = nil) raise "not callable: #{callee.inspect}" unless callee.respond_to?(:call) if block kwargs ? callee.call(*positional, **kwargs, &block) : callee.call(*positional, &block) else kwargs ? callee.call(*positional, **kwargs) : callee.call(*positional) end end |
.concat(values) ⇒ Object
383 384 385 |
# File 'lib/kapusta/compiler/runtime.rb', line 383 def concat(values) values.map { |value| stringify(value) }.join end |
.current_class_scope(receiver) ⇒ Object
419 420 421 |
# File 'lib/kapusta/compiler/runtime.rb', line 419 def current_class_scope(receiver) receiver.is_a?(Module) ? receiver : receiver.class end |
.destructure(pattern, value) ⇒ Object
493 494 495 496 497 |
# File 'lib/kapusta/compiler/runtime.rb', line 493 def destructure(pattern, value) bindings = {} destructure_into(pattern, value, bindings) bindings end |
.destructure_into(pattern, value, bindings) ⇒ Object
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 |
# File 'lib/kapusta/compiler/runtime.rb', line 499 def destructure_into(pattern, value, bindings) case pattern[0] when :sym name = pattern[1] bindings[name] = value unless name == '_' when :vec items = pattern[1] rest_idx = items.index { |item| item.is_a?(Array) && item[0] == :rest } if rest_idx before = items[0...rest_idx] rest_pattern = items[rest_idx][1] before.each_with_index do |item, i| destructure_into(item, value ? value[i] : nil, bindings) end rest_value = value ? (value[rest_idx..] || []) : [] destructure_into(rest_pattern, rest_value, bindings) else items.each_with_index do |item, i| destructure_into(item, value ? value[i] : nil, bindings) end end when :hash pattern[1].each do |key, subpattern| destructure_into(subpattern, value ? value[key] : nil, bindings) end when :ignore nil else raise "unknown destructure pattern: #{pattern.inspect}" end end |
.ensure_class(holder, path, super_class) ⇒ Object
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
# File 'lib/kapusta/compiler/runtime.rb', line 470 def ensure_class(holder, path, super_class) segments = path.split('.') last = segments.pop scope = holder.is_a?(Module) ? holder : Object segments.each do |segment| scope = if scope.const_defined?(segment, false) scope.const_get(segment, false) else mod = Module.new scope.const_set(segment, mod) mod end end if scope.const_defined?(last, false) scope.const_get(last, false) else klass = Class.new(super_class) scope.const_set(last, klass) klass end end |
.ensure_module(holder, path) ⇒ Object
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 |
# File 'lib/kapusta/compiler/runtime.rb', line 447 def ensure_module(holder, path) segments = path.split('.') last = segments.pop scope = holder.is_a?(Module) ? holder : Object segments.each do |segment| scope = if scope.const_defined?(segment, false) scope.const_get(segment, false) else mod = Module.new scope.const_set(segment, mod) mod end end if scope.const_defined?(last, false) scope.const_get(last, false) else mod = Module.new scope.const_set(last, mod) mod end end |
.get_cvar(receiver, name) ⇒ Object
431 432 433 |
# File 'lib/kapusta/compiler/runtime.rb', line 431 def get_cvar(receiver, name) current_class_scope(receiver).class_variable_get("@@#{Kapusta.kebab_to_snake(name)}") end |
.get_gvar(name) ⇒ Object
439 440 441 |
# File 'lib/kapusta/compiler/runtime.rb', line 439 def get_gvar(name) Kernel.eval("$#{Kapusta.kebab_to_snake(name)}", binding, __FILE__, __LINE__) # $stderr end |
.get_ivar(receiver, name) ⇒ Object
423 424 425 |
# File 'lib/kapusta/compiler/runtime.rb', line 423 def get_ivar(receiver, name) receiver.instance_variable_get("@#{Kapusta.kebab_to_snake(name)}") end |
.get_path(obj, keys) ⇒ Object
387 388 389 |
# File 'lib/kapusta/compiler/runtime.rb', line 387 def get_path(obj, keys) keys.reduce(obj) { |acc, key| acc[key] } end |
.helper_name(name) ⇒ Object
304 305 306 |
# File 'lib/kapusta/compiler/runtime.rb', line 304 def helper_name(name) "__kap_#{name}" end |
.helper_source(helpers) ⇒ Object
308 309 310 311 312 313 314 315 316 317 318 |
# File 'lib/kapusta/compiler/runtime.rb', line 308 def helper_source(helpers) ordered = [] seen = {} helpers.each { |name| append_helper_source(name.to_sym, ordered, seen) } return '' if ordered.empty? [ ordered.map { |name| HELPER_SOURCES.fetch(name) }.join("\n\n"), "private #{ordered.map { |name| ":#{helper_name(name)}" }.join(', ')}" ].join("\n\n") end |
.invoke_self(receiver, method_name, positional, kwargs = nil, block = nil) ⇒ Object
356 357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/kapusta/compiler/runtime.rb', line 356 def invoke_self(receiver, method_name, positional, kwargs = nil, block = nil) if block if kwargs receiver.send(method_name, *positional, **kwargs, &block) else receiver.send(method_name, *positional, &block) end else kwargs ? receiver.send(method_name, *positional, **kwargs) : receiver.send(method_name, *positional) end end |
.match_pattern(pattern, value) ⇒ Object
531 532 533 534 |
# File 'lib/kapusta/compiler/runtime.rb', line 531 def match_pattern(pattern, value) bindings = {} [match_pattern_into(pattern, value, bindings), bindings] end |
.match_pattern_into(pattern, value, bindings) ⇒ Object
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 |
# File 'lib/kapusta/compiler/runtime.rb', line 536 def match_pattern_into(pattern, value, bindings) case pattern[0] when :sym name = pattern[1] bindings[name] = value unless name == '_' true when :vec return false unless value.is_a?(Array) || value.respond_to?(:to_ary) array = value.is_a?(Array) ? value : value.to_ary items = pattern[1] rest_idx = items.index { |item| item.is_a?(Array) && item[0] == :rest } if rest_idx before = items[0...rest_idx] rest_pattern = items[rest_idx][1] return false if array.length < before.length before.each_with_index do |item, i| return false unless match_pattern_into(item, array[i], bindings) end match_pattern_into(rest_pattern, array[rest_idx..], bindings) else return false unless array.length == items.length items.each_with_index do |item, i| return false unless match_pattern_into(item, array[i], bindings) end true end when :hash return false unless value.is_a?(Hash) pattern[1].each do |key, subpattern| return false unless value.key?(key) return false unless match_pattern_into(subpattern, value[key], bindings) end true when :lit value == pattern[1] when :nil value.nil? else raise "bad pattern: #{pattern.inspect}" end end |
.method_path_value(base, segments) ⇒ Object
406 407 408 |
# File 'lib/kapusta/compiler/runtime.rb', line 406 def method_path_value(base, segments) segments.reduce(base) { |obj, segment| obj.public_send(Kapusta.kebab_to_snake(segment).to_sym) } end |
.print_values(*values) ⇒ Object
378 379 380 381 |
# File 'lib/kapusta/compiler/runtime.rb', line 378 def print_values(*values) $stdout.puts(values.map { |value| stringify(value) }.join("\t")) nil end |
.qget_path(obj, keys) ⇒ Object
391 392 393 394 395 396 397 398 |
# File 'lib/kapusta/compiler/runtime.rb', line 391 def qget_path(obj, keys) keys.each do |key| return nil if obj.nil? obj = obj[key] end obj end |
.send_call(receiver, method_name, positional, kwargs = nil, block = nil) ⇒ Object
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
# File 'lib/kapusta/compiler/runtime.rb', line 340 def send_call(receiver, method_name, positional, kwargs = nil, block = nil) if block if kwargs receiver.public_send(method_name, *positional, **kwargs, &block) else receiver.public_send(method_name, *positional, &block) end elsif kwargs receiver.public_send(method_name, *positional, **kwargs) else receiver.public_send(method_name, *positional) end end |
.set_cvar(receiver, name, value) ⇒ Object
435 436 437 |
# File 'lib/kapusta/compiler/runtime.rb', line 435 def set_cvar(receiver, name, value) current_class_scope(receiver).class_variable_set("@@#{Kapusta.kebab_to_snake(name)}", value) end |
.set_gvar(name, value) ⇒ Object
443 444 445 |
# File 'lib/kapusta/compiler/runtime.rb', line 443 def set_gvar(name, value) Kernel.eval("$#{Kapusta.kebab_to_snake(name)} = value", binding, __FILE__, __LINE__) # $stderr = value end |
.set_ivar(receiver, name, value) ⇒ Object
427 428 429 |
# File 'lib/kapusta/compiler/runtime.rb', line 427 def set_ivar(receiver, name, value) receiver.instance_variable_set("@#{Kapusta.kebab_to_snake(name)}", value) end |
.set_method_path(base, segments, value) ⇒ Object
410 411 412 413 414 415 416 417 |
# File 'lib/kapusta/compiler/runtime.rb', line 410 def set_method_path(base, segments, value) target = base segments[0...-1].each do |segment| target = target.public_send(Kapusta.kebab_to_snake(segment).to_sym) end setter = "#{Kapusta.kebab_to_snake(segments.last)}=" target.public_send(setter.to_sym, value) end |
.set_path(obj, keys, value) ⇒ Object
400 401 402 403 404 |
# File 'lib/kapusta/compiler/runtime.rb', line 400 def set_path(obj, keys, value) target = obj keys[0...-1].each { |key| target = target[key] } target[keys.last] = value end |
.stringify(value) ⇒ Object
369 370 371 372 373 374 375 376 |
# File 'lib/kapusta/compiler/runtime.rb', line 369 def stringify(value) case value when nil then 'nil' when true then 'true' when false then 'false' else value.to_s end end |