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