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