hocon 0.0.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.
Files changed (39) hide show
  1. data/LICENSE +202 -0
  2. data/README.md +19 -0
  3. data/lib/hocon.rb +2 -0
  4. data/lib/hocon/config_error.rb +12 -0
  5. data/lib/hocon/config_factory.rb +9 -0
  6. data/lib/hocon/config_object.rb +4 -0
  7. data/lib/hocon/config_parse_options.rb +53 -0
  8. data/lib/hocon/config_render_options.rb +46 -0
  9. data/lib/hocon/config_syntax.rb +7 -0
  10. data/lib/hocon/config_value_type.rb +26 -0
  11. data/lib/hocon/impl.rb +5 -0
  12. data/lib/hocon/impl/abstract_config_object.rb +64 -0
  13. data/lib/hocon/impl/abstract_config_value.rb +130 -0
  14. data/lib/hocon/impl/config_concatenation.rb +136 -0
  15. data/lib/hocon/impl/config_float.rb +9 -0
  16. data/lib/hocon/impl/config_impl.rb +10 -0
  17. data/lib/hocon/impl/config_impl_util.rb +78 -0
  18. data/lib/hocon/impl/config_int.rb +31 -0
  19. data/lib/hocon/impl/config_number.rb +27 -0
  20. data/lib/hocon/impl/config_string.rb +37 -0
  21. data/lib/hocon/impl/full_includer.rb +4 -0
  22. data/lib/hocon/impl/origin_type.rb +9 -0
  23. data/lib/hocon/impl/parseable.rb +151 -0
  24. data/lib/hocon/impl/parser.rb +882 -0
  25. data/lib/hocon/impl/path.rb +59 -0
  26. data/lib/hocon/impl/path_builder.rb +36 -0
  27. data/lib/hocon/impl/resolve_status.rb +18 -0
  28. data/lib/hocon/impl/simple_config.rb +11 -0
  29. data/lib/hocon/impl/simple_config_list.rb +70 -0
  30. data/lib/hocon/impl/simple_config_object.rb +178 -0
  31. data/lib/hocon/impl/simple_config_origin.rb +174 -0
  32. data/lib/hocon/impl/simple_include_context.rb +7 -0
  33. data/lib/hocon/impl/simple_includer.rb +19 -0
  34. data/lib/hocon/impl/token.rb +32 -0
  35. data/lib/hocon/impl/token_type.rb +42 -0
  36. data/lib/hocon/impl/tokenizer.rb +370 -0
  37. data/lib/hocon/impl/tokens.rb +157 -0
  38. data/lib/hocon/impl/unmergeable.rb +4 -0
  39. metadata +84 -0
