q-language 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. data/.gemtest +0 -0
  2. data/Rakefile +10 -0
  3. data/lib/q-language.rb +9 -0
  4. data/lib/q-language/device.rb +166 -0
  5. data/lib/q-language/environment.rb +358 -0
  6. data/lib/q-language/methods/array.rb +523 -0
  7. data/lib/q-language/methods/block.rb +26 -0
  8. data/lib/q-language/methods/class.rb +24 -0
  9. data/lib/q-language/methods/dynamic.rb +82 -0
  10. data/lib/q-language/methods/false.rb +47 -0
  11. data/lib/q-language/methods/hash.rb +351 -0
  12. data/lib/q-language/methods/implicit.rb +345 -0
  13. data/lib/q-language/methods/module.rb +39 -0
  14. data/lib/q-language/methods/nil.rb +47 -0
  15. data/lib/q-language/methods/number.rb +118 -0
  16. data/lib/q-language/methods/object.rb +155 -0
  17. data/lib/q-language/methods/string.rb +157 -0
  18. data/lib/q-language/methods/time.rb +110 -0
  19. data/lib/q-language/methods/token.rb +72 -0
  20. data/lib/q-language/methods/true.rb +14 -0
  21. data/lib/q-language/node.rb +45 -0
  22. data/lib/q-language/object.rb +125 -0
  23. data/lib/q-language/parser.rb +104 -0
  24. data/lib/q-language/writer.rb +90 -0
  25. data/test/methods/test_array.rb +191 -0
  26. data/test/methods/test_block.rb +66 -0
  27. data/test/methods/test_class.rb +34 -0
  28. data/test/methods/test_dynamic.rb +158 -0
  29. data/test/methods/test_false.rb +60 -0
  30. data/test/methods/test_hash.rb +10 -0
  31. data/test/methods/test_implicit.rb +332 -0
  32. data/test/methods/test_module.rb +55 -0
  33. data/test/methods/test_nil.rb +60 -0
  34. data/test/methods/test_number.rb +10 -0
  35. data/test/methods/test_object.rb +157 -0
  36. data/test/methods/test_string.rb +271 -0
  37. data/test/methods/test_time.rb +181 -0
  38. data/test/methods/test_token.rb +92 -0
  39. data/test/methods/test_true.rb +16 -0
  40. data/test/test.rb +23 -0
  41. metadata +103 -0
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright © 2010-2011 Jesse Sielaff
4
+ #
5
+
6
+ class QTrue < QObject
7
+ def stringify
8
+ "true"
9
+ end
10
+
11
+ def true?
12
+ true
13
+ end
14
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright © 2010-2011 Jesse Sielaff
4
+ #
5
+
6
+ class Q_Node
7
+ def initialize (node_type, name, klass, string, nodes)
8
+ @node_type = node_type
9
+
10
+ @value = case
11
+ when name then name
12
+ when klass then Object.const_get(:"Q#{klass}").from_s(string)
13
+ when nodes then nodes.each {|node| node.instance_variable_set(:@block, self) }
14
+ end
15
+ end
16
+
17
+ attr_reader :node_type, :value
18
+
19
+ # • User method
20
+ # Returns the block that contains the Node.
21
+ #
22
+ def block
23
+ @block
24
+ end
25
+
26
+ # • User method
27
+ # Returns an Array of the block Node's contents. If the Node is not a block,
28
+ # returns nil.
29
+ #
30
+ def nodes
31
+ @value if @node_type == :block
32
+ end
33
+
34
+ # • User method
35
+ # Returns a String representing the Node in Q script format.
36
+ #
37
+ def to_script
38
+ case @node_type
39
+ when :block then "{#{@value.map {|n| n.to_script }.join ?\ }}"
40
+ when :literal then "#{@value.to_qliteral}"
41
+ when :method then "#@value"
42
+ when :variable then ":#@value"
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,125 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright © 2010-2011 Jesse Sielaff
4
+ #
5
+
6
+ class Q_Object
7
+ def initialize (value)
8
+ @__value__ = value
9
+ end
10
+
11
+ Subclasses = []
12
+
13
+ %w:class dup extend hash nil? send:.each {|name| undef_method name }
14
+
15
+ # • User method
16
+ # Returns the Q_Environment in which the Q_Object is operating.
17
+ #
18
+ def env
19
+ @__environment__
20
+ end
21
+
22
+ # Returns the Q_Object.
23
+ #
24
+ def to_q
25
+ self
26
+ end
27
+
28
+ # Returns the Q literal string for the Q_Object's internally-stored ruby
29
+ # value.
30
+ #
31
+ def to_qliteral
32
+ value.to_qliteral
33
+ end
34
+
35
+ # • User method
36
+ # Returns the Q_Object's internally-stored ruby value.
37
+ #
38
+ def value
39
+ @__value__
40
+ end
41
+
42
+ # Adds the subclass to the Q_Object::Subclasses array.
43
+ #
44
+ def Q_Object.inherited (subclass)
45
+ Subclasses << subclass
46
+ subclass.const_set(:MethodArguments, {})
47
+ end
48
+
49
+ # Adds a key-value pair to the MethodArguments hash for the current Class. The
50
+ # key is the new method name. The value is a Hash containing a :reqs_left
51
+ # Array and a :reqs_right Array, each of which contains one Q class per item
52
+ # from the left or right side of the queue that will be passed as an argument
53
+ # to the new method. If the Hash contains a {block: true} key-value pair, the
54
+ # new method requires a block argument. If the Hash contains a {splat: qclass}
55
+ # key-value pair, the new method will be passed all items of that Q class from
56
+ # the queue as a splat argument.
57
+ #
58
+ def Q_Object.method_added (name)
59
+ # For compatibility with Ruby < 1.9.2
60
+ return unless UnboundMethod.public_method_defined? :parameters
61
+
62
+ method_args = self::MethodArguments[name] = { reqs_left: [], reqs_right: [] }
63
+
64
+ instance_method(name).parameters.each do |arg_type, object_type|
65
+ case arg_type
66
+ when :block then method_args[:block] = true
67
+ when :rest then method_args[:splat] = (object_type) ? const_get(:"Q#{class_name(object_type)}") : nil
68
+ when :req then (method_args.has_key?(:splat) ? method_args[:reqs_right] : method_args[:reqs_left]) << const_get(:"Q#{class_name(object_type)}")
69
+ end
70
+ end
71
+
72
+ # Splat arg serves as a divider:
73
+ #
74
+ # def foo (number, *, string)
75
+ # Args to the left come from the start of the queue;
76
+ # args to the right come from the end of the queue.
77
+ # The rest of the objects are left in the queue.
78
+ #
79
+ # def foo (number, *every_number, number)
80
+ # If a type name is given to the splat arg, it grabs
81
+ # every object of that type (except as needed by the
82
+ # required args).
83
+ end
84
+
85
+ # Returns an Array of the unique names of every method defined in a Q class.
86
+ #
87
+ def Q_Object.method_names
88
+ Subclasses.inject([]) {|ary, klass| ary | klass::MethodArguments.keys }
89
+ end
90
+
91
+ # Returns the class name corresponding to the given argument type string.
92
+ # Strips preceding 'every_' and trailing '_{number}'. For example, 'string'
93
+ # and 'string_2' result in 'String', 'some_class' results in 'SomeClass', and
94
+ # 'every_number' results in 'Number'.
95
+ #
96
+ def Q_Object.class_name (arg_type)
97
+ arg_type.to_s
98
+ .capitalize
99
+ .gsub(/_[0-9]+$/,'')
100
+ .gsub(/(?:^|_|([0-9]))(.)/) { "#{$1}#{$2.upcase}" }
101
+ .gsub(/^Every/,'')
102
+ end
103
+ end
104
+
105
+ QObject = Class.new(Q_Object)
106
+ QArray, QBlock, QDynamic, QFalse,
107
+ QHash, QModule, QNil, QNumber,
108
+ QString, QTime, QToken, QTrue =
109
+ *12.times.map { Class.new(QObject) }
110
+ QClass = Class.new(QModule)
111
+
112
+ Object.send(:define_method, :to_q) { Object.const_get(:"Q#{self.class}").new(self) }
113
+ FalseClass.send(:define_method, :to_q) { QFalse.new(false) }
114
+ NilClass.send(:define_method, :to_q) { QNil.new(nil) }
115
+ Numeric.send(:define_method, :to_q) { QNumber.new(Number(self)) }
116
+ Proc.send(:define_method, :to_q) { QBlock.new(self) }
117
+ Symbol.send(:define_method, :to_q) { QToken.new(self) }
118
+ TrueClass.send(:define_method, :to_q) { QTrue.new(true) }
119
+
120
+ Object.send(:define_method, :to_qliteral) { "#{self.class}(#{self})" }
121
+ FalseClass.send(:define_method, :to_qliteral) { "false" }
122
+ NilClass.send(:define_method, :to_qliteral) { nil }
123
+ Numeric.send(:define_method, :to_qliteral) { "Number(#{Number(self)})" }
124
+ Symbol.send(:define_method, :to_qliteral) { "Token(#{self})" }
125
+ TrueClass.send(:define_method, :to_qliteral) { "true" }
@@ -0,0 +1,104 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright © 2010-2011 Jesse Sielaff
4
+ #
5
+
6
+ require 'racc/parser'
7
+ require 'strscan'
8
+
9
+ class Q_Parser < Racc::Parser
10
+ def initialize (script)
11
+ @ss = StringScanner.new(script)
12
+ end
13
+
14
+ # Removes and returns one item from the Array of script tokens.
15
+ # Required by racc.
16
+ #
17
+ def next_token
18
+ @tokens.shift
19
+ end
20
+
21
+ # Raises a SyntaxError.
22
+ # Required by racc.
23
+ #
24
+ def on_error (*)
25
+ raise SyntaxError, "tokens do not form valid script"
26
+ end
27
+
28
+ # Parses the Q script, then returns an Array containing the outermost Node, an
29
+ # Array of every block Node, an Array of every literal Node, an Array of every
30
+ # method Node, and an Array of every variable Node. If any invalid tokens are
31
+ # encountered in the script or if the tokens do not form a valid Q script,
32
+ # raises a SyntaxError.
33
+ #
34
+ def parse
35
+ @tokens = []
36
+ @ss.reset
37
+
38
+ until @ss.eos?
39
+ @tokens << case c = @ss.getch
40
+ when /\s/ then next
41
+ when /[\p{alpha}_]/
42
+ @ss.unscan.scan /[\p{alnum}_]+[?!]?/
43
+ [:ID, @ss.matched.intern]
44
+ when /[}:{]/ then [c, 0]
45
+ when /\(/ then [:STRING, scan_literal]
46
+ else raise SyntaxError, "script contains invalid token `#{c}'"
47
+ end
48
+ end
49
+
50
+ @tokens << [false, false]
51
+
52
+ @blocks = []
53
+ @literals = []
54
+ @methods = []
55
+ @variables = []
56
+
57
+ return do_parse, @blocks, @literals, @methods, @variables
58
+ end
59
+
60
+ # Returns the string enclosed by the set of parentheses beginning at the '('
61
+ # exactly one character before the StringScanner's current pointer location
62
+ # and ending at the ')' character after all sets of parentheses have been
63
+ # balanced. Parenthesis characters escaped by a preceding '\' are not
64
+ # considered in the balance count. If the end of the string is reached before
65
+ # the closing ')' character, raises a SyntaxError.
66
+ #
67
+ def scan_literal
68
+ strings = []
69
+ p_count = 0
70
+
71
+ while s = @ss.scan_until(/(?=[()\\])/)
72
+ case (c = @ss.getch).ord
73
+ when 41 then (p_count == 0) ? (return (strings << s).join) : (p_count -= 1) # )
74
+ when 40 then p_count += 1 # (
75
+ when 92 then %w[) (].include?(@ss.peek(1)) ? (strings << s << c << @ss.getch) : (next) # \
76
+ end
77
+
78
+ strings << s << c
79
+ end
80
+
81
+ raise SyntaxError, "unterminated literal meets end of script"
82
+ end
83
+
84
+ Racc_debug_parser = false
85
+ Racc_arg = [
86
+ [2,9,6,7,2,10,6,7,2,3,12,13], [2,3,2,2,4,4,4,4,0,1,6,7],
87
+ [-9,-9,-8,-9,-9,-2,-9,-5,-6,14,-1,-7,-3,-4],
88
+ [6,9,-2,1,2,nil,5,5,nil,nil,nil,nil,nil,nil],
89
+ [8,4,11,1], [3,2,3,1], [nil,5,nil,nil], [nil,3,-1,-2], 7,
90
+ [0,0,:racc_error, 3,8,:new_block, 1,10,:block,
91
+ 2,10,:new_variable, 2,10,:new_literal, 1,10,:new_method,
92
+ 1,9,:new_node_array, 2,9,:append_node, 0,9,:new_empty_array],
93
+ { false=>0, error: 1, "{"=>2, "}"=>3, ":"=>4, ID: 5, STRING: 6 },
94
+ 14, 9, true]
95
+
96
+ define_method(:append_node) {|v,*| v[0] << v[1] }
97
+ define_method(:block) {|v,*| v[0] }
98
+ define_method(:new_block) {|v,*| @blocks[0...0] = Q_Node.new(:block, nil, nil, nil, v[1]) }
99
+ define_method(:new_literal) {|v,*| @literals[0...0] = Q_Node.new(:literal, nil, v[0], v[1], nil) }
100
+ define_method(:new_method) {|v,*| @methods[0...0] = Q_Node.new(:method, v[0], nil, nil, nil) }
101
+ define_method(:new_variable) {|v,*| @variables[0...0] = Q_Node.new(:variable, v[1], nil, nil, nil) }
102
+ define_method(:new_node_array) {|v,*| [v[0]] }
103
+ define_method(:new_empty_array) {|v,*| [] }
104
+ end
@@ -0,0 +1,90 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright © 2010-2011 Jesse Sielaff
4
+ #
5
+
6
+ class Q_Writer
7
+ # • User method
8
+ # Returns a String representing a random block of Q script.
9
+ #
10
+ def generate (remaining_depth = self.nesting)
11
+ b = 0...weight[:block]
12
+ l = b.end...(b.end + weight[:literal])
13
+ m = l.end...(l.end + weight[:method])
14
+ v = m.end...(m.end + weight[:variable])
15
+
16
+ strings = nodes.times.map do
17
+ case rand(v.end)
18
+ when b then (remaining_depth > 0) ? generate(remaining_depth - 1) : redo
19
+ when l then literals.sample.to_qliteral
20
+ when m then methods.sample
21
+ when v then (x = variables.sample) ? ":#{x}" : ()
22
+ end
23
+ end
24
+
25
+ "{#{strings.compact.join ?\ }}"
26
+ end
27
+
28
+ # • User method
29
+ # Returns "Q".
30
+ #
31
+ def language
32
+ ?Q
33
+ end
34
+
35
+ # • User method
36
+ # Sets the Array of literals to be sampled from when generating a random
37
+ # block, then returns the Q_Writer. If no argument is given, returns the
38
+ # current literals Array. Default value is [-1,0,1,2,5,10,'abc',:foo,:bar].
39
+ #
40
+ def literals (literals = nil)
41
+ literals ? (@literals = literals; self) : (@literals ||= [-1,0,1,2,5,10,'abc',:foo,:bar])
42
+ end
43
+
44
+ # • User method
45
+ # Sets the Array of methods to be sampled from when generating a random block,
46
+ # then returns the Q_Writer. If no argument is given, returns the current
47
+ # methods Array. Default value is the Array of all available Q methods.
48
+ #
49
+ def methods (methods = nil)
50
+ methods ? (@methods = methods.map(&:to_sym); self) : (@methods ||= Q_Object.method_names)
51
+ end
52
+
53
+ # • User method
54
+ # Sets the maximum depth of nested blocks in a random script, then returns the
55
+ # Q_Writer. If no argument is given, returns the current maximum depth of
56
+ # nesting. Default value is 5.
57
+ #
58
+ def nesting (nesting = nil)
59
+ nesting ? (@nesting = nesting.to_int; self) : (@nesting ||= 5)
60
+ end
61
+
62
+ # • User method
63
+ # Sets the maximum number of nodes per block in a random script, then returns
64
+ # the Q_Writer. A block node counts as only 1 node, ignoring the nodes inside
65
+ # the block. If no argument is given, gives the current maximum nodes per
66
+ # block. Default value is 7.
67
+ #
68
+ def nodes (nodes = nil)
69
+ nodes ? (@nodes = nodes.to_int; self) : (@nodes ||= 7)
70
+ end
71
+
72
+ # • User method
73
+ # Sets the Array of variables to be sampled from when generating a random
74
+ # block, then returns the Q_Writer. If no argument is given, returns the
75
+ # current variables Array. Default value is [:i,:j,:k,:l,:m,:n,:w,:x,:y,:z].
76
+ #
77
+ def variables (variables = nil)
78
+ variables ? (@variables = variables.map(&:to_sym); self) : (@variables ||= %w:i j k l m n w x y z:.map(&:to_sym))
79
+ end
80
+
81
+ # • User method
82
+ # Sets the relative frequency with which each node type will appear when
83
+ # generating a random block, then returns the Q_Writer. If no argument is
84
+ # given, returns the current node weight. Default values are block: 1,
85
+ # literal: 1, method: 1, variable: 1.
86
+ #
87
+ def weight (weight = nil)
88
+ weight ? (@weight = weight.to_hash.select {|k,v| [:block, :literal, :method, :variable].include?(k) && v.is_a?(Fixnum) }; @weight.default = 1; self) : (@weight ||= {block: 1, literal: 1, method: 1, variable: 1})
89
+ end
90
+ end
@@ -0,0 +1,191 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright © 2010-2011 Jesse Sielaff
4
+ #
5
+
6
+ require_relative '../test.rb'
7
+
8
+ class TestArray < TestQ
9
+ def test_add_returns_array_concatenation
10
+ eq [1,1,2,2], "{ { one one numbers } add { two two numbers } }"
11
+ end
12
+
13
+ def test_add_all_returns_array_concatenation
14
+ eq [1,1,2,2,[]], "{ { one one numbers } { two two numbers } { array arrays } add_all }"
15
+ end
16
+
17
+ def test_array_p_returns_self
18
+ returns :a, "{ :a array array? }"
19
+ end
20
+
21
+ def test_at_n_returns_nth_element
22
+ eq 2, "{ two one zero numbers at zero }"
23
+ end
24
+
25
+ def test_at_outside_of_range_returns_nil
26
+ eq nil, "{ two one zero numbers at Number(100) }"
27
+ end
28
+
29
+ def test_clear_returns_empty_array
30
+ eq [], "{ one two numbers clear }"
31
+ end
32
+
33
+ def test_clear_returns_same_array
34
+ returns :a, "{ one two :a numbers clear }"
35
+ end
36
+
37
+ def test_combination_returns_self
38
+ returns :a, "{ zero pi i e :a numbers combination two { } }"
39
+ end
40
+
41
+ def test_combination_calls_block_once_per_combination
42
+ eq 6, "{ zero pi i e numbers combination two { :a push i } :a elect length }", a: []
43
+ end
44
+
45
+ def test_combination_terminates_upon_break
46
+ eq 1, "{ zero pi i e numbers combination two { :a push i break_if { true } } :a elect length }", a: []
47
+ end
48
+
49
+ def test_combination_continues_upon_jump
50
+ eq 6, "{ zero pi i e numbers combination two { :a push i jump_if { true } :a elect push i } :a elect length }", a: []
51
+ end
52
+
53
+ def test_combination_terminates_upon_break
54
+ eq 1, "{ zero pi i e numbers combination two { :a push i break_if { true } } :a elect length }", a: []
55
+ end
56
+
57
+ def test_combination_calls_block_with_each_combination_as_argument
58
+ eq [[0,1],[0,2],[1,2]], "{ zero one two numbers combination two :c { :a push :c } :a elect }", a: []
59
+ end
60
+
61
+ def test_compact_returns_array_of_all_non_nil_items_in_array
62
+ eq [1,2], "{ one nil two objects compact }"
63
+ end
64
+
65
+ def test_compact_bang_returns_same_array
66
+ returns :a, "{ one nil two :a objects compact! }"
67
+ end
68
+
69
+ def test_compact_bang_removes_nils_from_array
70
+ eq [1,2], "{ one nil two objects compact! }"
71
+ end
72
+
73
+ def test_concat_returns_same_array
74
+ returns :a, "{ { one two :a numbers } concat { zero one numbers } }"
75
+ end
76
+
77
+ def test_concat_returns_array_concatenation
78
+ eq [1,2,0,1], "{ { one two numbers } concat { zero one numbers } }"
79
+ end
80
+
81
+ def test_count_returns_number_of_matching_objects_in_array
82
+ eq 3, "{ nil Token(a) nil Token(b) nil objects nil count }"
83
+ end
84
+
85
+ #######################
86
+ def test_count_by
87
+ end
88
+
89
+ def test_cycle
90
+ end
91
+
92
+ def test_cycle_n
93
+ end
94
+ #######################
95
+
96
+ def test_delete_returns_deleted_object
97
+ eq :b, "{ Token(a) Token(b) Token(c) tokens delete Token(b) }"
98
+ end
99
+
100
+ def test_delete_returns_nil_if_array_does_not_contain_object
101
+ eq nil, "{ Token(a) Token(b) Token(c) tokens delete Token(d) }"
102
+ end
103
+
104
+ def test_delete_removes_all_instances_of_deleted_object_from_array
105
+ eq [2], "{ one one two :a numbers delete one :a elect }"
106
+ end
107
+
108
+ def test_delete_at_returns_deleted_object
109
+ eq :c, "{ Token(a) Token(b) Token(c) tokens delete_at two }"
110
+ end
111
+
112
+ def test_delete_at_returns_nil_if_index_is_out_of_range
113
+ eq nil, "{ Token(a) Token(b) Token(c) tokens delete_at Number(100) }"
114
+ end
115
+
116
+ def test_delete_at_removes_object_at_index_from_array
117
+ eq [0, 2], "{ zero pi two :a numbers delete_at one :a elect }"
118
+ end
119
+
120
+ def test_delete_if_returns_self
121
+ returns :a, "{ zero one two :a numbers delete_if { } }"
122
+ end
123
+
124
+ def test_delete_if_calls_block_once_per_element
125
+ eq 3, "{ zero one two numbers delete_if { :a push i } :a elect length }", a: []
126
+ end
127
+
128
+ def test_delete_if_terminates_upon_break
129
+ eq 1, "{ zero one two numbers delete_if { :a push i break_if { true } } :a elect length }", a: []
130
+ end
131
+
132
+ def test_delete_if_continues_upon_jump
133
+ eq 3, "{ zero one two numbers delete_if { :a push i jump_if { true } :a elect push i } :a elect length }", a: []
134
+ end
135
+
136
+ def test_delete_if_terminates_upon_break
137
+ eq 1, "{ zero one two numbers delete_if { :a push i break_if { true } } :a elect length }", a: []
138
+ end
139
+
140
+ def test_delete_if_calls_block_with_each_element_as_argument
141
+ eq [2,3,4], "{ zero one two numbers delete_if :c { :a push { :c add two } } :a elect }", a: []
142
+ end
143
+
144
+ def test_delete_if_removes_element_from_array_when_block_returns_true
145
+ eq [], "{ :a delete_if :c { true } }", a: [1,2,3]
146
+ end
147
+
148
+ def test_delete_if_does_not_remove_element_from_array_when_block_returns_false
149
+ eq [1,3], "{ :a delete_if :c { :c eq? two } }", a: [1,2,3]
150
+ end
151
+
152
+ def test_drop_returns_array_of_elements_without_first_n_elements
153
+ eq [3,4,5], "{ :a drop two }", a: [1,2,3,4,5]
154
+ end
155
+
156
+ def test_drop_returns_array_of_all_elements_when_n_is_negative
157
+ eq [1,2,3,4,5], "{ :a drop negative two }", a: [1,2,3,4,5]
158
+ end
159
+
160
+ def test_drop_while_calls_block_once_per_element
161
+ eq 3, "{ zero one two numbers drop_while { true :a push i } :a elect length }", a: []
162
+ end
163
+
164
+ def test_drop_while_terminates_if_block_returns_false
165
+ eq 1, "{ zero one two numbers drop_while { false { :a push i } } :a elect length }", a: []
166
+ end
167
+
168
+ def test_drop_while_terminates_upon_break
169
+ eq 1, "{ zero one two numbers drop_while { :a push i break_if { true } } :a elect length }", a: []
170
+ end
171
+
172
+ def test_drop_while_continues_upon_jump
173
+ eq 3, "{ zero one two numbers drop_while { false { :a push i } jump_if { true } { :a elect push i } } :a elect length }", a: []
174
+ end
175
+
176
+ def test_delete_if_terminates_upon_break
177
+ eq 1, "{ zero one two numbers delete_if { :a push i break_if { true } } :a elect length }", a: []
178
+ end
179
+
180
+ def test_delete_if_calls_block_with_each_element_as_argument
181
+ eq [2,3,4], "{ zero one two numbers delete_if :c { :a push { :c add two } } :a elect }", a: []
182
+ end
183
+
184
+ def test_delete_if_removes_element_from_array_when_block_returns_true
185
+ eq [], "{ :a delete_if :c { true } }", a: [1,2,3]
186
+ end
187
+
188
+ def test_delete_if_does_not_remove_element_from_array_when_block_returns_false
189
+ eq [1,3], "{ :a delete_if :c { :c eq? two } }", a: [1,2,3]
190
+ end
191
+ end