rbtoon 0.1.0
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +89 -0
- data/lib/rbtoon/generated_parser.rb +737 -0
- data/lib/rbtoon/handler.rb +91 -0
- data/lib/rbtoon/nodes/array.rb +113 -0
- data/lib/rbtoon/nodes/base.rb +80 -0
- data/lib/rbtoon/nodes/blank.rb +15 -0
- data/lib/rbtoon/nodes/object.rb +106 -0
- data/lib/rbtoon/nodes/root.rb +29 -0
- data/lib/rbtoon/nodes/scalar.rb +81 -0
- data/lib/rbtoon/parse_error.rb +42 -0
- data/lib/rbtoon/parser.rb +37 -0
- data/lib/rbtoon/scanner.rb +452 -0
- data/lib/rbtoon/token.rb +33 -0
- data/lib/rbtoon/version.rb +7 -0
- data/lib/rbtoon.rb +126 -0
- metadata +62 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RbToon
|
|
4
|
+
class Handler # :nodoc: all
|
|
5
|
+
def initialize
|
|
6
|
+
@stack = [Nodes::Root.new]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def output
|
|
10
|
+
@stack.first
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def push_object(key)
|
|
14
|
+
object = Nodes::Object.new(current, key.position)
|
|
15
|
+
push_value(object)
|
|
16
|
+
|
|
17
|
+
@stack << object
|
|
18
|
+
push_value(key)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def push_array(l_bracket_token, size)
|
|
22
|
+
array = Nodes::Array.new(current, l_bracket_token.position, size)
|
|
23
|
+
push_value(array)
|
|
24
|
+
|
|
25
|
+
@stack << array
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def push_value(value)
|
|
29
|
+
current.push_value(value)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def push_values(values)
|
|
33
|
+
values.each { |value| push_value(value) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def push_tabular_fields(fields)
|
|
37
|
+
current.push_tabular_fields(fields)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def push_tabular_row(values)
|
|
41
|
+
row = Nodes::TabularRow.new(values, values.first.position)
|
|
42
|
+
push_value(row)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def push_blank(token)
|
|
46
|
+
return unless token
|
|
47
|
+
|
|
48
|
+
blank = Nodes::Blank.new(token)
|
|
49
|
+
push_value(blank)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def push_empty_object(position)
|
|
53
|
+
object = Nodes::EmptyObject.new(position)
|
|
54
|
+
push_value(object)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def pop
|
|
58
|
+
@stack.pop
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def quoted_string(token)
|
|
62
|
+
Nodes::QuotedString.new(token)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def unquoted_string(token)
|
|
66
|
+
Nodes::UnquotedString.new(token)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def empty_string(position)
|
|
70
|
+
Nodes::EmptyString.new(position)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def boolean(token)
|
|
74
|
+
Nodes::Boolean.new(token)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def null(token)
|
|
78
|
+
Nodes::Null.new(token)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def number(token)
|
|
82
|
+
Nodes::Number.new(token)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def current
|
|
88
|
+
@stack.last
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RbToon # :nodoc: all
|
|
4
|
+
module Nodes
|
|
5
|
+
class Array < StructureBase
|
|
6
|
+
def initialize(parent, position, size)
|
|
7
|
+
super(parent, position)
|
|
8
|
+
@size = size
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def push_tabular_fields(fields)
|
|
12
|
+
@fields = fields
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def validate(strict: true)
|
|
16
|
+
if @fields
|
|
17
|
+
validate_tabular_array(strict)
|
|
18
|
+
else
|
|
19
|
+
validate_array(strict)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_ruby(symbolize_names: false, **optargs)
|
|
24
|
+
values = non_blank_values
|
|
25
|
+
result =
|
|
26
|
+
if tabular?
|
|
27
|
+
to_ruby_tabular(values, symbolize_names, **optargs)
|
|
28
|
+
else
|
|
29
|
+
to_ruby_list(values, symbolize_names, **optargs)
|
|
30
|
+
end
|
|
31
|
+
result || []
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def kind
|
|
35
|
+
:array
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def tabular?
|
|
41
|
+
!@fields.nil?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def validate_tabular_array(strict)
|
|
45
|
+
check_blank(strict, 'tabular rows')
|
|
46
|
+
validate_array_size('tabular rows')
|
|
47
|
+
validate_tabular_row_size
|
|
48
|
+
@valus&.flatten&.each { |value| value.validate(strict:) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def validate_array(strict)
|
|
52
|
+
check_blank(strict, 'array')
|
|
53
|
+
validate_array_size('array items')
|
|
54
|
+
@values&.each { |value| value.validate(strict:) }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def validate_array_size(kind)
|
|
58
|
+
actual = non_blank_values&.size || 0
|
|
59
|
+
expected = @size.to_ruby
|
|
60
|
+
return if actual == expected
|
|
61
|
+
|
|
62
|
+
raise_parse_error "expected #{expected} #{kind}, but got #{actual}", position
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def validate_tabular_row_size
|
|
66
|
+
expected = @fields.size
|
|
67
|
+
non_blank_values.each do |row|
|
|
68
|
+
actual = row.size
|
|
69
|
+
next if actual == expected
|
|
70
|
+
|
|
71
|
+
position = row.position
|
|
72
|
+
raise_parse_error "expected #{expected} tabular row items, but got #{actual}", position
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def to_ruby_tabular(rows, symbolize_names, **optargs)
|
|
77
|
+
return unless rows
|
|
78
|
+
|
|
79
|
+
fields = to_ruby_list(@fields, symbolize_names, **optargs)
|
|
80
|
+
fields.map!(&:to_sym) if symbolize_names
|
|
81
|
+
|
|
82
|
+
rows.map do |row|
|
|
83
|
+
fields
|
|
84
|
+
.zip(row.to_ruby(symbolize_names:, **optargs))
|
|
85
|
+
.to_h
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def to_ruby_list(values, symbolize_names, **optargs)
|
|
90
|
+
values&.map { |value| value.to_ruby(symbolize_names:, **optargs) }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
class TabularRow < Base
|
|
95
|
+
def initialize(values, position)
|
|
96
|
+
super(position)
|
|
97
|
+
@values = values
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def size
|
|
101
|
+
@values.size
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def to_ruby(**optargs)
|
|
105
|
+
@values.map { |value| value.to_ruby(**optargs) }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def kind
|
|
109
|
+
:tabular_row
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RbToon # :nodoc: all
|
|
4
|
+
module Nodes
|
|
5
|
+
class Base
|
|
6
|
+
include RaiseParseError
|
|
7
|
+
|
|
8
|
+
def initialize(position)
|
|
9
|
+
@position = position
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
attr_reader :position
|
|
13
|
+
|
|
14
|
+
[
|
|
15
|
+
:array, :tabular_row, :blank, :object, :empty_object,
|
|
16
|
+
:root, :quoted_string, :unquoted_string, :empty_string,
|
|
17
|
+
:boolean, :null, :number
|
|
18
|
+
].each do |kind|
|
|
19
|
+
class_eval(<<~M, __FILE__, __LINE__ + 1)
|
|
20
|
+
# def array?
|
|
21
|
+
# kind == :array
|
|
22
|
+
# end
|
|
23
|
+
def #{kind}?
|
|
24
|
+
kind == :#{kind}
|
|
25
|
+
end
|
|
26
|
+
M
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def validate(strict: true)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class StructureBase < Base
|
|
34
|
+
def initialize(parent, position)
|
|
35
|
+
super(position)
|
|
36
|
+
@parent = parent
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
attr_reader :parent
|
|
40
|
+
|
|
41
|
+
def push_value(value)
|
|
42
|
+
(@values ||= []) << value
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def non_blank_values
|
|
48
|
+
@values&.reject(&:blank?)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def check_blank(strict, kind)
|
|
52
|
+
return unless strict && @values && inside_array?
|
|
53
|
+
|
|
54
|
+
blank =
|
|
55
|
+
if parent.avove_array_edge?(self)
|
|
56
|
+
@values.index(&:blank?)
|
|
57
|
+
else
|
|
58
|
+
@values[..-2].index(&:blank?)
|
|
59
|
+
end
|
|
60
|
+
return unless blank
|
|
61
|
+
|
|
62
|
+
position = @values[blank].position
|
|
63
|
+
raise_parse_error "blank lines inside #{kind} are not allowed", position
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
protected
|
|
67
|
+
|
|
68
|
+
def inside_array?
|
|
69
|
+
array? || parent&.inside_array? || false
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def avove_array_edge?(object)
|
|
73
|
+
return false unless inside_array?
|
|
74
|
+
return true unless @values.last.equal?(object)
|
|
75
|
+
|
|
76
|
+
parent.avove_array_edge?(self)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RbToon # :nodoc: all
|
|
4
|
+
module Nodes
|
|
5
|
+
class Object < StructureBase
|
|
6
|
+
def validate(strict: true)
|
|
7
|
+
check_blank(strict, 'array')
|
|
8
|
+
each_key_value do |key, value|
|
|
9
|
+
key.validate(strict:)
|
|
10
|
+
value.validate(strict:)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_ruby(symbolize_names: false, strict: true, path_expansion: false)
|
|
15
|
+
each_key_value.with_object({}) do |(key, value), result|
|
|
16
|
+
build_result(result, key, value, symbolize_names, strict, path_expansion)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def kind
|
|
21
|
+
:object
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def each_key_value(&)
|
|
27
|
+
if block_given?
|
|
28
|
+
non_blank_values.each_slice(2, &)
|
|
29
|
+
else
|
|
30
|
+
non_blank_values.each_slice(2)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def build_result(
|
|
35
|
+
result, key, value,
|
|
36
|
+
symbolize_names, strict, path_expansion
|
|
37
|
+
)
|
|
38
|
+
k = key.to_ruby(symbolize_names:, strict:, path_expansion:)
|
|
39
|
+
v = value.to_ruby(symbolize_names:, strict:, path_expansion:)
|
|
40
|
+
|
|
41
|
+
paths = split_path(key, k, symbolize_names, path_expansion)
|
|
42
|
+
insert_value(result, paths, v, strict, key.position)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def split_path(key_node, key, symbolize_names, path_expansion)
|
|
46
|
+
paths =
|
|
47
|
+
if path_expansion && expandable_key?(key_node, key)
|
|
48
|
+
key.split('.')
|
|
49
|
+
else
|
|
50
|
+
[key]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
paths.map!(&:to_sym) if symbolize_names
|
|
54
|
+
paths
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def expandable_key?(key_node, key)
|
|
58
|
+
key_node.unquoted_string? &&
|
|
59
|
+
/\A[_a-z][_a-z0-9]*(?:\.[_a-z][_a-z0-9]*)*\Z/i.match?(key)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def insert_value(result, paths, value, strict, position)
|
|
63
|
+
check_conflict(result, paths, value, strict, position)
|
|
64
|
+
|
|
65
|
+
path = paths.first
|
|
66
|
+
if paths.size > 1
|
|
67
|
+
result[path] = {} unless result[path].is_a?(Hash)
|
|
68
|
+
insert_value(result[path], paths[1..], value, strict, position)
|
|
69
|
+
elsif [result[path], value] in [Hash, Hash]
|
|
70
|
+
result[path].merge!(value)
|
|
71
|
+
else
|
|
72
|
+
result[path] = value
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def check_conflict(result, paths, value, strict, position)
|
|
77
|
+
return unless conflict?(result, paths, value, strict)
|
|
78
|
+
|
|
79
|
+
raise_parse_error "key conflict at \"#{paths.first}\"", position
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def conflict?(result, paths, value, strict)
|
|
83
|
+
path = paths.first
|
|
84
|
+
return false unless strict && result.key?(path)
|
|
85
|
+
|
|
86
|
+
if paths.size > 1
|
|
87
|
+
!result[path].is_a?(Hash)
|
|
88
|
+
elsif [result[path], value] in [Hash, Hash]
|
|
89
|
+
!result[path].keys.intersect?(value.keys)
|
|
90
|
+
else
|
|
91
|
+
true
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
class EmptyObject < Base
|
|
97
|
+
def to_ruby(**_optargs)
|
|
98
|
+
{}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def kind
|
|
102
|
+
:empty_object
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RbToon # :nodoc: all
|
|
4
|
+
module Nodes
|
|
5
|
+
class Root < Array
|
|
6
|
+
def initialize
|
|
7
|
+
super(nil, nil, nil)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def validate(strict: true)
|
|
11
|
+
@values&.each_with_index do |value, i|
|
|
12
|
+
i.positive? &&
|
|
13
|
+
(raise_parse_error 'two or more values at root depth', value.position)
|
|
14
|
+
value.validate(strict:)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_ruby(**optargs)
|
|
19
|
+
return {} unless @values
|
|
20
|
+
|
|
21
|
+
@values.first.to_ruby(**optargs)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def kind
|
|
25
|
+
:root
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RbToon # :nodoc: all
|
|
4
|
+
module Nodes
|
|
5
|
+
class Scalar < Base
|
|
6
|
+
def initialize(token)
|
|
7
|
+
super(token.position)
|
|
8
|
+
@token = token
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
attr_reader :token
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class QuotedString < Scalar
|
|
15
|
+
def to_ruby(**_optargs)
|
|
16
|
+
token.text[1..-2]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def kind
|
|
20
|
+
:quoted_string
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class UnquotedString < Scalar
|
|
25
|
+
def to_ruby(**_optargs)
|
|
26
|
+
token.text.strip
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def kind
|
|
30
|
+
:unquoted_string
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class EmptyString < Base
|
|
35
|
+
def to_ruby(**_optargs)
|
|
36
|
+
''
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def kind
|
|
40
|
+
:empty_string
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class Boolean < Scalar
|
|
45
|
+
def to_ruby(**_optargs)
|
|
46
|
+
token.text.strip == 'true'
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def kind
|
|
50
|
+
:boolean
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class Null < Scalar
|
|
55
|
+
def to_ruby(**_optargs)
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def kind
|
|
60
|
+
:null
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class Number < Scalar
|
|
65
|
+
def to_ruby(**_optargs)
|
|
66
|
+
text = token.text.strip
|
|
67
|
+
if text.match?(/[.e]/i)
|
|
68
|
+
value_f = text.to_f
|
|
69
|
+
value_i = value_f.to_i
|
|
70
|
+
value_f == value_i ? value_i : value_f
|
|
71
|
+
else
|
|
72
|
+
text.to_i
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def kind
|
|
77
|
+
:number
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RbToon
|
|
4
|
+
##
|
|
5
|
+
# The exception class is raised when the given Toon includes errors.
|
|
6
|
+
#
|
|
7
|
+
# Fields:
|
|
8
|
+
# +error_message+::
|
|
9
|
+
# Message string of the detected error.
|
|
10
|
+
# +position+::
|
|
11
|
+
# Position information where the error is detected.
|
|
12
|
+
class ParseError < StandardError
|
|
13
|
+
def initialize(error_message, position)
|
|
14
|
+
super(error_message)
|
|
15
|
+
@error_message = error_message
|
|
16
|
+
@position = position
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
##
|
|
20
|
+
# Read accessor to +error_message+ field.
|
|
21
|
+
attr_reader :error_message
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
# Read accessor to +position+ field.
|
|
25
|
+
attr_reader :position
|
|
26
|
+
|
|
27
|
+
##
|
|
28
|
+
# Return the +error_message+ string.
|
|
29
|
+
# The +position+ information is also included if provided.
|
|
30
|
+
def to_s
|
|
31
|
+
(position && "#{super} -- #{position}") || super
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
module RaiseParseError # :nodoc:
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def raise_parse_error(message, position = nil)
|
|
39
|
+
raise ParseError.new(message, position)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RbToon
|
|
4
|
+
class Parser < GeneratedParser # :nodoc:
|
|
5
|
+
include RaiseParseError
|
|
6
|
+
|
|
7
|
+
def initialize(scanner, handler, debug: false)
|
|
8
|
+
@scanner = scanner
|
|
9
|
+
@handler = handler
|
|
10
|
+
@yydebug = debug
|
|
11
|
+
super()
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def parse
|
|
15
|
+
do_parse
|
|
16
|
+
handler.output
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
attr_reader :scanner
|
|
22
|
+
attr_reader :handler
|
|
23
|
+
|
|
24
|
+
def next_token
|
|
25
|
+
scanner.next_token
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def on_error(_token_id, value, _value_stack)
|
|
29
|
+
message = "syntax error on value '#{value.text}' (#{value.kind})"
|
|
30
|
+
raise_parse_error message, value.position
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def to_list(val)
|
|
34
|
+
[val[0], *val[1]&.map { |_, value| value }]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|