toml-merge 1.0.0 → 2.0.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.
@@ -0,0 +1,256 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toml
4
+ module Merge
5
+ # Alias for the shared normalizer module from ast-merge
6
+ NodeTypingNormalizer = Ast::Merge::NodeTyping::Normalizer
7
+
8
+ # Normalizes backend-specific node types to canonical TOML types.
9
+ #
10
+ # Uses Ast::Merge::NodeTyping::Wrapper to wrap nodes with canonical
11
+ # merge_type, allowing portable merge rules across backends.
12
+ #
13
+ # ## Thread Safety
14
+ #
15
+ # All backend registration and lookup operations are thread-safe via
16
+ # the shared Ast::Merge::NodeTyping::Normalizer module.
17
+ #
18
+ # ## Backends
19
+ #
20
+ # Currently supports:
21
+ # - `:tree_sitter` - tree-sitter-toml grammar (via ruby_tree_sitter, tree_stump, FFI)
22
+ # - `:citrus` - toml-rb gem with Citrus parser
23
+ #
24
+ # ## Extensibility
25
+ #
26
+ # New backends can be registered at runtime:
27
+ #
28
+ # @example Registering a new backend
29
+ # NodeTypeNormalizer.register_backend(:my_toml_parser, {
30
+ # key_value: :pair,
31
+ # section: :table,
32
+ # section_array: :array_of_tables,
33
+ # })
34
+ #
35
+ # ## Canonical Types
36
+ #
37
+ # The following canonical types are used for portable merge rules:
38
+ #
39
+ # ### Document Structure
40
+ # - `:document` - Root document node
41
+ # - `:table` - Table/section header `[section]`
42
+ # - `:array_of_tables` - Array of tables `[[section]]`
43
+ # - `:pair` - Key-value pair `key = value`
44
+ #
45
+ # ### Key Types
46
+ # - `:bare_key` - Unquoted key
47
+ # - `:quoted_key` - Quoted key `"key"` or `'key'`
48
+ # - `:dotted_key` - Dotted key `a.b.c`
49
+ #
50
+ # ### Value Types
51
+ # - `:string` - String values (basic or literal)
52
+ # - `:integer` - Integer values
53
+ # - `:float` - Floating point values
54
+ # - `:boolean` - Boolean `true`/`false`
55
+ # - `:array` - Array values `[1, 2, 3]`
56
+ # - `:inline_table` - Inline table `{ key = value }`
57
+ #
58
+ # ### Date/Time Types
59
+ # - `:datetime` - Date/time values (offset, local, date-only, time-only)
60
+ #
61
+ # ### Other
62
+ # - `:comment` - Comment lines
63
+ #
64
+ # @see Ast::Merge::NodeTyping::Wrapper
65
+ # @see Ast::Merge::NodeTyping::Normalizer
66
+ module NodeTypeNormalizer
67
+ extend NodeTypingNormalizer
68
+
69
+ # Configure default backend mappings.
70
+ # Maps backend-specific type symbols to canonical type symbols.
71
+ #
72
+ # Both tree-sitter-toml and citrus/toml-rb produce similar node types,
73
+ # so the mappings are largely identity mappings with some normalization.
74
+ configure_normalizer(
75
+ # tree-sitter-toml grammar node types
76
+ # Reference: https://github.com/tree-sitter-grammars/tree-sitter-toml
77
+ # All native TreeHaver backends (mri, rust, ffi) use this grammar.
78
+ tree_sitter: {
79
+ # Document structure
80
+ document: :document,
81
+ table: :table,
82
+ table_array_element: :array_of_tables, # tree-sitter uses this name
83
+
84
+ # Key-value pairs
85
+ pair: :pair,
86
+
87
+ # Key types
88
+ bare_key: :bare_key,
89
+ quoted_key: :quoted_key,
90
+ dotted_key: :dotted_key,
91
+
92
+ # Value types
93
+ string: :string,
94
+ basic_string: :string,
95
+ literal_string: :string,
96
+ multiline_basic_string: :string,
97
+ multiline_literal_string: :string,
98
+ integer: :integer,
99
+ float: :float,
100
+ boolean: :boolean,
101
+ array: :array,
102
+ inline_table: :inline_table,
103
+
104
+ # Date/time types
105
+ offset_date_time: :datetime,
106
+ local_date_time: :datetime,
107
+ local_date: :datetime,
108
+ local_time: :datetime,
109
+
110
+ # Other
111
+ comment: :comment,
112
+
113
+ # Punctuation (usually not needed for merge logic, but map them)
114
+ "=": :equals,
115
+ "[": :bracket_open,
116
+ "]": :bracket_close,
117
+ "[[": :double_bracket_open,
118
+ "]]": :double_bracket_close,
119
+ "{": :brace_open,
120
+ "}": :brace_close,
121
+ ",": :comma,
122
+ }.freeze,
123
+
124
+ # Citrus/toml-rb backend node types
125
+ # These are produced by TreeHaver's Citrus adapter wrapping toml-rb
126
+ # Verified via examples/map_citrus_node_types.rb script
127
+ citrus: {
128
+ # Document structure
129
+ document: :document,
130
+ table: :table,
131
+ table_array: :array_of_tables, # Citrus produces :table_array (not :table_array_element)
132
+
133
+ # Key-value pairs
134
+ keyvalue: :pair, # Citrus produces :keyvalue (not :pair)
135
+ pair: :pair, # Keep for compatibility if TreeHaver normalizes
136
+
137
+ # Key types
138
+ bare_key: :bare_key,
139
+ quoted_key: :quoted_key,
140
+ dotted_key: :dotted_key,
141
+ key: :bare_key, # Citrus uses :key wrapper
142
+ stripped_key: :bare_key, # Citrus uses :stripped_key wrapper
143
+
144
+ # Value types - Citrus uses more specific type names
145
+ string: :string,
146
+ basic_string: :string,
147
+ literal_string: :string,
148
+ multiline_string: :string,
149
+ multiline_literal: :string,
150
+ integer: :integer,
151
+ decimal_integer: :integer,
152
+ hexadecimal_integer: :integer,
153
+ octal_integer: :integer,
154
+ binary_integer: :integer,
155
+ float: :float,
156
+ fractional_float: :float,
157
+ boolean: :boolean,
158
+ true: :boolean,
159
+ false: :boolean,
160
+ array: :array,
161
+ inline_table: :inline_table,
162
+
163
+ # Date/time types - Citrus uses specific names
164
+ datetime: :datetime,
165
+ date: :datetime,
166
+ time: :datetime,
167
+ local_date: :datetime,
168
+ local_time: :datetime,
169
+ local_datetime: :datetime,
170
+ offset_datetime: :datetime,
171
+ date_skeleton: :datetime,
172
+ time_skeleton: :datetime,
173
+
174
+ # Other
175
+ comment: :comment,
176
+ space: :whitespace,
177
+ line_break: :whitespace,
178
+ indent: :whitespace,
179
+ repeat: :whitespace,
180
+ unknown: :unknown,
181
+
182
+ # Punctuation
183
+ "=": :equals,
184
+ "[": :bracket_open,
185
+ "]": :bracket_close,
186
+ "[[": :double_bracket_open,
187
+ "]]": :double_bracket_close,
188
+ "{": :brace_open,
189
+ "}": :brace_close,
190
+ ",": :comma,
191
+ }.freeze,
192
+ )
193
+
194
+ class << self
195
+ # Default backend for TOML normalization
196
+ DEFAULT_BACKEND = :tree_sitter
197
+
198
+ # Get the canonical type for a backend-specific type.
199
+ # Overrides the shared Normalizer to default to :tree_sitter backend.
200
+ #
201
+ # @param backend_type [Symbol, String, nil] The backend's node type
202
+ # @param backend [Symbol] The backend identifier (defaults to :tree_sitter)
203
+ # @return [Symbol, nil] Canonical type (or original if no mapping)
204
+ def canonical_type(backend_type, backend = DEFAULT_BACKEND)
205
+ super(backend_type, backend)
206
+ end
207
+
208
+ # Wrap a node with its canonical type as merge_type.
209
+ # Overrides the shared Normalizer to default to :tree_sitter backend.
210
+ #
211
+ # @param node [Object] The backend node to wrap (must respond to #type)
212
+ # @param backend [Symbol] The backend identifier (defaults to :tree_sitter)
213
+ # @return [Ast::Merge::NodeTyping::Wrapper] Wrapped node with canonical merge_type
214
+ def wrap(node, backend = DEFAULT_BACKEND)
215
+ super(node, backend)
216
+ end
217
+
218
+ # Check if a type is a table type (regular or array of tables)
219
+ #
220
+ # @param type [Symbol, String] The type to check
221
+ # @return [Boolean]
222
+ def table_type?(type)
223
+ canonical = type.to_sym
224
+ %i[table array_of_tables].include?(canonical)
225
+ end
226
+
227
+ # Check if a type is a value type (string, integer, etc.)
228
+ #
229
+ # @param type [Symbol, String] The type to check
230
+ # @return [Boolean]
231
+ def value_type?(type)
232
+ canonical = type.to_sym
233
+ %i[string integer float boolean array inline_table datetime].include?(canonical)
234
+ end
235
+
236
+ # Check if a type is a key type
237
+ #
238
+ # @param type [Symbol, String] The type to check
239
+ # @return [Boolean]
240
+ def key_type?(type)
241
+ canonical = type.to_sym
242
+ %i[bare_key quoted_key dotted_key].include?(canonical)
243
+ end
244
+
245
+ # Check if a type is a container type (can have children)
246
+ #
247
+ # @param type [Symbol, String] The type to check
248
+ # @return [Boolean]
249
+ def container_type?(type)
250
+ canonical = type.to_sym
251
+ %i[document table array_of_tables array inline_table].include?(canonical)
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end