kdl 1.0.5 → 2.0.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +7 -1
  3. data/.gitignore +1 -0
  4. data/.gitmodules +4 -0
  5. data/Gemfile +6 -1
  6. data/README.md +51 -7
  7. data/Rakefile +6 -1
  8. data/bin/kdl +1 -1
  9. data/kdl.gemspec +2 -2
  10. data/lib/kdl/document.rb +58 -2
  11. data/lib/kdl/kdl.tab.rb +303 -228
  12. data/lib/kdl/kdl.yy +57 -49
  13. data/lib/kdl/node.rb +113 -12
  14. data/lib/kdl/parser_common.rb +26 -0
  15. data/lib/kdl/string_dumper.rb +30 -33
  16. data/lib/kdl/tokenizer.rb +350 -113
  17. data/lib/kdl/types/base64.rb +1 -1
  18. data/lib/kdl/types/country/iso3166_countries.rb +1 -1
  19. data/lib/kdl/types/country/iso3166_subdivisions.rb +1 -1
  20. data/lib/kdl/types/country.rb +2 -2
  21. data/lib/kdl/types/currency/iso4217_currencies.rb +1 -1
  22. data/lib/kdl/types/currency.rb +1 -1
  23. data/lib/kdl/types/date_time.rb +3 -3
  24. data/lib/kdl/types/decimal.rb +1 -1
  25. data/lib/kdl/types/duration/iso8601_parser.rb +1 -1
  26. data/lib/kdl/types/duration.rb +1 -1
  27. data/lib/kdl/types/email/parser.rb +2 -2
  28. data/lib/kdl/types/email.rb +1 -1
  29. data/lib/kdl/types/hostname/validator.rb +1 -1
  30. data/lib/kdl/types/hostname.rb +1 -1
  31. data/lib/kdl/types/ip.rb +1 -1
  32. data/lib/kdl/types/irl/parser.rb +1 -1
  33. data/lib/kdl/types/irl.rb +1 -1
  34. data/lib/kdl/types/regex.rb +1 -1
  35. data/lib/kdl/types/url.rb +1 -1
  36. data/lib/kdl/types/url_template.rb +1 -1
  37. data/lib/kdl/types/uuid.rb +1 -1
  38. data/lib/kdl/v1/document.rb +17 -0
  39. data/lib/kdl/v1/kdl.tab.rb +594 -0
  40. data/lib/kdl/v1/kdl.yy +89 -0
  41. data/lib/kdl/v1/node.rb +30 -0
  42. data/lib/kdl/v1/string_dumper.rb +28 -0
  43. data/lib/kdl/v1/tokenizer.rb +296 -0
  44. data/lib/kdl/v1/value.rb +89 -0
  45. data/lib/kdl/v1.rb +11 -0
  46. data/lib/kdl/value.rb +81 -12
  47. data/lib/kdl/version.rb +1 -1
  48. data/lib/kdl.rb +40 -1
  49. metadata +13 -4
data/lib/kdl/kdl.yy CHANGED
@@ -1,7 +1,6 @@
1
1
  class KDL::Parser
2
2
  options no_result_var
3
- token IDENT
4
- STRING RAWSTRING
3
+ token IDENT STRING RAWSTRING
5
4
  INTEGER FLOAT TRUE FALSE NULL
6
5
  WS NEWLINE
7
6
  LBRACE RBRACE
@@ -10,57 +9,66 @@ class KDL::Parser
10
9
  SEMICOLON
11
10
  EOF
12
11
  SLASHDASH
13
- ESCLINE
14
12
  rule
15
- document : nodes { KDL::Document.new(val[0]) }
16
- | linespaces { KDL::Document.new([])}
13
+ document : nodes { @output_module::Document.new(val[0]) }
14
+ | linespaces { @output_module::Document.new([]) }
17
15
 
