herb 0.8.9 → 0.8.10
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 +4 -4
- data/Makefile +22 -8
- data/config.yml +7 -0
- data/ext/herb/error_helpers.c +25 -0
- data/ext/herb/nodes.c +1 -1
- data/lib/herb/engine/compiler.rb +5 -3
- data/lib/herb/engine/debug_visitor.rb +38 -3
- data/lib/herb/engine.rb +16 -4
- data/lib/herb/errors.rb +20 -0
- data/lib/herb/version.rb +1 -1
- data/sig/herb/engine/debug_visitor.rbs +6 -0
- data/sig/herb/engine.rbs +2 -0
- data/sig/herb/errors.rbs +10 -0
- data/sig/serialized_ast_errors.rbs +3 -0
- data/src/analyze.c +26 -12
- data/src/analyze_helpers.c +32 -9
- data/src/errors.c +37 -0
- data/src/extract.c +6 -3
- data/src/include/analyze_helpers.h +18 -15
- data/src/include/errors.h +8 -0
- data/src/include/util/string.h +11 -0
- data/src/include/version.h +1 -1
- data/src/main.c +32 -44
- data/src/parser.c +6 -5
- data/vendor/prism/config.yml +4 -4
- data/vendor/prism/include/prism/ast.h +4 -4
- data/vendor/prism/include/prism/version.h +2 -2
- data/vendor/prism/src/prism.c +1 -1
- data/vendor/prism/templates/java/org/prism/Loader.java.erb +1 -1
- data/vendor/prism/templates/javascript/src/deserialize.js.erb +1 -1
- data/vendor/prism/templates/lib/prism/node.rb.erb +23 -15
- data/vendor/prism/templates/lib/prism/serialize.rb.erb +1 -1
- data/vendor/prism/templates/rbi/prism/node.rbi.erb +3 -0
- data/vendor/prism/templates/sig/prism/node.rbs.erb +3 -0
- data/vendor/prism/templates/sig/prism.rbs.erb +9 -10
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 99c86daa1824a8c955a69c6015f8930c49b43b3e4333cb4c16b30d62b0e9ba58
|
|
4
|
+
data.tar.gz: 8e71055933851fa346707121e806e85b59a62d4ef00a9e6c4e7014c26859cca1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 50134d7481f2d8bb436d9115d95e58af534a7be1ee4c24e5e193b738d32f605b4e767a49bec3b87b10956faaf1f7128c666e5db81dd4221ef163aa2e0857b505
|
|
7
|
+
data.tar.gz: 551022b3dd92e528d541696b829690bad55e749d2cff7993912cba3539b495266a8ee284cc2845c6d2c435eb029668bef9c8f699730270df0373ea5e0f8136b4
|
data/Makefile
CHANGED
|
@@ -67,15 +67,19 @@ ifeq ($(os),Linux)
|
|
|
67
67
|
endif
|
|
68
68
|
|
|
69
69
|
ifeq ($(os),Darwin)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
cc
|
|
75
|
-
clang_format
|
|
76
|
-
clang_tidy
|
|
70
|
+
llvm_version := 21
|
|
71
|
+
llvm_prefix ?= $(shell brew --prefix llvm@$(llvm_version))
|
|
72
|
+
check_prefix ?= $(shell brew --prefix check)
|
|
73
|
+
|
|
74
|
+
cc ?= $(llvm_prefix)/bin/clang
|
|
75
|
+
clang_format ?= $(llvm_prefix)/bin/clang-format
|
|
76
|
+
clang_tidy ?= $(llvm_prefix)/bin/clang-tidy
|
|
77
|
+
|
|
78
|
+
test_cflags = $(test_flags) -I$(check_prefix)/include
|
|
79
|
+
test_ldflags = -L$(check_prefix)/lib -lcheck -lm $(prism_ldflags)
|
|
77
80
|
endif
|
|
78
81
|
|
|
82
|
+
.PHONY: all
|
|
79
83
|
all: templates prism $(exec) $(lib_name) $(static_lib_name) test wasm clangd_config
|
|
80
84
|
|
|
81
85
|
$(exec): $(objects)
|
|
@@ -91,39 +95,49 @@ $(static_lib_name): $(objects)
|
|
|
91
95
|
src/%.o: src/%.c templates
|
|
92
96
|
$(cc) -c $(flags) -fPIC $< -o $@
|
|
93
97
|
|
|
94
|
-
test/%.o: test/%.c templates
|
|
98
|
+
test/%.o: test/%.c templates prism
|
|
95
99
|
$(cc) -c $(test_cflags) $(test_flags) $(prism_flags) $< -o $@
|
|
96
100
|
|
|
101
|
+
.PHONY: test
|
|
97
102
|
test: $(test_objects) $(non_main_objects)
|
|
98
103
|
$(cc) $(test_objects) $(non_main_objects) $(test_cflags) $(test_ldflags) -o $(test_exec)
|
|
99
104
|
|
|
105
|
+
.PHONY: clean
|
|
100
106
|
clean:
|
|
101
107
|
rm -f $(exec) $(test_exec) $(lib_name) $(shared_lib_name) $(ruby_extension)
|
|
102
108
|
rm -rf $(objects) $(test_objects) $(extension_objects) lib/herb/*.bundle tmp
|
|
103
109
|
rm -rf $(prism_path)
|
|
104
110
|
rake prism:clean
|
|
105
111
|
|
|
112
|
+
.PHONY: bundle_install
|
|
106
113
|
bundle_install:
|
|
107
114
|
bundle install
|
|
108
115
|
|
|
116
|
+
.PHONY: templates
|
|
109
117
|
templates: bundle_install
|
|
110
118
|
bundle exec rake templates
|
|
111
119
|
|
|
120
|
+
.PHONY: prism
|
|
112
121
|
prism: bundle_install
|
|
113
122
|
cd $(prism_path) && ruby templates/template.rb && make static && cd -
|
|
114
123
|
rake prism:vendor
|
|
115
124
|
|
|
125
|
+
.PHONY: format
|
|
116
126
|
format:
|
|
117
127
|
$(clang_format) -i $(project_and_extension_files)
|
|
118
128
|
|
|
129
|
+
.PHONY: lint
|
|
119
130
|
lint:
|
|
120
131
|
$(clang_format) --dry-run --Werror $(project_and_extension_files)
|
|
121
132
|
|
|
133
|
+
.PHONY: tidy
|
|
122
134
|
tidy:
|
|
123
135
|
$(clang_tidy) $(project_files) -- $(flags)
|
|
124
136
|
|
|
137
|
+
.PHONY: clangd_config
|
|
125
138
|
clangd_config:
|
|
126
139
|
@echo "$(flags) $(test_cflags)" | tr ' ' '\n' | sort -u > compile_flags.txt
|
|
127
140
|
|
|
141
|
+
.PHONY: wasm
|
|
128
142
|
wasm:
|
|
129
143
|
cd wasm && make
|
data/config.yml
CHANGED
|
@@ -178,6 +178,13 @@ errors:
|
|
|
178
178
|
|
|
179
179
|
fields: []
|
|
180
180
|
|
|
181
|
+
- name: ERBCaseWithConditionsError
|
|
182
|
+
message:
|
|
183
|
+
template: "A `case` statement with `when`/`in` in a single ERB tag cannot be formatted. Use separate tags for `case` and its conditions."
|
|
184
|
+
arguments: []
|
|
185
|
+
|
|
186
|
+
fields: []
|
|
187
|
+
|
|
181
188
|
warnings:
|
|
182
189
|
fields: []
|
|
183
190
|
types: []
|
data/ext/herb/error_helpers.c
CHANGED
|
@@ -341,6 +341,30 @@ static VALUE rb_erb_multiple_blocks_in_tag_error_from_c_struct(ERB_MULTIPLE_BLOC
|
|
|
341
341
|
return rb_class_new_instance(3, args, ERBMultipleBlocksInTagError);
|
|
342
342
|
};
|
|
343
343
|
|
|
344
|
+
static VALUE rb_erb_case_with_conditions_error_from_c_struct(ERB_CASE_WITH_CONDITIONS_ERROR_T* erb_case_with_conditions_error) {
|
|
345
|
+
if (erb_case_with_conditions_error == NULL) { return Qnil; }
|
|
346
|
+
|
|
347
|
+
ERROR_T* error = &erb_case_with_conditions_error->base;
|
|
348
|
+
|
|
349
|
+
VALUE Herb = rb_define_module("Herb");
|
|
350
|
+
VALUE Errors = rb_define_module_under(Herb, "Errors");
|
|
351
|
+
VALUE Error = rb_define_class_under(Errors, "Error", rb_cObject);
|
|
352
|
+
VALUE ERBCaseWithConditionsError = rb_define_class_under(Errors, "ERBCaseWithConditionsError", Error);
|
|
353
|
+
|
|
354
|
+
VALUE type = rb_utf8_str_new_cstr(error_type_to_string(error));
|
|
355
|
+
VALUE location = rb_location_from_c_struct(error->location);
|
|
356
|
+
VALUE message = rb_utf8_str_new_cstr(error->message);
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
VALUE args[3] = {
|
|
360
|
+
type,
|
|
361
|
+
location,
|
|
362
|
+
message
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
return rb_class_new_instance(3, args, ERBCaseWithConditionsError);
|
|
366
|
+
};
|
|
367
|
+
|
|
344
368
|
|
|
345
369
|
VALUE rb_error_from_c_struct(ERROR_T* error) {
|
|
346
370
|
if (!error) { return Qnil; }
|
|
@@ -358,6 +382,7 @@ VALUE rb_error_from_c_struct(ERROR_T* error) {
|
|
|
358
382
|
case ERB_CONTROL_FLOW_SCOPE_ERROR: return rb_erb_control_flow_scope_error_from_c_struct((ERB_CONTROL_FLOW_SCOPE_ERROR_T*) error); break;
|
|
359
383
|
case MISSINGERB_END_TAG_ERROR: return rb_missingerb_end_tag_error_from_c_struct((MISSINGERB_END_TAG_ERROR_T*) error); break;
|
|
360
384
|
case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: return rb_erb_multiple_blocks_in_tag_error_from_c_struct((ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T*) error); break;
|
|
385
|
+
case ERB_CASE_WITH_CONDITIONS_ERROR: return rb_erb_case_with_conditions_error_from_c_struct((ERB_CASE_WITH_CONDITIONS_ERROR_T*) error); break;
|
|
361
386
|
}
|
|
362
387
|
|
|
363
388
|
return Qnil;
|
data/ext/herb/nodes.c
CHANGED
|
@@ -464,7 +464,7 @@ static VALUE rb_erb_content_node_from_c_struct(AST_ERB_CONTENT_NODE_T* erb_conte
|
|
|
464
464
|
VALUE erb_content_node_tag_opening = rb_token_from_c_struct(erb_content_node->tag_opening);
|
|
465
465
|
VALUE erb_content_node_content = rb_token_from_c_struct(erb_content_node->content);
|
|
466
466
|
VALUE erb_content_node_tag_closing = rb_token_from_c_struct(erb_content_node->tag_closing);
|
|
467
|
-
/* #<Herb::Template::AnalyzedRubyField:
|
|
467
|
+
/* #<Herb::Template::AnalyzedRubyField:0x00007f8f1d2522c8 @name="analyzed_ruby", @options={kind: nil}> */
|
|
468
468
|
VALUE erb_content_node_analyzed_ruby = Qnil;
|
|
469
469
|
VALUE erb_content_node_parsed = (erb_content_node->parsed) ? Qtrue : Qfalse;
|
|
470
470
|
VALUE erb_content_node_valid = (erb_content_node->valid) ? Qtrue : Qfalse;
|
data/lib/herb/engine/compiler.rb
CHANGED
|
@@ -297,18 +297,20 @@ module Herb
|
|
|
297
297
|
end
|
|
298
298
|
|
|
299
299
|
def add_context_aware_expression(code, context)
|
|
300
|
+
closing = code.include?("#") ? "\n))" : "))"
|
|
301
|
+
|
|
300
302
|
case context
|
|
301
303
|
when :attribute_value
|
|
302
304
|
@engine.send(:with_buffer) {
|
|
303
|
-
@engine.src << " << #{@attrfunc}((" << code <<
|
|
305
|
+
@engine.src << " << #{@attrfunc}((" << code << closing
|
|
304
306
|
}
|
|
305
307
|
when :script_content
|
|
306
308
|
@engine.send(:with_buffer) {
|
|
307
|
-
@engine.src << " << #{@jsfunc}((" << code <<
|
|
309
|
+
@engine.src << " << #{@jsfunc}((" << code << closing
|
|
308
310
|
}
|
|
309
311
|
when :style_content
|
|
310
312
|
@engine.send(:with_buffer) {
|
|
311
|
-
@engine.src << " << #{@cssfunc}((" << code <<
|
|
313
|
+
@engine.src << " << #{@cssfunc}((" << code << closing
|
|
312
314
|
}
|
|
313
315
|
else
|
|
314
316
|
@engine.send(:add_expression_result_escaped, code)
|
|
@@ -178,7 +178,7 @@ module Herb
|
|
|
178
178
|
|
|
179
179
|
debug_attributes = [
|
|
180
180
|
create_debug_attribute("data-herb-debug-outline-type", view_type),
|
|
181
|
-
create_debug_attribute("data-herb-debug-file-name",
|
|
181
|
+
create_debug_attribute("data-herb-debug-file-name", component_display_name),
|
|
182
182
|
create_debug_attribute("data-herb-debug-file-relative-path", @relative_file_path || "unknown"),
|
|
183
183
|
create_debug_attribute("data-herb-debug-file-full-path", @filename&.to_s || "unknown")
|
|
184
184
|
]
|
|
@@ -231,7 +231,7 @@ module Herb
|
|
|
231
231
|
debug_attributes = [
|
|
232
232
|
create_debug_attribute("data-herb-debug-outline-type", outline_type),
|
|
233
233
|
create_debug_attribute("data-herb-debug-erb", escaped_erb),
|
|
234
|
-
create_debug_attribute("data-herb-debug-file-name",
|
|
234
|
+
create_debug_attribute("data-herb-debug-file-name", component_display_name),
|
|
235
235
|
create_debug_attribute("data-herb-debug-file-relative-path", @relative_file_path || "unknown"),
|
|
236
236
|
create_debug_attribute("data-herb-debug-file-full-path", @filename&.to_s || "unknown"),
|
|
237
237
|
create_debug_attribute("data-herb-debug-inserted", "true")
|
|
@@ -288,8 +288,43 @@ module Herb
|
|
|
288
288
|
def component?
|
|
289
289
|
return false unless @filename
|
|
290
290
|
|
|
291
|
+
@filename.to_s.match?(%r{(^|/)app/components/})
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def sidecar_component?
|
|
295
|
+
return false unless component?
|
|
296
|
+
return false unless @filename
|
|
297
|
+
|
|
298
|
+
@filename.basename.to_s.match?(/\Acomponent\.(html\.erb|html\.herb|erb|herb)\z/)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def component_display_name
|
|
302
|
+
return @filename&.basename&.to_s || "unknown" unless @filename
|
|
303
|
+
|
|
304
|
+
basename = @filename.basename.to_s
|
|
291
305
|
path = @filename.to_s
|
|
292
|
-
|
|
306
|
+
|
|
307
|
+
if sidecar_component? && (match = path.match(%r{/components/(.+)/component\.[^/]+\z}))
|
|
308
|
+
return match[1].split("/").map { |s| classify(s) }.join("::")
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
if component?
|
|
312
|
+
path_without_ext = path.sub(/\.(?:html\.erb|html\.herb|erb|herb)\z/, "")
|
|
313
|
+
|
|
314
|
+
if (match = path_without_ext.match(%r{/components/(.+)\z}))
|
|
315
|
+
return match[1].split("/").map { |s| classify(s) }.join("::")
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
basename
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def classify(name)
|
|
323
|
+
if name.respond_to?(:camelize)
|
|
324
|
+
name.camelize
|
|
325
|
+
else
|
|
326
|
+
name.split(/[_-]/).map(&:capitalize).join
|
|
327
|
+
end
|
|
293
328
|
end
|
|
294
329
|
|
|
295
330
|
def in_head_context?
|
data/lib/herb/engine.rb
CHANGED
|
@@ -212,11 +212,15 @@ module Herb
|
|
|
212
212
|
end
|
|
213
213
|
|
|
214
214
|
def add_expression_result(code)
|
|
215
|
-
with_buffer {
|
|
215
|
+
with_buffer {
|
|
216
|
+
@src << " << (" << code << comment_aware_newline(code) << ").to_s"
|
|
217
|
+
}
|
|
216
218
|
end
|
|
217
219
|
|
|
218
220
|
def add_expression_result_escaped(code)
|
|
219
|
-
with_buffer {
|
|
221
|
+
with_buffer {
|
|
222
|
+
@src << " << " << @escapefunc << "((" << code << comment_aware_newline(code) << "))"
|
|
223
|
+
}
|
|
220
224
|
end
|
|
221
225
|
|
|
222
226
|
def add_expression_block(indicator, code)
|
|
@@ -228,11 +232,19 @@ module Herb
|
|
|
228
232
|
end
|
|
229
233
|
|
|
230
234
|
def add_expression_block_result(code)
|
|
231
|
-
with_buffer {
|
|
235
|
+
with_buffer {
|
|
236
|
+
@src << " << " << code << comment_aware_newline(code)
|
|
237
|
+
}
|
|
232
238
|
end
|
|
233
239
|
|
|
234
240
|
def add_expression_block_result_escaped(code)
|
|
235
|
-
with_buffer {
|
|
241
|
+
with_buffer {
|
|
242
|
+
@src << " << " << @escapefunc << "(" << code << comment_aware_newline(code) << ")"
|
|
243
|
+
}
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def comment_aware_newline(code)
|
|
247
|
+
code.include?("#") ? "\n" : ""
|
|
236
248
|
end
|
|
237
249
|
|
|
238
250
|
def add_postamble(postamble)
|
data/lib/herb/errors.rb
CHANGED
|
@@ -518,5 +518,25 @@ module Herb
|
|
|
518
518
|
end
|
|
519
519
|
end
|
|
520
520
|
|
|
521
|
+
class ERBCaseWithConditionsError < Error
|
|
522
|
+
include Colors
|
|
523
|
+
|
|
524
|
+
#: () -> String
|
|
525
|
+
def inspect
|
|
526
|
+
tree_inspect.rstrip.gsub(/\s+$/, "")
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
#: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
|
|
530
|
+
def tree_inspect(indent: 0, depth: 0, depth_limit: 25)
|
|
531
|
+
output = +""
|
|
532
|
+
|
|
533
|
+
output += white("@ #{bold(red(error_name))} #{dimmed("(location: #{location.tree_inspect})\n")}")
|
|
534
|
+
output += white("└── message: #{green(message.inspect)}\n")
|
|
535
|
+
output += %(\n)
|
|
536
|
+
|
|
537
|
+
output.gsub(/^/, " " * indent)
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
|
|
521
541
|
end
|
|
522
542
|
end
|
data/lib/herb/version.rb
CHANGED
|
@@ -57,6 +57,12 @@ module Herb
|
|
|
57
57
|
|
|
58
58
|
def component?: () -> untyped
|
|
59
59
|
|
|
60
|
+
def sidecar_component?: () -> untyped
|
|
61
|
+
|
|
62
|
+
def component_display_name: () -> untyped
|
|
63
|
+
|
|
64
|
+
def classify: (untyped name) -> untyped
|
|
65
|
+
|
|
60
66
|
def in_head_context?: () -> untyped
|
|
61
67
|
|
|
62
68
|
def in_script_or_style_context?: () -> untyped
|
data/sig/herb/engine.rbs
CHANGED
data/sig/herb/errors.rbs
CHANGED
|
@@ -253,5 +253,15 @@ module Herb
|
|
|
253
253
|
# : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
|
|
254
254
|
def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
|
|
255
255
|
end
|
|
256
|
+
|
|
257
|
+
class ERBCaseWithConditionsError < Error
|
|
258
|
+
include Colors
|
|
259
|
+
|
|
260
|
+
# : () -> String
|
|
261
|
+
def inspect: () -> String
|
|
262
|
+
|
|
263
|
+
# : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
|
|
264
|
+
def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
|
|
265
|
+
end
|
|
256
266
|
end
|
|
257
267
|
end
|
data/src/analyze.c
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#include "include/util.h"
|
|
15
15
|
#include "include/util/hb_array.h"
|
|
16
16
|
#include "include/util/hb_string.h"
|
|
17
|
+
#include "include/util/string.h"
|
|
17
18
|
#include "include/visitor.h"
|
|
18
19
|
|
|
19
20
|
#include <prism.h>
|
|
@@ -34,17 +35,20 @@ static analyzed_ruby_T* herb_analyze_ruby(hb_string_T source) {
|
|
|
34
35
|
pm_visit_node(analyzed->root, search_until_nodes, analyzed);
|
|
35
36
|
pm_visit_node(analyzed->root, search_begin_nodes, analyzed);
|
|
36
37
|
pm_visit_node(analyzed->root, search_unless_nodes, analyzed);
|
|
38
|
+
pm_visit_node(analyzed->root, search_when_nodes, analyzed);
|
|
39
|
+
pm_visit_node(analyzed->root, search_in_nodes, analyzed);
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
search_unexpected_elsif_nodes(analyzed);
|
|
42
|
+
search_unexpected_else_nodes(analyzed);
|
|
43
|
+
search_unexpected_end_nodes(analyzed);
|
|
44
|
+
search_unexpected_when_nodes(analyzed);
|
|
45
|
+
search_unexpected_in_nodes(analyzed);
|
|
46
|
+
|
|
47
|
+
search_unexpected_rescue_nodes(analyzed);
|
|
48
|
+
search_unexpected_ensure_nodes(analyzed);
|
|
45
49
|
search_yield_nodes(analyzed->root, analyzed);
|
|
46
50
|
search_then_keywords(analyzed->root, analyzed);
|
|
47
|
-
|
|
51
|
+
search_unexpected_block_closing_nodes(analyzed);
|
|
48
52
|
|
|
49
53
|
if (!analyzed->valid) { pm_visit_node(analyzed->root, search_unclosed_control_flows, analyzed); }
|
|
50
54
|
|
|
@@ -57,8 +61,8 @@ static bool analyze_erb_content(const AST_NODE_T* node, void* data) {
|
|
|
57
61
|
|
|
58
62
|
const char* opening = erb_content_node->tag_opening->value;
|
|
59
63
|
|
|
60
|
-
if (
|
|
61
|
-
&&
|
|
64
|
+
if (!string_equals(opening, "<%%") && !string_equals(opening, "<%%=") && !string_equals(opening, "<%#")
|
|
65
|
+
&& !string_equals(opening, "<%graphql")) {
|
|
62
66
|
analyzed_ruby_T* analyzed = herb_analyze_ruby(hb_string(erb_content_node->content->value));
|
|
63
67
|
|
|
64
68
|
erb_content_node->parsed = true;
|
|
@@ -72,6 +76,16 @@ static bool analyze_erb_content(const AST_NODE_T* node, void* data) {
|
|
|
72
76
|
erb_content_node->base.errors
|
|
73
77
|
);
|
|
74
78
|
}
|
|
79
|
+
|
|
80
|
+
if (!analyzed->valid
|
|
81
|
+
&& ((analyzed->case_node_count > 0 && analyzed->when_node_count > 0)
|
|
82
|
+
|| (analyzed->case_match_node_count > 0 && analyzed->in_node_count > 0))) {
|
|
83
|
+
append_erb_case_with_conditions_error(
|
|
84
|
+
erb_content_node->base.location.start,
|
|
85
|
+
erb_content_node->base.location.end,
|
|
86
|
+
erb_content_node->base.errors
|
|
87
|
+
);
|
|
88
|
+
}
|
|
75
89
|
} else {
|
|
76
90
|
erb_content_node->parsed = false;
|
|
77
91
|
erb_content_node->valid = true;
|
|
@@ -274,8 +288,8 @@ static control_type_t detect_control_type(AST_ERB_CONTENT_NODE_T* erb_node) {
|
|
|
274
288
|
if (has_elsif_node(ruby)) { return CONTROL_TYPE_ELSIF; }
|
|
275
289
|
if (has_else_node(ruby)) { return CONTROL_TYPE_ELSE; }
|
|
276
290
|
if (has_end(ruby)) { return CONTROL_TYPE_END; }
|
|
277
|
-
if (has_when_node(ruby)) { return CONTROL_TYPE_WHEN; }
|
|
278
|
-
if (has_in_node(ruby)) { return CONTROL_TYPE_IN; }
|
|
291
|
+
if (has_when_node(ruby) && !has_case_node(ruby)) { return CONTROL_TYPE_WHEN; }
|
|
292
|
+
if (has_in_node(ruby) && !has_case_match_node(ruby)) { return CONTROL_TYPE_IN; }
|
|
279
293
|
if (has_rescue_node(ruby)) { return CONTROL_TYPE_RESCUE; }
|
|
280
294
|
if (has_ensure_node(ruby)) { return CONTROL_TYPE_ENSURE; }
|
|
281
295
|
if (has_block_closing(ruby)) { return CONTROL_TYPE_BLOCK_CLOSE; }
|
data/src/analyze_helpers.c
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include <string.h>
|
|
4
4
|
|
|
5
5
|
#include "include/analyzed_ruby.h"
|
|
6
|
+
#include "include/util/string.h"
|
|
6
7
|
|
|
7
8
|
bool has_if_node(analyzed_ruby_T* analyzed) {
|
|
8
9
|
return analyzed->if_node_count > 0;
|
|
@@ -83,7 +84,7 @@ bool has_then_keyword(analyzed_ruby_T* analyzed) {
|
|
|
83
84
|
bool has_error_message(analyzed_ruby_T* anlayzed, const char* message) {
|
|
84
85
|
for (const pm_diagnostic_t* error = (const pm_diagnostic_t*) anlayzed->parser.error_list.head; error != NULL;
|
|
85
86
|
error = (const pm_diagnostic_t*) error->node.next) {
|
|
86
|
-
if (
|
|
87
|
+
if (string_equals(error->message, message)) { return true; }
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
return false;
|
|
@@ -242,7 +243,28 @@ bool search_unless_nodes(const pm_node_t* node, void* data) {
|
|
|
242
243
|
return false;
|
|
243
244
|
}
|
|
244
245
|
|
|
245
|
-
bool
|
|
246
|
+
bool search_when_nodes(const pm_node_t* node, void* data) {
|
|
247
|
+
analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
|
|
248
|
+
|
|
249
|
+
if (node->type == PM_WHEN_NODE) { analyzed->when_node_count++; }
|
|
250
|
+
|
|
251
|
+
pm_visit_child_nodes(node, search_when_nodes, analyzed);
|
|
252
|
+
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
bool search_in_nodes(const pm_node_t* node, void* data) {
|
|
257
|
+
analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
|
|
258
|
+
|
|
259
|
+
if (node->type == PM_IN_NODE) { analyzed->in_node_count++; }
|
|
260
|
+
if (node->type == PM_MATCH_PREDICATE_NODE) { analyzed->in_node_count++; }
|
|
261
|
+
|
|
262
|
+
pm_visit_child_nodes(node, search_in_nodes, analyzed);
|
|
263
|
+
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
bool search_unexpected_elsif_nodes(analyzed_ruby_T* analyzed) {
|
|
246
268
|
if (has_error_message(analyzed, "unexpected 'elsif', ignoring it")) {
|
|
247
269
|
analyzed->elsif_node_count++;
|
|
248
270
|
return true;
|
|
@@ -251,7 +273,7 @@ bool search_elsif_nodes(analyzed_ruby_T* analyzed) {
|
|
|
251
273
|
return false;
|
|
252
274
|
}
|
|
253
275
|
|
|
254
|
-
bool
|
|
276
|
+
bool search_unexpected_else_nodes(analyzed_ruby_T* analyzed) {
|
|
255
277
|
if (has_error_message(analyzed, "unexpected 'else', ignoring it")) {
|
|
256
278
|
analyzed->else_node_count++;
|
|
257
279
|
return true;
|
|
@@ -260,7 +282,7 @@ bool search_else_nodes(analyzed_ruby_T* analyzed) {
|
|
|
260
282
|
return false;
|
|
261
283
|
}
|
|
262
284
|
|
|
263
|
-
bool
|
|
285
|
+
bool search_unexpected_end_nodes(analyzed_ruby_T* analyzed) {
|
|
264
286
|
if (has_error_message(analyzed, "unexpected 'end', ignoring it")) {
|
|
265
287
|
if (has_error_message(analyzed, "unexpected '=', ignoring it")) {
|
|
266
288
|
// `=end`
|
|
@@ -274,7 +296,7 @@ bool search_end_nodes(analyzed_ruby_T* analyzed) {
|
|
|
274
296
|
return false;
|
|
275
297
|
}
|
|
276
298
|
|
|
277
|
-
bool
|
|
299
|
+
bool search_unexpected_block_closing_nodes(analyzed_ruby_T* analyzed) {
|
|
278
300
|
if (has_error_message(analyzed, "unexpected '}', ignoring it")) {
|
|
279
301
|
analyzed->block_closing_count++;
|
|
280
302
|
return true;
|
|
@@ -283,7 +305,7 @@ bool search_block_closing_nodes(analyzed_ruby_T* analyzed) {
|
|
|
283
305
|
return false;
|
|
284
306
|
}
|
|
285
307
|
|
|
286
|
-
bool
|
|
308
|
+
bool search_unexpected_when_nodes(analyzed_ruby_T* analyzed) {
|
|
287
309
|
if (has_error_message(analyzed, "unexpected 'when', ignoring it")) {
|
|
288
310
|
analyzed->when_node_count++;
|
|
289
311
|
return true;
|
|
@@ -292,7 +314,7 @@ bool search_when_nodes(analyzed_ruby_T* analyzed) {
|
|
|
292
314
|
return false;
|
|
293
315
|
}
|
|
294
316
|
|
|
295
|
-
bool
|
|
317
|
+
bool search_unexpected_in_nodes(analyzed_ruby_T* analyzed) {
|
|
296
318
|
if (has_error_message(analyzed, "unexpected 'in', ignoring it")) {
|
|
297
319
|
analyzed->in_node_count++;
|
|
298
320
|
return true;
|
|
@@ -301,7 +323,7 @@ bool search_in_nodes(analyzed_ruby_T* analyzed) {
|
|
|
301
323
|
return false;
|
|
302
324
|
}
|
|
303
325
|
|
|
304
|
-
bool
|
|
326
|
+
bool search_unexpected_rescue_nodes(analyzed_ruby_T* analyzed) {
|
|
305
327
|
if (has_error_message(analyzed, "unexpected 'rescue', ignoring it")) {
|
|
306
328
|
analyzed->rescue_node_count++;
|
|
307
329
|
return true;
|
|
@@ -310,7 +332,7 @@ bool search_rescue_nodes(analyzed_ruby_T* analyzed) {
|
|
|
310
332
|
return false;
|
|
311
333
|
}
|
|
312
334
|
|
|
313
|
-
bool
|
|
335
|
+
bool search_unexpected_ensure_nodes(analyzed_ruby_T* analyzed) {
|
|
314
336
|
if (has_error_message(analyzed, "unexpected 'ensure', ignoring it")) {
|
|
315
337
|
analyzed->ensure_node_count++;
|
|
316
338
|
return true;
|
|
@@ -468,6 +490,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
|
|
|
468
490
|
if (has_opening && !has_valid_block_closing(block_node->opening_loc, block_node->closing_loc)) {
|
|
469
491
|
analyzed->unclosed_control_flow_count++;
|
|
470
492
|
}
|
|
493
|
+
|
|
471
494
|
break;
|
|
472
495
|
}
|
|
473
496
|
|
data/src/errors.c
CHANGED
|
@@ -511,6 +511,20 @@ void append_erb_multiple_blocks_in_tag_error(position_T start, position_T end, h
|
|
|
511
511
|
hb_array_append(errors, erb_multiple_blocks_in_tag_error_init(start, end));
|
|
512
512
|
}
|
|
513
513
|
|
|
514
|
+
ERB_CASE_WITH_CONDITIONS_ERROR_T* erb_case_with_conditions_error_init(position_T start, position_T end) {
|
|
515
|
+
ERB_CASE_WITH_CONDITIONS_ERROR_T* erb_case_with_conditions_error = malloc(sizeof(ERB_CASE_WITH_CONDITIONS_ERROR_T));
|
|
516
|
+
|
|
517
|
+
error_init(&erb_case_with_conditions_error->base, ERB_CASE_WITH_CONDITIONS_ERROR, start, end);
|
|
518
|
+
|
|
519
|
+
erb_case_with_conditions_error->base.message = herb_strdup("A `case` statement with `when`/`in` in a single ERB tag cannot be formatted. Use separate tags for `case` and its conditions.");
|
|
520
|
+
|
|
521
|
+
return erb_case_with_conditions_error;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
void append_erb_case_with_conditions_error(position_T start, position_T end, hb_array_T* errors) {
|
|
525
|
+
hb_array_append(errors, erb_case_with_conditions_error_init(start, end));
|
|
526
|
+
}
|
|
527
|
+
|
|
514
528
|
const char* error_type_to_string(ERROR_T* error) {
|
|
515
529
|
switch (error->type) {
|
|
516
530
|
case UNEXPECTED_ERROR: return "UNEXPECTED_ERROR";
|
|
@@ -525,6 +539,7 @@ const char* error_type_to_string(ERROR_T* error) {
|
|
|
525
539
|
case ERB_CONTROL_FLOW_SCOPE_ERROR: return "ERB_CONTROL_FLOW_SCOPE_ERROR";
|
|
526
540
|
case MISSINGERB_END_TAG_ERROR: return "MISSINGERB_END_TAG_ERROR";
|
|
527
541
|
case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: return "ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR";
|
|
542
|
+
case ERB_CASE_WITH_CONDITIONS_ERROR: return "ERB_CASE_WITH_CONDITIONS_ERROR";
|
|
528
543
|
}
|
|
529
544
|
|
|
530
545
|
return "Unknown error_type_T";
|
|
@@ -544,6 +559,7 @@ const char* error_human_type(ERROR_T* error) {
|
|
|
544
559
|
case ERB_CONTROL_FLOW_SCOPE_ERROR: return "ERBControlFlowScopeError";
|
|
545
560
|
case MISSINGERB_END_TAG_ERROR: return "MissingERBEndTagError";
|
|
546
561
|
case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: return "ERBMultipleBlocksInTagError";
|
|
562
|
+
case ERB_CASE_WITH_CONDITIONS_ERROR: return "ERBCaseWithConditionsError";
|
|
547
563
|
}
|
|
548
564
|
|
|
549
565
|
return "Unknown error_type_T";
|
|
@@ -638,6 +654,12 @@ static void error_free_erb_multiple_blocks_in_tag_error(ERB_MULTIPLE_BLOCKS_IN_T
|
|
|
638
654
|
error_free_base_error(&erb_multiple_blocks_in_tag_error->base);
|
|
639
655
|
}
|
|
640
656
|
|
|
657
|
+
static void error_free_erb_case_with_conditions_error(ERB_CASE_WITH_CONDITIONS_ERROR_T* erb_case_with_conditions_error) {
|
|
658
|
+
/* no ERB_CASE_WITH_CONDITIONS_ERROR_T specific fields to free up */
|
|
659
|
+
|
|
660
|
+
error_free_base_error(&erb_case_with_conditions_error->base);
|
|
661
|
+
}
|
|
662
|
+
|
|
641
663
|
void error_free(ERROR_T* error) {
|
|
642
664
|
if (!error) { return; }
|
|
643
665
|
|
|
@@ -654,6 +676,7 @@ void error_free(ERROR_T* error) {
|
|
|
654
676
|
case ERB_CONTROL_FLOW_SCOPE_ERROR: error_free_erb_control_flow_scope_error((ERB_CONTROL_FLOW_SCOPE_ERROR_T*) error); break;
|
|
655
677
|
case MISSINGERB_END_TAG_ERROR: error_free_missingerb_end_tag_error((MISSINGERB_END_TAG_ERROR_T*) error); break;
|
|
656
678
|
case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: error_free_erb_multiple_blocks_in_tag_error((ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T*) error); break;
|
|
679
|
+
case ERB_CASE_WITH_CONDITIONS_ERROR: error_free_erb_case_with_conditions_error((ERB_CASE_WITH_CONDITIONS_ERROR_T*) error); break;
|
|
657
680
|
}
|
|
658
681
|
}
|
|
659
682
|
|
|
@@ -877,6 +900,19 @@ static void error_pretty_print_erb_multiple_blocks_in_tag_error(ERB_MULTIPLE_BLO
|
|
|
877
900
|
pretty_print_quoted_property(hb_string("message"), hb_string(error->base.message), indent, relative_indent, true, buffer);
|
|
878
901
|
}
|
|
879
902
|
|
|
903
|
+
static void error_pretty_print_erb_case_with_conditions_error(ERB_CASE_WITH_CONDITIONS_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
|
|
904
|
+
if (!error) { return; }
|
|
905
|
+
|
|
906
|
+
hb_buffer_append(buffer, "@ ");
|
|
907
|
+
hb_buffer_append(buffer, error_human_type((ERROR_T*) error));
|
|
908
|
+
hb_buffer_append(buffer, " ");
|
|
909
|
+
|
|
910
|
+
pretty_print_location(error->base.location, buffer);
|
|
911
|
+
hb_buffer_append(buffer, "\n");
|
|
912
|
+
|
|
913
|
+
pretty_print_quoted_property(hb_string("message"), hb_string(error->base.message), indent, relative_indent, true, buffer);
|
|
914
|
+
}
|
|
915
|
+
|
|
880
916
|
void error_pretty_print(ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
|
|
881
917
|
if (!error) { return; }
|
|
882
918
|
|
|
@@ -893,5 +929,6 @@ void error_pretty_print(ERROR_T* error, const size_t indent, const size_t relati
|
|
|
893
929
|
case ERB_CONTROL_FLOW_SCOPE_ERROR: error_pretty_print_erb_control_flow_scope_error((ERB_CONTROL_FLOW_SCOPE_ERROR_T*) error, indent, relative_indent, buffer); break;
|
|
894
930
|
case MISSINGERB_END_TAG_ERROR: error_pretty_print_missingerb_end_tag_error((MISSINGERB_END_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
|
|
895
931
|
case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: error_pretty_print_erb_multiple_blocks_in_tag_error((ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
|
|
932
|
+
case ERB_CASE_WITH_CONDITIONS_ERROR: error_pretty_print_erb_case_with_conditions_error((ERB_CASE_WITH_CONDITIONS_ERROR_T*) error, indent, relative_indent, buffer); break;
|
|
896
933
|
}
|
|
897
934
|
}
|
data/src/extract.c
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
#include "include/lexer.h"
|
|
4
4
|
#include "include/util/hb_array.h"
|
|
5
5
|
#include "include/util/hb_buffer.h"
|
|
6
|
+
#include "include/util/string.h"
|
|
6
7
|
|
|
8
|
+
#include <assert.h>
|
|
7
9
|
#include <stdlib.h>
|
|
8
10
|
#include <string.h>
|
|
9
11
|
|
|
@@ -22,11 +24,11 @@ void herb_extract_ruby_to_buffer(const char* source, hb_buffer_T* output) {
|
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
case TOKEN_ERB_START: {
|
|
25
|
-
if (
|
|
27
|
+
if (string_equals(token->value, "<%#")) {
|
|
26
28
|
skip_erb_content = true;
|
|
27
29
|
is_comment_tag = true;
|
|
28
|
-
} else if (
|
|
29
|
-
||
|
|
30
|
+
} else if (string_equals(token->value, "<%%") || string_equals(token->value, "<%%=")
|
|
31
|
+
|| string_equals(token->value, "<%graphql")) {
|
|
30
32
|
skip_erb_content = true;
|
|
31
33
|
is_comment_tag = false;
|
|
32
34
|
} else {
|
|
@@ -129,6 +131,7 @@ char* herb_extract(const char* source, const herb_extract_language_T language) {
|
|
|
129
131
|
switch (language) {
|
|
130
132
|
case HERB_EXTRACT_LANGUAGE_RUBY: herb_extract_ruby_to_buffer(source, &output); break;
|
|
131
133
|
case HERB_EXTRACT_LANGUAGE_HTML: herb_extract_html_to_buffer(source, &output); break;
|
|
134
|
+
default: assert(0 && "invalid extract language");
|
|
132
135
|
}
|
|
133
136
|
|
|
134
137
|
return output.value;
|
|
@@ -34,26 +34,29 @@ bool is_brace_block(pm_location_t opening_location);
|
|
|
34
34
|
bool is_closing_brace(pm_location_t location);
|
|
35
35
|
bool has_valid_block_closing(pm_location_t opening_loc, pm_location_t closing_loc);
|
|
36
36
|
|
|
37
|
-
bool
|
|
37
|
+
bool search_begin_nodes(const pm_node_t* node, void* data);
|
|
38
38
|
bool search_block_nodes(const pm_node_t* node, void* data);
|
|
39
|
-
bool search_case_nodes(const pm_node_t* node, void* data);
|
|
40
39
|
bool search_case_match_nodes(const pm_node_t* node, void* data);
|
|
41
|
-
bool
|
|
40
|
+
bool search_case_nodes(const pm_node_t* node, void* data);
|
|
42
41
|
bool search_for_nodes(const pm_node_t* node, void* data);
|
|
43
|
-
bool
|
|
44
|
-
bool
|
|
45
|
-
bool search_unless_nodes(const pm_node_t* node, void* data);
|
|
46
|
-
bool search_elsif_nodes(analyzed_ruby_T* analyzed);
|
|
47
|
-
bool search_else_nodes(analyzed_ruby_T* analyzed);
|
|
48
|
-
bool search_end_nodes(analyzed_ruby_T* analyzed);
|
|
49
|
-
bool search_block_closing_nodes(analyzed_ruby_T* analyzed);
|
|
50
|
-
bool search_when_nodes(analyzed_ruby_T* analyzed);
|
|
51
|
-
bool search_in_nodes(analyzed_ruby_T* analyzed);
|
|
52
|
-
bool search_rescue_nodes(analyzed_ruby_T* analyzed);
|
|
53
|
-
bool search_ensure_nodes(analyzed_ruby_T* analyzed);
|
|
54
|
-
bool search_yield_nodes(const pm_node_t* node, void* data);
|
|
42
|
+
bool search_if_nodes(const pm_node_t* node, void* data);
|
|
43
|
+
bool search_in_nodes(const pm_node_t* node, void* data);
|
|
55
44
|
bool search_then_keywords(const pm_node_t* node, void* data);
|
|
56
45
|
bool search_unclosed_control_flows(const pm_node_t* node, void* data);
|
|
46
|
+
bool search_unless_nodes(const pm_node_t* node, void* data);
|
|
47
|
+
bool search_until_nodes(const pm_node_t* node, void* data);
|
|
48
|
+
bool search_when_nodes(const pm_node_t* node, void* data);
|
|
49
|
+
bool search_while_nodes(const pm_node_t* node, void* data);
|
|
50
|
+
bool search_yield_nodes(const pm_node_t* node, void* data);
|
|
51
|
+
|
|
52
|
+
bool search_unexpected_block_closing_nodes(analyzed_ruby_T* analyzed);
|
|
53
|
+
bool search_unexpected_else_nodes(analyzed_ruby_T* analyzed);
|
|
54
|
+
bool search_unexpected_elsif_nodes(analyzed_ruby_T* analyzed);
|
|
55
|
+
bool search_unexpected_end_nodes(analyzed_ruby_T* analyzed);
|
|
56
|
+
bool search_unexpected_ensure_nodes(analyzed_ruby_T* analyzed);
|
|
57
|
+
bool search_unexpected_in_nodes(analyzed_ruby_T* analyzed);
|
|
58
|
+
bool search_unexpected_rescue_nodes(analyzed_ruby_T* analyzed);
|
|
59
|
+
bool search_unexpected_when_nodes(analyzed_ruby_T* analyzed);
|
|
57
60
|
|
|
58
61
|
void check_erb_node_for_missing_end(const AST_NODE_T* node);
|
|
59
62
|
|
data/src/include/errors.h
CHANGED
|
@@ -24,6 +24,7 @@ typedef enum {
|
|
|
24
24
|
ERB_CONTROL_FLOW_SCOPE_ERROR,
|
|
25
25
|
MISSINGERB_END_TAG_ERROR,
|
|
26
26
|
ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR,
|
|
27
|
+
ERB_CASE_WITH_CONDITIONS_ERROR,
|
|
27
28
|
} error_type_T;
|
|
28
29
|
|
|
29
30
|
typedef struct ERROR_STRUCT {
|
|
@@ -102,6 +103,11 @@ typedef struct {
|
|
|
102
103
|
/* no additional fields */
|
|
103
104
|
} ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T;
|
|
104
105
|
|
|
106
|
+
typedef struct {
|
|
107
|
+
ERROR_T base;
|
|
108
|
+
/* no additional fields */
|
|
109
|
+
} ERB_CASE_WITH_CONDITIONS_ERROR_T;
|
|
110
|
+
|
|
105
111
|
UNEXPECTED_ERROR_T* unexpected_error_init(const char* description, const char* expected, const char* found, position_T start, position_T end);
|
|
106
112
|
void append_unexpected_error(const char* description, const char* expected, const char* found, position_T start, position_T end, hb_array_T* errors);
|
|
107
113
|
UNEXPECTED_TOKEN_ERROR_T* unexpected_token_error_init(token_type_T expected_type, token_T* found, position_T start, position_T end);
|
|
@@ -126,6 +132,8 @@ MISSINGERB_END_TAG_ERROR_T* missingerb_end_tag_error_init(const char* keyword, p
|
|
|
126
132
|
void append_missingerb_end_tag_error(const char* keyword, position_T start, position_T end, hb_array_T* errors);
|
|
127
133
|
ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T* erb_multiple_blocks_in_tag_error_init(position_T start, position_T end);
|
|
128
134
|
void append_erb_multiple_blocks_in_tag_error(position_T start, position_T end, hb_array_T* errors);
|
|
135
|
+
ERB_CASE_WITH_CONDITIONS_ERROR_T* erb_case_with_conditions_error_init(position_T start, position_T end);
|
|
136
|
+
void append_erb_case_with_conditions_error(position_T start, position_T end, hb_array_T* errors);
|
|
129
137
|
|
|
130
138
|
void error_init(ERROR_T* error, error_type_T type, position_T start, position_T end);
|
|
131
139
|
|
data/src/include/version.h
CHANGED
data/src/main.c
CHANGED
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
#include "include/io.h"
|
|
10
10
|
#include "include/ruby_parser.h"
|
|
11
11
|
#include "include/util/hb_buffer.h"
|
|
12
|
+
#include "include/util/string.h"
|
|
12
13
|
|
|
13
14
|
#include <stdio.h>
|
|
15
|
+
#include <stdlib.h>
|
|
14
16
|
#include <string.h>
|
|
15
17
|
#include <time.h>
|
|
16
18
|
|
|
@@ -34,22 +36,22 @@ void print_time_diff(const struct timespec start, const struct timespec end, con
|
|
|
34
36
|
|
|
35
37
|
int main(const int argc, char* argv[]) {
|
|
36
38
|
if (argc < 2) {
|
|
37
|
-
|
|
39
|
+
puts("./herb [command] [options]\n");
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
puts("Herb 🌿 Powerful and seamless HTML-aware ERB parsing and tooling.\n");
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
puts("./herb lex [file] - Lex a file");
|
|
44
|
+
puts("./herb parse [file] - Parse a file");
|
|
45
|
+
puts("./herb ruby [file] - Extract Ruby from a file");
|
|
46
|
+
puts("./herb html [file] - Extract HTML from a file");
|
|
47
|
+
puts("./herb prism [file] - Extract Ruby from a file and parse the Ruby source with Prism");
|
|
46
48
|
|
|
47
|
-
return
|
|
49
|
+
return EXIT_FAILURE;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
if (argc < 3) {
|
|
51
|
-
|
|
52
|
-
return
|
|
53
|
+
puts("Please specify input file.");
|
|
54
|
+
return EXIT_FAILURE;
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
hb_buffer_T output;
|
|
@@ -61,38 +63,20 @@ int main(const int argc, char* argv[]) {
|
|
|
61
63
|
struct timespec start, end;
|
|
62
64
|
clock_gettime(CLOCK_MONOTONIC, &start);
|
|
63
65
|
|
|
64
|
-
if (
|
|
65
|
-
AST_DOCUMENT_NODE_T* root = herb_parse(source, NULL);
|
|
66
|
-
clock_gettime(CLOCK_MONOTONIC, &end);
|
|
67
|
-
|
|
68
|
-
herb_analyze_parse_tree(root, source);
|
|
69
|
-
|
|
70
|
-
ast_pretty_print_node((AST_NODE_T*) root, 0, 0, &output);
|
|
71
|
-
printf("%s\n", output.value);
|
|
72
|
-
|
|
73
|
-
print_time_diff(start, end, "visiting");
|
|
74
|
-
|
|
75
|
-
ast_node_free((AST_NODE_T*) root);
|
|
76
|
-
free(output.value);
|
|
77
|
-
free(source);
|
|
78
|
-
|
|
79
|
-
return 0;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (strcmp(argv[1], "lex") == 0) {
|
|
66
|
+
if (string_equals(argv[1], "lex")) {
|
|
83
67
|
herb_lex_to_buffer(source, &output);
|
|
84
68
|
clock_gettime(CLOCK_MONOTONIC, &end);
|
|
85
69
|
|
|
86
|
-
|
|
70
|
+
puts(output.value);
|
|
87
71
|
print_time_diff(start, end, "lexing");
|
|
88
72
|
|
|
89
73
|
free(output.value);
|
|
90
74
|
free(source);
|
|
91
75
|
|
|
92
|
-
return
|
|
76
|
+
return EXIT_SUCCESS;
|
|
93
77
|
}
|
|
94
78
|
|
|
95
|
-
if (
|
|
79
|
+
if (string_equals(argv[1], "parse")) {
|
|
96
80
|
AST_DOCUMENT_NODE_T* root = herb_parse(source, NULL);
|
|
97
81
|
|
|
98
82
|
herb_analyze_parse_tree(root, source);
|
|
@@ -100,11 +84,11 @@ int main(const int argc, char* argv[]) {
|
|
|
100
84
|
clock_gettime(CLOCK_MONOTONIC, &end);
|
|
101
85
|
|
|
102
86
|
int silent = 0;
|
|
103
|
-
if (argc > 3 &&
|
|
87
|
+
if (argc > 3 && string_equals(argv[3], "--silent")) { silent = 1; }
|
|
104
88
|
|
|
105
89
|
if (!silent) {
|
|
106
90
|
ast_pretty_print_node((AST_NODE_T*) root, 0, 0, &output);
|
|
107
|
-
|
|
91
|
+
puts(output.value);
|
|
108
92
|
|
|
109
93
|
print_time_diff(start, end, "parsing");
|
|
110
94
|
}
|
|
@@ -113,36 +97,36 @@ int main(const int argc, char* argv[]) {
|
|
|
113
97
|
free(output.value);
|
|
114
98
|
free(source);
|
|
115
99
|
|
|
116
|
-
return
|
|
100
|
+
return EXIT_SUCCESS;
|
|
117
101
|
}
|
|
118
102
|
|
|
119
|
-
if (
|
|
103
|
+
if (string_equals(argv[1], "ruby")) {
|
|
120
104
|
herb_extract_ruby_to_buffer(source, &output);
|
|
121
105
|
clock_gettime(CLOCK_MONOTONIC, &end);
|
|
122
106
|
|
|
123
|
-
|
|
107
|
+
puts(output.value);
|
|
124
108
|
print_time_diff(start, end, "extracting Ruby");
|
|
125
109
|
|
|
126
110
|
free(output.value);
|
|
127
111
|
free(source);
|
|
128
112
|
|
|
129
|
-
return
|
|
113
|
+
return EXIT_SUCCESS;
|
|
130
114
|
}
|
|
131
115
|
|
|
132
|
-
if (
|
|
116
|
+
if (string_equals(argv[1], "html")) {
|
|
133
117
|
herb_extract_html_to_buffer(source, &output);
|
|
134
118
|
clock_gettime(CLOCK_MONOTONIC, &end);
|
|
135
119
|
|
|
136
|
-
|
|
120
|
+
puts(output.value);
|
|
137
121
|
print_time_diff(start, end, "extracting HTML");
|
|
138
122
|
|
|
139
123
|
free(output.value);
|
|
140
124
|
free(source);
|
|
141
125
|
|
|
142
|
-
return
|
|
126
|
+
return EXIT_SUCCESS;
|
|
143
127
|
}
|
|
144
128
|
|
|
145
|
-
if (
|
|
129
|
+
if (string_equals(argv[1], "prism")) {
|
|
146
130
|
printf("HTML+ERB File: \n%s\n", source);
|
|
147
131
|
|
|
148
132
|
char* ruby_source = herb_extract(source, HERB_EXTRACT_LANGUAGE_RUBY);
|
|
@@ -150,9 +134,13 @@ int main(const int argc, char* argv[]) {
|
|
|
150
134
|
|
|
151
135
|
herb_parse_ruby_to_stdout(ruby_source);
|
|
152
136
|
|
|
153
|
-
|
|
137
|
+
free(ruby_source);
|
|
138
|
+
free(output.value);
|
|
139
|
+
free(source);
|
|
140
|
+
|
|
141
|
+
return EXIT_SUCCESS;
|
|
154
142
|
}
|
|
155
143
|
|
|
156
144
|
printf("Unknown Command: %s\n", argv[1]);
|
|
157
|
-
return
|
|
145
|
+
return EXIT_FAILURE;
|
|
158
146
|
}
|
data/src/parser.c
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
#include "include/util/hb_array.h"
|
|
13
13
|
#include "include/util/hb_buffer.h"
|
|
14
14
|
#include "include/util/hb_string.h"
|
|
15
|
+
#include "include/util/string.h"
|
|
15
16
|
#include "include/visitor.h"
|
|
16
17
|
|
|
17
18
|
#include <stdio.h>
|
|
@@ -346,7 +347,7 @@ static AST_HTML_ATTRIBUTE_VALUE_NODE_T* parser_parse_quoted_html_attribute_value
|
|
|
346
347
|
while (!token_is(parser, TOKEN_EOF)
|
|
347
348
|
&& !(
|
|
348
349
|
token_is(parser, TOKEN_QUOTE) && opening_quote != NULL
|
|
349
|
-
&&
|
|
350
|
+
&& string_equals(parser->current_token->value, opening_quote->value)
|
|
350
351
|
)) {
|
|
351
352
|
if (token_is(parser, TOKEN_ERB_START)) {
|
|
352
353
|
parser_append_literal_node_from_buffer(parser, &buffer, children, start);
|
|
@@ -364,7 +365,7 @@ static AST_HTML_ATTRIBUTE_VALUE_NODE_T* parser_parse_quoted_html_attribute_value
|
|
|
364
365
|
token_T* next_token = lexer_next_token(parser->lexer);
|
|
365
366
|
|
|
366
367
|
if (next_token && next_token->type == TOKEN_QUOTE && opening_quote != NULL
|
|
367
|
-
&&
|
|
368
|
+
&& string_equals(next_token->value, opening_quote->value)) {
|
|
368
369
|
hb_buffer_append(&buffer, parser->current_token->value);
|
|
369
370
|
hb_buffer_append(&buffer, next_token->value);
|
|
370
371
|
|
|
@@ -387,7 +388,7 @@ static AST_HTML_ATTRIBUTE_VALUE_NODE_T* parser_parse_quoted_html_attribute_value
|
|
|
387
388
|
}
|
|
388
389
|
|
|
389
390
|
if (token_is(parser, TOKEN_QUOTE) && opening_quote != NULL
|
|
390
|
-
&&
|
|
391
|
+
&& string_equals(parser->current_token->value, opening_quote->value)) {
|
|
391
392
|
lexer_state_snapshot_T saved_state = lexer_save_state(parser->lexer);
|
|
392
393
|
|
|
393
394
|
token_T* potential_closing = parser->current_token;
|
|
@@ -415,7 +416,7 @@ static AST_HTML_ATTRIBUTE_VALUE_NODE_T* parser_parse_quoted_html_attribute_value
|
|
|
415
416
|
while (!token_is(parser, TOKEN_EOF)
|
|
416
417
|
&& !(
|
|
417
418
|
token_is(parser, TOKEN_QUOTE) && opening_quote != NULL
|
|
418
|
-
&&
|
|
419
|
+
&& string_equals(parser->current_token->value, opening_quote->value)
|
|
419
420
|
)) {
|
|
420
421
|
if (token_is(parser, TOKEN_ERB_START)) {
|
|
421
422
|
parser_append_literal_node_from_buffer(parser, &buffer, children, start);
|
|
@@ -445,7 +446,7 @@ static AST_HTML_ATTRIBUTE_VALUE_NODE_T* parser_parse_quoted_html_attribute_value
|
|
|
445
446
|
|
|
446
447
|
token_T* closing_quote = parser_consume_expected(parser, TOKEN_QUOTE, errors);
|
|
447
448
|
|
|
448
|
-
if (opening_quote != NULL && closing_quote != NULL &&
|
|
449
|
+
if (opening_quote != NULL && closing_quote != NULL && !string_equals(opening_quote->value, closing_quote->value)) {
|
|
449
450
|
append_quotes_mismatch_error(
|
|
450
451
|
opening_quote,
|
|
451
452
|
closing_quote,
|
data/vendor/prism/config.yml
CHANGED
|
@@ -1269,17 +1269,17 @@ nodes:
|
|
|
1269
1269
|
- name: opening_loc
|
|
1270
1270
|
type: location
|
|
1271
1271
|
comment: |
|
|
1272
|
-
Represents the location of the opening
|
|
1272
|
+
Represents the location of the opening `{` or `do`.
|
|
1273
1273
|
|
|
1274
1274
|
[1, 2, 3].each { |i| puts x }
|
|
1275
|
-
|
|
1275
|
+
^
|
|
1276
1276
|
- name: closing_loc
|
|
1277
1277
|
type: location
|
|
1278
1278
|
comment: |
|
|
1279
|
-
Represents the location of the closing
|
|
1279
|
+
Represents the location of the closing `}` or `end`.
|
|
1280
1280
|
|
|
1281
1281
|
[1, 2, 3].each { |i| puts x }
|
|
1282
|
-
|
|
1282
|
+
^
|
|
1283
1283
|
comment: |
|
|
1284
1284
|
Represents a block of ruby code.
|
|
1285
1285
|
|
|
@@ -1826,20 +1826,20 @@ typedef struct pm_block_node {
|
|
|
1826
1826
|
/**
|
|
1827
1827
|
* BlockNode#opening_loc
|
|
1828
1828
|
*
|
|
1829
|
-
* Represents the location of the opening
|
|
1829
|
+
* Represents the location of the opening `{` or `do`.
|
|
1830
1830
|
*
|
|
1831
1831
|
* [1, 2, 3].each { |i| puts x }
|
|
1832
|
-
*
|
|
1832
|
+
* ^
|
|
1833
1833
|
*/
|
|
1834
1834
|
pm_location_t opening_loc;
|
|
1835
1835
|
|
|
1836
1836
|
/**
|
|
1837
1837
|
* BlockNode#closing_loc
|
|
1838
1838
|
*
|
|
1839
|
-
* Represents the location of the closing
|
|
1839
|
+
* Represents the location of the closing `}` or `end`.
|
|
1840
1840
|
*
|
|
1841
1841
|
* [1, 2, 3].each { |i| puts x }
|
|
1842
|
-
*
|
|
1842
|
+
* ^
|
|
1843
1843
|
*/
|
|
1844
1844
|
pm_location_t closing_loc;
|
|
1845
1845
|
} pm_block_node_t;
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
/**
|
|
15
15
|
* The minor version of the Prism library as an int.
|
|
16
16
|
*/
|
|
17
|
-
#define PRISM_VERSION_MINOR
|
|
17
|
+
#define PRISM_VERSION_MINOR 9
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* The patch version of the Prism library as an int.
|
|
@@ -24,6 +24,6 @@
|
|
|
24
24
|
/**
|
|
25
25
|
* The version of the Prism library as a constant string.
|
|
26
26
|
*/
|
|
27
|
-
#define PRISM_VERSION "1.
|
|
27
|
+
#define PRISM_VERSION "1.9.0"
|
|
28
28
|
|
|
29
29
|
#endif
|
data/vendor/prism/src/prism.c
CHANGED
|
@@ -12438,7 +12438,7 @@ expect1_opening(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t di
|
|
|
12438
12438
|
|
|
12439
12439
|
pm_parser_err(parser, opening->start, opening->end, diag_id);
|
|
12440
12440
|
|
|
12441
|
-
parser->previous.start =
|
|
12441
|
+
parser->previous.start = parser->previous.end;
|
|
12442
12442
|
parser->previous.type = PM_TOKEN_MISSING;
|
|
12443
12443
|
}
|
|
12444
12444
|
|
|
@@ -101,7 +101,7 @@ public class Loader {
|
|
|
101
101
|
expect((byte) 'M', "incorrect prism header");
|
|
102
102
|
|
|
103
103
|
expect((byte) 1, "prism major version does not match");
|
|
104
|
-
expect((byte)
|
|
104
|
+
expect((byte) 9, "prism minor version does not match");
|
|
105
105
|
expect((byte) 0, "prism patch version does not match");
|
|
106
106
|
|
|
107
107
|
expect((byte) 1, "Loader.java requires no location fields in the serialized output");
|
|
@@ -183,25 +183,13 @@ module Prism
|
|
|
183
183
|
def tunnel(line, column)
|
|
184
184
|
queue = [self] #: Array[Prism::node]
|
|
185
185
|
result = [] #: Array[Prism::node]
|
|
186
|
+
offset = source.byte_offset(line, column)
|
|
186
187
|
|
|
187
188
|
while (node = queue.shift)
|
|
188
189
|
result << node
|
|
189
190
|
|
|
190
191
|
node.each_child_node do |child_node|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
start_line = child_location.start_line
|
|
194
|
-
end_line = child_location.end_line
|
|
195
|
-
|
|
196
|
-
if start_line == end_line
|
|
197
|
-
if line == start_line && column >= child_location.start_column && column < child_location.end_column
|
|
198
|
-
queue << child_node
|
|
199
|
-
break
|
|
200
|
-
end
|
|
201
|
-
elsif (line == start_line && column >= child_location.start_column) || (line == end_line && column < child_location.end_column)
|
|
202
|
-
queue << child_node
|
|
203
|
-
break
|
|
204
|
-
elsif line > start_line && line < end_line
|
|
192
|
+
if child_node.start_offset <= offset && offset < child_node.end_offset
|
|
205
193
|
queue << child_node
|
|
206
194
|
break
|
|
207
195
|
end
|
|
@@ -212,7 +200,7 @@ module Prism
|
|
|
212
200
|
end
|
|
213
201
|
|
|
214
202
|
# Returns the first node that matches the given block when visited in a
|
|
215
|
-
#
|
|
203
|
+
# breadth-first search. This is useful for finding a node that matches a
|
|
216
204
|
# particular condition.
|
|
217
205
|
#
|
|
218
206
|
# node.breadth_first_search { |node| node.node_id == node_id }
|
|
@@ -227,6 +215,26 @@ module Prism
|
|
|
227
215
|
|
|
228
216
|
nil
|
|
229
217
|
end
|
|
218
|
+
alias find breadth_first_search
|
|
219
|
+
|
|
220
|
+
# Returns all of the nodes that match the given block when visited in a
|
|
221
|
+
# breadth-first search. This is useful for finding all nodes that match a
|
|
222
|
+
# particular condition.
|
|
223
|
+
#
|
|
224
|
+
# node.breadth_first_search_all { |node| node.is_a?(Prism::CallNode) }
|
|
225
|
+
#
|
|
226
|
+
def breadth_first_search_all(&block)
|
|
227
|
+
queue = [self] #: Array[Prism::node]
|
|
228
|
+
results = [] #: Array[Prism::node]
|
|
229
|
+
|
|
230
|
+
while (node = queue.shift)
|
|
231
|
+
results << node if yield node
|
|
232
|
+
queue.concat(node.compact_child_nodes)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
results
|
|
236
|
+
end
|
|
237
|
+
alias find_all breadth_first_search_all
|
|
230
238
|
|
|
231
239
|
# Returns a list of the fields that exist for this node class. Fields
|
|
232
240
|
# describe the structure of the node. This kind of reflection is useful for
|
|
@@ -49,6 +49,9 @@ class Prism::Node
|
|
|
49
49
|
sig { params(block: T.proc.params(node: Prism::Node).returns(T::Boolean)).returns(T.nilable(Prism::Node)) }
|
|
50
50
|
def breadth_first_search(&block); end
|
|
51
51
|
|
|
52
|
+
sig { params(block: T.proc.params(node: Prism::Node).returns(T::Boolean)).returns(T::Array[Prism::Node]) }
|
|
53
|
+
def breadth_first_search_all(&block); end
|
|
54
|
+
|
|
52
55
|
sig { abstract.params(visitor: Prism::Visitor).returns(T.untyped) }
|
|
53
56
|
def accept(visitor); end
|
|
54
57
|
|
|
@@ -25,6 +25,9 @@ module Prism
|
|
|
25
25
|
def to_dot: () -> String
|
|
26
26
|
def tunnel: (Integer line, Integer column) -> Array[Prism::node]
|
|
27
27
|
def breadth_first_search: () { (Prism::node) -> bool } -> Prism::node?
|
|
28
|
+
alias find breadth_first_search
|
|
29
|
+
def breadth_first_search_all: () { (Prism::node) -> bool } -> Array[Prism::node]
|
|
30
|
+
alias find_all breadth_first_search_all
|
|
28
31
|
def newline!: (Array[untyped]) -> void
|
|
29
32
|
|
|
30
33
|
def save: (_Repository repository) -> void
|
|
@@ -23,15 +23,16 @@ module Prism
|
|
|
23
23
|
|
|
24
24
|
def self.<%= method %>: (
|
|
25
25
|
String source,
|
|
26
|
+
?command_line: String,
|
|
26
27
|
?encoding: Encoding | false,
|
|
27
28
|
?filepath: String,
|
|
28
29
|
?freeze: bool,
|
|
29
30
|
?frozen_string_literal: bool,
|
|
30
31
|
?line: Integer,
|
|
31
32
|
?main_script: bool,
|
|
32
|
-
?
|
|
33
|
+
?partial_script: bool,
|
|
33
34
|
?scopes: Array[Array[Symbol]],
|
|
34
|
-
?
|
|
35
|
+
?version: String
|
|
35
36
|
) -> <%= return_type %>
|
|
36
37
|
<%- end -%>
|
|
37
38
|
|
|
@@ -41,10 +42,6 @@ module Prism
|
|
|
41
42
|
?bool freeze
|
|
42
43
|
) -> ParseResult
|
|
43
44
|
|
|
44
|
-
def self.lex_ripper: (
|
|
45
|
-
String source
|
|
46
|
-
) -> Array[[[Integer, Integer], Symbol, String, untyped]]
|
|
47
|
-
|
|
48
45
|
# Methods taking a path to a Ruby file:
|
|
49
46
|
<%-
|
|
50
47
|
{
|
|
@@ -61,14 +58,15 @@ module Prism
|
|
|
61
58
|
|
|
62
59
|
def self.<%= method %>: (
|
|
63
60
|
String filepath,
|
|
61
|
+
?command_line: String,
|
|
64
62
|
?encoding: Encoding | false,
|
|
65
63
|
?freeze: bool,
|
|
66
64
|
?frozen_string_literal: bool,
|
|
67
65
|
?line: Integer,
|
|
68
66
|
?main_script: bool,
|
|
69
|
-
?
|
|
67
|
+
?partial_script: bool,
|
|
70
68
|
?scopes: Array[Array[Symbol]],
|
|
71
|
-
?
|
|
69
|
+
?version: String
|
|
72
70
|
) -> <%= return_type %>
|
|
73
71
|
<%- end -%>
|
|
74
72
|
|
|
@@ -78,15 +76,16 @@ module Prism
|
|
|
78
76
|
|
|
79
77
|
def self.parse_stream: (
|
|
80
78
|
_Stream stream,
|
|
79
|
+
?command_line: String,
|
|
81
80
|
?encoding: Encoding | false,
|
|
82
81
|
?filepath: String,
|
|
83
82
|
?freeze: bool,
|
|
84
83
|
?frozen_string_literal: bool,
|
|
85
84
|
?line: Integer,
|
|
86
85
|
?main_script: bool,
|
|
87
|
-
?
|
|
86
|
+
?partial_script: bool,
|
|
88
87
|
?scopes: Array[Array[Symbol]],
|
|
89
|
-
?
|
|
88
|
+
?version: String
|
|
90
89
|
) -> ParseResult
|
|
91
90
|
|
|
92
91
|
def self.scope: (?locals: Array[Symbol], ?forwarding: Array[Symbol]) -> Scope
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: herb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Marco Roth
|
|
@@ -148,6 +148,7 @@ files:
|
|
|
148
148
|
- src/include/util/hb_narray.h
|
|
149
149
|
- src/include/util/hb_string.h
|
|
150
150
|
- src/include/util/hb_system.h
|
|
151
|
+
- src/include/util/string.h
|
|
151
152
|
- src/include/version.h
|
|
152
153
|
- src/include/visitor.h
|
|
153
154
|
- src/io.c
|