herb 0.7.4-x86-linux-gnu → 0.7.5-x86-linux-gnu

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/config.yml +14 -14
  3. data/ext/herb/extension_helpers.c +9 -15
  4. data/ext/herb/extension_helpers.h +3 -3
  5. data/ext/herb/nodes.c +1 -1
  6. data/lib/herb/3.0/herb.so +0 -0
  7. data/lib/herb/3.1/herb.so +0 -0
  8. data/lib/herb/3.2/herb.so +0 -0
  9. data/lib/herb/3.3/herb.so +0 -0
  10. data/lib/herb/3.4/herb.so +0 -0
  11. data/lib/herb/engine.rb +10 -6
  12. data/lib/herb/version.rb +1 -1
  13. data/src/analyze.c +43 -80
  14. data/src/ast_node.c +10 -13
  15. data/src/ast_nodes.c +31 -33
  16. data/src/buffer.c +10 -1
  17. data/src/errors.c +35 -35
  18. data/src/include/ast_node.h +3 -3
  19. data/src/include/ast_nodes.h +32 -32
  20. data/src/include/errors.h +21 -21
  21. data/src/include/lexer_peek_helpers.h +8 -6
  22. data/src/include/lexer_struct.h +10 -9
  23. data/src/include/location.h +10 -13
  24. data/src/include/parser_helpers.h +1 -1
  25. data/src/include/position.h +3 -14
  26. data/src/include/pretty_print.h +1 -1
  27. data/src/include/prism_helpers.h +1 -1
  28. data/src/include/range.h +4 -13
  29. data/src/include/token.h +0 -3
  30. data/src/include/token_struct.h +2 -2
  31. data/src/include/version.h +1 -1
  32. data/src/lexer.c +3 -2
  33. data/src/lexer_peek_helpers.c +10 -4
  34. data/src/location.c +9 -37
  35. data/src/parser.c +98 -119
  36. data/src/parser_helpers.c +15 -15
  37. data/src/pretty_print.c +7 -12
  38. data/src/prism_helpers.c +7 -7
  39. data/src/range.c +2 -35
  40. data/src/token.c +25 -29
  41. data/templates/javascript/packages/core/src/visitor.ts.erb +29 -1
  42. data/templates/src/ast_nodes.c.erb +1 -3
  43. data/templates/src/errors.c.erb +5 -7
  44. data/templates/src/include/ast_nodes.h.erb +2 -2
  45. data/templates/src/include/errors.h.erb +3 -3
  46. metadata +2 -3
  47. data/src/position.c +0 -33
data/src/parser_helpers.c CHANGED
@@ -99,8 +99,8 @@ void parser_append_unexpected_error(parser_T* parser, const char* description, c
99
99
  description,
100
100
  expected,
101
101
  token_type_to_string(token->type),
102
- token->location->start,
103
- token->location->end,
102
+ token->location.start,
103
+ token->location.end,
104
104
  errors
105
105
  );
106
106
 
@@ -111,8 +111,8 @@ void parser_append_unexpected_token_error(parser_T* parser, token_type_T expecte
111
111
  append_unexpected_token_error(
112
112
  expected_type,
113
113
  parser->current_token,
114
- parser->current_token->location->start,
115
- parser->current_token->location->end,
114
+ parser->current_token->location.start,
115
+ parser->current_token->location.end,
116
116
  errors
117
117
  );
118
118
  }
