kdl 1.0.5 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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