Ruby  3.4.0dev (2024-11-05 revision ed06f018bdffe9bb7f8bdbf15fa5a727e402bfe9)
static_literals.c
2 
8 typedef struct {
11 
13  int32_t start_line;
14 
16  const char *encoding_name;
18 
19 static inline uint32_t
20 murmur_scramble(uint32_t value) {
21  value *= 0xcc9e2d51;
22  value = (value << 15) | (value >> 17);
23  value *= 0x1b873593;
24  return value;
25 }
26 
32 static uint32_t
33 murmur_hash(const uint8_t *key, size_t length) {
34  uint32_t hash = 0x9747b28c;
35  uint32_t segment;
36 
37  for (size_t index = length >> 2; index; index--) {
38  memcpy(&segment, key, sizeof(uint32_t));
39  key += sizeof(uint32_t);
40  hash ^= murmur_scramble(segment);
41  hash = (hash << 13) | (hash >> 19);
42  hash = hash * 5 + 0xe6546b64;
43  }
44 
45  segment = 0;
46  for (size_t index = length & 3; index; index--) {
47  segment <<= 8;
48  segment |= key[index - 1];
49  }
50 
51  hash ^= murmur_scramble(segment);
52  hash ^= (uint32_t) length;
53  hash ^= hash >> 16;
54  hash *= 0x85ebca6b;
55  hash ^= hash >> 13;
56  hash *= 0xc2b2ae35;
57  hash ^= hash >> 16;
58  return hash;
59 }
60 
64 static uint32_t
65 integer_hash(const pm_integer_t *integer) {
66  uint32_t hash;
67  if (integer->values) {
68  hash = murmur_hash((const uint8_t *) integer->values, sizeof(uint32_t) * integer->length);
69  } else {
70  hash = murmur_hash((const uint8_t *) &integer->value, sizeof(uint32_t));
71  }
72 
73  if (integer->negative) {
74  hash ^= murmur_scramble((uint32_t) 1);
75  }
76 
77  return hash;
78 }
79 
85 static uint32_t
86 node_hash(const pm_static_literals_metadata_t *metadata, const pm_node_t *node) {
87  switch (PM_NODE_TYPE(node)) {
88  case PM_INTEGER_NODE: {
89  // Integers hash their value.
90  const pm_integer_node_t *cast = (const pm_integer_node_t *) node;
91  return integer_hash(&cast->value);
92  }
93  case PM_SOURCE_LINE_NODE: {
94  // Source lines hash their line number.
95  const pm_line_column_t line_column = pm_newline_list_line_column(metadata->newline_list, node->location.start, metadata->start_line);
96  const int32_t *value = &line_column.line;
97  return murmur_hash((const uint8_t *) value, sizeof(int32_t));
98  }
99  case PM_FLOAT_NODE: {
100  // Floats hash their value.
101  const double *value = &((const pm_float_node_t *) node)->value;
102  return murmur_hash((const uint8_t *) value, sizeof(double));
103  }
104  case PM_RATIONAL_NODE: {
105  // Rationals hash their numerator and denominator.
106  const pm_rational_node_t *cast = (const pm_rational_node_t *) node;
107  return integer_hash(&cast->numerator) ^ integer_hash(&cast->denominator) ^ murmur_scramble((uint32_t) cast->base.type);
108  }
109  case PM_IMAGINARY_NODE: {
110  // Imaginaries hash their numeric value. Because their numeric value
111  // is stored as a subnode, we hash that node and then mix in the
112  // fact that this is an imaginary node.
113  const pm_node_t *numeric = ((const pm_imaginary_node_t *) node)->numeric;
114  return node_hash(metadata, numeric) ^ murmur_scramble((uint32_t) node->type);
115  }
116  case PM_STRING_NODE: {
117  // Strings hash their value and mix in their flags so that different
118  // encodings are not considered equal.
119  const pm_string_t *value = &((const pm_string_node_t *) node)->unescaped;
120 
121  pm_node_flags_t flags = node->flags;
123 
124  return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) flags);
125  }
126  case PM_SOURCE_FILE_NODE: {
127  // Source files hash their value and mix in their flags so that
128  // different encodings are not considered equal.
129  const pm_string_t *value = &((const pm_source_file_node_t *) node)->filepath;
130  return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t));
131  }
133  // Regular expressions hash their value and mix in their flags so
134  // that different encodings are not considered equal.
135  const pm_string_t *value = &((const pm_regular_expression_node_t *) node)->unescaped;
136  return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) node->flags);
137  }
138  case PM_SYMBOL_NODE: {
139  // Symbols hash their value and mix in their flags so that different
140  // encodings are not considered equal.
141  const pm_string_t *value = &((const pm_symbol_node_t *) node)->unescaped;
142  return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) node->flags);
143  }
144  default:
145  assert(false && "unreachable");
146  return 0;
147  }
148 }
149 
156 static pm_node_t *
157 pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *metadata, pm_node_t *node, bool replace, int (*compare)(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right)) {
158  // If we are out of space, we need to resize the hash. This will cause all
159  // of the nodes to be rehashed and reinserted into the new hash.
160  if (hash->size * 2 >= hash->capacity) {
161  // First, allocate space for the new node list.
162  uint32_t new_capacity = hash->capacity == 0 ? 4 : hash->capacity * 2;
163  pm_node_t **new_nodes = xcalloc(new_capacity, sizeof(pm_node_t *));
164  if (new_nodes == NULL) return NULL;
165 
166  // It turns out to be more efficient to mask the hash value than to use
167  // the modulo operator. Because our capacities are always powers of two,
168  // we can use a bitwise AND to get the same result as the modulo
169  // operator.
170  uint32_t mask = new_capacity - 1;
171 
172  // Now, rehash all of the nodes into the new list.
173  for (uint32_t index = 0; index < hash->capacity; index++) {
174  pm_node_t *node = hash->nodes[index];
175 
176  if (node != NULL) {
177  uint32_t index = node_hash(metadata, node) & mask;
178  new_nodes[index] = node;
179  }
180  }
181 
182  // Finally, free the old node list and update the hash.
183  xfree(hash->nodes);
184  hash->nodes = new_nodes;
185  hash->capacity = new_capacity;
186  }
187 
188  // Now, insert the node into the hash.
189  uint32_t mask = hash->capacity - 1;
190  uint32_t index = node_hash(metadata, node) & mask;
191 
192  // We use linear probing to resolve collisions. This means that if the
193  // current index is occupied, we will move to the next index and try again.
194  // We are guaranteed that this will eventually find an empty slot because we
195  // resize the hash when it gets too full.
196  while (hash->nodes[index] != NULL) {
197  if (compare(metadata, hash->nodes[index], node) == 0) break;
198  index = (index + 1) & mask;
199  }
200 
201  // If the current index is occupied, we need to return the node that was
202  // already in the hash. Otherwise, we can just increment the size and insert
203  // the new node.
204  pm_node_t *result = hash->nodes[index];
205 
206  if (result == NULL) {
207  hash->size++;
208  hash->nodes[index] = node;
209  } else if (replace) {
210  hash->nodes[index] = node;
211  }
212 
213  return result;
214 }
215 
219 static void
220 pm_node_hash_free(pm_node_hash_t *hash) {
221  if (hash->capacity > 0) xfree(hash->nodes);
222 }
223 
227 #define PM_NUMERIC_COMPARISON(left, right) ((left < right) ? -1 : (left > right) ? 1 : 0)
228 
232 static int64_t
233 pm_int64_value(const pm_static_literals_metadata_t *metadata, const pm_node_t *node) {
234  switch (PM_NODE_TYPE(node)) {
235  case PM_INTEGER_NODE: {
236  const pm_integer_t *integer = &((const pm_integer_node_t *) node)->value;
237  if (integer->values) return integer->negative ? INT64_MIN : INT64_MAX;
238 
239  int64_t value = (int64_t) integer->value;
240  return integer->negative ? -value : value;
241  }
242  case PM_SOURCE_LINE_NODE:
243  return (int64_t) pm_newline_list_line_column(metadata->newline_list, node->location.start, metadata->start_line).line;
244  default:
245  assert(false && "unreachable");
246  return 0;
247  }
248 }
249 
254 static int
255 pm_compare_integer_nodes(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
257  int64_t left_value = pm_int64_value(metadata, left);
258  int64_t right_value = pm_int64_value(metadata, right);
259  return PM_NUMERIC_COMPARISON(left_value, right_value);
260  }
261 
262  const pm_integer_t *left_integer = &((const pm_integer_node_t *) left)->value;
263  const pm_integer_t *right_integer = &((const pm_integer_node_t *) right)->value;
264  return pm_integer_compare(left_integer, right_integer);
265 }
266 
270 static int
271 pm_compare_float_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
272  const double left_value = ((const pm_float_node_t *) left)->value;
273  const double right_value = ((const pm_float_node_t *) right)->value;
274  return PM_NUMERIC_COMPARISON(left_value, right_value);
275 }
276 
280 static int
281 pm_compare_number_nodes(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
282  if (PM_NODE_TYPE(left) != PM_NODE_TYPE(right)) {
283  return PM_NUMERIC_COMPARISON(PM_NODE_TYPE(left), PM_NODE_TYPE(right));
284  }
285 
286  switch (PM_NODE_TYPE(left)) {
287  case PM_IMAGINARY_NODE:
288  return pm_compare_number_nodes(metadata, ((const pm_imaginary_node_t *) left)->numeric, ((const pm_imaginary_node_t *) right)->numeric);
289  case PM_RATIONAL_NODE: {
290  const pm_rational_node_t *left_rational = (const pm_rational_node_t *) left;
291  const pm_rational_node_t *right_rational = (const pm_rational_node_t *) right;
292 
293  int result = pm_integer_compare(&left_rational->denominator, &right_rational->denominator);
294  if (result != 0) return result;
295 
296  return pm_integer_compare(&left_rational->numerator, &right_rational->numerator);
297  }
298  case PM_INTEGER_NODE:
299  return pm_compare_integer_nodes(metadata, left, right);
300  case PM_FLOAT_NODE:
301  return pm_compare_float_nodes(metadata, left, right);
302  default:
303  assert(false && "unreachable");
304  return 0;
305  }
306 }
307 
311 static const pm_string_t *
312 pm_string_value(const pm_node_t *node) {
313  switch (PM_NODE_TYPE(node)) {
314  case PM_STRING_NODE:
315  return &((const pm_string_node_t *) node)->unescaped;
316  case PM_SOURCE_FILE_NODE:
317  return &((const pm_source_file_node_t *) node)->filepath;
318  case PM_SYMBOL_NODE:
319  return &((const pm_symbol_node_t *) node)->unescaped;
320  default:
321  assert(false && "unreachable");
322  return NULL;
323  }
324 }
325 
329 static int
330 pm_compare_string_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
331  const pm_string_t *left_string = pm_string_value(left);
332  const pm_string_t *right_string = pm_string_value(right);
333  return pm_string_compare(left_string, right_string);
334 }
335 
339 static int
340 pm_compare_regular_expression_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
341  const pm_regular_expression_node_t *left_regexp = (const pm_regular_expression_node_t *) left;
342  const pm_regular_expression_node_t *right_regexp = (const pm_regular_expression_node_t *) right;
343 
344  int result = pm_string_compare(&left_regexp->unescaped, &right_regexp->unescaped);
345  if (result != 0) return result;
346 
347  return PM_NUMERIC_COMPARISON(left_regexp->base.flags, right_regexp->base.flags);
348 }
349 
350 #undef PM_NUMERIC_COMPARISON
351 
355 pm_node_t *
356 pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line, pm_static_literals_t *literals, pm_node_t *node, bool replace) {
357  switch (PM_NODE_TYPE(node)) {
358  case PM_INTEGER_NODE:
359  case PM_SOURCE_LINE_NODE:
360  return pm_node_hash_insert(
361  &literals->integer_nodes,
363  .newline_list = newline_list,
364  .start_line = start_line,
365  .encoding_name = NULL
366  },
367  node,
368  replace,
369  pm_compare_integer_nodes
370  );
371  case PM_FLOAT_NODE:
372  return pm_node_hash_insert(
373  &literals->float_nodes,
375  .newline_list = newline_list,
376  .start_line = start_line,
377  .encoding_name = NULL
378  },
379  node,
380  replace,
381  pm_compare_float_nodes
382  );
383  case PM_RATIONAL_NODE:
384  case PM_IMAGINARY_NODE:
385  return pm_node_hash_insert(
386  &literals->number_nodes,
388  .newline_list = newline_list,
389  .start_line = start_line,
390  .encoding_name = NULL
391  },
392  node,
393  replace,
394  pm_compare_number_nodes
395  );
396  case PM_STRING_NODE:
397  case PM_SOURCE_FILE_NODE:
398  return pm_node_hash_insert(
399  &literals->string_nodes,
401  .newline_list = newline_list,
402  .start_line = start_line,
403  .encoding_name = NULL
404  },
405  node,
406  replace,
407  pm_compare_string_nodes
408  );
410  return pm_node_hash_insert(
411  &literals->regexp_nodes,
413  .newline_list = newline_list,
414  .start_line = start_line,
415  .encoding_name = NULL
416  },
417  node,
418  replace,
419  pm_compare_regular_expression_nodes
420  );
421  case PM_SYMBOL_NODE:
422  return pm_node_hash_insert(
423  &literals->symbol_nodes,
425  .newline_list = newline_list,
426  .start_line = start_line,
427  .encoding_name = NULL
428  },
429  node,
430  replace,
431  pm_compare_string_nodes
432  );
433  case PM_TRUE_NODE: {
434  pm_node_t *duplicated = literals->true_node;
435  if ((duplicated == NULL) || replace) literals->true_node = node;
436  return duplicated;
437  }
438  case PM_FALSE_NODE: {
439  pm_node_t *duplicated = literals->false_node;
440  if ((duplicated == NULL) || replace) literals->false_node = node;
441  return duplicated;
442  }
443  case PM_NIL_NODE: {
444  pm_node_t *duplicated = literals->nil_node;
445  if ((duplicated == NULL) || replace) literals->nil_node = node;
446  return duplicated;
447  }
449  pm_node_t *duplicated = literals->source_encoding_node;
450  if ((duplicated == NULL) || replace) literals->source_encoding_node = node;
451  return duplicated;
452  }
453  default:
454  return NULL;
455  }
456 }
457 
461 void
463  pm_node_hash_free(&literals->integer_nodes);
464  pm_node_hash_free(&literals->float_nodes);
465  pm_node_hash_free(&literals->number_nodes);
466  pm_node_hash_free(&literals->string_nodes);
467  pm_node_hash_free(&literals->regexp_nodes);
468  pm_node_hash_free(&literals->symbol_nodes);
469 }
470 
475 static bool
476 pm_static_literal_positive_p(const pm_node_t *node) {
477  switch (PM_NODE_TYPE(node)) {
478  case PM_FLOAT_NODE:
479  return ((const pm_float_node_t *) node)->value > 0;
480  case PM_INTEGER_NODE:
481  return !((const pm_integer_node_t *) node)->value.negative;
482  case PM_RATIONAL_NODE:
483  return !((const pm_rational_node_t *) node)->numerator.negative;
484  case PM_IMAGINARY_NODE:
485  return pm_static_literal_positive_p(((const pm_imaginary_node_t *) node)->numeric);
486  default:
487  assert(false && "unreachable");
488  return false;
489  }
490 }
491 
495 static inline void
496 pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_metadata_t *metadata, const pm_node_t *node) {
497  switch (PM_NODE_TYPE(node)) {
498  case PM_FALSE_NODE:
499  pm_buffer_append_string(buffer, "false", 5);
500  break;
501  case PM_FLOAT_NODE: {
502  const double value = ((const pm_float_node_t *) node)->value;
503 
504  if (isinf(value)) {
505  if (*node->location.start == '-') {
506  pm_buffer_append_byte(buffer, '-');
507  }
508  pm_buffer_append_string(buffer, "Infinity", 8);
509  } else if (value == 0.0) {
510  if (*node->location.start == '-') {
511  pm_buffer_append_byte(buffer, '-');
512  }
513  pm_buffer_append_string(buffer, "0.0", 3);
514  } else {
515  pm_buffer_append_format(buffer, "%g", value);
516 
517  // %g will not insert a .0 for 1e100 (we'll get back 1e+100). So
518  // we check for the decimal point and add it in here if it's not
519  // present.
520  if (pm_buffer_index(buffer, '.') == SIZE_MAX) {
521  size_t exponent_index = pm_buffer_index(buffer, 'e');
522  size_t index = exponent_index == SIZE_MAX ? pm_buffer_length(buffer) : exponent_index;
523  pm_buffer_insert(buffer, index, ".0", 2);
524  }
525  }
526 
527  break;
528  }
529  case PM_IMAGINARY_NODE: {
530  const pm_node_t *numeric = ((const pm_imaginary_node_t *) node)->numeric;
531  pm_buffer_append_string(buffer, "(0", 2);
532  if (pm_static_literal_positive_p(numeric)) pm_buffer_append_byte(buffer, '+');
533  pm_static_literal_inspect_node(buffer, metadata, numeric);
534  if (PM_NODE_TYPE_P(numeric, PM_RATIONAL_NODE)) {
535  pm_buffer_append_byte(buffer, '*');
536  }
537  pm_buffer_append_string(buffer, "i)", 2);
538  break;
539  }
540  case PM_INTEGER_NODE:
541  pm_integer_string(buffer, &((const pm_integer_node_t *) node)->value);
542  break;
543  case PM_NIL_NODE:
544  pm_buffer_append_string(buffer, "nil", 3);
545  break;
546  case PM_RATIONAL_NODE: {
547  const pm_rational_node_t *rational = (const pm_rational_node_t *) node;
548  pm_buffer_append_byte(buffer, '(');
549  pm_integer_string(buffer, &rational->numerator);
550  pm_buffer_append_byte(buffer, '/');
551  pm_integer_string(buffer, &rational->denominator);
552  pm_buffer_append_byte(buffer, ')');
553  break;
554  }
556  const pm_string_t *unescaped = &((const pm_regular_expression_node_t *) node)->unescaped;
557  pm_buffer_append_byte(buffer, '/');
558  pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_RUBY);
559  pm_buffer_append_byte(buffer, '/');
560 
565 
566  break;
567  }
569  pm_buffer_append_format(buffer, "#<Encoding:%s>", metadata->encoding_name);
570  break;
571  case PM_SOURCE_FILE_NODE: {
572  const pm_string_t *filepath = &((const pm_source_file_node_t *) node)->filepath;
573  pm_buffer_append_byte(buffer, '"');
574  pm_buffer_append_source(buffer, pm_string_source(filepath), pm_string_length(filepath), PM_BUFFER_ESCAPING_RUBY);
575  pm_buffer_append_byte(buffer, '"');
576  break;
577  }
578  case PM_SOURCE_LINE_NODE:
580  break;
581  case PM_STRING_NODE: {
582  const pm_string_t *unescaped = &((const pm_string_node_t *) node)->unescaped;
583  pm_buffer_append_byte(buffer, '"');
584  pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_RUBY);
585  pm_buffer_append_byte(buffer, '"');
586  break;
587  }
588  case PM_SYMBOL_NODE: {
589  const pm_string_t *unescaped = &((const pm_symbol_node_t *) node)->unescaped;
590  pm_buffer_append_byte(buffer, ':');
591  pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_RUBY);
592  break;
593  }
594  case PM_TRUE_NODE:
595  pm_buffer_append_string(buffer, "true", 4);
596  break;
597  default:
598  assert(false && "unreachable");
599  break;
600  }
601 }
602 
606 void
607 pm_static_literal_inspect(pm_buffer_t *buffer, const pm_newline_list_t *newline_list, int32_t start_line, const char *encoding_name, const pm_node_t *node) {
608  pm_static_literal_inspect_node(
609  buffer,
611  .newline_list = newline_list,
612  .start_line = start_line,
613  .encoding_name = encoding_name
614  },
615  node
616  );
617 }
@ PM_SOURCE_LINE_NODE
SourceLineNode.
Definition: ast.h:985
@ PM_NIL_NODE
NilNode.
Definition: ast.h:895
@ PM_RATIONAL_NODE
RationalNode.
Definition: ast.h:940
@ PM_FALSE_NODE
FalseNode.
Definition: ast.h:724
@ PM_SOURCE_FILE_NODE
SourceFileNode.
Definition: ast.h:982
@ PM_SYMBOL_NODE
SymbolNode.
Definition: ast.h:1000
@ PM_TRUE_NODE
TrueNode.
Definition: ast.h:1003
@ PM_REGULAR_EXPRESSION_NODE
RegularExpressionNode.
Definition: ast.h:946
@ PM_IMAGINARY_NODE
ImaginaryNode.
Definition: ast.h:775
@ PM_SOURCE_ENCODING_NODE
SourceEncodingNode.
Definition: ast.h:979
@ PM_FLOAT_NODE
FloatNode.
Definition: ast.h:733
@ PM_INTEGER_NODE
IntegerNode.
Definition: ast.h:817
@ PM_STRING_NODE
StringNode.
Definition: ast.h:994
@ PM_STRING_FLAGS_FORCED_BINARY_ENCODING
internal bytes forced the encoding to binary
Definition: ast.h:7493
@ PM_STRING_FLAGS_FORCED_UTF8_ENCODING
internal bytes forced the encoding to UTF-8
Definition: ast.h:7490
#define PM_NODE_FLAG_P(node, flag)
Return true if the given flag is set on the given node.
Definition: ast.h:1063
#define PM_NODE_TYPE_P(node, type)
Return true if the type of the given node matches the given type.
Definition: ast.h:1058
#define PM_NODE_TYPE(node)
Cast the type to an enum to allow the compiler to provide exhaustiveness checking.
Definition: ast.h:1053
@ PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE
i - ignores the case of characters when matching
Definition: ast.h:7438
@ PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT
n - forces the ASCII-8BIT encoding
Definition: ast.h:7453
@ PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE
m - allows $ to match the end of lines within strings
Definition: ast.h:7444
@ PM_REGULAR_EXPRESSION_FLAGS_EXTENDED
x - ignores whitespace and allows comments in regular expressions
Definition: ast.h:7441
uint16_t pm_node_flags_t
These are the flags embedded in the node struct.
Definition: ast.h:1040
#define xfree
Old name of ruby_xfree.
Definition: xmalloc.h:58
#define xcalloc
Old name of ruby_xcalloc.
Definition: xmalloc.h:55
size_t pm_buffer_index(const pm_buffer_t *buffer, char value)
Checks if the buffer includes the given value.
Definition: pm_buffer.c:290
void pm_buffer_append_format(pm_buffer_t *buffer, const char *format,...) PRISM_ATTRIBUTE_FORMAT(2
Append a formatted string to the buffer.
void void pm_buffer_append_string(pm_buffer_t *buffer, const char *value, size_t length)
Append a string to the buffer.
Definition: pm_buffer.c:119
PRISM_EXPORTED_FUNCTION size_t pm_buffer_length(const pm_buffer_t *buffer)
Return the length of the buffer.
Definition: pm_buffer.c:43
void pm_buffer_append_source(pm_buffer_t *buffer, const uint8_t *source, size_t length, pm_buffer_escaping_t escaping)
Append a slice of source code to the buffer.
Definition: pm_buffer.c:179
void pm_buffer_append_byte(pm_buffer_t *buffer, uint8_t value)
Append a single byte to the buffer.
Definition: pm_buffer.c:135
void pm_buffer_insert(pm_buffer_t *buffer, size_t index, const char *value, size_t length)
Insert the given string into the buffer at the given index.
Definition: pm_buffer.c:299
int pm_integer_compare(const pm_integer_t *left, const pm_integer_t *right)
Compare two integers.
Definition: pm_integer.c:545
PRISM_EXPORTED_FUNCTION void pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer)
Convert an integer to a decimal string.
Definition: pm_integer.c:607
pm_line_column_t pm_newline_list_line_column(const pm_newline_list_t *list, const uint8_t *cursor, int32_t start_line)
Returns the line and column of the given offset.
PRISM_EXPORTED_FUNCTION size_t pm_string_length(const pm_string_t *string)
Returns the length associated with the string.
Definition: pm_string.c:352
PRISM_EXPORTED_FUNCTION const uint8_t * pm_string_source(const pm_string_t *string)
Returns the start pointer associated with the string.
Definition: pm_string.c:360
int pm_string_compare(const pm_string_t *left, const pm_string_t *right)
Compare the underlying lengths and bytes of two strings.
Definition: pm_string.c:335
#define PRISM_ATTRIBUTE_UNUSED
GCC will warn if you specify a function or parameter that is unused at runtime.
Definition: defines.h:78
A set of static literal nodes that can be checked for duplicates.
void pm_static_literal_inspect(pm_buffer_t *buffer, const pm_newline_list_t *newline_list, int32_t start_line, const char *encoding_name, const pm_node_t *node)
Create a string-based representation of the given static literal.
pm_node_t * pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line, pm_static_literals_t *literals, pm_node_t *node, bool replace)
Add a node to the set of static literals.
void pm_static_literals_free(pm_static_literals_t *literals)
Free the internal memory associated with the given static literals set.
A pm_buffer_t is a simple memory buffer that stores data in a contiguous block of memory.
Definition: pm_buffer.h:22
FloatNode.
Definition: ast.h:3435
ImaginaryNode.
Definition: ast.h:4054
IntegerNode.
Definition: ast.h:4664
pm_integer_t value
IntegerNode::value.
Definition: ast.h:4674
A structure represents an arbitrary-sized integer.
Definition: pm_integer.h:20
size_t length
The number of allocated values.
Definition: pm_integer.h:25
uint32_t value
Embedded value for small integer.
Definition: pm_integer.h:36
uint32_t * values
List of 32-bit integers.
Definition: pm_integer.h:30
bool negative
Whether or not the integer is negative.
Definition: pm_integer.h:42
A line and column in a string.
int32_t line
The line number.
const uint8_t * start
A pointer to the start location of the range in the source.
Definition: ast.h:547
A list of offsets of newlines in a string.
An internal hash table for a set of nodes.
pm_node_t ** nodes
The array of nodes in the hash table.
uint32_t size
The size of the hash table.
uint32_t capacity
The space that has been allocated in the hash table.
This is the base structure that represents a node in the syntax tree.
Definition: ast.h:1069
pm_node_type_t type
This represents the type of the node.
Definition: ast.h:1074
pm_node_flags_t flags
This represents any flags on the node.
Definition: ast.h:1080
pm_location_t location
This is the location of the node in the source.
Definition: ast.h:1092
RationalNode.
Definition: ast.h:6297
pm_node_t base
The embedded base node.
Definition: ast.h:6299
pm_integer_t denominator
RationalNode::denominator.
Definition: ast.h:6318
pm_integer_t numerator
RationalNode::numerator.
Definition: ast.h:6309
RegularExpressionNode.
Definition: ast.h:6364
pm_node_t base
The embedded base node.
Definition: ast.h:6366
pm_string_t unescaped
RegularExpressionNode::unescaped.
Definition: ast.h:6387
SourceFileNode.
Definition: ast.h:6751
A small struct used for passing around a subset of the information that is stored on the parser.
const char * encoding_name
The name of the encoding that the parser is using.
const pm_newline_list_t * newline_list
The list of newline offsets to use to calculate line numbers.
int32_t start_line
The line number that the parser starts on.
Certain sets of nodes (hash keys and when clauses) check for duplicate nodes to alert the user of pot...
pm_node_hash_t string_nodes
This is the set of StringNode and SourceFileNode instances.
pm_node_t * false_node
A pointer to the last FalseNode instance that was inserted, or NULL.
pm_node_hash_t regexp_nodes
This is the set of RegularExpressionNode instances.
pm_node_t * source_encoding_node
A pointer to the last SourceEncodingNode instance that was inserted, or NULL.
pm_node_t * nil_node
A pointer to the last NilNode instance that was inserted, or NULL.
pm_node_hash_t number_nodes
This is the set of RationalNode and ImaginaryNode instances.
pm_node_t * true_node
A pointer to the last TrueNode instance that was inserted, or NULL.
pm_node_hash_t symbol_nodes
This is the set of SymbolNode instances.
pm_node_hash_t float_nodes
This is the set of FloatNode instances.
pm_node_hash_t integer_nodes
This is the set of IntegerNode and SourceLineNode instances.
StringNode.
Definition: ast.h:6857
A generic string type that can have various ownership semantics.
Definition: pm_string.h:33
SymbolNode.
Definition: ast.h:6949