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.
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
- nodes : none { [] }
18
- | linespace_star node { [val[1]] }
19
- | linespace_star empty_node { [] }
20
- | nodes node { [*val[0], val[1]] }
21
- | nodes empty_node { val[0] }
22
- node : node_decl node_term { val[0].tap { |x| x.children = nil } }
23
- | node_decl node_children node_term { val[0].tap { |x| x.children = val[1] } }
24
- | node_decl empty_children node_term { val[0].tap { |x| x.children = nil } }
25
- node_decl : identifier { KDL::Node.new(val[0]) }
26
- | type identifier { KDL::Node.new(val[1], type: val[0]) }
27
- | node_decl WS value { val[0].tap { |x| x.arguments << val[2] } }
28
- | node_decl WS SLASHDASH ws_star value { val[0] }
29
- | node_decl WS property { val[0].tap { |x| x.properties[val[2][0]] = val[2][1] } }
30
- | node_decl WS SLASHDASH ws_star property { val[0] }
31
- empty_node: SLASHDASH ws_star node
32
- node_children: ws_star LBRACE nodes RBRACE { val[2] }
33
- | ws_star LBRACE linespace_star RBRACE { [] }
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
- | WS empty_children
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
- ws_star: none | WS
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
- def parse(str)
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 = nil, type: nil)
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.nil?
22
+ unless children.empty?
23
23
  s += " {\n"
24
- unless children.empty?
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[0]
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, whole, fraction, exponent = *s.match(/^([-+]?[\d_]+)(?:\.(\d+))?(?:[eE]([-+]?[\d_]+))?$/)
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