18
- nodes : none { [] }
19
- | linespaces node { [val[1]] }
20
- | linespaces empty_node { [] }
21
- | nodes node { [*val[0], val[1]] }
22
- | nodes empty_node { val[0] }
23
- node : untyped_node { val[0] }
24
- | type untyped_node { val[1].as_type(val[0], @type_parsers.fetch(val[0], nil)) }
25
- untyped_node : node_decl node_term { val[0].tap { |x| x.children = [] } }
26
- | node_decl node_children node_term { val[0].tap { |x| x.children = val[1] } }
27
- | node_decl empty_children node_term { val[0].tap { |x| x.children = [] } }
28
- node_decl : identifier { KDL::Node.new(val[0]) }
29
- | node_decl ws value { val[0].tap { |x| x.arguments << val[2] } }
30
- | node_decl ws SLASHDASH ws_star value { val[0] }
31
- | node_decl ws property { val[0].tap { |x| x.properties[val[2][0]] = val[2][1] } }
32
- | node_decl ws SLASHDASH ws_star property { val[0] }
33
- empty_node : SLASHDASH ws_star node
34
- node_children : ws_star LBRACE nodes RBRACE { val[2] }
35
- | ws_star LBRACE linespaces RBRACE { [] }
36
- empty_children: SLASHDASH node_children
37
- | ws empty_children
16
+ nodes : none { [] }
17
+ | linespaces node { [val[1]] }
18
+ | linespaces empty_node { [] }
19
+ | nodes node { [*val[0], val[1]] }
20
+ | nodes empty_node { val[0] }
21
+ node : unterm_node node_term { val[0] }
22
+ unterm_node : untyped_node { val[0] }
23
+ | type untyped_node { val[1].as_type(val[0], @type_parsers.fetch(val[0], nil)) }
24
+ untyped_node : node_decl { val[0].tap { |x| x.children = [] } }
25
+ | node_decl node_children { val[0].tap { |x| x.children = val[1] } }
26
+ | node_decl empty_childrens { val[0].tap { |x| x.children = [] } }
27
+ | node_decl empty_childrens node_children { val[0].tap { |x| x.children = val[2] } }
28
+ | node_decl node_children empty_childrens { val[0].tap { |x| x.children = val[1] } }
29
+ | node_decl empty_childrens node_children empty_childrens { val[0].tap { |x| x.children = val[2] } }
30
+ node_decl : identifier { @output_module::Node.new(val[0]) }
31
+ | node_decl ws_plus value { val[0].tap { |x| x.arguments << val[2] } }
32
+ | node_decl ws_plus slashdash value { val[0] }
33
+ | node_decl ws_plus property { val[0].tap { |x| x.properties[val[2][0]] = val[2][1] } }
34
+ | node_decl ws_plus slashdash property { val[0] }
35
+ | node_decl ws_plus { val[0] }
36
+ empty_node : slashdash node
37
+ node_children : ws_star LBRACE nodes RBRACE { val[2] }
38
+ | ws_star LBRACE linespaces RBRACE { [] }
39
+ | ws_star LBRACE nodes unterm_node ws_star RBRACE { [*val[2], val[3]] }
40
+ | ws_star LBRACE linespaces unterm_node ws_star RBRACE { [val[3]] }
41
+ empty_children : slashdash node_children
42
+ | ws_plus empty_children
43
+ empty_childrens: empty_children | empty_childrens empty_children
38
44
  node_term: linespaces | semicolon_term
39
45
  semicolon_term: SEMICOLON | SEMICOLON linespaces
46
+ slashdash: SLASHDASH | slashdash ws_plus | slashdash linespaces
40
47
 
41
- type : LPAREN identifier RPAREN { val[1] }
48
+ type : LPAREN ws_star identifier ws_star RPAREN ws_star { val[2] }
42
49
 
