sfp 0.1.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/lib/sfp/Sfplib.rb ADDED
@@ -0,0 +1,357 @@
1
+ module Sfp
2
+ module SfpLangHelper
3
+ attr_accessor :root_dir, :home_dir
4
+ attr_reader :root, :used_classes, :arrays, :conformant
5
+
6
+ def init
7
+ @root = Hash.new
8
+ @now = @root
9
+ @id = 0
10
+ @root['Object'] = { '_self' => 'Object', '_context' => 'class', '_parent' => @root }
11
+ @unexpanded_classes = Array.new
12
+ @used_classes = Array.new
13
+ @arrays = Hash.new
14
+ @conformant = false
15
+ end
16
+
17
+ def next_id
18
+ nid = "c" + @id.to_s
19
+ @id += 1
20
+ return nid
21
+ end
22
+
23
+ def null_value(isa=nil)
24
+ return { '_context' => 'null', '_isa' => isa }
25
+ end
26
+
27
+ def to_ref(path)
28
+ ref = "$." + path
29
+ return ref
30
+ end
31
+
32
+ def to_sfp
33
+ return @root
34
+ end
35
+
36
+ def create_constraint(name, type='and')
37
+ return { '_self' => name,
38
+ '_context' => 'constraint',
39
+ '_type' => type,
40
+ '_parent' => @now
41
+ }
42
+ end
43
+
44
+ def process_file(file)
45
+ filepath = file
46
+ filepath = @root_dir + "/" + file if not @root_dir.nil? and file[0,1] != '/'
47
+ filepath = @home_dir + "/" + file if not @home_dir.nil? and not File.exist?(filepath)
48
+
49
+ raise Exception, 'File not found: ' + file if not File.exist?(filepath)
50
+
51
+ new_home_dir = File.expand_path(File.dirname(filepath))
52
+ parser = Sfp::Parser.new({:root_dir => @root_dir, :home_dir => new_home_dir})
53
+ parser.parse(File.read(filepath))
54
+
55
+ parser.root.each_pair { |key,val|
56
+ if val['_context'] == 'class' or val['_context'] == 'composite'
57
+ @root[key] = val
58
+ elsif val['_context'] == 'state' or val['_context'] == 'constraint'
59
+ if @root.has_key?(key)
60
+ if @root[key]['_context'] != val['_context']
61
+ @root[key] = val
62
+ else
63
+ val['_context'].each_pair { |k2,v2| @root[key][k2] = v2 }
64
+ end
65
+ else
66
+ @root[key] = val
67
+ end
68
+ end
69
+ }
70
+ end
71
+
72
+ def goto_parent(remove_parent=false)
73
+ n = @now
74
+ @now = @now['_parent']
75
+ n.delete('_parent') if remove_parent
76
+ return n
77
+ end
78
+
79
+ def expand_classes
80
+ @unexpanded_classes.each { |c|
81
+ sclass = @root.at?(c['_extends'])
82
+ c.inherits( sclass )
83
+ c['_super'] = (sclass.has_key?('_super') ? sclass['_super'].clone : Array.new)
84
+ c['_super'] << c['_extends']
85
+ }
86
+ end
87
+
88
+ def expand_object(obj)
89
+ return false if not Sfp::Helper.expand_object(obj, @root)
90
+ @used_classes = @used_classes.concat(obj['_classes']).uniq
91
+ end
92
+
93
+ def deep_clone(value)
94
+ if value.is_a?(Hash)
95
+ result = value.clone
96
+ value.each { |k,v|
97
+ if k != '_parent'
98
+ result[k] = deep_clone(v)
99
+ result[k]['_parent'] = result if result[k].is_a?(Hash) and result[k].has_key?('_parent')
100
+ end
101
+ }
102
+ result
103
+ elsif value.is_a?(Array)
104
+ result = Array.new
105
+ value.each { |v| result << deep_clone(v) }
106
+ result
107
+ else
108
+ value
109
+ end
110
+ end
111
+ end
112
+
113
+ module Helper
114
+ def self.deep_clone(value)
115
+ if value.is_a?(Hash)
116
+ result = value.clone
117
+ value.each { |k,v|
118
+ if k != '_parent'
119
+ result[k] = deep_clone(v)
120
+ result[k]['_parent'] = result if result[k].is_a?(Hash) and result[k].has_key?('_parent')
121
+ end
122
+ }
123
+ result
124
+ elsif value.is_a?(Array)
125
+ result = Array.new
126
+ value.each { |v| result << deep_clone(v) }
127
+ result
128
+ else
129
+ value
130
+ end
131
+ end
132
+
133
+ def self.to_json(sfp)
134
+ root = Sfp::Helper.deep_clone(sfp)
135
+ root.accept(Sfp::Visitor::ParentEliminator.new)
136
+ return JSON.generate(root)
137
+ end
138
+
139
+ def self.to_pretty_json(sfp)
140
+ root = Sfp::Helper.deep_clone(sfp)
141
+ root.accept(Sfp::Visitor::ParentEliminator.new)
142
+ return JSON.pretty_generate(root)
143
+ end
144
+
145
+ def self.expand_object(obj, root)
146
+ return false if obj == nil or root == nil or
147
+ not obj.has_key?('_isa') or obj['_isa'] == nil
148
+ objclass = root.at?(obj['_isa'])
149
+ if objclass.nil? or objclass.is_a?(Sfp::Unknown) or objclass.is_a?(Sfp::Undefined)
150
+ raise Exception, 'Super class is not found: ' + obj['_self'] + ' < ' + obj['_isa']
151
+ end
152
+ obj.inherits( objclass )
153
+ obj['_classes'] = (objclass.has_key?('_super') ? objclass['_super'].clone : Array.new)
154
+ obj['_classes'] << obj['_isa']
155
+ return true
156
+ end
157
+ end
158
+
159
+
160
+ class Null
161
+ attr_accessor :type
162
+ end
163
+
164
+ # Instance of this class will be returned as the value of a non-exist variable
165
+ class Undefined
166
+ attr_accessor :path
167
+ def initialize(path=nil); @path = path; end
168
+ def to_s; (@path.nil? ? "<sfp::undefined>" : "<sfp::undefined[#{@path}]>"); end
169
+ end
170
+
171
+ # Instance of this class will be return as the value of an unknown variable
172
+ # in open-world assumption.
173
+ class Unknown
174
+ attr_accessor :path
175
+ def initialize(path=nil); @path = path; end
176
+ def to_s; (@path.nil? ? "<sfp::unknown>" : "<sfp::unknown[#{@path}]>"); end
177
+ end
178
+ end
179
+
180
+ # return a fullpath of reference of this context
181
+ Hash.send(:define_method, "ref") {
182
+ return '$' if not self.has_key?('_parent') or self['_parent'] == nil
183
+ me = (self.has_key?('_self') ? self['_self'] : '')
184
+ return self['_parent'].ref + "." + me
185
+ }
186
+
187
+ # accept method as implementation of Visitor pattern
188
+ Hash.send(:define_method, "accept") { |visitor|
189
+ keys = self.keys
190
+ keys.each do |key|
191
+ next if key == '_parent' or not self.has_key?(key)
192
+ value = self[key]
193
+ go_next = visitor.visit(key, value, self)
194
+ value.accept(visitor) if value.is_a?(Hash) and go_next == true
195
+ end
196
+ }
197
+
198
+ # resolve a reference, return nil if there's no value with given address
199
+ Hash.send(:define_method, "at?") { |addr|
200
+ return Sfp::Unknown.new if not addr.is_a?(String)
201
+
202
+ addrs = addr.split('.', 2)
203
+
204
+ if addrs[0] == '$'
205
+ return self.root.at?(addrs[1])
206
+ elsif addrs[0] == 'root'
207
+ return self.root.at?(addrs[1])
208
+ elsif addrs[0] == 'this' or addrs[0] == 'self'
209
+ return self.at?(addrs[1])
210
+ elsif addrs[0] == 'parent'
211
+ return nil if not self.has_key?('_parent')
212
+ return self['_parent'] if addrs[1].nil?
213
+ return self['_parent'].at?(addrs[1])
214
+ elsif self.has_key?(addrs[0])
215
+ if addrs.length == 1
216
+ return self[addrs[0]]
217
+ else
218
+ return self[addrs[0]].at?(addrs[1]) if self[addrs[0]].is_a?(Hash) and addrs[1] != ''
219
+ end
220
+ end
221
+
222
+ return Sfp::Unknown.new
223
+ }
224
+
225
+ Hash.send(:define_method, "type?") { |name|
226
+ return nil if not self.has_key?(name)
227
+ value = self[name]
228
+ if value != nil
229
+ return '$.Boolean' if value.is_a?(TrueClass) or value.is_a?(FalseClass)
230
+ return '$.Integer' if value.is_a?(Numeric)
231
+ return '$.String' if value.is_a?(String) and not value.isref
232
+ return value['_isa'] if value.isobject or value.isnull
233
+ return "(#{value['_isa']})" if value.is_a?(Hash) and value.isset and value.has_key?('_isa')
234
+ end
235
+
236
+ return nil
237
+ }
238
+
239
+ # return root context of this context
240
+ Hash.send(:define_method, "root") {
241
+ return self if not self.has_key?('_parent') or self['_parent'] == nil
242
+ return self['_parent'].root
243
+ }
244
+
245
+ # return true if this context is a constraint, otherwise false
246
+ Hash.send(:define_method, "isconstraint") {
247
+ return (self.has_key?('_context') and self['_context'] == 'constraint')
248
+ }
249
+
250
+ Hash.send(:define_method, "isset") {
251
+ return (self.has_key?('_context') and self['_context'] == 'set')
252
+ }
253
+
254
+ Hash.send(:define_method, "isprocedure") {
255
+ return (self.has_key?('_context') and self['_context'] == 'procedure')
256
+ }
257
+
258
+ # return true if this context is a class, otherwise false
259
+ Hash.send(:define_method, "isclass") {
260
+ return (self.has_key?('_context') and self['_context'] == 'class')
261
+ }
262
+
263
+ # return superclass' reference if this context is a sub-class, otherwise nil
264
+ Hash.send(:define_method, "extends") {
265
+ return self['_extends'] if self.isclass and self.has_key?('_extends')
266
+ return nil
267
+ }
268
+
269
+ # return true if this class has been expanded, otherwise false
270
+ Hash.send(:define_method, "expanded") {
271
+ @expanded = false if not defined?(@expanded)
272
+ return @expanded
273
+ }
274
+
275
+ # copy attributes and procedures from superclass to itself
276
+ Hash.send(:define_method, 'inherits') { |parent|
277
+ return if not parent.is_a?(Hash)
278
+ parent.each_pair { |key,value|
279
+ next if key[0,1] == '_' or self.has_key?(key)
280
+ if value.is_a?(Hash)
281
+ self[key] = Sfp::Helper.deep_clone(value)
282
+ self[key]['_parent'] = self
283
+ else
284
+ self[key] = value
285
+ end
286
+ }
287
+ @expanded = true
288
+ }
289
+
290
+ # return true if this context is an object, otherwise false
291
+ Hash.send(:define_method, 'isobject') {
292
+ return (self.has_key?('_context') and self['_context'] == 'object')
293
+ }
294
+
295
+ Hash.send(:define_method, 'isa') {
296
+ return nil if not self.isobject or not self.has_key?('_isa')
297
+ return self['_isa']
298
+ }
299
+
300
+ Hash.send(:define_method, 'isvalue') {
301
+ return self.isobject
302
+ }
303
+
304
+ Hash.send(:define_method, 'isnull') {
305
+ return (self.has_key?('_context') and self['_context'] == 'null')
306
+ }
307
+
308
+ Hash.send(:define_method, 'iseither') {
309
+ return (self['_context'] == 'either')
310
+ }
311
+
312
+ Hash.send(:define_method, 'tostring') {
313
+ return 'null' if self.isnull
314
+ return self.ref
315
+ }
316
+
317
+ # add path to the end of a reference
318
+ String.send(:define_method, "push") { |value|
319
+ return self.to_s + "." + value
320
+ }
321
+
322
+ # return first element and keep the rest
323
+ String.send(:define_method, 'explode') {
324
+ return self.split('.', 2)
325
+ }
326
+
327
+ # return an array of [ parent, last_element]
328
+ String.send(:define_method, 'pop_ref') {
329
+ return self.extract
330
+ }
331
+
332
+ # return an array of [ parent, last_element ]
333
+ String.send(:define_method, 'extract') {
334
+ return self if not self.isref
335
+ parts = self.split('.')
336
+ return self if parts.length <= 1
337
+ last = parts[ parts.length - 1 ]
338
+ len = self.length - last.length - 1
339
+ return [self[0, len], last]
340
+ }
341
+
342
+
343
+ # return true if this string is a reference, otherwise false
344
+ String.send(:define_method, 'isref') {
345
+ s = self.to_s
346
+ return true if (s.length > 0 and s[0,1] == '$')
347
+ return false
348
+ }
349
+
350
+ # return the parent of this path
351
+ # e.g.: if self == 'a.b.c.d', it will return 'a.b.c'
352
+ String.send(:define_method, 'to_top') {
353
+ return self if self == '$'
354
+ parts = self.split('.')
355
+ return self[0, self.length - parts[parts.length-1].length - 1]
356
+ }
357
+
data/lib/sfp/parser.rb ADDED
@@ -0,0 +1,128 @@
1
+ module Sfp
2
+ # main class which processes configuration description in SFP language either
3
+ # in file or as a string
4
+ class Parser
5
+ # enable this class to process SFP into FDR (SAS+)
6
+ include Sfp::SasTranslator
7
+
8
+ attr_accessor :root_dir, :home_dir, :conformant
9
+
10
+ def initialize(params={})
11
+ @root_dir = (params[:root_dir].is_a?(String) ?
12
+ params[:root_dir].strip :
13
+ nil)
14
+ @home_dir = (params[:home_dir].is_a?(String) ?
15
+ params[:home_dir].strip :
16
+ nil)
17
+ @root = params[:root]
18
+ @conformant = !!params[:conformant]
19
+ end
20
+
21
+ # @param string : a string in SFP language
22
+ def parse(string)
23
+ lexer = SfpLang::Lexer.new(string)
24
+ tokens = ANTLR3::CommonTokenStream.new(lexer)
25
+ parser = SfpLang::Parser.new(tokens)
26
+ parser.root_dir = @root_dir
27
+ parser.home_dir = @home_dir
28
+ parser.sfp
29
+ @root = parser.root
30
+ @conformant = parser.conformant
31
+ @parser_arrays = parser.arrays
32
+ end
33
+
34
+ =begin
35
+ # Parse SFP file and return its JSON representation
36
+ def self.parse_file(file)
37
+ return file_to_sfp(file)
38
+ end
39
+
40
+ def self.file_to_sfp(file)
41
+ parser = Parser.new
42
+ parser.parse_file(file)
43
+ return parser.to_sfp
44
+ end
45
+
46
+ def self.to_sfp(string)
47
+ parser = Parser.new
48
+ parser.parse(string)
49
+ return parser.to_sfp
50
+ end
51
+
52
+ # parse SFP file
53
+ def parse_file(file)
54
+ f = File.open(file, 'rb')
55
+ lexer = SFP::Lexer.new(f)
56
+ tokens = ANTLR3::CommonTokenStream.new(lexer)
57
+ parser = SFP::Parser.new(tokens)
58
+ parser.root_dir = (@root_dir == nil or @root_dir == '' ?
59
+ File.expand_path('.') : @root_dir)
60
+ parser.home_dir = File.dirname(f.path)
61
+ parser.sfp
62
+ @conformant = parser.conformant
63
+ @root = parser.root
64
+ @parser_arrays = parser.arrays
65
+ end
66
+
67
+ # parse SFP in a string
68
+ def parse(text)
69
+ lexer = SFP::Lexer.new(text)
70
+ tokens = ANTLR3::CommonTokenStream.new(lexer)
71
+ parser = SFP::Parser.new(tokens)
72
+ parser.root_dir = (@root_dir == nil or @root_dir == '' ?
73
+ File.expand_path(File.dirname('.')) : @root_dir)
74
+ parser.home_dir = parser.root_dir
75
+ parser.sfp
76
+ @root = parser.root
77
+ @parser_arrays = parser.arrays
78
+ end
79
+
80
+ # dump the parsed specification into standard output
81
+ def dump(root=nil)
82
+ return if root == nil
83
+ root = Nuri::Sfp.deep_clone(@root)
84
+ root.accept(ParentEliminator.new)
85
+ puts JSON.pretty_generate(root)
86
+ end
87
+
88
+ def self.dump(root)
89
+ return if root == nil
90
+ root = Nuri::Sfp.deep_clone(root)
91
+ root.accept(ParentEliminator.new)
92
+ puts JSON.pretty_generate(root)
93
+ end
94
+
95
+ def to_sfp
96
+ @root
97
+ end
98
+
99
+ def to_json
100
+ root = self.to_sfp
101
+ return if root == nil
102
+ root = Nuri::Sfp.deep_clone(root)
103
+ return Nuri::Sfp.to_json(root)
104
+ end
105
+ =end
106
+
107
+ def to_json(params={})
108
+ return '' if @root.nil?
109
+ return Sfp::Helper.to_pretty_json(@root) if params[:pretty]
110
+ return Sfp::Helper.to_json(@root)
111
+ end
112
+ end
113
+
114
+ =begin
115
+ def self.to_json(sfp)
116
+ root = Sfp::Helper.deep_clone(sfp)
117
+ root.accept(Nuri::Sfp::ParentEliminator.new)
118
+ return JSON.generate(root)
119
+ end
120
+
121
+ def self.to_pretty_json(sfp)
122
+ root = Nuri::Sfp.deep_clone(sfp)
123
+ root.accept(Nuri::Sfp::ParentEliminator.new)
124
+ return JSON.pretty_generate(root)
125
+ end
126
+ =end
127
+
128
+ end