bare-rb 0.1.3 → 0.2.1
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.
- checksums.yaml +4 -4
- data/lib/bare-rb.rb +44 -46
- data/lib/exceptions.rb +24 -0
- data/lib/generative_testing/gen.rb +26 -0
- data/lib/generative_testing/grammar_util.rb +22 -0
- data/lib/generative_testing/monkey_patch.rb +262 -0
- data/lib/lexer.rb +17 -16
- data/lib/parser.rb +22 -22
- data/lib/types.rb +475 -291
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0206b2d45e4ea60de2e60934cda498eac9fd03c627f01455e6eb0a2e59163af9
|
4
|
+
data.tar.gz: 2477608d876cae8e775575dbd8f1f39db985414249c55c2d94c1251a569bdab5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d7430875487bff66b60e21b1893ada24df12bad88661b3dc06b3f71ab0f88877e6a24a08381f8180398ada99a31b2fc319f8880f676758db9bd767ad3d7321e
|
7
|
+
data.tar.gz: d2765a0a51f394ee02070b4ab3ceb56ac6c38793d3ee8ce020256df69302af016fcb4c435436ef93b25057d842d281a0cb5d20c8cea14c0621963da661fccc47
|
data/lib/bare-rb.rb
CHANGED
@@ -2,64 +2,65 @@ require 'set'
|
|
2
2
|
require_relative "types"
|
3
3
|
require_relative "lexer"
|
4
4
|
require_relative "parser"
|
5
|
+
require_relative 'dfs'
|
6
|
+
require_relative 'generative_testing/gen'
|
7
|
+
require_relative 'generative_testing/monkey_patch'
|
5
8
|
|
6
9
|
class Bare
|
7
|
-
def self.encode(msg, schema, type=nil)
|
8
|
-
|
9
|
-
|
10
|
-
|
10
|
+
def self.encode(msg, schema, type = nil)
|
11
|
+
buffer = "".b
|
12
|
+
if schema.is_a?(BareTypes::Schema)
|
13
|
+
raise NoTypeProvided.new("To encode with a schema as opposed to a raw type you must specify which type in the schema you want to encode as a symbol.\nBare.encode(msg, schema, :Type)") if type.nil?
|
14
|
+
unless schema.types.include?(type)
|
15
|
+
raise("#{type} is not a type found in this schema. Choose from #{schema.types.keys}")
|
16
|
+
end
|
17
|
+
schema[type].encode(msg, buffer)
|
11
18
|
else
|
12
|
-
schema.encode(msg)
|
19
|
+
schema.encode(msg, buffer)
|
13
20
|
end
|
21
|
+
buffer
|
14
22
|
end
|
15
23
|
|
16
|
-
def self.decode(msg, schema, type=nil)
|
17
|
-
if schema.is_a?(
|
18
|
-
raise NoTypeProvided("To decode with a schema as opposed to a raw type you must specify which type in the same you want to encode as a symbol.\nBare.encode(msg, schema, :Type)") if type.nil?
|
19
|
-
|
24
|
+
def self.decode(msg, schema, type = nil)
|
25
|
+
if schema.is_a?(BareTypes::Schema)
|
26
|
+
raise NoTypeProvided.new("To decode with a schema as opposed to a raw type you must specify which type in the same you want to encode as a symbol.\nBare.encode(msg, schema, :Type)") if type.nil?
|
27
|
+
value, _ = schema[type].decode(msg)
|
28
|
+
value
|
20
29
|
else
|
21
|
-
schema.decode(msg)
|
30
|
+
value, _ = schema.decode(msg)
|
31
|
+
value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns a schema and a binary input
|
36
|
+
# optionally write these to files
|
37
|
+
def self.generative_test(schema_path=nil, binary_path=nil)
|
38
|
+
schema = BareTypes::Schema.make
|
39
|
+
input = schema.create_input
|
40
|
+
key = schema.types.keys[0]
|
41
|
+
binary = Bare.encode(input[key], schema, key)
|
42
|
+
unless binary_path.nil?
|
43
|
+
file = File.open(binary_path, 'wb+')
|
44
|
+
file.write(binary)
|
45
|
+
file.close
|
46
|
+
end
|
47
|
+
unless schema_path.nil?
|
48
|
+
file = File.open(schema_path, 'w+')
|
49
|
+
file.write(schema.to_s)
|
50
|
+
file.close
|
22
51
|
end
|
52
|
+
return schema, binary, key
|
23
53
|
end
|
24
54
|
|
25
55
|
def self.parse_schema(path)
|
26
|
-
# Hash of class names to BARE
|
56
|
+
# Hash of class names to BARE types
|
27
57
|
# Eg. types['Customer'] == Bare.i32
|
28
|
-
|
29
|
-
Bare.Schema(
|
58
|
+
parsed = parser(lexer(path))
|
59
|
+
Bare.Schema(parsed)
|
30
60
|
end
|
31
61
|
|
32
62
|
def self.Schema(hash)
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
class Schema
|
37
|
-
|
38
|
-
def ==(otherSchema)
|
39
|
-
return false unless otherSchema.is_a?(Bare::Schema)
|
40
|
-
return @types == otherSchema.types
|
41
|
-
end
|
42
|
-
|
43
|
-
def types
|
44
|
-
@types
|
45
|
-
end
|
46
|
-
|
47
|
-
def [](key)
|
48
|
-
return @types[key]
|
49
|
-
end
|
50
|
-
|
51
|
-
def initialize(types)
|
52
|
-
@types = types
|
53
|
-
@types.keys.each do |key|
|
54
|
-
if @types[key].is_a?(Symbol)
|
55
|
-
@types[key] = @types[@types[key]]
|
56
|
-
else
|
57
|
-
# Users may user symbols to reference not yet defined types
|
58
|
-
# here we recursively call our bare classes to finalize their types
|
59
|
-
@types[key].finalize_references(@types)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
+
BareTypes::Schema.new(hash)
|
63
64
|
end
|
64
65
|
|
65
66
|
# These classes are wrapped in methods for ergonomics.
|
@@ -160,7 +161,4 @@ class Bare
|
|
160
161
|
def self.Enum(*opts)
|
161
162
|
return BareTypes::Enum.new(*opts)
|
162
163
|
end
|
163
|
-
|
164
|
-
|
165
164
|
end
|
166
|
-
|
data/lib/exceptions.rb
CHANGED
@@ -5,6 +5,30 @@ class BareException < StandardError
|
|
5
5
|
end
|
6
6
|
end
|
7
7
|
|
8
|
+
class InvalidBool < BareException
|
9
|
+
def initialize(msg = nil)
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class CircularSchema < BareException
|
15
|
+
def initialize(msg = nil)
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class ReferenceException < BareException
|
21
|
+
def initialize(msg=nil)
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class FixedDataSizeWrong < BareException
|
27
|
+
def initialize(msg=nil)
|
28
|
+
super
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
8
32
|
class NoTypeProvided < BareException
|
9
33
|
def initialize(msg = nil)
|
10
34
|
super
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative '../bare-rb'
|
2
|
+
require_relative './monkey_patch'
|
3
|
+
require_relative './grammar_util'
|
4
|
+
|
5
|
+
def get_type(depth, names = [], can_be_symbol = true)
|
6
|
+
if names.size == 0
|
7
|
+
can_be_symbol = false
|
8
|
+
end
|
9
|
+
terminators = [BareTypes::Data, BareTypes::DataFixedLen,
|
10
|
+
BareTypes::U8, BareTypes::U16, BareTypes::U32, BareTypes::U64,
|
11
|
+
BareTypes::I8, BareTypes::I16, BareTypes::I32, BareTypes::I64,
|
12
|
+
BareTypes::F32, BareTypes::F64]
|
13
|
+
aggregates = [BareTypes::Array, BareTypes::ArrayFixedLen,
|
14
|
+
BareTypes::Struct]
|
15
|
+
|
16
|
+
all = terminators + aggregates
|
17
|
+
|
18
|
+
# 1/5 changes of a reference
|
19
|
+
if rand(5) == 0 && names.size != 1 && can_be_symbol
|
20
|
+
names[rand(names.size)]
|
21
|
+
elsif depth >= 10 # if depth >= 10 only use terminating types
|
22
|
+
all[rand(terminators.size)].make(depth + 1, names)
|
23
|
+
else
|
24
|
+
all[rand(all.size)].make(depth + 1, names)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
def create_user_type_name
|
2
|
+
upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
3
|
+
lower = upper.downcase
|
4
|
+
digit = "0123456789"
|
5
|
+
|
6
|
+
name = upper[rand(upper.size)]
|
7
|
+
loop do
|
8
|
+
if rand(50) < 5
|
9
|
+
break
|
10
|
+
end
|
11
|
+
num = rand(3)
|
12
|
+
if num == 0
|
13
|
+
name << upper[rand(upper.size)]
|
14
|
+
elsif num == 1
|
15
|
+
name << lower[rand(lower.size)]
|
16
|
+
else
|
17
|
+
name << digit[rand(digit.size)]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
name.to_sym
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,262 @@
|
|
1
|
+
require_relative '../bare-rb'
|
2
|
+
require_relative './grammar_util'
|
3
|
+
|
4
|
+
# 10MB max data size
|
5
|
+
DATA_MAX_SIZE = 30000
|
6
|
+
ARRAY_MAX_SIZE = 40
|
7
|
+
STRUCT_FIELDS_MAX = 5
|
8
|
+
|
9
|
+
# Monkey patch every bare class to include make and create_input
|
10
|
+
# make - a factory to create a random variant of the bare class
|
11
|
+
# create_input - creates an input that could be used with Bare.Encode for this type
|
12
|
+
|
13
|
+
class BareTypes::Reference
|
14
|
+
def create_input
|
15
|
+
self.ref.create_input
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.make(depth, names)
|
19
|
+
self.ref.make(depth, names)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# region Integers
|
24
|
+
class BareTypes::U8
|
25
|
+
def self.make(depth, names)
|
26
|
+
BareTypes::U8.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_input
|
30
|
+
rand(256)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class BareTypes::U16
|
35
|
+
def self.make(depth, names)
|
36
|
+
BareTypes::U16.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_input
|
40
|
+
rand(2 ** 16)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class BareTypes::U32
|
45
|
+
def self.make(depth, names)
|
46
|
+
BareTypes::U32.new
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_input
|
50
|
+
rand(2 ** 32)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class BareTypes::U64
|
55
|
+
def self.make(depth, names)
|
56
|
+
BareTypes::U64.new
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_input
|
60
|
+
rand(2 ** 64)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class BareTypes::I8
|
65
|
+
def self.make(depth, names)
|
66
|
+
BareTypes::I8.new
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_input
|
70
|
+
rand(2 ** 8) - (2 ** 7)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class BareTypes::I16
|
75
|
+
def self.make(depth, names)
|
76
|
+
BareTypes::I16.new
|
77
|
+
end
|
78
|
+
|
79
|
+
def create_input
|
80
|
+
rand(2 ** 16) - (2 ** 15)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class BareTypes::I32
|
85
|
+
def self.make(depth, names)
|
86
|
+
BareTypes::I32.new
|
87
|
+
end
|
88
|
+
|
89
|
+
def create_input
|
90
|
+
rand(2 ** 32) - (2 ** 31)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class BareTypes::I64
|
95
|
+
def self.make(depth, names)
|
96
|
+
BareTypes::I64.new
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_input
|
100
|
+
rand(2 ** 64) - (2 ** 63)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# endregion
|
105
|
+
|
106
|
+
#region Floats
|
107
|
+
class BareTypes::F32
|
108
|
+
def self.make(depth, names)
|
109
|
+
self.new
|
110
|
+
end
|
111
|
+
|
112
|
+
def create_input
|
113
|
+
float = nil
|
114
|
+
loop do
|
115
|
+
input = [rand(266), rand(266), rand(266), rand(266)]
|
116
|
+
float = input.pack("cccc").unpack('e')
|
117
|
+
if float[0] == float[0] && !float[0].nan?
|
118
|
+
break
|
119
|
+
end
|
120
|
+
end
|
121
|
+
float[0]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class BareTypes::F64
|
126
|
+
def self.make(depth, names)
|
127
|
+
self.new
|
128
|
+
end
|
129
|
+
|
130
|
+
def create_input
|
131
|
+
float = nil
|
132
|
+
loop do
|
133
|
+
input = [rand(266), rand(266), rand(266), rand(266), rand(266), rand(266), rand(266), rand(266)]
|
134
|
+
float = input.pack("cccccccc").unpack('E')
|
135
|
+
if float[0] == float[0] && !float[0].nan?
|
136
|
+
break
|
137
|
+
end
|
138
|
+
end
|
139
|
+
float[0]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
#endregion
|
144
|
+
|
145
|
+
#region Data
|
146
|
+
class BareTypes::DataFixedLen
|
147
|
+
def self.make(depth, names)
|
148
|
+
length = rand(max = DATA_MAX_SIZE) + 1
|
149
|
+
self.new(length)
|
150
|
+
end
|
151
|
+
|
152
|
+
def create_input
|
153
|
+
# 100 random bytes
|
154
|
+
arr = []
|
155
|
+
0.upto(length - 1).each do |i|
|
156
|
+
arr << i % 256
|
157
|
+
end
|
158
|
+
arr.pack('c*')
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class BareTypes::Data
|
163
|
+
def self.make(depth, names)
|
164
|
+
self.new
|
165
|
+
end
|
166
|
+
|
167
|
+
def create_input
|
168
|
+
arr = []
|
169
|
+
0.upto(rand(DATA_MAX_SIZE)).each do |i|
|
170
|
+
arr << i % 256
|
171
|
+
end
|
172
|
+
arr.pack('c*')
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
#endregion
|
177
|
+
|
178
|
+
#region Array
|
179
|
+
class BareTypes::Array
|
180
|
+
def self.make(depth, names)
|
181
|
+
BareTypes::Array.new(get_type(depth + 1, names))
|
182
|
+
end
|
183
|
+
|
184
|
+
def create_input
|
185
|
+
count = rand(ARRAY_MAX_SIZE) + 1
|
186
|
+
arr = []
|
187
|
+
0.upto(count) do
|
188
|
+
arr << @type.create_input
|
189
|
+
end
|
190
|
+
arr
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
class BareTypes::ArrayFixedLen
|
195
|
+
def self.make(depth, names)
|
196
|
+
self.new(get_type(depth + 1, names,), rand(ARRAY_MAX_SIZE) + 1)
|
197
|
+
end
|
198
|
+
|
199
|
+
def create_input
|
200
|
+
arr = []
|
201
|
+
0.upto(@size - 1) do
|
202
|
+
arr << @type.create_input
|
203
|
+
end
|
204
|
+
arr
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
#endregion
|
209
|
+
|
210
|
+
#region Agg Types
|
211
|
+
|
212
|
+
class BareTypes::Struct
|
213
|
+
def self.make(depth, names)
|
214
|
+
hash = {}
|
215
|
+
0.upto(rand(STRUCT_FIELDS_MAX) + 1) do
|
216
|
+
hash[create_user_type_name.to_sym] = get_type(depth + 1, names)
|
217
|
+
end
|
218
|
+
self.new(hash)
|
219
|
+
end
|
220
|
+
|
221
|
+
def create_input
|
222
|
+
input = {}
|
223
|
+
@mapping.keys.each do |name|
|
224
|
+
input[name] = @mapping[name].create_input
|
225
|
+
end
|
226
|
+
input
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# endregion
|
231
|
+
|
232
|
+
class BareTypes::Schema
|
233
|
+
def create_input
|
234
|
+
input = {}
|
235
|
+
@types.each do |key, type|
|
236
|
+
input[key] = type.create_input
|
237
|
+
end
|
238
|
+
input
|
239
|
+
end
|
240
|
+
|
241
|
+
def self.make
|
242
|
+
schema = nil
|
243
|
+
loop do
|
244
|
+
names = []
|
245
|
+
schema = {}
|
246
|
+
0.upto(rand(10)+1) do
|
247
|
+
names << create_user_type_name.to_sym
|
248
|
+
end
|
249
|
+
names.each do |name|
|
250
|
+
without_this_name = names.select { |n| n != name }
|
251
|
+
schema[name] = get_type(0, without_this_name, false)
|
252
|
+
end
|
253
|
+
begin
|
254
|
+
schema = Bare.Schema(schema)
|
255
|
+
rescue CircularSchema
|
256
|
+
next
|
257
|
+
end
|
258
|
+
break
|
259
|
+
end
|
260
|
+
schema
|
261
|
+
end
|
262
|
+
end
|
data/lib/lexer.rb
CHANGED
@@ -3,55 +3,56 @@ require_relative './exceptions'
|
|
3
3
|
def lexer(path)
|
4
4
|
tokens = []
|
5
5
|
line_num = 0
|
6
|
-
File.open(path)
|
6
|
+
file = File.open(path)
|
7
|
+
file.each do |line|
|
7
8
|
while line.size > 0
|
8
9
|
if /^#/.match(line)
|
9
10
|
break
|
10
11
|
elsif /^\n/.match(line)
|
11
12
|
break
|
12
13
|
elsif /^ /.match(line)
|
13
|
-
line = line[1..]
|
14
|
+
line = line[1..line.size]
|
14
15
|
elsif /^</.match(line)
|
15
|
-
line = line[1..]
|
16
|
+
line = line[1..line.size]
|
16
17
|
tokens << :less_than
|
17
18
|
elsif /^>/.match(line)
|
18
|
-
line = line[1..]
|
19
|
+
line = line[1..line.size]
|
19
20
|
tokens << :greater_than
|
20
21
|
next
|
21
22
|
elsif /^{/.match(line)
|
22
|
-
line = line[1..]
|
23
|
+
line = line[1..line.size]
|
23
24
|
tokens << :open_block
|
24
25
|
elsif /^=/.match(line)
|
25
|
-
line = line[1..]
|
26
|
+
line = line[1..line.size]
|
26
27
|
tokens << :equal
|
27
28
|
elsif /^}/.match(line)
|
28
|
-
line = line[1..]
|
29
|
+
line = line[1..line.size]
|
29
30
|
tokens << :close_block
|
30
31
|
elsif /^\[/.match(line)
|
31
|
-
line = line[1..]
|
32
|
+
line = line[1..line.size]
|
32
33
|
tokens << :open_brace
|
33
34
|
elsif /^\]/.match(line)
|
34
|
-
line = line[1..]
|
35
|
+
line = line[1..line.size]
|
35
36
|
tokens << :close_brace
|
36
37
|
elsif /^\(/.match(line)
|
37
|
-
line = line[1..]
|
38
|
+
line = line[1..line.size]
|
38
39
|
tokens << :open_paren
|
39
40
|
elsif /^\)/.match(line)
|
40
|
-
line = line[1..]
|
41
|
+
line = line[1..line.size]
|
41
42
|
tokens << :close_paren
|
42
43
|
elsif /^\|/.match(line)
|
43
|
-
line = line[1..]
|
44
|
+
line = line[1..line.size]
|
44
45
|
tokens << :bar
|
45
46
|
elsif match = /^([0-9]+)/.match(line)
|
46
47
|
tokens << match[0].to_i
|
47
|
-
line = line[(match[0].size)..]
|
48
|
+
line = line[(match[0].size)..line.size]
|
48
49
|
next
|
49
|
-
elsif match = /^[a-z,A-Z,_][_,a-z,A-Z,0-9]
|
50
|
+
elsif match = /^[a-z,A-Z,_][_,a-z,A-Z,0-9]*/.match(line)
|
50
51
|
tokens << match[0]
|
51
|
-
line = line[(match[0].size)..]
|
52
|
+
line = line[(match[0].size)..line.size]
|
52
53
|
elsif /:/.match(line)
|
53
54
|
tokens << :colon
|
54
|
-
line = line[1..]
|
55
|
+
line = line[1..line.size]
|
55
56
|
else
|
56
57
|
raise SchemaParsingException.new("Unable to lex line #{line_num} near #{line.inspect}")
|
57
58
|
end
|
data/lib/parser.rb
CHANGED
@@ -38,15 +38,15 @@ class Parser
|
|
38
38
|
name = tokens[0]
|
39
39
|
int_repr = tokens[2]
|
40
40
|
enum_hash[int_repr] = name
|
41
|
-
tokens = tokens[3..]
|
41
|
+
tokens = tokens[3..tokens.size]
|
42
42
|
else
|
43
43
|
enum_hash[count] = tokens[0]
|
44
44
|
count += 1
|
45
|
-
tokens = tokens[1..]
|
45
|
+
tokens = tokens[1..tokens.size]
|
46
46
|
end
|
47
47
|
end
|
48
48
|
enum = Bare.Enum(enum_hash)
|
49
|
-
return tokens[1..], enum
|
49
|
+
return tokens[1..tokens.size], enum
|
50
50
|
end
|
51
51
|
|
52
52
|
def parse_union(tokens)
|
@@ -55,13 +55,13 @@ class Parser
|
|
55
55
|
# type A_UNION ( int | uint | data = 7 | f32 )
|
56
56
|
while tokens[0] != :close_paren
|
57
57
|
if tokens[0] == :bar
|
58
|
-
tokens = tokens[1..]
|
58
|
+
tokens = tokens[1..tokens.size]
|
59
59
|
else
|
60
60
|
if tokens[1] == :equal
|
61
61
|
raise SchemaParsingException.new("Equals sign in union must be followed by a number") unless tokens[2].is_a?(Numeric)
|
62
62
|
count = tokens[2]
|
63
63
|
tokens, type = self.parse(tokens)
|
64
|
-
tokens = tokens[2..]
|
64
|
+
tokens = tokens[2..tokens.size]
|
65
65
|
union_hash[count] = type
|
66
66
|
count += 1
|
67
67
|
else
|
@@ -78,63 +78,63 @@ class Parser
|
|
78
78
|
struct_fields = {}
|
79
79
|
while tokens.size >= 2 and tokens[1] == :colon
|
80
80
|
name = tokens[0]
|
81
|
-
tokens, type = self.parse(tokens[2..])
|
81
|
+
tokens, type = self.parse(tokens[2..tokens.size])
|
82
82
|
struct_fields[name.to_sym] = type
|
83
83
|
end
|
84
|
-
return tokens[1..], struct_fields
|
84
|
+
return tokens[1..tokens.size], struct_fields
|
85
85
|
end
|
86
86
|
|
87
87
|
def parse(tokens)
|
88
88
|
while tokens.size > 0
|
89
89
|
if tokens[0] == "type"
|
90
90
|
name = tokens[1]
|
91
|
-
tokens, type = self.parse(tokens[2..])
|
91
|
+
tokens, type = self.parse(tokens[2..tokens.size])
|
92
92
|
@definitions[name.to_sym] = type
|
93
93
|
elsif tokens[0] == "map"
|
94
94
|
raise SchemaParsingException.new("Map must be followed by a '[' eg. map[string]data") if tokens[1] != :open_brace
|
95
|
-
tokens, map_from_type = parse(tokens[2..])
|
95
|
+
tokens, map_from_type = parse(tokens[2..tokens.size])
|
96
96
|
raise SchemaParsingException.new("Map to type must be followed by a ']' eg. map[string]data") if tokens[0] != :close_brace
|
97
|
-
tokens, map_to_type = parse(tokens[1..])
|
97
|
+
tokens, map_to_type = parse(tokens[1..tokens.size])
|
98
98
|
return tokens, Bare.Map(map_from_type, map_to_type)
|
99
99
|
elsif tokens[0] == "data" && tokens.size > 3 && tokens[1] == :less_than
|
100
100
|
raise SchemaParsingException.new("data< must be followed by a number for a fixed sized bare data") unless tokens[2].is_a?(Numeric)
|
101
101
|
raise SchemaParsingException.new("data<# must be followed by a >") unless tokens[3] == :greater_than
|
102
|
-
return tokens[4..], Bare.DataFixedLen(tokens[2])
|
102
|
+
return tokens[4..tokens.size], Bare.DataFixedLen(tokens[2])
|
103
103
|
elsif tokens[0] == "enum"
|
104
104
|
name = tokens[1]
|
105
105
|
raise SchemaParsingException.new("Enum must be followed by a '{'") if tokens[2] != :open_block
|
106
|
-
tokens, enum = parse_enum(tokens[3..])
|
106
|
+
tokens, enum = parse_enum(tokens[3..tokens.size])
|
107
107
|
@definitions[name.to_sym] = enum
|
108
108
|
elsif tokens[0] == "optional"
|
109
109
|
raise SchemaParsingException.new("Optional must be followed by a '< TYPE > you are missing the first <'") if tokens[1] != :less_than
|
110
|
-
tokens, optional_type = self.parse(tokens[2..])
|
110
|
+
tokens, optional_type = self.parse(tokens[2..tokens.size])
|
111
111
|
raise SchemaParsingException.new("Optional must be followed by a '< TYPE >' you are missing the last >") if tokens[0] != :greater_than
|
112
|
-
return tokens[1..], Bare.Optional(optional_type)
|
112
|
+
return tokens[1..tokens.size], Bare.Optional(optional_type)
|
113
113
|
elsif tokens[0] == :open_brace
|
114
114
|
if tokens[1].is_a?(Numeric)
|
115
115
|
size = tokens[1]
|
116
116
|
raise SchemaParsingException.new("Fixed Length Array size must be followed by a ']'") if tokens[2] != :close_brace
|
117
|
-
tokens, arr_type = parse(tokens[3..])
|
117
|
+
tokens, arr_type = parse(tokens[3..tokens.size])
|
118
118
|
return tokens, Bare.ArrayFixedLen(arr_type, size)
|
119
119
|
else
|
120
|
-
tokens, arr_type = parse(tokens[2..])
|
120
|
+
tokens, arr_type = parse(tokens[2..tokens.size])
|
121
121
|
return tokens, Bare.Array(arr_type)
|
122
122
|
end
|
123
123
|
elsif tokens[0] == :open_paren
|
124
|
-
tokens, union_hash = parse_union(tokens[1..])
|
124
|
+
tokens, union_hash = parse_union(tokens[1..tokens.size])
|
125
125
|
raise SchemaParsingException.new("Union must be followed by a ')'") if tokens[0] != :close_paren
|
126
|
-
return tokens[1..], Bare.Union(union_hash)
|
126
|
+
return tokens[1..tokens.size], Bare.Union(union_hash)
|
127
127
|
elsif tokens[0] == :open_block
|
128
|
-
tokens, struct_fields = parse_struct(tokens[1..])
|
128
|
+
tokens, struct_fields = parse_struct(tokens[1..tokens.size])
|
129
129
|
strct = Bare.Struct(struct_fields)
|
130
130
|
return tokens, strct
|
131
131
|
elsif @primitives.include?(tokens[0])
|
132
132
|
type = @primitives[tokens[0]]
|
133
|
-
return tokens[1..], type
|
133
|
+
return tokens[1..tokens.size], type
|
134
134
|
elsif @definitions.keys.include?(tokens[0].to_sym) # User defined type
|
135
|
-
return tokens[1..],
|
135
|
+
return tokens[1..tokens.size], tokens[0].to_sym
|
136
136
|
elsif tokens[0].is_a?(String) && tokens[0][0].upcase == tokens[0][0] # Not yet defined user type
|
137
|
-
return tokens[1..], tokens[0].to_sym
|
137
|
+
return tokens[1..tokens.size], tokens[0].to_sym
|
138
138
|
else
|
139
139
|
raise SchemaParsingException.new("Unable to parse token: #{tokens[0]}")
|
140
140
|
end
|