43
- identifier: IDENT { val[0].value }
44
- | STRING { val[0].value }
45
- | RAWSTRING { val[0].value }
50
+ identifier : IDENT { val[0].value }
51
+ | STRING { val[0].value }
52
+ | RAWSTRING { val[0].value }
46
53
 
47
- property: identifier EQUALS value { [val[0], val[2]] }
54
+ property : identifier EQUALS value { [val[0], val[2]] }
48
55
 
49
56
  value : untyped_value
50
57
  | type untyped_value { val[1].as_type(val[0], @type_parsers.fetch(val[0], nil)) }
51
58
 
52
- untyped_value : STRING { KDL::Value::String.new(val[0].value) }
53
- | RAWSTRING { KDL::Value::String.new(val[0].value) }
54
- | INTEGER { KDL::Value::Int.new(val[0].value) }
55
- | FLOAT { KDL::Value::Float.new(val[0].value, format: val[0].meta[:format]) }
56
- | boolean { KDL::Value::Boolean.new(val[0]) }
57
- | NULL { KDL::Value::Null }
59
+ untyped_value : IDENT { @output_module::Value::String.new(val[0].value) }
60
+ | STRING { @output_module::Value::String.new(val[0].value) }
61
+ | RAWSTRING { @output_module::Value::String.new(val[0].value) }
62
+ | INTEGER { @output_module::Value::Int.new(val[0].value) }
63
+ | FLOAT { @output_module::Value::Float.new(val[0].value, format: val[0].meta[:format]) }
64
+ | boolean { @output_module::Value::Boolean.new(val[0]) }
65
+ | NULL { @output_module::Value::Null }
58
66
 
59
67
  boolean : TRUE { true }
60
68
  | FALSE { false }
61
69
 
62
- ws: WS | ESCLINE | WS ESCLINE | ESCLINE WS | WS ESCLINE WS
63
- ws_star: none | ws
70
+ ws_plus: WS | WS ws_plus
71
+ ws_star: none | ws_plus
64
72
  linespace: WS | NEWLINE | EOF
65
73
  linespaces: linespace | linespaces linespace
66
74
 
@@ -68,18 +76,18 @@ nodes : none { [] }
68
76
 
69
77
  ---- inner
70
78
 
71
- def parse(str, options = {})
72
- if options.fetch(:parse_types, true)
73
- @type_parsers = ::KDL::Types::MAPPING.merge(options.fetch(:type_parsers, {}))
74
- else
75
- @type_parsers = {}
76
- end
77
- @tokenizer = ::KDL::Tokenizer.new(str)
78
- do_parse
79
+ include KDL::ParserCommon
80
+
81
+ def initialize(**options)
82
+ init(**options)
79
83
  end
80
84
 
81
- private
85
+ def parser_version
86
+ 2
87
+ end
82
88
 
83
- def next_token
84
- @tokenizer.next_token
89
+ def parse(str)
90
+ @tokenizer = ::KDL::Tokenizer.new(str)
91
+ check_version
92
+ do_parse
85
93
  end
data/lib/kdl/node.rb CHANGED
@@ -1,32 +1,114 @@
1
1
  module KDL
2
2
  class Node
3
+ class Custom < Node
4
+ def self.call(node, type)
5
+ new(node.name, arguments: node.arguments, properties: node.properties, children: node.children, type:)
6
+ end
7
+
8
+ def version
9
+ nil
10
+ end
11
+
12
+ def to_v1
13
+ self
14
+ end
15
+
16
+ def to_v2
17
+ self
18
+ end
19
+ end
20
+
21
+ include Enumerable
22
+
3
23
  attr_accessor :name, :arguments, :properties, :children, :type
4
24
 
5
- def initialize(name, arguments = [], properties = {}, children = [], type: nil)
25
+ def initialize(name, _args = [], _props = {}, _children = [],
26
+ arguments: _args,
27
+ properties: _props,
28
+ children: _children,
29
+ type: nil
30
+ )
6
31
  @name = name
