rogdl 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -0
- data/README.txt +51 -0
- data/lib/array.rb +18 -0
- data/lib/fixnum.rb +10 -0
- data/lib/hash.rb +10 -0
- data/lib/nil.rb +21 -0
- data/lib/node.rb +139 -0
- data/lib/parser.rb +398 -0
- data/lib/rogdl.rb +14 -0
- data/lib/schema.rb +140 -0
- data/lib/string.rb +38 -0
- data/lib/symbol.rb +9 -0
- data/lib/writer.rb +117 -0
- data/test/test_array.rb +30 -0
- data/test/test_fixnum.rb +13 -0
- data/test/test_hash.rb +19 -0
- data/test/test_nil.rb +30 -0
- data/test/test_node.rb +279 -0
- data/test/test_parser.rb +189 -0
- data/test/test_rogdl.rb +11 -0
- data/test/test_schema.rb +66 -0
- data/test/test_string.rb +32 -0
- data/test/test_symbol.rb +13 -0
- data/test/test_writer.rb +71 -0
- metadata +70 -0
data/History.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
rogdl
|
2
|
+
by Ben Hidalgo
|
3
|
+
http://rogdl.rubyforge.org/
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Rogdl is designed to make using the Ordered Graph Data Language (OGDL) simple and easy within Ruby. It contains a parser, some utilities and classes to support OGDL files.
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
OGDL Parsing. A bridge to ERB. XML file generation. Schema definition and validation of OGDL documents. Fully OGDL syntax not yet supported (but most is.) Simple OGDL writer not yet written (haven't needed it yet).
|
12
|
+
|
13
|
+
== SYNOPSIS:
|
14
|
+
|
15
|
+
clouds = Parser.parse_file("./rspec/clouds.ogdl")
|
16
|
+
families = clouds.find_all_named('family')
|
17
|
+
writer = Writer.new.load_templates('./rspec')
|
18
|
+
writer.write_to_files(families, proc {|family| "./rspec/samples/html/#{family.name.gvalue}.htm"})
|
19
|
+
|
20
|
+
== REQUIREMENTS:
|
21
|
+
|
22
|
+
erb
|
23
|
+
|
24
|
+
== INSTALL:
|
25
|
+
|
26
|
+
sudo gem install rogdl
|
27
|
+
|
28
|
+
== LICENSE:
|
29
|
+
|
30
|
+
(The MIT License)
|
31
|
+
|
32
|
+
Copyright (c) 2007 FIX
|
33
|
+
|
34
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
35
|
+
a copy of this software and associated documentation files (the
|
36
|
+
'Software'), to deal in the Software without restriction, including
|
37
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
38
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
39
|
+
permit persons to whom the Software is furnished to do so, subject to
|
40
|
+
the following conditions:
|
41
|
+
|
42
|
+
The above copyright notice and this permission notice shall be
|
43
|
+
included in all copies or substantial portions of the Software.
|
44
|
+
|
45
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
46
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
47
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
48
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
49
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
50
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
51
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/array.rb
ADDED
data/lib/fixnum.rb
ADDED
data/lib/hash.rb
ADDED
data/lib/nil.rb
ADDED
data/lib/node.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
module Rogdl
|
2
|
+
class Node
|
3
|
+
include Enumerable
|
4
|
+
include Comparable
|
5
|
+
|
6
|
+
attr_accessor :gname, :gparent
|
7
|
+
|
8
|
+
def initialize(aName)
|
9
|
+
self.gname = aName
|
10
|
+
end
|
11
|
+
|
12
|
+
def subs(key,value)
|
13
|
+
@@subs ||= {}
|
14
|
+
@@subs[key] = value
|
15
|
+
end
|
16
|
+
|
17
|
+
def each
|
18
|
+
children.each {|element| yield(element)}
|
19
|
+
end
|
20
|
+
|
21
|
+
def empty?
|
22
|
+
children.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
def glength
|
26
|
+
children.length
|
27
|
+
end
|
28
|
+
|
29
|
+
def gvalue
|
30
|
+
if empty?
|
31
|
+
return nil
|
32
|
+
else
|
33
|
+
val = gfirst.gname
|
34
|
+
return val unless val[0,1] == ':'
|
35
|
+
return @@subs[val[1, val.length].to_sym]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
alias gval gvalue
|
39
|
+
|
40
|
+
def gfirst
|
41
|
+
return children.first
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def [](aName)
|
46
|
+
if aName.is_a?(String)
|
47
|
+
children.each do |node|
|
48
|
+
return node if node.gname == aName
|
49
|
+
end
|
50
|
+
return nil
|
51
|
+
elsif aName.is_a?(Fixnum)
|
52
|
+
return children[aName]
|
53
|
+
else
|
54
|
+
throw "aName must be a String or Fixnum"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def []=(arg1, arg2)
|
59
|
+
a1 = arg1.to_n
|
60
|
+
a1.add arg2.to_n
|
61
|
+
add(a1)
|
62
|
+
end
|
63
|
+
|
64
|
+
def method_missing(keyword, *args)
|
65
|
+
word = keyword.to_s
|
66
|
+
if word.reverse[0,2] == 'zz'
|
67
|
+
return self["#{keyword}"] unless self["#{keyword}"].nil?
|
68
|
+
return find_all_named(word[0, word.length-2])
|
69
|
+
else
|
70
|
+
return self["#{keyword}"]
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
def find_all_named(aName)
|
76
|
+
if aName.is_a?(String)
|
77
|
+
find_all {|node| node.gname == aName}
|
78
|
+
elsif aName.is_a?(Array)
|
79
|
+
find_all {|node| aName.include?(node.gname)}
|
80
|
+
else
|
81
|
+
throw 'aName must be a String or Array'
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
alias gkids find_all_named
|
86
|
+
|
87
|
+
def <=>(anOther)
|
88
|
+
self.gname <=> anOther.gname
|
89
|
+
end
|
90
|
+
|
91
|
+
def add(aNode)
|
92
|
+
node = aNode.to_n
|
93
|
+
children << node
|
94
|
+
node.gparent = self
|
95
|
+
return self
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_n
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
def to_xml
|
103
|
+
return "<#{@gname}/>" if empty?
|
104
|
+
s = "<#{@gname}>"
|
105
|
+
if glength == 1 && gfirst.glength == 0
|
106
|
+
s += gvalue
|
107
|
+
else
|
108
|
+
children.each {|node| s += node.to_xml}
|
109
|
+
end
|
110
|
+
s += "</#{@gname}>"
|
111
|
+
return s
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
def eql?(bNode)
|
117
|
+
return self.flatten.collect {|n| n.gname} == bNode.flatten.collect {|n| n.gname}
|
118
|
+
end
|
119
|
+
|
120
|
+
def ==(bNode)
|
121
|
+
return false if bNode.nil?
|
122
|
+
return self.eql?(bNode)
|
123
|
+
end
|
124
|
+
|
125
|
+
def flatten
|
126
|
+
me = []
|
127
|
+
me << self
|
128
|
+
self.each {|n| me << n.flatten}
|
129
|
+
return me.flatten
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def children
|
135
|
+
@children ||= []
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
data/lib/parser.rb
ADDED
@@ -0,0 +1,398 @@
|
|
1
|
+
require 'lib/node'
|
2
|
+
module Rogdl
|
3
|
+
|
4
|
+
|
5
|
+
InLine = :in_line
|
6
|
+
InPhrase = :in_phrase
|
7
|
+
InClause = :in_clause
|
8
|
+
InSQString = :in_sq_string
|
9
|
+
InDQString = :in_dq_string
|
10
|
+
InString = :in_string
|
11
|
+
InEscape = :in_escape
|
12
|
+
InWhitespace = :in_whitespace
|
13
|
+
InComment = :in_comment
|
14
|
+
Indent = :indent
|
15
|
+
|
16
|
+
SchemaString = '#schema://'
|
17
|
+
|
18
|
+
class Parser
|
19
|
+
attr_accessor :state, :spaces, :lines, :prev_state, :escape, :indenting, :line_count
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@spaces = 0
|
26
|
+
@lines = [[]]
|
27
|
+
@prev_state = nil
|
28
|
+
@escape = false
|
29
|
+
@state = InLine
|
30
|
+
@indenting = true
|
31
|
+
@line_count = 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def Parser.parse_file(filename)
|
35
|
+
file = File.new(filename)
|
36
|
+
contents = ""
|
37
|
+
file.each_byte do |ch|
|
38
|
+
contents << ch
|
39
|
+
end
|
40
|
+
first_line = contents.split("\n").first
|
41
|
+
schema = nil
|
42
|
+
if first_line[0, SchemaString.length] == SchemaString
|
43
|
+
schema = Schema.new(Parser.parse_file(first_line[SchemaString.length, first_line.length]))
|
44
|
+
end
|
45
|
+
node = Parser.parse(contents)
|
46
|
+
if !schema.nil?
|
47
|
+
messages = schema.validate(node);
|
48
|
+
if messages.length > 0
|
49
|
+
puts filename + ' does not match the supplied schema'
|
50
|
+
puts messages
|
51
|
+
end
|
52
|
+
end
|
53
|
+
return node
|
54
|
+
end
|
55
|
+
|
56
|
+
def Parser.parse(text)
|
57
|
+
p = Parser.new
|
58
|
+
p.translate(text)
|
59
|
+
return p.convert
|
60
|
+
end
|
61
|
+
|
62
|
+
def append(char)
|
63
|
+
@lines.last.last << char
|
64
|
+
@indenting = false
|
65
|
+
end
|
66
|
+
|
67
|
+
def add_line
|
68
|
+
@lines << []
|
69
|
+
@indenting = true
|
70
|
+
end
|
71
|
+
|
72
|
+
def indent
|
73
|
+
@lines.last << nil
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_s
|
77
|
+
@lines.to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
def changeto(state)
|
81
|
+
@state = state
|
82
|
+
end
|
83
|
+
|
84
|
+
def complain(message)
|
85
|
+
raise message + " line count: #{@line_count}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def translate(string)
|
89
|
+
string.length.times {|i| read(string[i,1])}
|
90
|
+
end
|
91
|
+
|
92
|
+
def capture(state)
|
93
|
+
@prev_state = state
|
94
|
+
end
|
95
|
+
|
96
|
+
# def end_string
|
97
|
+
# @lines.last << ''
|
98
|
+
# end
|
99
|
+
|
100
|
+
def begin_string
|
101
|
+
@lines.last << ''
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
def read(char)
|
106
|
+
case
|
107
|
+
when self.state == InLine
|
108
|
+
read_line(char)
|
109
|
+
|
110
|
+
when self.state == InString
|
111
|
+
read_string(char)
|
112
|
+
|
113
|
+
when self.state == InSQString
|
114
|
+
read_sq_string(char)
|
115
|
+
|
116
|
+
when self.state == InDQString
|
117
|
+
read_dq_string(char)
|
118
|
+
|
119
|
+
when self.state == InPhrase
|
120
|
+
read_phrase(char)
|
121
|
+
|
122
|
+
when self.state == InComment
|
123
|
+
read_comment(char)
|
124
|
+
|
125
|
+
else
|
126
|
+
complain "unexpected state #{@state}"
|
127
|
+
end #switch state
|
128
|
+
end # read
|
129
|
+
|
130
|
+
def read_line(char)
|
131
|
+
case
|
132
|
+
when char == "'" # Single Quotes
|
133
|
+
complain 'invalid indentation ' if @spaces != 0
|
134
|
+
begin_string
|
135
|
+
capture(InLine)
|
136
|
+
changeto(InSQString)
|
137
|
+
|
138
|
+
when char == '"' # Double Quotes
|
139
|
+
complain 'invalid indentation ' if @spaces != 0
|
140
|
+
begin_string
|
141
|
+
capture(InLine)
|
142
|
+
changeto(InDQString)
|
143
|
+
|
144
|
+
when char == ' ' # Whitespace
|
145
|
+
if @indenting
|
146
|
+
@spaces += 1
|
147
|
+
if @spaces == 4
|
148
|
+
@spaces = 0
|
149
|
+
indent
|
150
|
+
end
|
151
|
+
else
|
152
|
+
# ignore in line whitespace
|
153
|
+
end
|
154
|
+
|
155
|
+
when char == '#' # Comment
|
156
|
+
changeto(InComment)
|
157
|
+
|
158
|
+
when char == "\t" # Tab
|
159
|
+
complain "invalid number of white spaces" if @spaces != 0
|
160
|
+
indent
|
161
|
+
|
162
|
+
when char == "\n" # New Line
|
163
|
+
add_line
|
164
|
+
@line_count += 1
|
165
|
+
@spaces = 0
|
166
|
+
|
167
|
+
when char == "(" # Open parentheses
|
168
|
+
count = 0
|
169
|
+
@lines.last.each {|n| count += 1 if n.nil?}
|
170
|
+
add_line
|
171
|
+
indent
|
172
|
+
count.times {indent}
|
173
|
+
changeto(InPhrase)
|
174
|
+
|
175
|
+
when char == ")" # Close parentheses
|
176
|
+
complain "close parentheses is unexpected"
|
177
|
+
|
178
|
+
when char == "," # Comma
|
179
|
+
complain "comma is unexpected"
|
180
|
+
|
181
|
+
else # Normal Character
|
182
|
+
complain "invalid indentation parsing character: #{char}" if @spaces != 0
|
183
|
+
begin_string
|
184
|
+
capture(InLine)
|
185
|
+
changeto(InString)
|
186
|
+
read(char)
|
187
|
+
|
188
|
+
end # switch
|
189
|
+
|
190
|
+
end # read_line
|
191
|
+
|
192
|
+
def read_phrase(char)
|
193
|
+
|
194
|
+
case
|
195
|
+
when char == "'" # Single Quotes
|
196
|
+
capture(InPhrase)
|
197
|
+
changeto(InSQString)
|
198
|
+
begin_string
|
199
|
+
|
200
|
+
when char == '"' # Double Quotes
|
201
|
+
capture(InPhrase)
|
202
|
+
changeto(InDQString)
|
203
|
+
begin_string
|
204
|
+
|
205
|
+
when char == ' ' # Whitespace
|
206
|
+
# ignore whitespace
|
207
|
+
|
208
|
+
when char == '#' # Comment
|
209
|
+
complain "unexpected comment '#' in phrase"
|
210
|
+
|
211
|
+
when char == "\t" # Tab
|
212
|
+
# ignore Tab
|
213
|
+
|
214
|
+
when char == "\n" # New Line
|
215
|
+
# complain "unexpected new line character in phrase"
|
216
|
+
# add_line
|
217
|
+
changeto(InLine)
|
218
|
+
read(char)
|
219
|
+
|
220
|
+
when char == "(" # Open parentheses
|
221
|
+
complain "Invalid open parentheses"
|
222
|
+
|
223
|
+
when char == ")" # Close parentheses
|
224
|
+
changeto(InLine)
|
225
|
+
|
226
|
+
when char == "," # Comma
|
227
|
+
count = 0
|
228
|
+
@lines.last.each {|n| count += 1 if n.nil?}
|
229
|
+
add_line
|
230
|
+
count.times {indent}
|
231
|
+
|
232
|
+
else # Normal Character
|
233
|
+
capture(InPhrase)
|
234
|
+
changeto(InString)
|
235
|
+
begin_string
|
236
|
+
read(char)
|
237
|
+
|
238
|
+
end # switch
|
239
|
+
|
240
|
+
end # read_phrase
|
241
|
+
|
242
|
+
def read_string(char)
|
243
|
+
case
|
244
|
+
when char == "'" # Single Quotes
|
245
|
+
complain "single quotes not allowed inside of Node name"
|
246
|
+
|
247
|
+
when char == '"' # Double Quotes
|
248
|
+
complain "double quotes not allowed inside of Node name"
|
249
|
+
|
250
|
+
when char == ' ' # Whitespace
|
251
|
+
changeto(@prev_state)
|
252
|
+
|
253
|
+
when char == '#' # Comment
|
254
|
+
complain "double quotes not allowed inside of Node name"
|
255
|
+
|
256
|
+
when char == '\\' # Escape
|
257
|
+
complain "escape is not allowed inside of Node name"
|
258
|
+
|
259
|
+
when char == "\t" # Tab
|
260
|
+
complain "tabs not allowed inside of Node name"
|
261
|
+
|
262
|
+
when char == "\n" # New Line
|
263
|
+
changeto(InLine)
|
264
|
+
add_line
|
265
|
+
@line_count += 1
|
266
|
+
|
267
|
+
when char == "," # Comma
|
268
|
+
complain "unexpected comma" unless @prev_state == InPhrase
|
269
|
+
changeto(InPhrase)
|
270
|
+
read(char)
|
271
|
+
|
272
|
+
when char == ")" # Close parentheses
|
273
|
+
changeto(@prev_state)
|
274
|
+
|
275
|
+
else # Normal Character
|
276
|
+
append(char)
|
277
|
+
end # switch
|
278
|
+
|
279
|
+
end # read_string
|
280
|
+
|
281
|
+
|
282
|
+
|
283
|
+
def read_sq_string(char)
|
284
|
+
case
|
285
|
+
when char == "'" # Single Quotes
|
286
|
+
if @escape
|
287
|
+
append(char)
|
288
|
+
@escape = false
|
289
|
+
else
|
290
|
+
changeto(@prev_state)
|
291
|
+
end
|
292
|
+
|
293
|
+
when char == '\\' # Escape
|
294
|
+
@escape = true
|
295
|
+
|
296
|
+
when char == "\n" # New Line
|
297
|
+
complain "new line is unexpected"
|
298
|
+
|
299
|
+
else # Normal Character
|
300
|
+
append(char)
|
301
|
+
@escape = false
|
302
|
+
end # switch
|
303
|
+
|
304
|
+
end # read_sq_string
|
305
|
+
|
306
|
+
def read_dq_string(char)
|
307
|
+
case
|
308
|
+
when char == '"' # Double Quotes
|
309
|
+
if @escape
|
310
|
+
append(char)
|
311
|
+
@escape = false
|
312
|
+
else
|
313
|
+
changeto(@prev_state)
|
314
|
+
end
|
315
|
+
|
316
|
+
when char == '\\' # Escape
|
317
|
+
@escape = true
|
318
|
+
|
319
|
+
when char == "\n" # New Line
|
320
|
+
complain "new line is unexpected"
|
321
|
+
|
322
|
+
else # Normal Character
|
323
|
+
append(char)
|
324
|
+
@escape = false
|
325
|
+
end # switch
|
326
|
+
|
327
|
+
end # read_sq_string
|
328
|
+
|
329
|
+
|
330
|
+
|
331
|
+
def read_comment(char)
|
332
|
+
case
|
333
|
+
when char == "\n" # New Line
|
334
|
+
add_line
|
335
|
+
changeto(InLine)
|
336
|
+
else
|
337
|
+
# ignore characters
|
338
|
+
|
339
|
+
end # switch
|
340
|
+
|
341
|
+
end # read_line
|
342
|
+
|
343
|
+
|
344
|
+
def convert
|
345
|
+
|
346
|
+
nodes = convert_nodes
|
347
|
+
|
348
|
+
nodes.each_with_index do |line, i|
|
349
|
+
first_in_line = nil
|
350
|
+
line.each_with_index do |node, j|
|
351
|
+
|
352
|
+
if !node.nil?
|
353
|
+
|
354
|
+
if first_in_line.nil?
|
355
|
+
first_in_line = node
|
356
|
+
#find parent here
|
357
|
+
|
358
|
+
nodes[0..i].reverse.each do |array|
|
359
|
+
|
360
|
+
if !array[j-1].nil? && i != 0
|
361
|
+
array[j-1].add(node)
|
362
|
+
break
|
363
|
+
end
|
364
|
+
|
365
|
+
end
|
366
|
+
|
367
|
+
else
|
368
|
+
first_in_line.add(node)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
# puts parent.flatten.collect {|n| n.gname}
|
374
|
+
return nodes[0][0]
|
375
|
+
end
|
376
|
+
|
377
|
+
def convert_nodes
|
378
|
+
|
379
|
+
nodes = []
|
380
|
+
|
381
|
+
@lines.each_with_index do |line, i|
|
382
|
+
if line != []
|
383
|
+
temp = []
|
384
|
+
nodes << temp
|
385
|
+
|
386
|
+
line.each_with_index do |name, j|
|
387
|
+
temp[j] = Node.new(name) unless name.nil?
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
return nodes
|
392
|
+
end
|
393
|
+
|
394
|
+
|
395
|
+
end # class Parser
|
396
|
+
|
397
|
+
|
398
|
+
end # module Rogdl
|