Class: Forthic::MapWord

Inherits:
Object
  • Object
show all
Defined in:
lib/forthic/words/map_word.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(items, forthic, forthic_location, flags) ⇒ MapWord

Returns a new instance of MapWord.



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/forthic/words/map_word.rb', line 7

def initialize(items, forthic, forthic_location, flags)
  @forthic = forthic
  @forthic_location = forthic_location
  @items = items
  @flags = flags

  # MAP flags
  @depth = flags[:depth] || 0
  @num_interps = flags[:interps] || 1
  @push_error = flags[:push_error]
  @with_key = flags[:with_key]

  @cur_index = 0
  @result = []
  @errors = []
  @is_debugging = false
  @processing_item = false
  @is_done = false
end

Instance Attribute Details

#cur_indexObject

Returns the value of attribute cur_index.



5
6
7
# File 'lib/forthic/words/map_word.rb', line 5

def cur_index
  @cur_index
end

#depthObject

Returns the value of attribute depth.



5
6
7
# File 'lib/forthic/words/map_word.rb', line 5

def depth
  @depth
end

#errorsObject

Returns the value of attribute errors.



5
6
7
# File 'lib/forthic/words/map_word.rb', line 5

def errors
  @errors
end

#flagsObject

Returns the value of attribute flags.



5
6
7
# File 'lib/forthic/words/map_word.rb', line 5

def flags
  @flags
end

#forthicObject

Returns the value of attribute forthic.



5
6
7
# File 'lib/forthic/words/map_word.rb', line 5

def forthic
  @forthic
end

#forthic_locationObject

Returns the value of attribute forthic_location.



5
6
7
# File 'lib/forthic/words/map_word.rb', line 5

def forthic_location
  @forthic_location
end

#is_debuggingObject

Returns the value of attribute is_debugging.



5
6
7
# File 'lib/forthic/words/map_word.rb', line 5

def is_debugging
  @is_debugging
end

#is_doneObject

Returns the value of attribute is_done.



5
6
7
# File 'lib/forthic/words/map_word.rb', line 5

def is_done
  @is_done
end

#itemsObject

Returns the value of attribute items.



5
6
7
# File 'lib/forthic/words/map_word.rb', line 5

def items
  @items
end

#num_interpsObject

Returns the value of attribute num_interps.



5
6
7
# File 'lib/forthic/words/map_word.rb', line 5

def num_interps
  @num_interps
end

#processing_itemObject

Returns the value of attribute processing_item.



5
6
7
# File 'lib/forthic/words/map_word.rb', line 5

def processing_item
  @processing_item
end

#push_errorObject

Returns the value of attribute push_error.



5
6
7
# File 'lib/forthic/words/map_word.rb', line 5

def push_error
  @push_error
end

#resultObject

Returns the value of attribute result.



5
6
7
# File 'lib/forthic/words/map_word.rb', line 5

def result
  @result
end

#with_keyObject

Returns the value of attribute with_key.



5
6
7
# File 'lib/forthic/words/map_word.rb', line 5

def with_key
  @with_key
end

Instance Method Details

#execute(interp) ⇒ Object



27
28
29
# File 'lib/forthic/words/map_word.rb', line 27

def execute(interp)
  normal_execute(interp)
end

#map(interp, items) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/forthic/words/map_word.rb', line 90

def map(interp, items)
  forthic = @forthic
  forthic_location = @forthic_location
  self_ref = self

  if !items
    interp.stack_push(items)
    return
  end

  # This maps the forthic over an item, storing errors if needed
  map_value = lambda do |key, value, errors|
    interp.stack_push(key) if self_ref.with_key
    interp.stack_push(value)

    if self_ref.push_error
      error = nil
      begin
        # If this runs successfully, it would have pushed the result onto the stack
        interp.run(forthic, forthic_location)
      rescue => e
        # Since this didn't run successfully, push nil onto the stack
        interp.stack_push(nil)
        error = e
      end
      errors.push(error)
    else
      interp.run(forthic, forthic_location)
    end
    interp.stack_pop
  end

  # This recursively descends a record structure
  descend_record = lambda do |record, depth, accum, errors|
    record.each do |k, item|
      if depth > 0
        if item.is_a?(Array)
          accum[k] = []
          descend_list.call(item, depth - 1, accum[k], errors)
        else
          accum[k] = {}
          descend_record.call(item, depth - 1, accum[k], errors)
        end
      else
        accum[k] = map_value.call(k, item, errors)
      end
    end
    accum
  end

  # This recursively descends a list
  descend_list = lambda do |items, depth, accum, errors|
    items.each_with_index do |item, i|
      if depth > 0
        if item.is_a?(Array)
          accum.push([])
          descend_list.call(item, depth - 1, accum.last, errors)
        else
          accum.push({})
          descend_record.call(item, depth - 1, accum.last, errors)
        end
      else
        accum.push(map_value.call(i, item, errors))
      end
    end
    accum
  end

  errors = []
  result = if items.is_a?(Array)
    descend_list.call(items, @depth, [], errors)
  else
    descend_record.call(items, @depth, {}, errors)
  end
  @result = result
  @errors = errors
  [result, errors]
end

#normal_execute(interp) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/forthic/words/map_word.rb', line 31

def normal_execute(interp)
  @is_debugging = false
  items = @items
  if !items || items.empty?
    interp.stack_push(items)
    return
  end

  @result = []
  @errors = []
  if @num_interps > 1
    interp.stack_push(items)
    interp.run("LENGTH")
    num_items = interp.stack_pop
    group_size = (num_items.to_f / @num_interps).ceil
    interp.stack_push(items)
    interp.stack_push(group_size)
    interp.run("GROUPS-OF")
    groups = interp.stack_pop

    # Clone and load up interpreters
    interp_runs = []

    groups.each do |group|
      new_interp = interp.dup
      interp_run = -> { map(new_interp, group) }
      interp_runs.push(interp_run)
    end

    # Run in parallel using threads
    threads = interp_runs.map do |interp_run|
      Thread.new { interp_run.call }
    end
    run_results = threads.map(&:value)

    # Gather results
    is_array = items.is_a?(Array)
    array_result = []
    object_result = {}
    errors = []
    run_results.each do |res|
      if is_array
        array_result.concat(res[0])
      else
        object_result.merge!(res[0])
      end
      errors.concat(res[1])
    end
    @result = is_array ? array_result : object_result
    @errors = errors
  else
    map(interp, items)
  end

  # Return results
  interp.stack_push(@result)
  interp.stack_push(@errors) if @push_error
end