7
32
  @arguments = arguments
8
- @properties = properties
33
+ @properties = properties.transform_keys(&:to_s)
9
34
  @children = children
10
35
  @type = type
11
36
  end
12
37
 
13
- def to_s(level = 0)
38
+ def [](key)
39
+ case key
40
+ when Integer
41
+ arguments[key]&.value
42
+ when String, Symbol
43
+ properties[key.to_s]&.value
44
+ else
45
+ raise ArgumentError, "node can only be indexed by Integer, String, or Symbol"
46
+ end
47
+ end
48
+
49
+ def child(key)
50
+ case key
51
+ when Integer
52
+ children[key]
53
+ when String, Symbol
54
+ children.find { _1.name == key.to_s }
55
+ else
56
+ raise ArgumentError, "node can only be indexed by Integer, String, or Symbol"
57
+ end
58
+ end
59
+
60
+ def arg(key)
61
+ child(key)&.arguments&.first&.value
62
+ end
63
+
64
+ def args(key)
65
+ child(key)&.arguments&.map(&:value)
66
+ end
67
+
68
+ def each_arg(key, &block)
69
+ args(key)&.each(&block)
70
+ end
71
+
72
+ def dash_vals(key)
73
+ child(key)
74
+ &.children
75
+ &.select { _1.name == "-" }
76
+ &.map { _1.arguments.first&.value }
77
+ end
78
+
79
+ def each_dash_val(key, &block)
80
+ dash_vals(key)&.each(&block)
81
+ end
82
+
83
+ def each(&block)
84
+ children.each(&block)
85
+ end
86
+
87
+ def <=>(other)
88
+ name <=> other.name
89
+ end
90
+
91
+ def to_s(level = 0, m = :to_s)
14
92
  indent = ' ' * level
