q-language 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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