Module: CArray::FuseSource

Defined in:
lib/carray/fuse_source.rb

Defined Under Namespace

Classes: Leaves

Class Method Summary collapse

Class Method Details

.body_source(block) ⇒ Object

-- the block's own text ---------------------------------------------



38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/carray/fuse_source.rb', line 38

def self.body_source (block)
  text = extract(block)
  wrapped = "proc " + text
  node = Prism.parse(wrapped).value
              .breadth_first_search { |n| n.is_a?(Prism::BlockNode) }
  inner = node && node.body
  unless inner
    raise ArgumentError,
          "CArray.fuse could not read an expression out of this block"
  end
  wrapped.byteslice(inner.location.start_offset...inner.location.end_offset)
end

.evaluate(block) ⇒ Object



28
29
30
31
32
33
34
# File 'lib/carray/fuse_source.rb', line 28

def self.evaluate (block)
  result = eval(rewrite(body_source(block)), block.binding,
                *block.source_location)
  # An expression that is just an array is that array; the shadow put
  # around it has nothing to fuse.
  result.is_a?(CALazyMarker) ? result.parent : result
end

.extract(block) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/carray/fuse_source.rb', line 51

def self.extract (block)
  sequence = RubyVM::InstructionSequence.of(block) rescue nil
  location = sequence && sequence.to_a[4][:code_location]
  path     = sequence && (sequence.absolute_path || sequence.path)
  unless location && path && File.readable?(path)
    raise ArgumentError,
          "CArray.fuse cannot read this block's source (defined in irb, " \
          "eval, or a file that is no longer there).  Write `.lazy` on " \
          "the operands instead: `a.lazy + b.lazy`."
  end
  lines = File.readlines(path)
  first_line, first_column, last_line, last_column = location
  # The columns count bytes, not characters, so a line with anything
  # multi-byte on it slices in the wrong place unless this does too.
  if first_line == last_line
    lines[first_line - 1].byteslice(first_column...last_column)
  else
    [lines[first_line - 1].byteslice(first_column..),
     *lines[first_line...(last_line - 1)],
     lines[last_line - 1].byteslice(0...last_column)].join
  end
end

.rewrite(source) ⇒ Object



113
114
115
116
117
118
119
120
121
# File 'lib/carray/fuse_source.rb', line 113

def self.rewrite (source)
  visitor = Leaves.new
  Prism.parse(source).value.accept(visitor)
  out = source.dup
  visitor.spots.sort_by { |start, _| -start }.each do |start, stop|
    out[start...stop] = "::CArray::FuseSource.shadow(#{source[start...stop]})"
  end
  out
end

.shadow(value) ⇒ Object

Runtime coercion, so the rewrite never has to work out what a name holds: anything that is not an array passes through untouched.



24
25
26
# File 'lib/carray/fuse_source.rb', line 24

def self.shadow (value)
  value.is_a?(CArray) ? value.lazy : value
end