@@ -121,12 +121,12 @@ void parser_append_literal_node_from_buffer(
121
121
  const parser_T* parser,
122
122
  buffer_T* buffer,
123
123
  array_T* children,
124
- position_T* start
124
+ position_T start
125
125
  ) {
126
126
  if (buffer_length(buffer) == 0) { return; }
127
127
 
128
128
  AST_LITERAL_NODE_T* literal =
129
- ast_literal_node_init(buffer_value(buffer), start, parser->current_token->location->start, NULL);
129
+ ast_literal_node_init(buffer_value(buffer), start, parser->current_token->location.start, NULL);
130
130
 
131
131
  if (children != NULL) { array_append(children, literal); }
132
132
  buffer_clear(buffer);
@@ -149,7 +149,7 @@ token_T* parser_consume_expected(parser_T* parser, const token_type_T expected_t
149
149
  if (token == NULL) {
150
150
  token = parser_advance(parser);
151
151
 
152
- append_unexpected_token_error(expected_type, token, token->location->start, token->location->end, array);
152
+ append_unexpected_token_error(expected_type, token, token->location.start, token->location.end, array);
153
153
  }
154
154
 
155
155
  return token;
@@ -162,8 +162,8 @@ AST_HTML_ELEMENT_NODE_T* parser_handle_missing_close_tag(
162
162
  ) {
163
163
  append_missing_closing_tag_error(
164
164
  open_tag->tag_name,
165
- open_tag->tag_name->location->start,
166
- open_tag->tag_name->location->end,
165
+ open_tag->tag_name->location.start,
166
+ open_tag->tag_name->location.end,
167
167
  errors
168
168
  );
169
169
 
@@ -174,8 +174,8 @@ AST_HTML_ELEMENT_NODE_T* parser_handle_missing_close_tag(
174
174
  NULL,
175
175
  false,
176
176
  ELEMENT_SOURCE_HTML,
177
- open_tag->base.location->start,
178
- open_tag->base.location->end,
177
+ open_tag->base.location.start,
178
+ open_tag->base.location.end,
179
179
  errors
180
180
  );
181
181
  }
@@ -192,15 +192,15 @@ void parser_handle_mismatched_tags(
192
192
  append_tag_names_mismatch_error(
193
193
  expected_tag,
194
194
  actual_tag,
195
- actual_tag->location->start,
196
- actual_tag->location->end,
195
+ actual_tag->location.start,
196
+ actual_tag->location.end,
197
197
  errors
198
198
  );
199
199
  } else {
200
200
  append_missing_opening_tag_error(
201
201
  close_tag->tag_name,
202
- close_tag->tag_name->location->start,
203
- close_tag->tag_name->location->end,
202
+ close_tag->tag_name->location.start,
203
+ close_tag->tag_name->location.end,
204
204
  errors
205
205
  );
206
206
  }
data/src/pretty_print.c CHANGED
@@ -158,16 +158,16 @@ void pretty_print_errors(
158
158
  }
159
159
  }
160
160
 
161
- void pretty_print_location(location_T* location, buffer_T* buffer) {
161
+ void pretty_print_location(location_T location, buffer_T* buffer) {
162
162
  buffer_append(buffer, "(location: (");
163
163
  char location_string[128];
164
164
  sprintf(
165
165
  location_string,
166
- "%zu,%zu)-(%zu,%zu",
167
- (location->start && location->start->line) ? location->start->line : 0,
168
- (location->start && location->start->column) ? location->start->column : 0,
169
- (location->end && location->end->line) ? location->end->line : 0,
170
- (location->end && location->end->column) ? location->end->column : 0
166
+ "%u,%u)-(%u,%u",
167
+ location.start.line,
168
+ location.start.column,
169
+ location.end.line,
170
+ location.end.column
171
171
  );
172
172
  buffer_append(buffer, location_string);
173
173
  buffer_append(buffer, "))");
@@ -188,12 +188,7 @@ void pretty_print_position_property(
188
188
 
189
189
  char position_string[128];
190
190
 
191
- sprintf(
192
- position_string,
193
- "%zu:%zu",
194
- (position->line) ? position->line : 0,
195
- (position->column) ? position->column : 0
196
- );
191
+ sprintf(position_string, "%u:%u", (position->line) ? position->line : 0, (position->column) ? position->column : 0);
197
192
 
198
193
  buffer_append(buffer, position_string);
199
194
  buffer_append(buffer, ")");
data/src/prism_helpers.c CHANGED
@@ -16,15 +16,15 @@ const char* pm_error_level_to_string(pm_error_level_t level) {
16
16
  }
17
17
  }
18
18
 
19
- position_T* position_from_source_with_offset(const char* source, size_t offset) {
20
- position_T* position = position_init(1, 0);
19
+ position_T position_from_source_with_offset(const char* source, size_t offset) {
20
+ position_T position = { .line = 1, .column = 0 };
21
21
 
22
22
  for (size_t i = 0; i < offset; i++) {
23
23
  if (is_newline(source[i])) {
24
- position->line++;
25
- position->column = 0;
24
+ position.line++;
25
+ position.column = 0;
26
26
  } else {
27
- position->column++;
27
+ position.column++;
28
28
  }
29
29
  }
30
30
 
@@ -40,8 +40,8 @@ RUBY_PARSE_ERROR_T* ruby_parse_error_from_prism_error(
40
40
  size_t start_offset = (size_t) (error->location.start - parser->start);
41
41
  size_t end_offset = (size_t) (error->location.end - parser->start);
42
42
 
43
- position_T* start = position_from_source_with_offset(source, start_offset);
44
- position_T* end = position_from_source_with_offset(source, end_offset);
43
+ position_T start = position_from_source_with_offset(source, start_offset);
44
+ position_T end = position_from_source_with_offset(source, end_offset);
45
45
 
46
46
  return ruby_parse_error_init(
47
47
  error->message,
data/src/range.c CHANGED
@@ -1,38 +1,5 @@
1
1
  #include "include/range.h"
2
2
 
3
- size_t range_sizeof(void) {
4
- return sizeof(range_T);
5
- }
6
-
7
- range_T* range_init(const size_t from, const size_t to) {
8
- range_T* range = calloc(1, range_sizeof());
9
-
10
- range->from = from;
11
- range->to = to;
12
-
13
- return range;
14
- }
15
-
16
- size_t range_from(const range_T* range) {
17
- return range->from;
18
- }
19
-
20
- size_t range_to(const range_T* range) {
21
- return range->to;
22
- }
23
-
24
- size_t range_length(range_T* range) {
25
- return range_to(range) - range_from(range);
26
- }
27
-
28
- range_T* range_copy(range_T* range) {
29
- if (!range) { return NULL; }
30
-
31
- return range_init(range_from(range), range_to(range));
32
- }
33
-
34
- void range_free(range_T* range) {
35
- if (range == NULL) { return; }
36
-
37
- free(range);
3
+ uint32_t range_length(range_T range) {
4
+ return range.to - range.from;
38
5
  }
data/src/token.c CHANGED
@@ -2,6 +2,7 @@
2
2
  #include "include/json.h"
3
3
  #include "include/lexer.h"
4
4
  #include "include/position.h"
5
+ #include "include/range.h"
5
6
  #include "include/token_struct.h"
6
7
  #include "include/util.h"
7
8
 
@@ -28,10 +29,15 @@ token_T* token_init(const char* value, const token_type_T type, lexer_T* lexer)
28
29
  }
29
30
 
30
31
  token->type = type;
31
- token->range = range_init(lexer->previous_position, lexer->current_position);
32
-
33
- token->location =
34
- location_from(lexer->previous_line, lexer->previous_column, lexer->current_line, lexer->current_column);
32
+ token->range = (range_T) { .from = lexer->previous_position, .to = lexer->current_position };
33
+
34
+ location_from(
35
+ &token->location,
36
+ lexer->previous_line,
37
+ lexer->previous_column,
38
+ lexer->current_line,
39
+ lexer->current_column
40
+ );
35
41
 
36
42
  lexer->previous_line = lexer->current_line;
37
43
  lexer->previous_column = lexer->current_column;
@@ -84,7 +90,7 @@ const char* token_type_to_string(const token_type_T type) {
84
90
 
85
91
  char* token_to_string(const token_T* token) {
86
92
  const char* type_string = token_type_to_string(token->type);
87
- const char* template = "#<Herb::Token type=\"%s\" value=\"%s\" range=[%d, %d] start=(%d:%d) end=(%d:%d)>";
93
+ const char* template = "#<Herb::Token type=\"%s\" value=\"%s\" range=[%u, %u] start=(%u:%u) end=(%u:%u)>";
88
94
 
89
95
  char* string = calloc(strlen(type_string) + strlen(template) + strlen(token->value) + 16, sizeof(char));
90
96
  char* escaped;
@@ -100,12 +106,12 @@ char* token_to_string(const token_T* token) {
100
106
  template,
101
107
  type_string,
102
108
  escaped,
103
- token->range->from,
104
- token->range->to,
105
- token->location->start->line,
106
- token->location->start->column,
107
- token->location->end->line,
108
- token->location->end->column
109
+ token->range.from,
110
+ token->range.to,
111
+ token->location.start.line,
112
+ token->location.start.column,
113
+ token->location.end.line,
114
+ token->location.end.column
109
115
  );
110
116
 
111
117
  free(escaped);
@@ -122,24 +128,24 @@ char* token_to_json(const token_T* token) {
122
128
 
123
129
  buffer_T range = buffer_new();
124
130
  json_start_array(&json, "range");
125
- json_add_size_t(&range, NULL, token->range->from);
126
- json_add_size_t(&range, NULL, token->range->to);
131
+ json_add_size_t(&range, NULL, token->range.from);
132
+ json_add_size_t(&range, NULL, token->range.to);
127
133
  buffer_concat(&json, &range);
128
134
  buffer_free(&range);
129
135
  json_end_array(&json);
130
136
 
131
137
  buffer_T start = buffer_new();
132
138
  json_start_object(&json, "start");
133
- json_add_size_t(&start, "line", token->location->start->line);
134
- json_add_size_t(&start, "column", token->location->start->column);
139
+ json_add_size_t(&start, "line", token->location.start.line);
140
+ json_add_size_t(&start, "column", token->location.start.column);
135
141
  buffer_concat(&json, &start);
136
142
  buffer_free(&start);
137
143
  json_end_object(&json);
138
144
 
139
145
  buffer_T end = buffer_new();
140
146
  json_start_object(&json, "end");
141
- json_add_size_t(&end, "line", token->location->end->line);
142
- json_add_size_t(&end, "column", token->location->end->column);
147
+ json_add_size_t(&end, "line", token->location.end.line);
148
+ json_add_size_t(&end, "column", token->location.end.column);
143
149
  buffer_concat(&json, &end);
144
150
  buffer_free(&end);
145
151
  json_end_object(&json);
@@ -157,14 +163,6 @@ int token_type(const token_T* token) {
157
163
  return token->type;
158
164
  }
159
165
 
160
- position_T* token_start_position(token_T* token) {
161
- return token->location->start;
162
- }
163
-
164
- position_T* token_end_position(token_T* token) {
165
- return token->location->end;
166
- }
167
-
168
166
  token_T* token_copy(token_T* token) {
169
167
  if (!token) { return NULL; }
170
168
 
@@ -184,8 +182,8 @@ token_T* token_copy(token_T* token) {
184
182
  }
185
183
 
186
184
  new_token->type = token->type;
187
- new_token->range = range_copy(token->range);
188
- new_token->location = location_copy(token->location);
185
+ new_token->range = token->range;
186
+ new_token->location = token->location;
189
187
 
190
188
  return new_token;
191
189
  }
@@ -194,8 +192,6 @@ void token_free(token_T* token) {
194
192
  if (!token) { return; }
195
193
 
196
194
  if (token->value != NULL) { free(token->value); }
197
- if (token->range != NULL) { range_free(token->range); }
198
- if (token->location != NULL) { location_free(token->location); }
199
195
 
200
196
  free(token);
201
197
  }
@@ -1,11 +1,27 @@
1
1
  import {
2
2
  Node,
3
+ ERBNode,
3
4
  <%- nodes.each do |node| -%>
4
5
  <%= node.name %>,
5
6
  <%- end -%>
6
7
  } from "./nodes.js"
7
8
 
8
- export class Visitor {
9
+ /**
10
+ * Interface that enforces all node visit methods are implemented
11
+ * This ensures that any class implementing IVisitor must have a visit method for every node type
12
+ */
13
+ export interface IVisitor {
14
+ visit(node: Node | null | undefined): void
15
+ visitAll(nodes: (Node | null | undefined)[]): void
16
+ visitChildNodes(node: Node): void
17
+ <%- nodes.each do |node| -%>
18
+ visit<%= node.name %>(node: <%= node.name %>): void
19
+ <%- end -%>
20
+ visitNode(node: Node): void
21
+ visitERBNode(node: ERBNode): void
22
+ }
23
+
24
+ export class Visitor implements IVisitor {
9
25
  visit(node: Node | null | undefined): void {
10
26
  if (!node) return
11
27
 
@@ -20,8 +36,20 @@ export class Visitor {
20
36
  node.compactChildNodes().forEach(node => node.accept(this))
21
37
  }
22
38
 
39
+ visitNode(_node: Node): void {
40
+ // Default implementation does nothing
41
+ }
42
+
43
+ visitERBNode(_node: ERBNode): void {
44
+ // Default implementation does nothing
45
+ }
46
+
23
47
  <%- nodes.each do |node| -%>
24
48
  visit<%= node.name %>(node: <%= node.name %>): void {
49
+ this.visitNode(node)
50
+ <%- if node.name.start_with?("ERB") -%>
51
+ this.visitERBNode(node)
52
+ <%- end -%>
25
53
  this.visitChildNodes(node)
26
54
  }
27
55
 
@@ -13,7 +13,7 @@
13
13
 
14
14
  <%- nodes.each do |node| -%>
15
15
  <%- node_arguments = node.fields.any? ? node.fields.map { |field| [field.c_type, " ", field.name].join } : [] -%>
16
- <%- arguments = node_arguments + ["position_T* start_position", "position_T* end_position", "array_T* errors"] -%>
16
+ <%- arguments = node_arguments + ["position_T start_position", "position_T end_position", "array_T* errors"] -%>
17
17
 
18
18
  <%= node.struct_type %>* ast_<%= node.human %>_init(<%= arguments.join(", ") %>) {
19
19
  <%= node.struct_type %>* <%= node.human %> = malloc(sizeof(<%= node.struct_type %>));
@@ -81,8 +81,6 @@ void ast_free_base_node(AST_NODE_T* node) {
81
81
  array_free(&node->errors);
82
82
  }
83
83
 
84
- if (node->location) { location_free(node->location); }
85
-
86
84
  free(node);
87
85
  }
88
86
 
@@ -17,15 +17,16 @@ size_t error_sizeof(void) {
17
17
  return sizeof(struct ERROR_STRUCT);
18
18
  }
19
19
 
20
- void error_init(ERROR_T* error, const error_type_T type, position_T* start, position_T* end) {
20
+ void error_init(ERROR_T* error, const error_type_T type, position_T start, position_T end) {
21
21
  if (!error) { return; }
22
22
 
23
23
  error->type = type;
24
- error->location = location_init(position_copy(start), position_copy(end));
24
+ error->location.start = start;
25
+ error->location.end = end;
25
26
  }
26
27
  <%- errors.each do |error| -%>
27
28
  <%- error_arguments = error.fields.any? ? error.fields.map { |field| [field.c_type, " ", field.name].join } : [] -%>
28
- <%- arguments = error_arguments + ["position_T* start", "position_T* end"] -%>
29
+ <%- arguments = error_arguments + ["position_T start", "position_T end"] -%>
29
30
 
30
31
  <%= error.struct_type %>* <%= error.human %>_init(<%= arguments.join(", ") %>) {
31
32
  <%= error.struct_type %>* <%= error.human %> = malloc(sizeof(<%= error.struct_type %>));
@@ -72,7 +73,7 @@ void error_init(ERROR_T* error, const error_type_T type, position_T* start, posi
72
73
  <%- error.fields.each do |field| -%>
73
74
  <%- case field -%>
74
75
  <%- when Herb::Template::PositionField -%>
75
- <%= error.human %>-><%= field.name %> = position_copy(<%= field.name %>);
76
+ <%= error.human %>-><%= field.name %> = <%= field.name %>;
76
77
  <%- when Herb::Template::TokenField -%>
77
78
  <%= error.human %>-><%= field.name %> = token_copy(<%= field.name %>);
78
79
  <%- when Herb::Template::TokenTypeField -%>
@@ -116,7 +117,6 @@ const char* error_human_type(ERROR_T* error) {
116
117
  void error_free_base_error(ERROR_T* error) {
117
118
  if (error == NULL) { return; }
118
119
 
119
- if (error->location != NULL) { location_free(error->location); }
120
120
  if (error->message != NULL) { free(error->message); }
121
121
 
122
122
  free(error);
@@ -130,8 +130,6 @@ static void error_free_<%= error.human %>(<%= error.struct_type %>* <%= error.hu
130
130
  <%- end -%>
131
131
  <%- error.fields.each do |field| -%>
132
132
  <%- case field -%>
133
- <%- when Herb::Template::PositionField -%>
134
- if (<%= error.human %>-><%= field.name %> != NULL) { position_free(<%= error.human %>-><%= field.name %>); }
135
133
  <%- when Herb::Template::TokenField -%>
136
134
  if (<%= error.human %>-><%= field.name %> != NULL) { token_free(<%= error.human %>-><%= field.name %>); }
137
135
  <%- when Herb::Template::TokenTypeField -%>
@@ -20,7 +20,7 @@ typedef enum {
20
20
 
21
21
  typedef struct AST_NODE_STRUCT {
22
22
  ast_node_type_T type;
23
- location_T* location;
23
+ location_T location;
24
24
  // maybe a range too?
25
25
  array_T* errors;
26
26
  } AST_NODE_T;
@@ -36,7 +36,7 @@ typedef struct <%= node.struct_name %> {
36
36
 
37
37
  <%- nodes.each do |node| -%>
38
38
  <%- node_arguments = node.fields.any? ? node.fields.map { |field| [field.c_type, " ", field.name].join } : [] -%>
39
- <%- arguments = node_arguments + ["position_T* start_position", "position_T* end_position", "array_T* errors"] -%>
39
+ <%- arguments = node_arguments + ["position_T start_position", "position_T end_position", "array_T* errors"] -%>
40
40
  <%= node.struct_type %>* ast_<%= node.human %>_init(<%= arguments.join(", ") %>);
41
41
  <%- end -%>
42
42
 
@@ -16,7 +16,7 @@ typedef enum {
16
16
 
17
17
  typedef struct ERROR_STRUCT {
18
18
  error_type_T type;
19
- location_T* location;
19
+ location_T location;
20
20
  char* message;
21
21
  } ERROR_T;
22
22
 
@@ -31,12 +31,12 @@ typedef struct {
31
31
 
32
32
  <%- errors.each do |error| -%>
33
33
  <%- error_arguments = error.fields.any? ? error.fields.map { |field| [field.c_type, " ", field.name].join } : [] -%>
34
- <%- arguments = error_arguments + ["position_T* start", "position_T* end"] -%>
34
+ <%- arguments = error_arguments + ["position_T start", "position_T end"] -%>
35
35
  <%= error.struct_type %>* <%= error.human %>_init(<%= arguments.join(", ") %>);
36
36
  void append_<%= error.human %>(<%= (arguments << "array_T* errors").join(", ") %>);
37
37
  <%- end -%>
38
38
 
39
- void error_init(ERROR_T* error, error_type_T type, position_T* start, position_T* end);
39
+ void error_init(ERROR_T* error, error_type_T type, position_T start, position_T end);
40
40
 
41
41
  size_t error_sizeof(void);
42
42
  error_type_T error_type(ERROR_T* error);
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.7.4
4
+ version: 0.7.5
5
5
  platform: x86-linux-gnu
6
6
  authors:
7
7
  - Marco Roth
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-09-26 00:00:00.000000000 Z
10
+ date: 2025-10-06 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: Powerful and seamless HTML-aware ERB parsing and tooling.
13
13
  email:
@@ -167,7 +167,6 @@ files:
167
167
  - src/memory.c
168
168
  - src/parser.c
169
169
  - src/parser_helpers.c
170
- - src/position.c
171
170
  - src/pretty_print.c
172
171
  - src/prism_helpers.c
173
172
  - src/range.c
data/src/position.c DELETED
@@ -1,33 +0,0 @@
1
- #include "include/position.h"
2
- #include "include/memory.h"
3
-
4
- size_t position_sizeof(void) {
5
- return sizeof(position_T);
6
- }
7
-
8
- position_T* position_init(const size_t line, const size_t column) {
9
- position_T* position = safe_malloc(position_sizeof());
10
-
11
- position->line = line;
12
- position->column = column;
13
-
14
- return position;
15
- }
16
-
17
- size_t position_line(const position_T* position) {
18
- return position->line;
19
- }
20
-
21
- size_t position_column(const position_T* position) {
22
- return position->column;
23
- }
24
-
25
- position_T* position_copy(position_T* position) {
26
- if (position == NULL) { return NULL; }
27
-
28
- return position_init(position_line(position), position_column(position));
29
- }
30
-
31
- void position_free(position_T* position) {
32
- free(position);
33
- }