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.
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