Class: Kumi::Core::Analyzer::Folder

Inherits:
Object
  • Object
show all
Defined in:
lib/kumi/core/analyzer/folder.rb

Instance Method Summary collapse

Constructor Details

#initialize(pass, nast_module, order, registry) ⇒ Folder

Returns a new instance of Folder.



7
8
9
10
11
12
13
14
# File 'lib/kumi/core/analyzer/folder.rb', line 7

def initialize(pass, nast_module, order, registry)
  @nast_module = nast_module
  @order = order
  @registry = registry
  @pass = pass
  @known_constants = {}
  @changed = false
end

Instance Method Details

#debug(msg) ⇒ Object



16
17
18
# File 'lib/kumi/core/analyzer/folder.rb', line 16

def debug(msg)
  @pass.method(:debug).call(msg)
end

#foldObject



20
21
22
23
24
25
26
27
28
29
30
# File 'lib/kumi/core/analyzer/folder.rb', line 20

def fold
  folded_decls = {}
  @order.each do |decl_name|
    decl = @nast_module.decls[decl_name]
    next unless decl

    folded_decls[decl_name] = fold_declaration(decl)
  end

  [NAST::Module.new(decls: folded_decls), @changed]
end

#fold_call(node) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/kumi/core/analyzer/folder.rb', line 68

def fold_call(node)
  folded_args = node.args.map { |arg| fold_node(arg) }
  folded_call = if folded_args.map(&:object_id) == node.args.map(&:object_id)
                  node
                else
                  NAST::Call.new(fn: node.fn,
                                 args: folded_args,
                                 opts: node.opts,
                                 loc: node.loc)
                end

  debug "[FOLD]     - Attempting to evaluate call to :#{folded_call.fn}"
  evaluated_value = ConstantEvaluator.evaluate(folded_call, @registry, @known_constants)

  if evaluated_value.nil?
    debug "[FOLD]       - Could not evaluate. Keeping call."
    folded_call
  else
    debug "[FOLD]       - SUCCESS! Folded call to :#{folded_call.fn} -> #{evaluated_value.inspect}"
    @changed = true
    NAST::Const.new(value: evaluated_value, loc: node.loc)
  end
end

#fold_declaration(decl) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/kumi/core/analyzer/folder.rb', line 32

def fold_declaration(decl)
  debug "[FOLD]   Folding declaration :#{decl.name}"
  new_body = fold_node(decl.body)
  @changed ||= !new_body.equal?(decl.body)

  # *** FIX ***: A node is constant if it's a Const OR a Tuple of only Consts.
  is_constant = new_body.is_a?(NAST::Const) ||
                (new_body.is_a?(NAST::Tuple) && new_body.args.all? { |arg| arg.is_a?(NAST::Const) })

  if is_constant
    @known_constants[decl.name] = new_body
    debug "[FOLD]     - Identified :#{decl.name} as a constant value."
  end

  NAST::Declaration.new(name: decl.name, body: new_body, loc: decl.loc, meta: decl.meta)
end

#fold_node(node) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/kumi/core/analyzer/folder.rb', line 49

def fold_node(node)
  case node
  when NAST::Call
    fold_call(node)
  when NAST::Ref
    resolved_node = @known_constants.fetch(node.name, node)
    unless resolved_node.equal?(node)
      debug "[FOLD]     - Propagating constant for ref :#{node.name}"
      @changed = true
    end
    resolved_node
  when NAST::Tuple
    new_args = node.args.map { |arg| fold_node(arg) }
    new_args.map(&:object_id) == node.args.map(&:object_id) ? node : NAST::Tuple.new(args: new_args, loc: node.loc)
  else
    node
  end
end