@@ -0,0 +1,59 @@
1
+ require 'hocon/impl'
2
+ require 'stringio'
3
+
4
+ class Hocon::Impl::Path
5
+ # this doesn't have a very precise meaning, just to reduce
6
+ # noise from quotes in the rendered path for average cases
7
+ def self.has_funky_chars?(s)
8
+ length = s.length
9
+ if length == 0
10
+ return false
11
+ end
12
+
13
+ # if the path starts with something that could be a number,
14
+ # we need to quote it because the number could be invalid,
15
+ # for example it could be a hyphen with no digit afterward
16
+ # or the exponent "e" notation could be mangled.
17
+ first = s[0]
18
+ unless first =~ /[[:alpha:]]/
19
+ return true
20
+ end
21
+
22
+ s.chars.each do |c|
23
+ unless (c =~ /[[:alnum:]]/) || (c == '-') || (c == '_')
24
+ return true
25
+ end
26
+ end
27
+
28
+ false
29
+ end
30
+
31
+ def initialize(first, remainder)
32
+ @first = first
33
+ @remainder = remainder
34
+ end
35
+ attr_reader :first, :remainder
36
+
37
+ #
38
+ # toString() is a debugging-oriented version while this is an
39
+ # error-message-oriented human-readable one.
40
+ #
41
+ def render
42
+ sb = StringIO.new
43
+ append_to_string_builder(sb)
44
+ sb.string
45
+ end
46
+
47
+ def append_to_string_builder(sb)
48
+ if self.class.has_funky_chars?(@first) || @first.empty?
49
+ sb << ConfigImplUtil.render_json_string(@first)
50
+ else
51
+ sb << @first
52
+ end
53
+
54
+ unless @remainder.nil?
55
+ sb << "."
56
+ @remainder.append_to_string_builder(sb)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,36 @@
1
+ require 'hocon/impl'
2
+ require 'hocon/impl/path'
3
+
4
+ class Hocon::Impl::PathBuilder
5
+ Path = Hocon::Impl::Path
6
+
7
+ def initialize
8
+ @keys = []
9
+ @result = nil
10
+ end
11
+
12
+ def check_can_append
13
+ if @result
14
+ raise ConfigBugError, "Adding to PathBuilder after getting result"
15
+ end
16
+ end
17
+
18
+ def append_key(key)
19
+ check_can_append
20
+ @keys.push(key)
21
+ end
22
+
23
+ def result
24
+ # note: if keys is empty, we want to return null, which is a valid
25
+ # empty path
26
+ if @result.nil?
27
+ remainder = nil
28
+ while !@keys.empty?
29
+ key = @keys.pop
30
+ remainder = Path.new(key, remainder)
31
+ end
32
+ @result = remainder
33
+ end
34
+ @result
35
+ end
36
+ end
@@ -0,0 +1,18 @@
1
+ require 'hocon/impl'
2
+
3
+ class Hocon::Impl::ResolveStatus
4
+ UNRESOLVED = 0
5
+ RESOLVED = 1
6
+
7
+ def self.from_values(values)
8
+ if values.any? { |v| v.resolve_status == UNRESOLVED }
9
+ UNRESOLVED
10
+ else
11
+ RESOLVED
12
+ end
13
+ end
14
+
15
+ def self.from_boolean(resolved)
16
+ resolved ? RESOLVED : UNRESOLVED
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ require 'hocon/impl'
2
+
3
+ class Hocon::Impl::SimpleConfig
4
+ def initialize(object)
5
+ @object = object
6
+ end
7
+
8
+ def root
9
+ @object
10
+ end
11
+ end
@@ -0,0 +1,70 @@
1
+ require 'hocon/impl'
2
+ require 'hocon/impl/resolve_status'
3
+ require 'hocon/config_value_type'
4
+
5
+ class Hocon::Impl::SimpleConfigList < Hocon::Impl::AbstractConfigValue
6
+ ResolveStatus = Hocon::Impl::ResolveStatus
7
+
8
+ def initialize(origin, value, status = ResolveStatus.from_values(value))
9
+ super(origin)
10
+ @value = value
11
+ @resolved = (status == ResolveStatus::RESOLVED)
12
+
13
+ # kind of an expensive debug check (makes this constructor pointless)
14
+ if status != ResolveStatus.from_values(value)
15
+ raise ConfigBugError, "SimpleConfigList created with wrong resolve status: #{self}"
16
+ end
17
+ end
18
+
19
+ def value_type
20
+ Hocon::ConfigValueType::LIST
21
+ end
22
+
23
+ def unwrapped
24
+ @value.map { |v| v.unwrapped }
25
+ end
26
+
27
+ def render_value_to_sb(sb, indent_size, at_root, options)
28
+ if @value.empty?
29
+ sb << "[]"
30
+ else
31
+ sb << "["
32
+ if options.formatted?
33
+ sb << "\n"
34
+ end
35
+ @value.each do |v|
36
+ if options.origin_comments?
37
+ indent(sb, indent_size + 1, options)
38
+ sb << "# "
39
+ sb << v.origin.description
40
+ sb << "\n"
41
+ end
42
+ if options.comments?
43
+ v.origin.comments.each do |comment|
44
+ sb << "# "
45
+ sb << comment
46
+ sb << "\n"
47
+ end
48
+ end
49
+ indent(sb, indent_size + 1, options)
50
+
51
+ v.render_value_to_sb(sb, indent_size + 1, at_root, options)
52
+ sb << ","
53
+ if options.formatted?
54
+ sb << "\n"
55
+ end
56
+ end
57
+
58
+ # couldn't figure out a better way to chop characters off of the end of
59
+ # the StringIO. This relies on making sure that, prior to returning the
60
+ # final string, we take a substring that ends at sb.pos.
61
+ sb.pos = sb.pos - 1 # chop or newline
62
+ if options.formatted?
63
+ sb.pos = sb.pos - 1 # also chop comma
64
+ sb << "\n"
65
+ indent(sb, indent_size, options)
66
+ end
67
+ sb << "]"
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,178 @@
1
+ require 'hocon/impl'
2
+ require 'hocon/impl/simple_config_origin'
3
+ require 'hocon/impl/abstract_config_object'
4
+ require 'hocon/impl/resolve_status'
5
+ require 'set'
6
+
7
+ class Hocon::Impl::SimpleConfigObject < Hocon::Impl::AbstractConfigObject
8
+ def self.empty_missing(base_origin)
9
+ self.new(
10
+ Hocon::Impl::SimpleConfigOrigin.new_simple("#{base_origin.description} (not found)"),
11
+ {})
12
+ end
13
+
14
+ def initialize(origin, value,
15
+ status = Hocon::Impl::ResolveStatus.from_values(value.values),
16
+ ignores_fallbacks = false)
17
+ super(origin)
18
+ if value.nil?
19
+ raise ConfigBugError, "creating config object with null map"
20
+ end
21
+ @value = value
22
+ @resolved = (status == Hocon::Impl::ResolveStatus::RESOLVED)
23
+ @ignores_fallbacks = ignores_fallbacks
24
+
25
+ # Kind of an expensive debug check. Comment out?
26
+ if status != Hocon::Impl::ResolveStatus.from_values(value.values)
27
+ raise ConfigBugError, "Wrong resolved status on #{self}"
28
+ end
29
+ end
30
+
31
+ attr_reader :value
32
+
33
+ def new_copy_with_status(new_status, new_origin, new_ignores_fallbacks = nil)
34
+ Hocon::Impl::SimpleConfigObject.new(new_origin,
35
+ @value, new_status, new_ignores_fallbacks)
36
+ end
37
+
38
+ def ignores_fallbacks?
39
+ @ignores_fallbacks
40
+ end
41
+
42
+ def unwrapped
43
+ @value.merge(@value) { |k,v| v.unwrapped }
44
+ end
45
+
46
+ def merged_with_object(abstract_fallback)
47
+ require_not_ignoring_fallbacks
48
+
49
+ unless abstract_fallback.is_a?(Hocon::Impl::SimpleConfigObject)
50
+ raise ConfigBugError, "should not be reached (merging non-SimpleConfigObject)"
51
+ end
52
+
53
+ fallback = abstract_fallback
54
+ changed = false
55
+ all_resolved = true
56
+ merged = {}
57
+ all_keys = key_set.union(fallback.key_set)
58
+ all_keys.each do |key|
59
+ first = @value[key]
60
+ second = fallback.value[key]
61
+ kept =
62
+ if first.nil?
63
+ second
64
+ elsif second.nil?
65
+ first
66
+ else
67
+ first.with_fallback(second)
68
+ end
69
+ merged[key] = kept
70
+
71
+ if first != kept
72
+ changed = true
73
+ end
74
+
75
+ if kept.resolve_status == Hocon::Impl::ResolveStatus::UNRESOLVED
76
+ all_resolved = false
77
+ end
78
+ end
79
+
80
+ new_resolve_status = Hocon::Impl::ResolveStatus.from_boolean(all_resolved)
81
+ new_ignores_fallbacks = fallback.ignores_fallbacks?
82
+
83
+ if changed
84
+ Hocon::Impl::SimpleConfigObject.new(merge_origins([self, fallback]),
85
+ merged, new_resolve_status,
86
+ new_ignores_fallbacks)
87
+ elsif (new_resolve_status != resolve_status) || (new_ignores_fallbacks != ignores_fallbacks?)
88
+ newCopy(new_resolve_status, origin, new_ignores_fallbacks)
89
+ else
90
+ self
91
+ end
92
+ end
93
+
94
+ def render_value_to_sb(sb, indent_size, at_root, options)
95
+ if empty?
96
+ sb << "{}"
97
+ else
98
+ outer_braces = options.json? || !at_root
99
+
100
+ inner_indent =
101
+ if outer_braces
102
+ sb << "{"
103
+ if options.formatted?
104
+ sb << "\n"
105
+ end
106
+ indent_size + 1
107
+ else
108
+ indent_size
109
+ end
110
+
111
+ separator_count = 0
112
+ key_set.each do |k|
113
+ v = @value[k]
114
+
115
+ if options.origin_comments?
116
+ indent(sb, inner_indent, options)
117
+ sb << "# "
118
+ sb << v.origin.description
119
+ sb << "\n"
120
+ end
121
+ if options.comments?
122
+ v.origin.comments.each do |comment|
123
+ indent(sb, inner_indent, options)
124
+ sb << "#"
125
+ if !comment.start_with?(" ")
126
+ sb << " "
127
+ end
128
+ sb << comment
129
+ sb << "\n"
130
+ end
131
+ end
132
+ indent(sb, inner_indent, options)
133
+ v.render_to_sb(sb, inner_indent, false, k.to_s, options)
134
+
135
+ if options.formatted?
136
+ if options.json?
137
+ sb << ","
138
+ separator_count = 2
139
+ else
140
+ separator_count = 1
141
+ end
142
+ sb << "\n"
143
+ else
144
+ sb << ","
145
+ separator_count = 1
146
+ end
147
+ end
148
+ # chop last commas/newlines
149
+ # couldn't figure out a better way to chop characters off of the end of
150
+ # the StringIO. This relies on making sure that, prior to returning the
151
+ # final string, we take a substring that ends at sb.pos.
152
+ sb.pos = sb.pos - separator_count
153
+
154
+ if outer_braces
155
+ if options.formatted?
156
+ sb << "\n" # put a newline back
157
+ if outer_braces
158
+ indent(sb, indent_size, options)
159
+ end
160
+ end
161
+ sb << "}"
162
+ end
163
+ end
164
+ if at_root && options.formatted?
165
+ sb << "\n"
166
+ end
167
+ end
168
+
169
+
170
+ def key_set
171
+ Set.new(@value.keys)
172
+ end
173
+
174
+ def empty?
175
+ @value.empty?
176
+ end
177
+
178
+ end
@@ -0,0 +1,174 @@
1
+ require 'uri'
2
+ require 'hocon/impl'
3
+ require 'hocon/impl/origin_type'
4
+
5
+ class Hocon::Impl::SimpleConfigOrigin
6
+
7
+ MERGE_OF_PREFIX = "merge of "
8
+
9
+ def self.new_file(file_path)
10
+ url = URI.join('file:///', file_path)
11
+ self.new(file_path, -1, -1,
12
+ Hocon::Impl::OriginType::FILE,
13
+ url, nil)
14
+ end
15
+
16
+ def self.new_simple(description)
17
+ self.new(description, -1, -1,
18
+ Hocon::Impl::OriginType::GENERIC,
19
+ nil, nil)
20
+ end
21
+
22
+ def self.remove_merge_of_prefix(desc)
23
+ if desc.start_with?(MERGE_OF_PREFIX)
24
+ desc = desc[MERGE_OF_PREFIX.length, desc.length - 1]
25
+ end
26
+ desc
27
+ end
28
+
29
+ def self.merge_two(a, b)
30
+ merged_desc = nil
31
+ merged_start_line = nil
32
+ merged_end_line = nil
33
+ merged_comments = nil
34
+
35
+ merged_type =
36
+ if a.origin_type == b.origin_type
37
+ a.origin_type
38
+ else
39
+ Hocon::Impl::OriginType.GENERIC
40
+ end
41
+
42
+ # first use the "description" field which has no line numbers
43
+ # cluttering it.
44
+ a_desc = remove_merge_of_prefix(a.description)
45
+ b_desc = remove_merge_of_prefix(b.description)
46
+
47
+ if a_desc == b_desc
48
+ merged_desc = a_desc
49
+ if a.line_number < 0
50
+ merged_start_line = b.line_number
51
+ elsif b.line_number < 0
52
+ merged_start_line = a.line_number
53
+ else
54
+ merged_start_line = [a.line_number, b.line_number].min
55
+ end
56
+
57
+ merged_end_line = [a.end_line_number, b.end_line_number].max
58
+ else
59
+ # this whole merge song-and-dance was intended to avoid this case
60
+ # whenever possible, but we've lost. Now we have to lose some
61
+ # structured information and cram into a string.
62
+ #
63
+ # description() method includes line numbers, so use it instead
64
+ # of description field.
65
+ a_full = remove_merge_of_prefix(a.description)
66
+ b_full = remove_merge_of_prefix(b.description)
67
+
68
+ merged_desc = "#{MERGE_OF_PREFIX}#{a_full},#{b_full}"
69
+ merged_start_line = -1
70
+ merged_end_line = -1
71
+ end
72
+
73
+ merged_url =
74
+ if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(a.url_or_nil, b.url_or_nil)
75
+ a.url_or_nil
76
+ else
77
+ nil
78
+ end
79
+
80
+ if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(a.comments_or_nil, b.comments_or_nil)
81
+ merged_comments = a.comments_or_nil
82
+ else
83
+ merged_comments = []
84
+ if a.comments_or_nil
85
+ merged_comments.concat(a.comments_or_nil)
86
+ end
87
+ if b.comments_or_nil
88
+ merged_comments.concat(b.comments_or_nil)
89
+ end
90
+ end
91
+
92
+ Hocon::Impl::SimpleConfigOrigin.new(
93
+ merged_desc, merged_start_line, merged_end_line,
94
+ merged_type, merged_url, merged_comments)
95
+ end
96
+
97
+ def self.merge_origins(stack)
98
+ if stack.empty?
99
+ raise ConfigBugError, "can't merge empty list of origins"
100
+ elsif stack.length == 1
101
+ stack[0]
102
+ elsif stack.length == 2
103
+ merge_two(stack[0], stack[1])
104
+ else
105
+ remaining = stack.clone
106
+ while remaining.length > 2
107
+ merged = merge_three(remaining[0], remaining[1], remaining[2])
108
+ remaining.pop
109
+ remaining.pop
110
+ remaining.pop
111
+ end
112
+
113
+ # should be down to either 1 or 2
114
+ merge_origins(remaining)
115
+ end
116
+ end
117
+
118
+
119
+ def initialize(description, line_number, end_line_number,
120
+ origin_type, url, comments)
121
+ if !description
122
+ raise ArgumentError, "description may not be nil"
123
+ end
124
+
125
+ @description = description
126
+ @line_number = line_number
127
+ @end_line_number = end_line_number
128
+ @origin_type = origin_type
129
+ @url_or_nil = url
130
+ @comments_or_nil = comments
131
+ end
132
+
133
+ attr_reader :description, :line_number, :end_line_number, :origin_type,
134
+ :url_or_nil, :comments_or_nil
135
+
136
+ def set_line_number(line_number)
137
+ if (line_number == @line_number) and
138
+ (line_number == @end_line_number)
139
+ self
140
+ else
141
+ Hocon::Impl::SimpleConfigOrigin.new(
142
+ @description, line_number, line_number,
143
+ @origin_type, @url_or_nil, @comments_or_nil)
144
+ end
145
+ end
146
+
147
+ def set_comments(comments)
148
+ if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(comments, @comments_or_nil)
149
+ self
150
+ else
151
+ Hocon::Impl::SimpleConfigOrigin.new(
152
+ @description, @line_number, @end_line_number,
153
+ @origin_type, @url_or_nil, comments)
154
+ end
155
+ end
156
+
157
+ def prepend_comments(comments)
158
+ if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(comments, @comments_or_nil)
159
+ self
160
+ elsif @comments_or_nil.nil?
161
+ set_comments(comments)
162
+ else
163
+ merged = []
164
+ merged.concat(comments)
165
+ merged.concat(@comments_or_nil)
166
+ set_comments(merged)
167
+ end
168
+ end
169
+
170
+ def comments
171
+ @comments_or_nil || []
172
+ end
173
+
174
+ end