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.
- data/.gemtest +0 -0
- data/Rakefile +10 -0
- data/lib/q-language.rb +9 -0
- data/lib/q-language/device.rb +166 -0
- data/lib/q-language/environment.rb +358 -0
- data/lib/q-language/methods/array.rb +523 -0
- data/lib/q-language/methods/block.rb +26 -0
- data/lib/q-language/methods/class.rb +24 -0
- data/lib/q-language/methods/dynamic.rb +82 -0
- data/lib/q-language/methods/false.rb +47 -0
- data/lib/q-language/methods/hash.rb +351 -0
- data/lib/q-language/methods/implicit.rb +345 -0
- data/lib/q-language/methods/module.rb +39 -0
- data/lib/q-language/methods/nil.rb +47 -0
- data/lib/q-language/methods/number.rb +118 -0
- data/lib/q-language/methods/object.rb +155 -0
- data/lib/q-language/methods/string.rb +157 -0
- data/lib/q-language/methods/time.rb +110 -0
- data/lib/q-language/methods/token.rb +72 -0
- data/lib/q-language/methods/true.rb +14 -0
- data/lib/q-language/node.rb +45 -0
- data/lib/q-language/object.rb +125 -0
- data/lib/q-language/parser.rb +104 -0
- data/lib/q-language/writer.rb +90 -0
- data/test/methods/test_array.rb +191 -0
- data/test/methods/test_block.rb +66 -0
- data/test/methods/test_class.rb +34 -0
- data/test/methods/test_dynamic.rb +158 -0
- data/test/methods/test_false.rb +60 -0
- data/test/methods/test_hash.rb +10 -0
- data/test/methods/test_implicit.rb +332 -0
- data/test/methods/test_module.rb +55 -0
- data/test/methods/test_nil.rb +60 -0
- data/test/methods/test_number.rb +10 -0
- data/test/methods/test_object.rb +157 -0
- data/test/methods/test_string.rb +271 -0
- data/test/methods/test_time.rb +181 -0
- data/test/methods/test_token.rb +92 -0
- data/test/methods/test_true.rb +16 -0
- data/test/test.rb +23 -0
- metadata +103 -0
@@ -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
|