boost_info 0.1.3 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/boost_info.gemspec +1 -0
- data/lib/boost_info/generator.rb +47 -27
- data/lib/boost_info/node.rb +133 -44
- data/lib/boost_info/parser.rb +96 -104
- data/lib/boost_info/version.rb +1 -1
- data/lib/boost_info.rb +10 -4
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0114ee49982c750a6dd040d3ad3cbe442016bc04
|
4
|
+
data.tar.gz: 9294cc5f854ce61386ca2fd54291ebb72fa5c0ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f59e7d7de56fbd7ca43ddd23e0b854bd243c27a39069ced005d7bb2565d66f050a18fbe83e297631805670c6e7a577788c87f37d2f12e1c8932b2ecfe70e4699
|
7
|
+
data.tar.gz: f87f5e05fe8e597d1fd23c26a0c5c8c5b6eec943891593fc8c097d92ba984950886ab7159da53cf15b2157da207198196e58587c8e0abddd67adee7932ec100c
|
data/README.md
CHANGED
data/boost_info.gemspec
CHANGED
@@ -5,6 +5,7 @@ require 'boost_info/version'
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "boost_info"
|
8
|
+
spec.description = "Simple parser for Boost INFO format."
|
8
9
|
spec.version = BoostInfo::VERSION
|
9
10
|
spec.authors = ["Igor Vetrov", "Alexander Tsygankov"]
|
10
11
|
spec.email = ["capybarov@gmail.com"]
|
data/lib/boost_info/generator.rb
CHANGED
@@ -1,43 +1,63 @@
|
|
1
|
-
require 'boost_info/node'
|
2
|
-
|
3
1
|
module BoostInfo
|
4
2
|
class Generator
|
5
|
-
def initialize(
|
6
|
-
|
7
|
-
@hash = hash
|
3
|
+
def initialize(root_node, opts={})
|
4
|
+
@root_node = root_node
|
8
5
|
@opts = opts
|
9
6
|
@opts[:indent] ||= 4
|
10
7
|
end
|
11
8
|
|
12
|
-
def
|
13
|
-
@
|
14
|
-
|
15
|
-
|
16
|
-
|
9
|
+
def to_hash
|
10
|
+
build_hash(@root_node)
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_info
|
14
|
+
build_info(@root_node, 0).join("\n") + "\n"
|
17
15
|
end
|
18
16
|
|
19
17
|
private
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
else
|
30
|
-
v = v.to_s
|
31
|
-
# Wrap spaces and special symbols in double quotes
|
32
|
-
v = %["#{ v }"] if v[/[\s\/:;.,]/]
|
33
|
-
@result << add_indent("#{ k } #{ v }\n", level)
|
34
|
-
end
|
19
|
+
def build_hash(parent_node)
|
20
|
+
hash = {}
|
21
|
+
parent_node.childrens.each do |node|
|
22
|
+
key = @opts[:symbolize_keys] ? node.key.to_sym : node.key
|
23
|
+
if node.childrens
|
24
|
+
hash[key] = build_hash(node)
|
25
|
+
else
|
26
|
+
hash[key] = node.value
|
35
27
|
end
|
36
28
|
end
|
29
|
+
hash
|
30
|
+
end
|
37
31
|
|
38
|
-
|
39
|
-
|
40
|
-
|
32
|
+
def build_info(parent_node, level)
|
33
|
+
lines = []
|
34
|
+
last_node_index = parent_node.childrens.size - 1
|
35
|
+
parent_node.childrens.each_with_index do |node,index|
|
36
|
+
if node.childrens.nil?
|
37
|
+
value = wrap_in_quotes(node.value)
|
38
|
+
lines << add_indent("#{node.key} #{value}", level)
|
39
|
+
elsif node.childrens.any?
|
40
|
+
lines << add_indent("#{node.key} {", level)
|
41
|
+
nested_lines = build_info(node, level + 1)
|
42
|
+
lines.concat(nested_lines)
|
43
|
+
elsif node.childrens.empty?
|
44
|
+
lines << add_indent("#{node.key} { }", level)
|
45
|
+
end
|
46
|
+
if level > 0 && index == last_node_index
|
47
|
+
lines << add_indent("}", level - 1)
|
48
|
+
end
|
41
49
|
end
|
50
|
+
lines
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_indent(string, level)
|
54
|
+
indent = @opts[:indent] * level
|
55
|
+
"#{' ' * indent if indent > 0}#{string}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def wrap_in_quotes(value)
|
59
|
+
string = value.to_s
|
60
|
+
string =~ /[\s\/:;.,{}]/ ? '"' + string + '"' : string
|
61
|
+
end
|
42
62
|
end
|
43
63
|
end
|
data/lib/boost_info/node.rb
CHANGED
@@ -2,67 +2,156 @@
|
|
2
2
|
|
3
3
|
module BoostInfo
|
4
4
|
class Node
|
5
|
-
attr_accessor :
|
5
|
+
attr_accessor :root, :key, :value, :parent, :childrens
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@
|
12
|
-
|
13
|
-
@
|
7
|
+
def initialize(params={})
|
8
|
+
@root = params[:root]
|
9
|
+
@key = params[:key].to_s if params[:key] # normalize key
|
10
|
+
@value = params[:value]
|
11
|
+
@parent = params[:parent]
|
12
|
+
|
13
|
+
@childrens = nil
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
def get(key_to_get, params={})
|
17
|
+
return [] unless @childrens
|
18
|
+
|
19
|
+
if key_to_get.is_a?(Regexp)
|
20
|
+
@childrens.select { |c| c.key =~ key_to_get }
|
20
21
|
else
|
21
|
-
|
22
|
+
@childrens.select { |c| c.key == key_to_get.to_s }
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
26
|
+
def find_by_path(path, params={})
|
27
|
+
if path.size > 1
|
28
|
+
next_node_key = path.shift
|
29
|
+
node = get(next_node_key).first
|
30
|
+
node.find_by_path(path) if node
|
31
|
+
elsif path.size == 1
|
32
|
+
last_key = path.last.to_s
|
33
|
+
get(last_key).first
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
def find_by_key(key, params={})
|
38
|
+
result = []
|
39
|
+
childrens.each do |node|
|
40
|
+
if key.is_a?(Regexp)
|
41
|
+
result << node if node.key =~ key
|
42
|
+
else
|
43
|
+
result << node if node.key == key.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
if node.childrens
|
47
|
+
result_from_children = node.find_by_key(key, params)
|
48
|
+
result.concat(result_from_children)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
result
|
40
52
|
end
|
41
53
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
54
|
+
def parents
|
55
|
+
result = []
|
56
|
+
unless root
|
57
|
+
result << parent
|
58
|
+
result.concat(parent.parents)
|
59
|
+
end
|
60
|
+
result
|
45
61
|
end
|
46
62
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
63
|
+
def siblings
|
64
|
+
parent_childrens = (parent && parent.childrens) || []
|
65
|
+
parent_childrens.reject { |c| c == self }
|
50
66
|
end
|
51
67
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
68
|
+
def auto_insert(path, value, params={})
|
69
|
+
params[:force] ||= true
|
70
|
+
if params[:force]
|
71
|
+
params[:delete_if] = ->(node) { node.childrens.nil? }
|
72
|
+
end
|
73
|
+
|
74
|
+
current_node = self
|
75
|
+
path.each do |new_key|
|
76
|
+
existing_node = current_node.get(new_key).first
|
77
|
+
current_node = if existing_node && existing_node.childrens
|
78
|
+
existing_node
|
79
|
+
else
|
80
|
+
current_node.insert(new_key, value, params)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
current_node
|
84
|
+
end
|
85
|
+
|
86
|
+
def insert(key, value, params={})
|
87
|
+
node = Node.new(key: key, value: value)
|
88
|
+
insert_node(node, params)
|
89
|
+
node
|
90
|
+
end
|
91
|
+
|
92
|
+
def insert_node(node, params={})
|
93
|
+
node.parent = self
|
94
|
+
@childrens ||= []
|
95
|
+
@childrens.delete_if(¶ms[:delete_if]) if params[:delete_if]
|
96
|
+
|
97
|
+
if params[:after]
|
98
|
+
insert_node_after(params[:after], node)
|
99
|
+
elsif params[:before]
|
100
|
+
insert_node_before(params[:before], node)
|
101
|
+
elsif params[:prepend]
|
102
|
+
@childrens.unshift(node)
|
103
|
+
else
|
104
|
+
@childrens << node
|
64
105
|
end
|
65
|
-
|
106
|
+
node
|
107
|
+
end
|
108
|
+
|
109
|
+
def insert_node_after(key, node)
|
110
|
+
index = @childrens.index { |c| c.key == key.to_s }
|
111
|
+
index += 1 if index
|
112
|
+
index = @childrens.size if index > @childrens.size || index.nil?
|
113
|
+
@childrens.insert(index, node)
|
114
|
+
node
|
115
|
+
end
|
116
|
+
|
117
|
+
def insert_node_before(key, node)
|
118
|
+
index = @childrens.index { |c| c.key == key.to_s }
|
119
|
+
index ||= 0
|
120
|
+
@childrens.insert(index, node)
|
121
|
+
node
|
122
|
+
end
|
123
|
+
|
124
|
+
def delete(key)
|
125
|
+
node = find_children(key.to_s)
|
126
|
+
return unless node
|
127
|
+
|
128
|
+
delete_children(node)
|
129
|
+
end
|
130
|
+
|
131
|
+
def find_children(key_to_find)
|
132
|
+
return unless @childrens
|
133
|
+
key_to_find = key_to_find.to_s
|
134
|
+
@childrens.find { |children| children.key == key_to_find }
|
135
|
+
end
|
136
|
+
|
137
|
+
def delete_children(node)
|
138
|
+
return unless @childrens
|
139
|
+
|
140
|
+
node.parent = nil
|
141
|
+
|
142
|
+
index = @childrens.index(node)
|
143
|
+
@childrens.delete_at(index)
|
144
|
+
@childrens = nil if @childrens.empty?
|
145
|
+
|
146
|
+
node
|
147
|
+
end
|
148
|
+
|
149
|
+
def to_h(params={})
|
150
|
+
BoostInfo::Generator.new(self, params).to_hash
|
151
|
+
end
|
152
|
+
|
153
|
+
def to_info(params={})
|
154
|
+
BoostInfo::Generator.new(self, params).to_info
|
66
155
|
end
|
67
156
|
end
|
68
157
|
end
|
data/lib/boost_info/parser.rb
CHANGED
@@ -1,138 +1,130 @@
|
|
1
1
|
require 'boost_info/node'
|
2
2
|
|
3
3
|
module BoostInfo
|
4
|
-
class ParseError < StandardError; end
|
5
|
-
|
6
4
|
class Parser
|
7
|
-
|
5
|
+
class ParseError < StandardError; end
|
8
6
|
|
9
|
-
|
10
|
-
@@tokens = '{}'
|
11
|
-
@@open_token = '{'
|
12
|
-
@@close_token = '}'
|
7
|
+
attr_accessor :root_node
|
13
8
|
|
14
|
-
|
15
|
-
|
16
|
-
raise TypeError, "no implicit conversion of #{source.class} into String"
|
17
|
-
end
|
9
|
+
OPEN_TOKEN = '{'
|
10
|
+
CLOSE_TOKEN ='}'
|
18
11
|
|
19
|
-
|
20
|
-
@nodes = []
|
21
|
-
@level = 0
|
12
|
+
def initialize(source, opts={})
|
22
13
|
@source = source
|
23
|
-
@tokens = []
|
24
14
|
@opts = opts
|
25
15
|
@opts[:symbolize_keys] ||= false
|
26
|
-
|
27
|
-
# without empty lines and comments
|
28
|
-
tokenize! { |token| @tokens << token }
|
16
|
+
@root_node = Node.new(root: true)
|
29
17
|
end
|
30
18
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
closes = @source.count(@@close_token)
|
35
|
-
if opens != closes
|
36
|
-
raise BoostInfo::ParseError, "open [#{opens}] and close tokens [#{closes}] does not match..."
|
37
|
-
end
|
38
|
-
nodes = {}
|
39
|
-
@tokens.each do |token|
|
40
|
-
if @@open_token == token
|
41
|
-
@level += 1
|
42
|
-
elsif @@close_token == token
|
43
|
-
@level -= 1
|
44
|
-
else
|
45
|
-
list = token.split
|
46
|
-
if list.size > 2
|
47
|
-
val = list[1..-1].join(" ")
|
48
|
-
else
|
49
|
-
val = list[1]
|
50
|
-
end
|
51
|
-
val = val.gsub(/\A["]+|["]+\z/, "") if val
|
52
|
-
node = Node.new(@level, list[0], val)
|
53
|
-
nodes[@level] = node
|
54
|
-
if @level == 0
|
55
|
-
@nodes << node
|
56
|
-
else
|
57
|
-
nodes[@level] = node unless nodes.has_key?(@level)
|
58
|
-
nodes[@level-1].add_child(node)
|
59
|
-
end
|
60
|
-
end
|
19
|
+
def self.from_info(source, opts={})
|
20
|
+
unless source.is_a?(String)
|
21
|
+
fail TypeError, "no implicit conversion of #{source.class} into String"
|
61
22
|
end
|
62
|
-
|
23
|
+
new(source, opts).parse_info
|
63
24
|
end
|
64
25
|
|
65
|
-
def
|
66
|
-
|
67
|
-
|
68
|
-
@nodes.each do |node|
|
69
|
-
result_node = node.find(name)
|
70
|
-
break if result_node
|
26
|
+
def self.from_hash(source, opts={})
|
27
|
+
unless source.is_a?(Hash)
|
28
|
+
fail TypeError, "no implicit conversion of #{source.class} into Hash"
|
71
29
|
end
|
72
|
-
|
30
|
+
new(source, opts).parse_hash
|
73
31
|
end
|
74
32
|
|
75
|
-
def
|
76
|
-
|
77
|
-
|
78
|
-
end
|
33
|
+
def parse_info
|
34
|
+
lines = process_source(@source)
|
35
|
+
traverse_info(@root_node, lines, -1)
|
79
36
|
|
80
|
-
|
81
|
-
# Print node with children
|
82
|
-
puts node
|
83
|
-
node.children.each { |child| print_node(child) }
|
84
|
-
puts "#{'| '*node.level}#{node.level} #{node.name} close" if node.has_children?
|
37
|
+
@root_node
|
85
38
|
end
|
86
39
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
40
|
+
def parse_hash
|
41
|
+
traverse_hash(@source, @root_node)
|
42
|
+
|
43
|
+
@root_node
|
90
44
|
end
|
91
45
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
46
|
+
private
|
47
|
+
|
48
|
+
def process_source(source)
|
49
|
+
lines = []
|
50
|
+
source.each_line do |line|
|
51
|
+
new_line = line.sub(/;.+[^"']$/, '').strip # remove comments
|
52
|
+
next if new_line.empty?
|
53
|
+
# include file
|
54
|
+
if new_line.match(/#include\s+(\S+)/)
|
55
|
+
file_path = strip_quotes(Regexp.last_match(1))
|
56
|
+
file = File.open(file_path)
|
57
|
+
lines_to_include = process_source(file)
|
58
|
+
lines.concat(lines_to_include)
|
59
|
+
# process inline section
|
60
|
+
elsif new_line.match(/#{OPEN_TOKEN}.*#{CLOSE_TOKEN}/)
|
61
|
+
splitted_lines = line.sub('{', "{\n").sub('}', "\n}").lines
|
62
|
+
.map(&:strip)
|
63
|
+
.reject(&:empty?)
|
64
|
+
lines.concat(splitted_lines)
|
65
|
+
#just add the line
|
66
|
+
else
|
67
|
+
lines << new_line
|
68
|
+
end
|
100
69
|
end
|
101
|
-
result
|
102
|
-
end
|
103
70
|
|
104
|
-
|
105
|
-
|
71
|
+
open_tokens = lines.count { |line| line.end_with?(OPEN_TOKEN) }
|
72
|
+
close_tokens = lines.count { |line| line.end_with?(CLOSE_TOKEN) }
|
73
|
+
if open_tokens != close_tokens
|
74
|
+
fail ParseError, "open [#{open_tokens}] and close tokens [#{close_tokens}] does not match..."
|
75
|
+
end
|
76
|
+
lines
|
106
77
|
end
|
107
78
|
|
108
|
-
def
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
end
|
79
|
+
def traverse_info(parent_node, lines, index_start)
|
80
|
+
index_end = lines.size - 1
|
81
|
+
lines.each_with_index do |line,index|
|
82
|
+
next if index <= index_start
|
113
83
|
|
114
|
-
|
84
|
+
index_end = index
|
115
85
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
86
|
+
key, value = extract_key_and_value(line)
|
87
|
+
node = Node.new(key: key, value: value)
|
88
|
+
parent_node.insert_node(node) if key
|
89
|
+
|
90
|
+
case line
|
91
|
+
when /#{OPEN_TOKEN}/
|
92
|
+
index_start = traverse_info(node, lines, index)
|
93
|
+
when /#{CLOSE_TOKEN}/
|
94
|
+
parent_node.childrens ||= []
|
95
|
+
break
|
121
96
|
end
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
end
|
133
|
-
end
|
134
|
-
yield token.strip unless token.empty?
|
97
|
+
end
|
98
|
+
index_end
|
99
|
+
end
|
100
|
+
|
101
|
+
def traverse_hash(hash, parent_node)
|
102
|
+
hash.each do |k,v|
|
103
|
+
node = parent_node.insert(k, v)
|
104
|
+
if v.is_a?(Hash)
|
105
|
+
node.childrens = []
|
106
|
+
traverse_hash(v, node)
|
135
107
|
end
|
136
108
|
end
|
109
|
+
parent_node
|
110
|
+
end
|
111
|
+
|
112
|
+
def extract_key_and_value(string)
|
113
|
+
key = nil
|
114
|
+
value = nil
|
115
|
+
|
116
|
+
tokens = string.split(/\s+/)
|
117
|
+
raw_key = tokens.shift.sub('}', '').strip
|
118
|
+
key = raw_key unless raw_key.empty?
|
119
|
+
|
120
|
+
tokens.pop if tokens.last == OPEN_TOKEN
|
121
|
+
value = strip_quotes(tokens.join(' ')) unless tokens.empty?
|
122
|
+
value = value.to_i if value.to_s =~ /^\d+$/
|
123
|
+
[key, value]
|
124
|
+
end
|
125
|
+
|
126
|
+
def strip_quotes(string)
|
127
|
+
string.gsub(/^["']+|["']+$/, '')
|
128
|
+
end
|
137
129
|
end
|
138
130
|
end
|
data/lib/boost_info/version.rb
CHANGED
data/lib/boost_info.rb
CHANGED
@@ -5,16 +5,22 @@ require 'boost_info/generator'
|
|
5
5
|
|
6
6
|
module BoostInfo
|
7
7
|
def self.parse(source, opts={})
|
8
|
-
BoostInfo::Parser.
|
8
|
+
root_node = BoostInfo::Parser.from_info(source, opts)
|
9
|
+
root_node.to_h(opts)
|
9
10
|
end
|
10
11
|
|
11
|
-
def self.
|
12
|
-
BoostInfo::
|
12
|
+
def self.from_hash(hash, opts={})
|
13
|
+
BoostInfo::Parser.from_hash(hash, opts)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.from_info(source, opts={})
|
17
|
+
BoostInfo::Parser.from_info(source, opts)
|
13
18
|
end
|
14
19
|
end
|
15
20
|
|
16
21
|
class Hash
|
17
22
|
def to_info(opts={})
|
18
|
-
BoostInfo.
|
23
|
+
root_node = BoostInfo::Parser.from_hash(self, opts)
|
24
|
+
root_node.to_info(opts)
|
19
25
|
end
|
20
26
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: boost_info
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Igor Vetrov
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-07-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '5.6'
|
56
|
-
description:
|
56
|
+
description: Simple parser for Boost INFO format.
|
57
57
|
email:
|
58
58
|
- capybarov@gmail.com
|
59
59
|
executables: []
|
@@ -95,8 +95,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
95
|
version: '0'
|
96
96
|
requirements: []
|
97
97
|
rubyforge_project:
|
98
|
-
rubygems_version: 2.4.
|
98
|
+
rubygems_version: 2.4.6
|
99
99
|
signing_key:
|
100
100
|
specification_version: 4
|
101
101
|
summary: Simple parser for Boost INFO format
|
102
102
|
test_files: []
|
103
|
+
has_rdoc:
|