herb 0.4.3 → 0.5.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 +4 -4
- data/README.md +1 -1
- data/ext/herb/error_helpers.c +1 -1
- data/ext/herb/error_helpers.h +1 -1
- data/ext/herb/nodes.c +2 -2
- data/ext/herb/nodes.h +1 -1
- data/lib/herb/ast/nodes.rb +1 -1
- data/lib/herb/errors.rb +1 -1
- data/lib/herb/version.rb +1 -1
- data/lib/herb/visitor.rb +1 -1
- data/sig/serialized_ast_errors.rbs +1 -1
- data/sig/serialized_ast_nodes.rbs +1 -1
- data/src/analyze.c +2 -1
- data/src/ast_nodes.c +1 -1
- data/src/ast_pretty_print.c +1 -1
- data/src/errors.c +1 -1
- data/src/include/ast_nodes.h +1 -1
- data/src/include/ast_pretty_print.h +1 -1
- data/src/include/errors.h +1 -1
- data/src/include/parser.h +12 -0
- data/src/include/parser_helpers.h +9 -0
- data/src/include/token_struct.h +1 -0
- data/src/include/version.h +1 -1
- data/src/lexer.c +1 -0
- data/src/parser.c +121 -1
- data/src/parser_helpers.c +46 -0
- data/src/token.c +1 -0
- data/src/visitor.c +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: afe14304b15d889956676e86746b1c644874c838107f2969850f2d6c8d52f02d
|
4
|
+
data.tar.gz: b9fe773d58e2a83c28f6a226c6aea6de1b8ce6dc372c8fb24552c75b4ace74f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fcb85bd4f29941b854c6fc2dba8b2a7d0795702e4303d3401d8df475c66a27f432b0020fc67f5413e7ce5039ff0d2b42c5195afd0109d7f8a4448939440ebc06
|
7
|
+
data.tar.gz: 232b1023973cf69349cd8b4a5aa9dff3ab21e0797945f9a54a748a2d9beced26e557b476accb148dbe933f08263c38a6843a8069acf5ad1b647ebf02dfa501c6
|
data/README.md
CHANGED
@@ -37,7 +37,7 @@ Herb provides a complete ecosystem of HTML+ERB tooling, designed to simplify and
|
|
37
37
|
Automatic, consistent formatting for HTML+ERB files, reducing manual styling and enforcing a standard across projects. Currently in experimental preview - use with caution on version-controlled files.
|
38
38
|
|
39
39
|
- **Herb Linter** ([available now](https://herb-tools.dev/projects/linter)):
|
40
|
-
Static analysis for your HTML+ERB templates to enforce best practices and quickly identify common mistakes with
|
40
|
+
Static analysis for your HTML+ERB templates to enforce best practices and quickly identify common mistakes with plenty of rules.
|
41
41
|
|
42
42
|
You can use Herb programmatically in **Ruby**, as well as in **JavaScript** via Node.js, WebAssembly, or directly in browsers.
|
43
43
|
|
data/ext/herb/error_helpers.c
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
2
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release-
|
2
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/ext/herb/error_helpers.c.erb
|
3
3
|
|
4
4
|
#include <ruby.h>
|
5
5
|
|
data/ext/herb/error_helpers.h
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
2
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release-
|
2
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/ext/herb/error_helpers.h.erb
|
3
3
|
|
4
4
|
#ifndef HERB_EXTENSION_ERROR_HELPERS_H
|
5
5
|
#define HERB_EXTENSION_ERROR_HELPERS_H
|
data/ext/herb/nodes.c
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
2
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release-
|
2
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/ext/herb/nodes.c.erb
|
3
3
|
|
4
4
|
#include <ruby.h>
|
5
5
|
|
@@ -415,7 +415,7 @@ static VALUE rb_erb_content_node_from_c_struct(AST_ERB_CONTENT_NODE_T* erb_conte
|
|
415
415
|
VALUE erb_content_node_tag_opening = rb_token_from_c_struct(erb_content_node->tag_opening);
|
416
416
|
VALUE erb_content_node_content = rb_token_from_c_struct(erb_content_node->content);
|
417
417
|
VALUE erb_content_node_tag_closing = rb_token_from_c_struct(erb_content_node->tag_closing);
|
418
|
-
/* #<Herb::Template::AnalyzedRubyField:
|
418
|
+
/* #<Herb::Template::AnalyzedRubyField:0x00007ffffed68688 @name="analyzed_ruby", @options={kind: nil}> */
|
419
419
|
VALUE erb_content_node_analyzed_ruby = Qnil;
|
420
420
|
VALUE erb_content_node_parsed = (erb_content_node->parsed) ? Qtrue : Qfalse;
|
421
421
|
VALUE erb_content_node_valid = (erb_content_node->valid) ? Qtrue : Qfalse;
|
data/ext/herb/nodes.h
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
2
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release-
|
2
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/ext/herb/nodes.h.erb
|
3
3
|
|
4
4
|
#ifndef HERB_EXTENSION_NODES_H
|
5
5
|
#define HERB_EXTENSION_NODES_H
|
data/lib/herb/ast/nodes.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# typed: true
|
3
3
|
|
4
4
|
# NOTE: This file is generated by the templates/template.rb script and should not be
|
5
|
-
# modified manually. See /Users/marcoroth/Development/herb-release-
|
5
|
+
# modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/lib/herb/ast/nodes.rb.erb
|
6
6
|
|
7
7
|
module Herb
|
8
8
|
module AST
|
data/lib/herb/errors.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# typed: true
|
3
3
|
|
4
4
|
# NOTE: This file is generated by the templates/template.rb script and should not be
|
5
|
-
# modified manually. See /Users/marcoroth/Development/herb-release-
|
5
|
+
# modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/lib/herb/errors.rb.erb
|
6
6
|
|
7
7
|
module Herb
|
8
8
|
module Errors
|
data/lib/herb/version.rb
CHANGED
data/lib/herb/visitor.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# typed: true
|
3
3
|
|
4
4
|
# NOTE: This file is generated by the templates/template.rb script and should not be
|
5
|
-
# modified manually. See /Users/marcoroth/Development/herb-release-
|
5
|
+
# modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/lib/herb/visitor.rb.erb
|
6
6
|
|
7
7
|
module Herb
|
8
8
|
class Visitor
|
@@ -2,7 +2,7 @@
|
|
2
2
|
# typed: true
|
3
3
|
|
4
4
|
# NOTE: This file is generated by the templates/template.rb script and should not be
|
5
|
-
# modified manually. See /Users/marcoroth/Development/herb-release-
|
5
|
+
# modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/sig/serialized_ast_errors.rbs.erb
|
6
6
|
|
7
7
|
module Herb
|
8
8
|
type serialized_unexpected_error = serialized_error & {
|
@@ -2,7 +2,7 @@
|
|
2
2
|
# typed: true
|
3
3
|
|
4
4
|
# NOTE: This file is generated by the templates/template.rb script and should not be
|
5
|
-
# modified manually. See /Users/marcoroth/Development/herb-release-
|
5
|
+
# modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/sig/serialized_ast_nodes.rbs.erb
|
6
6
|
|
7
7
|
module Herb
|
8
8
|
type serialized_document_node = serialized_node & {
|
data/src/analyze.c
CHANGED
@@ -50,7 +50,8 @@ static bool analyze_erb_content(const AST_NODE_T* node, void* data) {
|
|
50
50
|
AST_ERB_CONTENT_NODE_T* erb_content_node = (AST_ERB_CONTENT_NODE_T*) node;
|
51
51
|
|
52
52
|
const char* opening = erb_content_node->tag_opening->value;
|
53
|
-
|
53
|
+
|
54
|
+
if (strcmp(opening, "<%%") != 0 && strcmp(opening, "<%%=") != 0 && strcmp(opening, "<%#") != 0) {
|
54
55
|
analyzed_ruby_T* analyzed = herb_analyze_ruby(erb_content_node->content->value);
|
55
56
|
|
56
57
|
if (false) { pretty_print_analyed_ruby(analyzed, erb_content_node->content->value); }
|
data/src/ast_nodes.c
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
2
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release-
|
2
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/src/ast_nodes.c.erb
|
3
3
|
|
4
4
|
#include <stdio.h>
|
5
5
|
#include <stdbool.h>
|
data/src/ast_pretty_print.c
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
2
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release-
|
2
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/src/ast_pretty_print.c.erb
|
3
3
|
|
4
4
|
#include "include/ast_node.h"
|
5
5
|
#include "include/ast_nodes.h"
|
data/src/errors.c
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
2
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release-
|
2
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/src/errors.c.erb
|
3
3
|
|
4
4
|
#include "include/array.h"
|
5
5
|
#include "include/errors.h"
|
data/src/include/ast_nodes.h
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
2
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release-
|
2
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/src/include/ast_nodes.h.erb
|
3
3
|
|
4
4
|
#ifndef HERB_AST_NODES_H
|
5
5
|
#define HERB_AST_NODES_H
|
@@ -1,5 +1,5 @@
|
|
1
1
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
2
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release-
|
2
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/src/include/ast_pretty_print.h.erb
|
3
3
|
|
4
4
|
#ifndef HERB_AST_PRETTY_PRINT_H
|
5
5
|
#define HERB_AST_PRETTY_PRINT_H
|
data/src/include/errors.h
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
2
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release-
|
2
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/src/include/errors.h.erb
|
3
3
|
|
4
4
|
#ifndef HERB_ERRORS_H
|
5
5
|
#define HERB_ERRORS_H
|
data/src/include/parser.h
CHANGED
@@ -5,10 +5,22 @@
|
|
5
5
|
#include "ast_node.h"
|
6
6
|
#include "lexer.h"
|
7
7
|
|
8
|
+
typedef enum {
|
9
|
+
FOREIGN_CONTENT_UNKNOWN = 0,
|
10
|
+
FOREIGN_CONTENT_SCRIPT,
|
11
|
+
FOREIGN_CONTENT_STYLE,
|
12
|
+
// FOREIGN_CONTENT_RUBY,
|
13
|
+
// FOREIGN_CONTENT_TEMPLATE
|
14
|
+
} foreign_content_type_T;
|
15
|
+
|
16
|
+
typedef enum { PARSER_STATE_DATA, PARSER_STATE_FOREIGN_CONTENT } parser_state_T;
|
17
|
+
|
8
18
|
typedef struct PARSER_STRUCT {
|
9
19
|
lexer_T* lexer;
|
10
20
|
token_T* current_token;
|
11
21
|
array_T* open_tags_stack;
|
22
|
+
parser_state_T state;
|
23
|
+
foreign_content_type_T foreign_content_type;
|
12
24
|
} parser_T;
|
13
25
|
|
14
26
|
parser_T* parser_init(lexer_T* lexer);
|
@@ -24,6 +24,15 @@ void parser_append_literal_node_from_buffer(
|
|
24
24
|
|
25
25
|
bool parser_in_svg_context(const parser_T* parser);
|
26
26
|
|
27
|
+
foreign_content_type_T parser_get_foreign_content_type(const char* tag_name);
|
28
|
+
bool parser_is_foreign_content_tag(const char* tag_name);
|
29
|
+
const char* parser_get_foreign_content_closing_tag(foreign_content_type_T type);
|
30
|
+
|
31
|
+
void parser_enter_foreign_content(parser_T* parser, foreign_content_type_T type);
|
32
|
+
void parser_exit_foreign_content(parser_T* parser);
|
33
|
+
|
34
|
+
bool parser_is_expected_closing_tag_name(const char* tag_name, foreign_content_type_T expected_type);
|
35
|
+
|
27
36
|
token_T* parser_advance(parser_T* parser);
|
28
37
|
token_T* parser_consume_if_present(parser_T* parser, token_type_T type);
|
29
38
|
token_T* parser_consume_expected(parser_T* parser, token_type_T type, array_T* array);
|
data/src/include/token_struct.h
CHANGED
data/src/include/version.h
CHANGED
data/src/lexer.c
CHANGED
@@ -325,6 +325,7 @@ token_T* lexer_next_token(lexer_T* lexer) {
|
|
325
325
|
|
326
326
|
case '"':
|
327
327
|
case '\'': return lexer_advance_current(lexer, TOKEN_QUOTE);
|
328
|
+
case '`': return lexer_advance_current(lexer, TOKEN_BACKTICK);
|
328
329
|
|
329
330
|
default: {
|
330
331
|
if (isalnum(lexer->current_character)) { return lexer_parse_identifier(lexer); }
|
data/src/parser.c
CHANGED
@@ -17,6 +17,7 @@
|
|
17
17
|
#include <strings.h>
|
18
18
|
|
19
19
|
static void parser_parse_in_data_state(parser_T* parser, array_T* children, array_T* errors);
|
20
|
+
static void parser_parse_foreign_content(parser_T* parser, array_T* children, array_T* errors);
|
20
21
|
static AST_ERB_CONTENT_NODE_T* parser_parse_erb_tag(parser_T* parser);
|
21
22
|
|
22
23
|
size_t parser_sizeof(void) {
|
@@ -29,6 +30,8 @@ parser_T* parser_init(lexer_T* lexer) {
|
|
29
30
|
parser->lexer = lexer;
|
30
31
|
parser->current_token = lexer_next_token(lexer);
|
31
32
|
parser->open_tags_stack = array_init(16);
|
33
|
+
parser->state = PARSER_STATE_DATA;
|
34
|
+
parser->foreign_content_type = FOREIGN_CONTENT_UNKNOWN;
|
32
35
|
|
33
36
|
return parser;
|
34
37
|
}
|
@@ -383,6 +386,30 @@ static AST_HTML_ATTRIBUTE_VALUE_NODE_T* parser_parse_html_attribute_value(parser
|
|
383
386
|
// <div id="home">
|
384
387
|
if (token_is(parser, TOKEN_QUOTE)) { return parser_parse_quoted_html_attribute_value(parser, children, errors); }
|
385
388
|
|
389
|
+
if (token_is(parser, TOKEN_BACKTICK)) {
|
390
|
+
token_T* token = parser_advance(parser);
|
391
|
+
position_T* start = position_copy(token->location->start);
|
392
|
+
position_T* end = position_copy(token->location->end);
|
393
|
+
|
394
|
+
append_unexpected_error(
|
395
|
+
"Invalid quote character for HTML attribute",
|
396
|
+
"single quote (') or double quote (\")",
|
397
|
+
"backtick (`)",
|
398
|
+
start,
|
399
|
+
end,
|
400
|
+
errors
|
401
|
+
);
|
402
|
+
|
403
|
+
AST_HTML_ATTRIBUTE_VALUE_NODE_T* value =
|
404
|
+
ast_html_attribute_value_node_init(NULL, children, NULL, false, start, end, errors);
|
405
|
+
|
406
|
+
position_free(start);
|
407
|
+
position_free(end);
|
408
|
+
token_free(token);
|
409
|
+
|
410
|
+
return value;
|
411
|
+
}
|
412
|
+
|
386
413
|
token_T* token = parser_advance(parser);
|
387
414
|
|
388
415
|
append_unexpected_error(
|
@@ -412,9 +439,19 @@ static AST_HTML_ATTRIBUTE_VALUE_NODE_T* parser_parse_html_attribute_value(parser
|
|
412
439
|
static AST_HTML_ATTRIBUTE_NODE_T* parser_parse_html_attribute(parser_T* parser) {
|
413
440
|
AST_HTML_ATTRIBUTE_NAME_NODE_T* attribute_name = parser_parse_html_attribute_name(parser);
|
414
441
|
|
442
|
+
while (token_is_any_of(parser, TOKEN_WHITESPACE, TOKEN_NEWLINE)) {
|
443
|
+
token_T* whitespace = parser_advance(parser);
|
444
|
+
token_free(whitespace);
|
445
|
+
}
|
446
|
+
|
415
447
|
token_T* equals = parser_consume_if_present(parser, TOKEN_EQUALS);
|
416
448
|
|
417
449
|
if (equals != NULL) {
|
450
|
+
while (token_is_any_of(parser, TOKEN_WHITESPACE, TOKEN_NEWLINE)) {
|
451
|
+
token_T* whitespace = parser_advance(parser);
|
452
|
+
token_free(whitespace);
|
453
|
+
}
|
454
|
+
|
418
455
|
AST_HTML_ATTRIBUTE_VALUE_NODE_T* attribute_value = parser_parse_html_attribute_value(parser);
|
419
456
|
|
420
457
|
AST_HTML_ATTRIBUTE_NODE_T* attribute_node = ast_html_attribute_node_init(
|
@@ -596,7 +633,13 @@ static AST_HTML_ELEMENT_NODE_T* parser_parse_html_regular_element(
|
|
596
633
|
|
597
634
|
parser_push_open_tag(parser, open_tag->tag_name);
|
598
635
|
|
599
|
-
|
636
|
+
if (open_tag->tag_name->value && parser_is_foreign_content_tag(open_tag->tag_name->value)) {
|
637
|
+
foreign_content_type_T content_type = parser_get_foreign_content_type(open_tag->tag_name->value);
|
638
|
+
parser_enter_foreign_content(parser, content_type);
|
639
|
+
parser_parse_foreign_content(parser, body, errors);
|
640
|
+
} else {
|
641
|
+
parser_parse_in_data_state(parser, body, errors);
|
642
|
+
}
|
600
643
|
|
601
644
|
if (!token_is(parser, TOKEN_HTML_TAG_START_CLOSE)) { return parser_handle_missing_close_tag(open_tag, body, errors); }
|
602
645
|
|
@@ -687,6 +730,83 @@ static AST_ERB_CONTENT_NODE_T* parser_parse_erb_tag(parser_T* parser) {
|
|
687
730
|
return erb_node;
|
688
731
|
}
|
689
732
|
|
733
|
+
static void parser_parse_foreign_content(parser_T* parser, array_T* children, array_T* errors) {
|
734
|
+
buffer_T content = buffer_new();
|
735
|
+
position_T* start = position_copy(parser->current_token->location->start);
|
736
|
+
const char* expected_closing_tag = parser_get_foreign_content_closing_tag(parser->foreign_content_type);
|
737
|
+
|
738
|
+
if (expected_closing_tag == NULL) {
|
739
|
+
parser_exit_foreign_content(parser);
|
740
|
+
position_free(start);
|
741
|
+
buffer_free(&content);
|
742
|
+
|
743
|
+
return;
|
744
|
+
}
|
745
|
+
|
746
|
+
while (!token_is(parser, TOKEN_EOF)) {
|
747
|
+
if (token_is(parser, TOKEN_ERB_START)) {
|
748
|
+
parser_append_literal_node_from_buffer(parser, &content, children, start);
|
749
|
+
|
750
|
+
AST_ERB_CONTENT_NODE_T* erb_node = parser_parse_erb_tag(parser);
|
751
|
+
array_append(children, erb_node);
|
752
|
+
|
753
|
+
position_free(start);
|
754
|
+
start = position_copy(parser->current_token->location->start);
|
755
|
+
|
756
|
+
continue;
|
757
|
+
}
|
758
|
+
|
759
|
+
if (token_is(parser, TOKEN_HTML_TAG_START_CLOSE)) {
|
760
|
+
size_t saved_position = parser->lexer->current_position;
|
761
|
+
size_t saved_line = parser->lexer->current_line;
|
762
|
+
size_t saved_column = parser->lexer->current_column;
|
763
|
+
size_t saved_previous_position = parser->lexer->previous_position;
|
764
|
+
size_t saved_previous_line = parser->lexer->previous_line;
|
765
|
+
size_t saved_previous_column = parser->lexer->previous_column;
|
766
|
+
|
767
|
+
char saved_char = parser->lexer->current_character;
|
768
|
+
lexer_state_T saved_state = parser->lexer->state;
|
769
|
+
|
770
|
+
token_T* next_token = lexer_next_token(parser->lexer);
|
771
|
+
bool is_potential_match = false;
|
772
|
+
|
773
|
+
if (next_token && next_token->type == TOKEN_IDENTIFIER && next_token->value) {
|
774
|
+
is_potential_match = parser_is_expected_closing_tag_name(next_token->value, parser->foreign_content_type);
|
775
|
+
}
|
776
|
+
|
777
|
+
parser->lexer->current_position = saved_position;
|
778
|
+
parser->lexer->current_line = saved_line;
|
779
|
+
parser->lexer->current_column = saved_column;
|
780
|
+
parser->lexer->previous_position = saved_previous_position;
|
781
|
+
parser->lexer->previous_line = saved_previous_line;
|
782
|
+
parser->lexer->previous_column = saved_previous_column;
|
783
|
+
parser->lexer->current_character = saved_char;
|
784
|
+
parser->lexer->state = saved_state;
|
785
|
+
|
786
|
+
if (next_token) { token_free(next_token); }
|
787
|
+
|
788
|
+
if (is_potential_match) {
|
789
|
+
parser_append_literal_node_from_buffer(parser, &content, children, start);
|
790
|
+
parser_exit_foreign_content(parser);
|
791
|
+
|
792
|
+
position_free(start);
|
793
|
+
buffer_free(&content);
|
794
|
+
|
795
|
+
return;
|
796
|
+
}
|
797
|
+
}
|
798
|
+
|
799
|
+
token_T* token = parser_advance(parser);
|
800
|
+
buffer_append(&content, token->value);
|
801
|
+
token_free(token);
|
802
|
+
}
|
803
|
+
|
804
|
+
parser_append_literal_node_from_buffer(parser, &content, children, start);
|
805
|
+
parser_exit_foreign_content(parser);
|
806
|
+
position_free(start);
|
807
|
+
buffer_free(&content);
|
808
|
+
}
|
809
|
+
|
690
810
|
static void parser_parse_in_data_state(parser_T* parser, array_T* children, array_T* errors) {
|
691
811
|
while (token_is_none_of(parser, TOKEN_HTML_TAG_START_CLOSE, TOKEN_EOF)) {
|
692
812
|
if (token_is(parser, TOKEN_ERB_START)) {
|
data/src/parser_helpers.c
CHANGED
@@ -8,6 +8,7 @@
|
|
8
8
|
#include "include/lexer.h"
|
9
9
|
#include "include/parser.h"
|
10
10
|
#include "include/token.h"
|
11
|
+
#include "include/token_matchers.h"
|
11
12
|
|
12
13
|
#include <stdio.h>
|
13
14
|
#include <strings.h>
|
@@ -54,6 +55,43 @@ bool parser_in_svg_context(const parser_T* parser) {
|
|
54
55
|
return false;
|
55
56
|
}
|
56
57
|
|
58
|
+
// ===== Foreign Content Handling =====
|
59
|
+
|
60
|
+
foreign_content_type_T parser_get_foreign_content_type(const char* tag_name) {
|
61
|
+
if (tag_name == NULL) { return FOREIGN_CONTENT_UNKNOWN; }
|
62
|
+
|
63
|
+
if (strcasecmp(tag_name, "script") == 0) { return FOREIGN_CONTENT_SCRIPT; }
|
64
|
+
if (strcasecmp(tag_name, "style") == 0) { return FOREIGN_CONTENT_STYLE; }
|
65
|
+
|
66
|
+
return FOREIGN_CONTENT_UNKNOWN;
|
67
|
+
}
|
68
|
+
|
69
|
+
bool parser_is_foreign_content_tag(const char* tag_name) {
|
70
|
+
return parser_get_foreign_content_type(tag_name) != FOREIGN_CONTENT_UNKNOWN;
|
71
|
+
}
|
72
|
+
|
73
|
+
const char* parser_get_foreign_content_closing_tag(foreign_content_type_T type) {
|
74
|
+
switch (type) {
|
75
|
+
case FOREIGN_CONTENT_SCRIPT: return "script";
|
76
|
+
case FOREIGN_CONTENT_STYLE: return "style";
|
77
|
+
default: return NULL;
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
void parser_enter_foreign_content(parser_T* parser, foreign_content_type_T type) {
|
82
|
+
if (parser == NULL) { return; }
|
83
|
+
|
84
|
+
parser->state = PARSER_STATE_FOREIGN_CONTENT;
|
85
|
+
parser->foreign_content_type = type;
|
86
|
+
}
|
87
|
+
|
88
|
+
void parser_exit_foreign_content(parser_T* parser) {
|
89
|
+
if (parser == NULL) { return; }
|
90
|
+
|
91
|
+
parser->state = PARSER_STATE_DATA;
|
92
|
+
parser->foreign_content_type = FOREIGN_CONTENT_UNKNOWN;
|
93
|
+
}
|
94
|
+
|
57
95
|
void parser_append_unexpected_error(parser_T* parser, const char* description, const char* expected, array_T* errors) {
|
58
96
|
token_T* token = parser_advance(parser);
|
59
97
|
|
@@ -166,3 +204,11 @@ void parser_handle_mismatched_tags(
|
|
166
204
|
);
|
167
205
|
}
|
168
206
|
}
|
207
|
+
|
208
|
+
bool parser_is_expected_closing_tag_name(const char* tag_name, foreign_content_type_T expected_type) {
|
209
|
+
const char* expected_tag_name = parser_get_foreign_content_closing_tag(expected_type);
|
210
|
+
|
211
|
+
if (expected_tag_name == NULL || tag_name == NULL) { return false; }
|
212
|
+
|
213
|
+
return strcmp(tag_name, expected_tag_name) == 0;
|
214
|
+
}
|
data/src/token.c
CHANGED
@@ -55,6 +55,7 @@ const char* token_type_to_string(const token_type_T type) {
|
|
55
55
|
case TOKEN_HTML_COMMENT_END: return "TOKEN_HTML_COMMENT_END";
|
56
56
|
case TOKEN_EQUALS: return "TOKEN_EQUALS";
|
57
57
|
case TOKEN_QUOTE: return "TOKEN_QUOTE";
|
58
|
+
case TOKEN_BACKTICK: return "TOKEN_BACKTICK";
|
58
59
|
case TOKEN_DASH: return "TOKEN_DASH";
|
59
60
|
case TOKEN_UNDERSCORE: return "TOKEN_UNDERSCORE";
|
60
61
|
case TOKEN_EXCLAMATION: return "TOKEN_EXCLAMATION";
|
data/src/visitor.c
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
2
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release-
|
2
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/src/visitor.c.erb
|
3
3
|
|
4
4
|
#include <stdio.h>
|
5
5
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: herb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marco Roth
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-08-
|
10
|
+
date: 2025-08-17 00:00:00.000000000 Z
|
11
11
|
dependencies: []
|
12
12
|
description: Powerful and seamless HTML-aware ERB parsing and tooling.
|
13
13
|
email:
|