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 :bind
        name = pattern[1]
        allow_nil = pattern[2]
        return false if value.nil? && !allow_nil

        bindings[name] = value
        true
      when :ref
        bindings.key?(pattern[1]) && bindings[pattern[1]] == value
      when :wild
        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 :pin
        value == pattern[1]
      when :or
        pattern[1].any? do |option|
          option_bindings = bindings.dup
          next false unless kap_match_pattern_into(option, value, option_bindings)

          bindings.replace(option_bindings)
          true
        end
      else
        raise "bad pattern: #{pattern.inspect}"
      end
    end
  RUBY
}.transform_values(&:freeze).freeze

Class Method Summary collapse

Class Method Details

.append_helper_source(name, ordered, seen) ⇒ Object



352
353
354
355
356
357
358
359
360
# File 'lib/kapusta/compiler/runtime.rb', line 352

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

.helper_name(name) ⇒ Object



336
337
338
# File 'lib/kapusta/compiler/runtime.rb', line 336

def helper_name(name)
  "kap_#{name}"
end

.helper_source(helpers) ⇒ Object



340
341
342
343
344
345
346
347
348
349
350
# File 'lib/kapusta/compiler/runtime.rb', line 340

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