kdl 1.0.0.rc1 → 1.0.2
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/.github/workflows/ruby.yml +1 -1
- data/.gitignore +1 -0
- data/README.md +16 -2
- data/bin/kdl +8 -0
- data/kdl.gemspec +2 -1
- data/lib/kdl/kdl.tab.rb +208 -179
- data/lib/kdl/kdl.yy +30 -22
- data/lib/kdl/node.rb +20 -5
- data/lib/kdl/tokenizer.rb +6 -19
- data/lib/kdl/types/base64.rb +21 -0
- data/lib/kdl/types/country/iso3166_countries.rb +512 -0
- data/lib/kdl/types/country/iso3166_subdivisions.rb +3927 -0
- data/lib/kdl/types/country.rb +69 -0
- data/lib/kdl/types/currency/iso4217_currencies.rb +189 -0
- data/lib/kdl/types/currency.rb +26 -0
- data/lib/kdl/types/date_time.rb +41 -0
- data/lib/kdl/types/decimal.rb +13 -0
- data/lib/kdl/types/duration/iso8601_parser.rb +147 -0
- data/lib/kdl/types/duration.rb +28 -0
- data/lib/kdl/types/email/parser.rb +151 -0
- data/lib/kdl/types/email.rb +47 -0
- data/lib/kdl/types/hostname/validator.rb +51 -0
- data/lib/kdl/types/hostname.rb +36 -0
- data/lib/kdl/types/ip.rb +32 -0
- data/lib/kdl/types/irl/parser.rb +123 -0
- data/lib/kdl/types/irl.rb +46 -0
- data/lib/kdl/types/regex.rb +13 -0
- data/lib/kdl/types/url.rb +30 -0
- data/lib/kdl/types/url_template.rb +328 -0
- data/lib/kdl/types/uuid.rb +17 -0
- data/lib/kdl/types.rb +22 -0
- data/lib/kdl/value.rb +13 -2
- data/lib/kdl/version.rb +1 -1
- data/lib/kdl.rb +3 -2
- metadata +43 -6
data/lib/kdl/kdl.yy
CHANGED
@@ -10,29 +10,31 @@ class KDL::Parser
|
|
10
10
|
SEMICOLON
|
11
11
|
EOF
|
12
12
|
SLASHDASH
|
13
|
+
ESCLINE
|
13
14
|
rule
|
14
15
|
document : nodes { KDL::Document.new(val[0]) }
|
15
16
|
| linespaces { KDL::Document.new([])}
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
node
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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 { [] }
|
34
36
|
empty_children: SLASHDASH node_children
|
35
|
-
|
|
37
|
+
| ws empty_children
|
36
38
|
node_term: linespaces | semicolon_term
|
37
39
|
semicolon_term: SEMICOLON | SEMICOLON linespaces
|
38
40
|
|
@@ -45,7 +47,7 @@ rule
|
|
45
47
|
property: identifier EQUALS value { [val[0], val[2]] }
|
46
48
|
|
47
49
|
value : untyped_value
|
48
|
-
| type untyped_value { val[1].as_type(val[0]) }
|
50
|
+
| type untyped_value { val[1].as_type(val[0], @type_parsers.fetch(val[0], nil)) }
|
49
51
|
|
50
52
|
untyped_value : STRING { KDL::Value::String.new(val[0].value) }
|
51
53
|
| RAWSTRING { KDL::Value::String.new(val[0].value) }
|
@@ -57,15 +59,21 @@ rule
|
|
57
59
|
boolean : TRUE { true }
|
58
60
|
| FALSE { false }
|
59
61
|
|
60
|
-
|
62
|
+
ws: WS | ESCLINE | WS ESCLINE | ESCLINE WS | WS ESCLINE WS
|
63
|
+
ws_star: none | ws
|
61
64
|
linespace: WS | NEWLINE | EOF
|
62
65
|
linespaces: linespace | linespaces linespace
|
63
|
-
linespace_star: none | linespaces
|
64
66
|
|
65
67
|
none: { nil }
|
66
68
|
|
67
69
|
---- inner
|
68
|
-
|
70
|
+
|
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
|
69
77
|
@tokenizer = ::KDL::Tokenizer.new(str)
|
70
78
|
do_parse
|
71
79
|
end
|
data/lib/kdl/node.rb
CHANGED
@@ -2,7 +2,7 @@ module KDL
|
|
2
2
|
class Node
|
3
3
|
attr_accessor :name, :arguments, :properties, :children, :type
|
4
4
|
|
5
|
-
def initialize(name, arguments = [], properties = {}, children =
|
5
|
+
def initialize(name, arguments = [], properties = {}, children = [], type: nil)
|
6
6
|
@name = name
|
7
7
|
@arguments = arguments
|
8
8
|
@properties = properties
|
@@ -19,11 +19,9 @@ module KDL
|
|
19
19
|
unless properties.empty?
|
20
20
|
s += " #{properties.map { |k, v| "#{id_to_s k}=#{v}" }.join(' ')}"
|
21
21
|
end
|
22
|
-
unless children.
|
22
|
+
unless children.empty?
|
23
23
|
s += " {\n"
|
24
|
-
|
25
|
-
s += children.map { |c| "#{c.to_s(level + 1)}\n" }.join("\n")
|
26
|
-
end
|
24
|
+
s += children.map { |c| "#{c.to_s(level + 1)}\n" }.join("\n")
|
27
25
|
s += "#{indent}}"
|
28
26
|
end
|
29
27
|
s
|
@@ -38,6 +36,23 @@ module KDL
|
|
38
36
|
children == other.children
|
39
37
|
end
|
40
38
|
|
39
|
+
def as_type(type, parser = nil)
|
40
|
+
if parser.nil?
|
41
|
+
@type = type
|
42
|
+
self
|
43
|
+
else
|
44
|
+
result = parser.call(self, type)
|
45
|
+
|
46
|
+
return self.as_type(type) if result.nil?
|
47
|
+
|
48
|
+
unless result.is_a?(::KDL::Node)
|
49
|
+
raise ArgumentError, "expected parser to return an instance of ::KDL::Node, got `#{result.class}'"
|
50
|
+
end
|
51
|
+
|
52
|
+
result
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
41
56
|
private
|
42
57
|
|
43
58
|
def id_to_s(id)
|
data/lib/kdl/tokenizer.rb
CHANGED
@@ -67,6 +67,7 @@ module KDL
|
|
67
67
|
@line = 1
|
68
68
|
@column = 1
|
69
69
|
@type_context = false
|
70
|
+
@last_token = nil
|
70
71
|
end
|
71
72
|
|
72
73
|
def next_token
|
@@ -124,15 +125,13 @@ module KDL
|
|
124
125
|
end
|
125
126
|
when '\\'
|
126
127
|
t = Tokenizer.new(@str, @index + 1)
|
127
|
-
la = t.next_token
|
128
|
-
if la == :NEWLINE
|
129
|
-
@index = t.index
|
130
|
-
new_line
|
131
|
-
elsif la == :WS && (lan = t.next_token[0]) == :NEWLINE
|
128
|
+
la = t.next_token
|
129
|
+
if la[0] == :NEWLINE || la[0] == :EOF || (la[0] == :WS && (lan = t.next_token[0]) == :NEWLINE || lan == :EOF)
|
132
130
|
@index = t.index
|
133
131
|
new_line
|
132
|
+
return token(:ESCLINE, "\\#{la[1].value}")
|
134
133
|
else
|
135
|
-
raise_error "Unexpected '\\'"
|
134
|
+
raise_error "Unexpected '\\' (#{la[0]})"
|
136
135
|
end
|
137
136
|
when *SYMBOLS.keys
|
138
137
|
return token(SYMBOLS[c], c).tap { traverse(1) }
|
@@ -292,18 +291,6 @@ module KDL
|
|
292
291
|
if WHITEPACE.include?(c)
|
293
292
|
traverse(1)
|
294
293
|
@buffer += c
|
295
|
-
elsif c == "\\"
|
296
|
-
t = Tokenizer.new(@str, @index + 1)
|
297
|
-
la = t.next_token[0]
|
298
|
-
if la == :NEWLINE
|
299
|
-
@index = t.index
|
300
|
-
new_line
|
301
|
-
elsif (la == :WS && (lan = t.next_token[0]) == :NEWLINE)
|
302
|
-
@index = t.index
|
303
|
-
new_line
|
304
|
-
else
|
305
|
-
raise_error "Unexpected '\\'"
|
306
|
-
end
|
307
294
|
elsif c == "/" && @str[@index + 1] == '*'
|
308
295
|
self.context = :multi_line_comment
|
309
296
|
@comment_nesting = 1
|
@@ -357,7 +344,7 @@ module KDL
|
|
357
344
|
end
|
358
345
|
|
359
346
|
def parse_float(s)
|
360
|
-
match,
|
347
|
+
match, _, fraction, exponent = *s.match(/^([-+]?[\d_]+)(?:\.([\d_]+))?(?:[eE]([-+]?[\d_]+))?$/)
|
361
348
|
raise_error "Invalid floating point value #{s}" if match.nil?
|
362
349
|
|
363
350
|
s = munch_underscores(s)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module KDL
|
4
|
+
module Types
|
5
|
+
class Base64 < Value
|
6
|
+
RGX = /^[A-Za-z0-9+\/=]+$/.freeze
|
7
|
+
|
8
|
+
def self.call(value, type = 'base64')
|
9
|
+
return nil unless value.is_a? ::KDL::Value::String
|
10
|
+
|
11
|
+
unless RGX.match?(value.value)
|
12
|
+
raise ArgumentError, "invalid base64: #{value.value}"
|
13
|
+
end
|
14
|
+
|
15
|
+
data = ::Base64.decode64(value.value)
|
16
|
+
new(data, type: type)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
MAPPING['base64'] = Base64
|
20
|
+
end
|
21
|
+
end
|