15
- s = "#{indent}#{type ? "(#{id_to_s type})" : ''}#{id_to_s name}"
93
+ s = "#{indent}#{type ? "(#{id_to_s type, m })" : ''}#{id_to_s name, m}"
16
94
  unless arguments.empty?
17
- s += " #{arguments.map(&:to_s).join(' ')}"
95
+ s += " #{arguments.map(&m).join(' ')}"
18
96
  end
19
97
  unless properties.empty?
20
- s += " #{properties.map { |k, v| "#{id_to_s k}=#{v}" }.join(' ')}"
98
+ s += " #{properties.map { |k, v| "#{id_to_s k, m}=#{v.public_send(m)}" }.join(' ')}"
21
99
  end
22
100
  unless children.empty?
23
101
  s += " {\n"
24
- s += children.map { |c| "#{c.to_s(level + 1)}\n" }.join("\n")
25
- s += "#{indent}}"
102
+ s += children.map { |c| "#{c.public_send(m, level + 1)}" }.join("\n")
103
+ s += "\n#{indent}}"
26
104
  end
27
105
  s
28
106
  end
29
107
 
108
+ def inspect(level = 0)
109
+ to_s(level, :inspect)
110
+ end
111
+
30
112
  def ==(other)
31
113
  return false unless other.is_a?(Node)
32
114
 
@@ -45,18 +127,37 @@ module KDL
45
127
 
46
128
  return self.as_type(type) if result.nil?
47
129
 
48
- unless result.is_a?(::KDL::Node)
49
- raise ArgumentError, "expected parser to return an instance of ::KDL::Node, got `#{result.class}'"
130
+ unless result.is_a?(::KDL::Node::Custom)
131
+ raise ArgumentError, "expected parser to return an instance of ::KDL::Node::Custom, got `#{result.class}'"
50
132
  end
51
133
 
52
134
  result
53
135
  end
54
136
  end
55
137
 
138
+ def version
139
+ 2
140
+ end
141
+
142
+ def to_v2
143
+ self
144
+ end
145
+
146
+ def to_v1
147
+ ::KDL::V1::Node.new(name,
148
+ arguments: arguments.map(&:to_v1),
149
+ properties: properties.transform_values(&:to_v1),
150
+ children: children.map(&:to_v1),
151
+ type: type
152
+ )
153
+ end
154
+
56
155
  private
57
156
 
58
- def id_to_s(id)
59
- StringDumper.stringify_identifier(id)
157
+ def id_to_s(id, m = :to_s)
158
+ return id.public_send(m) unless m == :to_s
159
+
160
+ StringDumper.call(id.to_s)
60
161
  end
61
162
  end
62
163
  end
@@ -0,0 +1,26 @@
1
+ module KDL
2
+ module ParserCommon
3
+
4
+ private
5
+
6
+ def init(parse_types: true, type_parsers: {}, output_module: ::KDL)
7
+ @output_module = output_module
8
+ if parse_types
9
+ @type_parsers = ::KDL::Types::MAPPING.merge(type_parsers)
10
+ else
11
+ @type_parsers = {}
12
+ end
13
+ end
14
+
15
+ def next_token
16
+ @tokenizer.next_token
17
+ end
18
+
19
+ def check_version
20
+ return unless doc_version = @tokenizer.version_directive
21
+ if doc_version != parser_version
22
+ raise ParseError, "Version mismatch, document specified v#{doc_version}, but this is a v#{parser_version} parser"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,45 +1,42 @@
1
1
  module KDL
2
2
  module StringDumper
3
- class << self
4
- def call(string)
5
- %("#{string.each_char.map { |char| escape(char) }.join}")
6
- end
7
-
8
- def stringify_identifier(ident)
9
- if bare_identifier?(ident)
10
- ident
11
- else
12
- call(ident)
13
- end
14
- end
3
+ def call(string)
4
+ return string if bare_identifier?(string)
15
5
 
16
- private
6
+ %("#{string.each_char.map { |char| escape(char) }.join}")
7
+ end
17
8
 
18
- def print?(char)
19
- ' ' <= char && char <= '\x7e'
20
- end
9
+ private
21
10
 
22
- def escape(char)
23
- case char
24
- when "\n" then '\n'
25
- when "\r" then '\r'
26
- when "\t" then '\t'
27
- when '\\' then '\\\\'
28
- when '"' then '\"'
29
- when "\b" then '\b'
30
- when "\f" then '\f'
31
- else char
32
- end
11
+ def escape(char)
12
+ case char
13
+ when "\n" then '\n'
14
+ when "\r" then '\r'
15
+ when "\t" then '\t'
16
+ when '\\' then '\\\\'
17
+ when '"' then '\"'
18
+ when "\b" then '\b'
19
+ when "\f" then '\f'
20
+ else char
33
21
  end
22
+ end
34
23
 
35
- def unicode_escape(char)
36
- "\\u{#{char.codepoints.first.to_s(16)}}"
37
- end
24
+ FORBIDDEN =
25
+ Tokenizer::SYMBOLS.keys +
26
+ Tokenizer::WHITESPACE +
27
+ Tokenizer::NEWLINES +
28
+ "()[]/\\\"#".chars +
29
+ ("\x0".."\x20").to_a
38
30
 
39
- def bare_identifier?(name)
40
- escape_chars = '\\\/(){}<>;\[\]=,"'
41
- name =~ /^([^0-9\-+\s#{escape_chars}][^\s#{escape_chars}]*|[\-+](?!true|false|null)[^0-9\s#{escape_chars}][^\s#{escape_chars}]*)$/
31
+ def bare_identifier?(name)
32
+ case name
33
+ when '', 'true', 'fase', 'null', '#true', '#false', '#null', /\A\.?\d/
34
+ false
35
+ else
36
+ !name.each_char.any? { |c| FORBIDDEN.include?(c) }
42
37
  end
43
38
  end
39
+
40
+ extend self
44
41
  end
45
42
  end