herb 0.11.1 → 0.11.2
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/Rakefile +4 -2
- data/config.yml +6 -0
- data/ext/herb/extconf.rb +8 -31
- data/ext/herb/nodes.c +75 -75
- data/ext/herb/parser_options_helpers.c +26 -0
- data/lib/herb/analysis/partial_index.rb +1 -1
- data/lib/herb/analysis/partial_resolution.rb +27 -0
- data/lib/herb/ast/helpers.rb +1 -1
- data/lib/herb/configuration.rb +11 -1
- data/lib/herb/dev/classifier.rb +1 -1
- data/lib/herb/engine/compiler.rb +5 -8
- data/lib/herb/engine/slots/dynamics_compiler.rb +53 -14
- data/lib/herb/engine/slots/subtree_compiler.rb +1 -1
- data/lib/herb/engine/slots/visitor.rb +32 -4
- data/lib/herb/engine/visitors/instrumentation_visitor.rb +18 -6
- data/lib/herb/parser_options.rb +6 -3
- data/lib/herb/project.rb +19 -8
- data/lib/herb/version.rb +1 -1
- data/lib/herb.rb +1 -1
- data/sig/herb/analysis/partial_resolution.rbs +8 -0
- data/sig/herb/configuration.rbs +3 -0
- data/sig/herb/engine/slots/dynamics_compiler.rbs +24 -7
- data/sig/herb/engine/slots/visitor.rbs +14 -0
- data/sig/herb/engine/visitors/instrumentation_visitor.rbs +5 -1
- data/sig/herb/parser_options.rbs +6 -4
- data/sig/herb.rbs +2 -2
- data/src/analyze/action_view/attribute_extraction_helpers.c +40 -21
- data/src/analyze/action_view/generated_handlers.c +36 -36
- data/src/analyze/action_view/image_tag.c +7 -3
- data/src/analyze/action_view/javascript_include_tag.c +6 -3
- data/src/analyze/action_view/link_to.c +23 -23
- data/src/analyze/action_view/registry.c +7 -3
- data/src/analyze/action_view/tag_helper_node_builders.c +6 -13
- data/src/analyze/action_view/tag_helpers.c +139 -90
- data/src/analyze/action_view/turbo_frame_tag.c +5 -4
- data/src/analyze/analyze.c +35 -19
- data/src/analyze/analyze_helpers.c +161 -188
- data/src/analyze/analyzed_ruby.c +8 -7
- data/src/analyze/control_type.c +29 -22
- data/src/analyze/herb_directives.c +16 -20
- data/src/analyze/iteration_nodes.c +69 -24
- data/src/analyze/parse_errors.c +32 -22
- data/src/analyze/postfix_conditionals.c +23 -13
- data/src/analyze/prism_annotate.c +29 -28
- data/src/analyze/render_nodes.c +51 -41
- data/src/analyze/strict_locals.c +14 -15
- data/src/analyze/ternary_conditionals.c +14 -10
- data/src/herb.c +10 -8
- data/src/include/analyze/action_view/tag_helper_handler.h +1 -1
- data/src/include/analyze/action_view/tag_helper_node_builders.h +1 -0
- data/src/include/analyze/action_view/tag_helpers.h +1 -1
- data/src/include/analyze/analyze.h +3 -2
- data/src/include/analyze/analyzed_ruby.h +4 -2
- data/src/include/analyze/helpers.h +6 -6
- data/src/include/herb.h +3 -2
- data/src/include/parser/parser_options.h +5 -0
- data/src/include/prism/prism_context.h +12 -12
- data/src/include/prism/prism_helpers.h +30 -0
- data/src/include/prism_version.h +11 -0
- data/src/include/version.h +1 -1
- data/src/parser/parser_helpers.c +1 -2
- data/src/parser.c +6 -7
- data/src/parser_options_defaults.c +1 -0
- data/src/prism/prism_helpers.c +84 -50
- data/src/prism/ruby_parser.c +20 -20
- data/templates/ext/herb/nodes.c.erb +5 -5
- data/templates/ext/herb/parser_options_helpers.c.erb +33 -0
- data/templates/java/nodes.c.erb +6 -6
- data/templates/java/parser_options_helpers.c.erb +41 -2
- data/templates/java/parser_options_helpers.h.erb +2 -1
- data/templates/javascript/packages/node/extension/nodes.cpp.erb +5 -5
- data/templates/javascript/packages/node/extension/parser_options_helpers.cpp.erb +34 -2
- data/templates/javascript/packages/node/extension/parser_options_helpers.h.erb +1 -1
- data/templates/lib/herb/parser_options.rb.erb +1 -1
- data/templates/rust/src/prism/generated_deserialize.rs.erb +90 -44
- data/templates/rust/src/prism/generated_prettyprint.rs.erb +18 -27
- data/templates/src/analyze/action_view/generated_handlers.c.erb +23 -22
- data/templates/src/include/parser/parser_options.h.erb +10 -0
- data/templates/src/include/prism_version.h.erb +17 -0
- data/templates/src/parser_options_defaults.c.erb +1 -1
- data/templates/template.rb +17 -2
- data/templates/wasm/nodes.cpp.erb +5 -5
- data/templates/wasm/parser_options_helpers.cpp.erb +24 -0
- data/vendor/prism/Rakefile +76 -7
- data/vendor/prism/config.yml +174 -161
- data/vendor/prism/include/prism/arena.h +45 -0
- data/vendor/prism/include/prism/arena.hpp +42 -0
- data/vendor/prism/include/prism/ast.h +2738 -611
- data/vendor/prism/include/prism/ast.hpp +641 -0
- data/vendor/prism/include/prism/buffer.h +60 -0
- data/vendor/prism/include/prism/buffer.hpp +55 -0
- data/vendor/prism/include/prism/comments.h +72 -0
- data/vendor/prism/include/prism/compiler/accel.h +26 -0
- data/vendor/prism/include/prism/compiler/align.h +36 -0
- data/vendor/prism/include/prism/compiler/assume.h +25 -0
- data/vendor/prism/include/prism/compiler/branch.h +26 -0
- data/vendor/prism/include/prism/compiler/bswap.h +46 -0
- data/vendor/prism/include/prism/compiler/cplusplus.hpp +16 -0
- data/vendor/prism/include/prism/compiler/endianness.h +34 -0
- data/vendor/prism/include/prism/compiler/exceptions.hpp +17 -0
- data/vendor/prism/include/prism/compiler/exported.h +24 -0
- data/vendor/prism/include/prism/compiler/fallthrough.h +22 -0
- data/vendor/prism/include/prism/compiler/filesystem.h +32 -0
- data/vendor/prism/include/prism/compiler/flex_array.h +19 -0
- data/vendor/prism/include/prism/compiler/force_inline.h +21 -0
- data/vendor/prism/include/prism/compiler/format.h +27 -0
- data/vendor/prism/include/prism/compiler/inline.h +17 -0
- data/vendor/prism/include/prism/compiler/lifetimebound.hpp +25 -0
- data/vendor/prism/include/prism/compiler/nodiscard.h +22 -0
- data/vendor/prism/include/prism/compiler/nonnull.h +18 -0
- data/vendor/prism/include/prism/compiler/unused.h +18 -0
- data/vendor/prism/include/prism/constant_pool.h +106 -0
- data/vendor/prism/include/prism/diagnostic.h +55 -402
- data/vendor/prism/include/prism/errors_format.h +61 -0
- data/vendor/prism/include/prism/excludes.h +29 -0
- data/vendor/prism/include/prism/integer.h +41 -0
- data/vendor/prism/include/prism/internal/allocator.h +68 -0
- data/vendor/prism/include/prism/internal/allocator_debug.h +88 -0
- data/vendor/prism/include/prism/internal/arena.h +108 -0
- data/vendor/prism/include/prism/internal/bit.h +42 -0
- data/vendor/prism/include/prism/internal/buffer.h +94 -0
- data/vendor/prism/include/prism/internal/char.h +145 -0
- data/vendor/prism/include/prism/internal/comments.h +54 -0
- data/vendor/prism/include/prism/internal/constant_pool.h +152 -0
- data/vendor/prism/include/prism/internal/diagnostic.h +389 -0
- data/vendor/prism/include/prism/{encoding.h → internal/encoding.h} +27 -68
- data/vendor/prism/include/prism/internal/integer.h +68 -0
- data/vendor/prism/include/prism/internal/isinf.h +16 -0
- data/vendor/prism/include/prism/internal/line_offset_list.h +34 -0
- data/vendor/prism/include/prism/{util/pm_list.h → internal/list.h} +11 -52
- data/vendor/prism/include/prism/internal/magic_comments.h +23 -0
- data/vendor/prism/include/prism/internal/memchr.h +15 -0
- data/vendor/prism/include/prism/internal/node.h +42 -0
- data/vendor/prism/include/prism/internal/options.h +232 -0
- data/vendor/prism/include/prism/internal/parser.h +1015 -0
- data/vendor/prism/include/prism/internal/regexp.h +26 -0
- data/vendor/prism/include/prism/internal/serialize.h +39 -0
- data/vendor/prism/include/prism/internal/source.h +72 -0
- data/vendor/prism/include/prism/internal/static_literals.h +60 -0
- data/vendor/prism/include/prism/internal/stringy.h +30 -0
- data/vendor/prism/include/prism/{util/pm_strncasecmp.h → internal/strncasecmp.h} +3 -17
- data/vendor/prism/include/prism/{util/pm_strpbrk.h → internal/strpbrk.h} +7 -20
- data/vendor/prism/include/prism/internal/tokens.h +11 -0
- data/vendor/prism/include/prism/internal/xxh3.h +16 -0
- data/vendor/prism/include/prism/json.h +40 -0
- data/vendor/prism/include/prism/line_offset_list.h +69 -0
- data/vendor/prism/include/prism/magic_comments.h +53 -0
- data/vendor/prism/include/prism/node.h +59 -49
- data/vendor/prism/include/prism/node.hpp +272 -0
- data/vendor/prism/include/prism/node_type.hpp +38 -0
- data/vendor/prism/include/prism/options.h +154 -279
- data/vendor/prism/include/prism/options.hpp +43 -0
- data/vendor/prism/include/prism/parser.h +367 -870
- data/vendor/prism/include/prism/parser.hpp +274 -0
- data/vendor/prism/include/prism/prettyprint.h +14 -9
- data/vendor/prism/include/prism/serialize.h +123 -0
- data/vendor/prism/include/prism/source.h +163 -0
- data/vendor/prism/include/prism/stream.h +36 -0
- data/vendor/prism/include/prism/string_query.h +82 -0
- data/vendor/prism/include/prism/stringy.h +61 -0
- data/vendor/prism/include/prism/stringy.hpp +31 -0
- data/vendor/prism/include/prism/version.h +20 -3
- data/vendor/prism/include/prism.h +35 -320
- data/vendor/prism/include/prism.hpp +24 -0
- data/vendor/prism/src/arena.c +122 -0
- data/vendor/prism/src/{util/pm_buffer.c → buffer.c} +53 -21
- data/vendor/prism/src/{util/pm_char.c → char.c} +28 -57
- data/vendor/prism/src/comments.c +455 -0
- data/vendor/prism/src/constant_pool.c +406 -0
- data/vendor/prism/src/diagnostic.c +120 -68
- data/vendor/prism/src/encoding.c +8 -3
- data/vendor/prism/src/errors_format.c +519 -0
- data/vendor/prism/src/{util/pm_integer.c → integer.c} +38 -22
- data/vendor/prism/src/json.c +5733 -0
- data/vendor/prism/src/line_offset_list.c +100 -0
- data/vendor/prism/src/list.c +24 -0
- data/vendor/prism/src/{util/pm_memchr.c → memchr.c} +8 -6
- data/vendor/prism/src/node.c +3465 -7787
- data/vendor/prism/src/options.c +161 -54
- data/vendor/prism/src/parser.c +371 -0
- data/vendor/prism/src/prettyprint.c +421 -354
- data/vendor/prism/src/prism.c +8522 -7280
- data/vendor/prism/src/regexp.c +960 -45
- data/vendor/prism/src/serialize.c +2434 -2048
- data/vendor/prism/src/source.c +524 -0
- data/vendor/prism/src/static_literals.c +303 -360
- data/vendor/prism/src/string_query.c +180 -0
- data/vendor/prism/src/stringy.c +61 -0
- data/vendor/prism/src/{util/pm_strncasecmp.c → strncasecmp.c} +5 -4
- data/vendor/prism/src/strpbrk.c +447 -0
- data/vendor/prism/src/{token_type.c → tokens.c} +39 -18
- data/vendor/prism/src/xxh3.c +292 -0
- data/vendor/prism/templates/ext/prism/api_node.c.erb +58 -44
- data/vendor/prism/templates/include/prism/ast.h.erb +67 -17
- data/vendor/prism/templates/include/prism/ast.hpp.erb +31 -0
- data/vendor/prism/templates/include/prism/internal/diagnostic.h.erb +60 -0
- data/vendor/prism/templates/java/{org → api/src/main/java-templates/org/ruby_lang}/prism/AbstractNodeVisitor.java.erb +1 -1
- data/vendor/prism/templates/java/{org → api/src/main/java-templates/org/ruby_lang}/prism/Loader.java.erb +232 -133
- data/vendor/prism/templates/java/{org → api/src/main/java-templates/org/ruby_lang}/prism/Nodes.java.erb +62 -23
- data/vendor/prism/templates/javascript/src/deserialize.js.erb +153 -146
- data/vendor/prism/templates/javascript/src/nodes.js.erb +4 -9
- data/vendor/prism/templates/lib/prism/compiler.rb.erb +14 -5
- data/vendor/prism/templates/lib/prism/dispatcher.rb.erb +20 -12
- data/vendor/prism/templates/lib/prism/dot_visitor.rb.erb +37 -11
- data/vendor/prism/templates/lib/prism/dsl.rb.erb +43 -4
- data/vendor/prism/templates/lib/prism/inspect_visitor.rb.erb +26 -10
- data/vendor/prism/templates/lib/prism/mutation_compiler.rb.erb +5 -2
- data/vendor/prism/templates/lib/prism/node.rb.erb +283 -105
- data/vendor/prism/templates/lib/prism/reflection.rb.erb +11 -2
- data/vendor/prism/templates/lib/prism/serialize.rb.erb +291 -134
- data/vendor/prism/templates/lib/prism/visitor.rb.erb +18 -0
- data/vendor/prism/templates/src/diagnostic.c.erb +114 -63
- data/vendor/prism/templates/src/json.c.erb +130 -0
- data/vendor/prism/templates/src/node.c.erb +79 -236
- data/vendor/prism/templates/src/prettyprint.c.erb +24 -13
- data/vendor/prism/templates/src/serialize.c.erb +369 -183
- data/vendor/prism/templates/src/{token_type.c.erb → tokens.c.erb} +22 -11
- data/vendor/prism/templates/template.rb +193 -91
- metadata +100 -42
- data/vendor/prism/include/prism/defines.h +0 -260
- data/vendor/prism/include/prism/pack.h +0 -163
- data/vendor/prism/include/prism/regexp.h +0 -43
- data/vendor/prism/include/prism/static_literals.h +0 -121
- data/vendor/prism/include/prism/util/pm_buffer.h +0 -236
- data/vendor/prism/include/prism/util/pm_char.h +0 -204
- data/vendor/prism/include/prism/util/pm_constant_pool.h +0 -218
- data/vendor/prism/include/prism/util/pm_integer.h +0 -130
- data/vendor/prism/include/prism/util/pm_memchr.h +0 -29
- data/vendor/prism/include/prism/util/pm_newline_list.h +0 -113
- data/vendor/prism/include/prism/util/pm_string.h +0 -200
- data/vendor/prism/src/pack.c +0 -509
- data/vendor/prism/src/util/pm_constant_pool.c +0 -342
- data/vendor/prism/src/util/pm_list.c +0 -49
- data/vendor/prism/src/util/pm_newline_list.c +0 -125
- data/vendor/prism/src/util/pm_string.c +0 -381
- data/vendor/prism/src/util/pm_strpbrk.c +0 -206
- data/vendor/prism/templates/include/prism/diagnostic.h.erb +0 -130
- data/vendor/prism/templates/rbi/prism/dsl.rbi.erb +0 -68
- data/vendor/prism/templates/rbi/prism/node.rbi.erb +0 -167
- data/vendor/prism/templates/rbi/prism/visitor.rbi.erb +0 -18
- data/vendor/prism/templates/sig/prism/_private/dot_visitor.rbs.erb +0 -45
- data/vendor/prism/templates/sig/prism/dsl.rbs.erb +0 -31
- data/vendor/prism/templates/sig/prism/mutation_compiler.rbs.erb +0 -7
- data/vendor/prism/templates/sig/prism/node.rbs.erb +0 -136
- data/vendor/prism/templates/sig/prism/visitor.rbs.erb +0 -17
- data/vendor/prism/templates/sig/prism.rbs.erb +0 -92
|
@@ -1,236 +1,123 @@
|
|
|
1
|
-
#include "prism/static_literals.h"
|
|
1
|
+
#include "prism/internal/static_literals.h"
|
|
2
|
+
|
|
3
|
+
#include "prism/compiler/inline.h"
|
|
4
|
+
#include "prism/compiler/unused.h"
|
|
5
|
+
|
|
6
|
+
#include "prism/internal/allocator.h"
|
|
7
|
+
#include "prism/internal/buffer.h"
|
|
8
|
+
#include "prism/internal/integer.h"
|
|
9
|
+
#include "prism/internal/isinf.h"
|
|
10
|
+
#include "prism/internal/parser.h"
|
|
11
|
+
#include "prism/internal/stringy.h"
|
|
12
|
+
#include "prism/internal/xxh3.h"
|
|
13
|
+
|
|
14
|
+
#include <assert.h>
|
|
15
|
+
#include <inttypes.h>
|
|
16
|
+
#include <math.h>
|
|
17
|
+
#include <stdio.h>
|
|
18
|
+
#include <stdlib.h>
|
|
19
|
+
#include <string.h>
|
|
2
20
|
|
|
3
21
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
22
|
+
* Hash the key built from the given values. Nodes that compare as equal must
|
|
23
|
+
* produce identical keys, so each value is either held directly or is the
|
|
24
|
+
* digest of a variable-length value. The key is an array so that its values are
|
|
25
|
+
* contiguous with no padding between them, which makes every hashed byte
|
|
26
|
+
* defined.
|
|
7
27
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
/** The line number that the parser starts on. */
|
|
13
|
-
int32_t start_line;
|
|
14
|
-
|
|
15
|
-
/** The name of the encoding that the parser is using. */
|
|
16
|
-
const char *encoding_name;
|
|
17
|
-
} pm_static_literals_metadata_t;
|
|
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;
|
|
28
|
+
static PRISM_INLINE uint64_t
|
|
29
|
+
key_hash(uint64_t type, uint64_t a, uint64_t b) {
|
|
30
|
+
const uint64_t key[3] = { type, a, b };
|
|
31
|
+
return pm_xxh3((const uint8_t *) key, sizeof(key));
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
35
|
+
* Return the hash of the value of an integer. An integer that fits into a
|
|
36
|
+
* uint32_t keys on its value directly, and a larger one keys on the digest of
|
|
37
|
+
* its words.
|
|
31
38
|
*/
|
|
32
|
-
static
|
|
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
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Hash the value of an integer and return it.
|
|
63
|
-
*/
|
|
64
|
-
static uint32_t
|
|
39
|
+
static uint64_t
|
|
65
40
|
integer_hash(const pm_integer_t *integer) {
|
|
66
|
-
uint32_t
|
|
67
|
-
|
|
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;
|
|
41
|
+
uint64_t magnitude = integer->values ? pm_xxh3((const uint8_t *) integer->values, sizeof(uint32_t) * integer->length) : integer->value;
|
|
42
|
+
return key_hash(PM_INTEGER_NODE, integer->negative, magnitude);
|
|
78
43
|
}
|
|
79
44
|
|
|
80
45
|
/**
|
|
81
|
-
* Return the hash of the given node. It is important that nodes that
|
|
82
|
-
* equivalent static literal values have the same
|
|
83
|
-
* these
|
|
46
|
+
* Return the hash of the key of the given node. It is important that nodes that
|
|
47
|
+
* have equivalent static literal values have the same key. This is because we
|
|
48
|
+
* hash these keys to look for duplicates.
|
|
84
49
|
*/
|
|
85
|
-
static
|
|
86
|
-
node_hash(const
|
|
50
|
+
static uint64_t
|
|
51
|
+
node_hash(const pm_parser_t *parser, const pm_node_t *node) {
|
|
87
52
|
switch (PM_NODE_TYPE(node)) {
|
|
88
|
-
case PM_INTEGER_NODE:
|
|
89
|
-
|
|
90
|
-
const pm_integer_node_t *cast = (const pm_integer_node_t *) node;
|
|
91
|
-
return integer_hash(&cast->value);
|
|
92
|
-
}
|
|
53
|
+
case PM_INTEGER_NODE:
|
|
54
|
+
return integer_hash(&((const pm_integer_node_t *) node)->value);
|
|
93
55
|
case PM_SOURCE_LINE_NODE: {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const int32_t *value = &line_column.line;
|
|
97
|
-
return murmur_hash((const uint8_t *) value, sizeof(int32_t));
|
|
56
|
+
int64_t line = (int64_t) pm_line_offset_list_line_column(&parser->line_offsets, node->location.start, parser->start_line).line;
|
|
57
|
+
return key_hash(PM_INTEGER_NODE, line < 0, (uint64_t) (line < 0 ? -line : line));
|
|
98
58
|
}
|
|
99
59
|
case PM_FLOAT_NODE: {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
60
|
+
const pm_float_node_t *cast = (const pm_float_node_t *) node;
|
|
61
|
+
double value = cast->value;
|
|
62
|
+
if (value == 0.0) value = 0.0; /* Normalize -0.0 to 0.0. */
|
|
63
|
+
|
|
64
|
+
uint64_t bits;
|
|
65
|
+
memcpy(&bits, &value, sizeof(bits));
|
|
66
|
+
return key_hash(PM_FLOAT_NODE, bits, 0);
|
|
103
67
|
}
|
|
104
68
|
case PM_RATIONAL_NODE: {
|
|
105
|
-
// Rationals hash their numerator and denominator.
|
|
106
69
|
const pm_rational_node_t *cast = (const pm_rational_node_t *) node;
|
|
107
|
-
return integer_hash(&cast->numerator)
|
|
70
|
+
return key_hash(PM_RATIONAL_NODE, integer_hash(&cast->numerator), integer_hash(&cast->denominator));
|
|
108
71
|
}
|
|
109
72
|
case PM_IMAGINARY_NODE: {
|
|
110
|
-
|
|
111
|
-
|
|
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);
|
|
73
|
+
const pm_imaginary_node_t *cast = (const pm_imaginary_node_t *) node;
|
|
74
|
+
return key_hash(PM_IMAGINARY_NODE, node_hash(parser, cast->numeric), 0);
|
|
115
75
|
}
|
|
116
76
|
case PM_STRING_NODE: {
|
|
117
|
-
|
|
118
|
-
// encodings are not considered equal.
|
|
119
|
-
const pm_string_t *value = &((const pm_string_node_t *) node)->unescaped;
|
|
77
|
+
const pm_string_node_t *cast = (const pm_string_node_t *) node;
|
|
120
78
|
|
|
121
|
-
pm_node_flags_t
|
|
122
|
-
|
|
79
|
+
pm_node_flags_t mask = PM_STRING_FLAGS_FORCED_BINARY_ENCODING;
|
|
80
|
+
if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) mask |= PM_STRING_FLAGS_FORCED_UTF8_ENCODING;
|
|
123
81
|
|
|
124
|
-
return
|
|
82
|
+
return key_hash(PM_STRING_NODE, pm_xxh3(pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped)), node->flags & mask);
|
|
125
83
|
}
|
|
126
84
|
case PM_SOURCE_FILE_NODE: {
|
|
127
|
-
|
|
128
|
-
|
|
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));
|
|
85
|
+
const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node;
|
|
86
|
+
return key_hash(PM_STRING_NODE, pm_xxh3(pm_string_source(&cast->filepath), pm_string_length(&cast->filepath)), 0);
|
|
131
87
|
}
|
|
132
88
|
case PM_REGULAR_EXPRESSION_NODE: {
|
|
133
|
-
|
|
134
|
-
|
|
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);
|
|
89
|
+
const pm_regular_expression_node_t *cast = (const pm_regular_expression_node_t *) node;
|
|
90
|
+
return key_hash(PM_REGULAR_EXPRESSION_NODE, pm_xxh3(pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped)), node->flags & (pm_node_flags_t) ~PM_REGULAR_EXPRESSION_FLAGS_ONCE);
|
|
137
91
|
}
|
|
138
92
|
case PM_SYMBOL_NODE: {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
93
|
+
const pm_symbol_node_t *cast = (const pm_symbol_node_t *) node;
|
|
94
|
+
|
|
95
|
+
pm_node_flags_t mask = PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING;
|
|
96
|
+
if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) mask |= PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING;
|
|
97
|
+
|
|
98
|
+
return key_hash(PM_SYMBOL_NODE, pm_xxh3(pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped)), node->flags & mask);
|
|
143
99
|
}
|
|
100
|
+
case PM_FALSE_NODE:
|
|
101
|
+
case PM_NIL_NODE:
|
|
102
|
+
case PM_SOURCE_ENCODING_NODE:
|
|
103
|
+
case PM_TRUE_NODE:
|
|
104
|
+
return key_hash(PM_NODE_TYPE(node), 0, 0);
|
|
144
105
|
default:
|
|
145
106
|
assert(false && "unreachable");
|
|
146
107
|
return 0;
|
|
147
108
|
}
|
|
148
109
|
}
|
|
149
110
|
|
|
150
|
-
/**
|
|
151
|
-
* Insert a node into the node hash. It accepts the hash that should hold the
|
|
152
|
-
* new node, the parser that generated the node, the node to insert, and a
|
|
153
|
-
* comparison function. The comparison function is used for collision detection,
|
|
154
|
-
* and must be able to compare all node types that will be stored in this hash.
|
|
155
|
-
*/
|
|
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
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Free the internal memory associated with the given node hash.
|
|
218
|
-
*/
|
|
219
|
-
static void
|
|
220
|
-
pm_node_hash_free(pm_node_hash_t *hash) {
|
|
221
|
-
if (hash->capacity > 0) xfree(hash->nodes);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
111
|
/**
|
|
225
112
|
* Compare two values that can be compared with a simple numeric comparison.
|
|
226
113
|
*/
|
|
227
|
-
#define
|
|
114
|
+
#define COMPARE(left_, right_) (((left_) < (right_)) ? -1 : ((left_) > (right_)) ? 1 : 0)
|
|
228
115
|
|
|
229
116
|
/**
|
|
230
117
|
* Return the integer value of the given node as an int64_t.
|
|
231
118
|
*/
|
|
232
119
|
static int64_t
|
|
233
|
-
|
|
120
|
+
int64_value(const pm_parser_t *parser, const pm_node_t *node) {
|
|
234
121
|
switch (PM_NODE_TYPE(node)) {
|
|
235
122
|
case PM_INTEGER_NODE: {
|
|
236
123
|
const pm_integer_t *integer = &((const pm_integer_node_t *) node)->value;
|
|
@@ -240,7 +127,7 @@ pm_int64_value(const pm_static_literals_metadata_t *metadata, const pm_node_t *n
|
|
|
240
127
|
return integer->negative ? -value : value;
|
|
241
128
|
}
|
|
242
129
|
case PM_SOURCE_LINE_NODE:
|
|
243
|
-
return (int64_t)
|
|
130
|
+
return (int64_t) pm_line_offset_list_line_column(&parser->line_offsets, node->location.start, parser->start_line).line;
|
|
244
131
|
default:
|
|
245
132
|
assert(false && "unreachable");
|
|
246
133
|
return 0;
|
|
@@ -252,11 +139,9 @@ pm_int64_value(const pm_static_literals_metadata_t *metadata, const pm_node_t *n
|
|
|
252
139
|
* instances.
|
|
253
140
|
*/
|
|
254
141
|
static int
|
|
255
|
-
|
|
142
|
+
compare_integer_nodes(const pm_parser_t *parser, const pm_node_t *left, const pm_node_t *right) {
|
|
256
143
|
if (PM_NODE_TYPE_P(left, PM_SOURCE_LINE_NODE) || PM_NODE_TYPE_P(right, PM_SOURCE_LINE_NODE)) {
|
|
257
|
-
|
|
258
|
-
int64_t right_value = pm_int64_value(metadata, right);
|
|
259
|
-
return PM_NUMERIC_COMPARISON(left_value, right_value);
|
|
144
|
+
return COMPARE(int64_value(parser, left), int64_value(parser, right));
|
|
260
145
|
}
|
|
261
146
|
|
|
262
147
|
const pm_integer_t *left_integer = &((const pm_integer_node_t *) left)->value;
|
|
@@ -268,48 +153,15 @@ pm_compare_integer_nodes(const pm_static_literals_metadata_t *metadata, const pm
|
|
|
268
153
|
* A comparison function for comparing two FloatNode instances.
|
|
269
154
|
*/
|
|
270
155
|
static int
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
const double right_value = ((const pm_float_node_t *) right)->value;
|
|
274
|
-
return PM_NUMERIC_COMPARISON(left_value, right_value);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* A comparison function for comparing two nodes that have attached numbers.
|
|
279
|
-
*/
|
|
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
|
-
}
|
|
156
|
+
compare_float_nodes(PRISM_UNUSED const pm_parser_t *parser, const pm_node_t *left, const pm_node_t *right) {
|
|
157
|
+
return COMPARE(((const pm_float_node_t *) left)->value, ((const pm_float_node_t *) right)->value);
|
|
306
158
|
}
|
|
307
159
|
|
|
308
160
|
/**
|
|
309
161
|
* Return a pointer to the string value of the given node.
|
|
310
162
|
*/
|
|
311
163
|
static const pm_string_t *
|
|
312
|
-
|
|
164
|
+
string_value(const pm_node_t *node) {
|
|
313
165
|
switch (PM_NODE_TYPE(node)) {
|
|
314
166
|
case PM_STRING_NODE:
|
|
315
167
|
return &((const pm_string_node_t *) node)->unescaped;
|
|
@@ -325,134 +177,246 @@ pm_string_value(const pm_node_t *node) {
|
|
|
325
177
|
|
|
326
178
|
/**
|
|
327
179
|
* A comparison function for comparing two nodes that have attached strings.
|
|
180
|
+
* Only the flags in the given mask take part in the comparison.
|
|
181
|
+
*
|
|
182
|
+
* The FORCED_* flags record how a literal was written. An escape that locks the
|
|
183
|
+
* encoding to UTF-8 (a `\u` escape above U+007F, or any `\u` in a character
|
|
184
|
+
* literal) sets FORCED_UTF8 whether or not the file is already UTF-8. So in a
|
|
185
|
+
* UTF-8 file a `\u00E9` escape and a literal e-acute carry different flags
|
|
186
|
+
* while being the same string, and the flag is left out of the mask for them
|
|
187
|
+
* to compare equally. A source file node never carries a FORCED_* flag, because
|
|
188
|
+
* every instance of __FILE__ in a parse resolves to the same encoding.
|
|
328
189
|
*/
|
|
329
190
|
static int
|
|
330
|
-
|
|
331
|
-
const pm_string_t *left_string =
|
|
332
|
-
const pm_string_t *right_string =
|
|
333
|
-
|
|
191
|
+
compare_string_nodes(const pm_node_t *left, const pm_node_t *right, pm_node_flags_t mask) {
|
|
192
|
+
const pm_string_t *left_string = string_value(left);
|
|
193
|
+
const pm_string_t *right_string = string_value(right);
|
|
194
|
+
|
|
195
|
+
int result = pm_string_compare(left_string, right_string);
|
|
196
|
+
if (result != 0) return result;
|
|
197
|
+
|
|
198
|
+
/*
|
|
199
|
+
* Equal bytes are not enough. In a binary source file the two bytes written
|
|
200
|
+
* as `"\xC3\xA9"` stay BINARY while a `"\u00E9"` escape is forced to UTF-8,
|
|
201
|
+
* so those are distinct keys even though the bytes match.
|
|
202
|
+
*/
|
|
203
|
+
return COMPARE(left->flags & mask, right->flags & mask);
|
|
334
204
|
}
|
|
335
205
|
|
|
336
206
|
/**
|
|
337
|
-
* A comparison function for comparing two RegularExpressionNode instances.
|
|
207
|
+
* A comparison function for comparing two RegularExpressionNode instances. The
|
|
208
|
+
* once flag is left out because it only controls how many times an
|
|
209
|
+
* interpolated regular expression is compiled, not its value.
|
|
338
210
|
*/
|
|
339
211
|
static int
|
|
340
|
-
|
|
212
|
+
compare_regular_expression_nodes(PRISM_UNUSED const pm_parser_t *parser, const pm_node_t *left, const pm_node_t *right) {
|
|
341
213
|
const pm_regular_expression_node_t *left_regexp = (const pm_regular_expression_node_t *) left;
|
|
342
214
|
const pm_regular_expression_node_t *right_regexp = (const pm_regular_expression_node_t *) right;
|
|
343
215
|
|
|
344
216
|
int result = pm_string_compare(&left_regexp->unescaped, &right_regexp->unescaped);
|
|
345
217
|
if (result != 0) return result;
|
|
346
218
|
|
|
347
|
-
|
|
219
|
+
pm_node_flags_t mask = (pm_node_flags_t) ~PM_REGULAR_EXPRESSION_FLAGS_ONCE;
|
|
220
|
+
return COMPARE(left->flags & mask, right->flags & mask);
|
|
348
221
|
}
|
|
349
222
|
|
|
350
|
-
#undef PM_NUMERIC_COMPARISON
|
|
351
|
-
|
|
352
223
|
/**
|
|
353
|
-
*
|
|
224
|
+
* Return the type that the given node is compared as. A source line node can
|
|
225
|
+
* equal an integer node and a source file node can equal a string node, so
|
|
226
|
+
* each takes the type of the node it can equal.
|
|
354
227
|
*/
|
|
355
|
-
|
|
356
|
-
|
|
228
|
+
static enum pm_node_type
|
|
229
|
+
static_literal_type(const pm_node_t *node) {
|
|
357
230
|
switch (PM_NODE_TYPE(node)) {
|
|
358
|
-
case PM_INTEGER_NODE:
|
|
359
231
|
case PM_SOURCE_LINE_NODE:
|
|
360
|
-
return
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
232
|
+
return PM_INTEGER_NODE;
|
|
233
|
+
case PM_SOURCE_FILE_NODE:
|
|
234
|
+
return PM_STRING_NODE;
|
|
235
|
+
default:
|
|
236
|
+
return PM_NODE_TYPE(node);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* A comparison function for comparing any two static literal nodes. Nodes that
|
|
242
|
+
* are compared as different types are never equal.
|
|
243
|
+
*/
|
|
244
|
+
static int
|
|
245
|
+
compare_nodes(const pm_parser_t *parser, const pm_node_t *left, const pm_node_t *right) {
|
|
246
|
+
enum pm_node_type left_type = static_literal_type(left);
|
|
247
|
+
enum pm_node_type right_type = static_literal_type(right);
|
|
248
|
+
if (left_type != right_type) return COMPARE(left_type, right_type);
|
|
249
|
+
|
|
250
|
+
switch (left_type) {
|
|
251
|
+
case PM_INTEGER_NODE:
|
|
252
|
+
return compare_integer_nodes(parser, left, right);
|
|
371
253
|
case PM_FLOAT_NODE:
|
|
372
|
-
return
|
|
373
|
-
&literals->float_nodes,
|
|
374
|
-
&(pm_static_literals_metadata_t) {
|
|
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:
|
|
254
|
+
return compare_float_nodes(parser, left, right);
|
|
384
255
|
case PM_IMAGINARY_NODE:
|
|
385
|
-
return
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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,
|
|
400
|
-
&(pm_static_literals_metadata_t) {
|
|
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
|
-
);
|
|
409
|
-
case PM_REGULAR_EXPRESSION_NODE:
|
|
410
|
-
return pm_node_hash_insert(
|
|
411
|
-
&literals->regexp_nodes,
|
|
412
|
-
&(pm_static_literals_metadata_t) {
|
|
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,
|
|
424
|
-
&(pm_static_literals_metadata_t) {
|
|
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;
|
|
256
|
+
return compare_nodes(parser, ((const pm_imaginary_node_t *) left)->numeric, ((const pm_imaginary_node_t *) right)->numeric);
|
|
257
|
+
case PM_RATIONAL_NODE: {
|
|
258
|
+
const pm_rational_node_t *left_rational = (const pm_rational_node_t *) left;
|
|
259
|
+
const pm_rational_node_t *right_rational = (const pm_rational_node_t *) right;
|
|
260
|
+
|
|
261
|
+
int result = pm_integer_compare(&left_rational->denominator, &right_rational->denominator);
|
|
262
|
+
if (result != 0) return result;
|
|
263
|
+
|
|
264
|
+
return pm_integer_compare(&left_rational->numerator, &right_rational->numerator);
|
|
437
265
|
}
|
|
438
|
-
case
|
|
439
|
-
|
|
440
|
-
if (
|
|
441
|
-
return
|
|
266
|
+
case PM_STRING_NODE: {
|
|
267
|
+
pm_node_flags_t mask = PM_STRING_FLAGS_FORCED_BINARY_ENCODING;
|
|
268
|
+
if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) mask |= PM_STRING_FLAGS_FORCED_UTF8_ENCODING;
|
|
269
|
+
return compare_string_nodes(left, right, mask);
|
|
270
|
+
}
|
|
271
|
+
case PM_SYMBOL_NODE: {
|
|
272
|
+
pm_node_flags_t mask = PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING;
|
|
273
|
+
if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) mask |= PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING;
|
|
274
|
+
return compare_string_nodes(left, right, mask);
|
|
442
275
|
}
|
|
443
|
-
case
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
276
|
+
case PM_REGULAR_EXPRESSION_NODE:
|
|
277
|
+
return compare_regular_expression_nodes(parser, left, right);
|
|
278
|
+
case PM_FALSE_NODE:
|
|
279
|
+
case PM_NIL_NODE:
|
|
280
|
+
case PM_SOURCE_ENCODING_NODE:
|
|
281
|
+
case PM_TRUE_NODE:
|
|
282
|
+
return 0;
|
|
283
|
+
default:
|
|
284
|
+
assert(false && "unreachable");
|
|
285
|
+
return 0;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
#undef COMPARE
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Place an entry into a hash table that is known not to hold an equal node, so
|
|
293
|
+
* no comparisons are needed to find its slot.
|
|
294
|
+
*/
|
|
295
|
+
static void
|
|
296
|
+
pm_static_literals_place(pm_static_literals_entry_t *entries, uint32_t mask, pm_static_literals_entry_t entry) {
|
|
297
|
+
uint32_t index = (uint32_t) (entry.hash & mask);
|
|
298
|
+
while (entries[index].node != NULL) {
|
|
299
|
+
index = (index + 1) & mask;
|
|
300
|
+
}
|
|
301
|
+
entries[index] = entry;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Move the nodes of the set into a new hash table with the given capacity. A
|
|
306
|
+
* set that has no hash table yet hashes its inline nodes. A set that already
|
|
307
|
+
* has one moves its entries, which keep their hashes.
|
|
308
|
+
*/
|
|
309
|
+
static void
|
|
310
|
+
pm_static_literals_resize(const pm_parser_t *parser, pm_static_literals_t *literals, uint32_t capacity) {
|
|
311
|
+
pm_static_literals_entry_t *entries = xcalloc(capacity, sizeof(pm_static_literals_entry_t));
|
|
312
|
+
if (entries == NULL) abort();
|
|
313
|
+
|
|
314
|
+
/* It turns out to be more efficient to mask the hash value than to use the
|
|
315
|
+
* modulo operator. Because our capacities are always powers of two, we can
|
|
316
|
+
* use a bitwise AND to get the same result as the modulo operator. */
|
|
317
|
+
uint32_t mask = capacity - 1;
|
|
318
|
+
|
|
319
|
+
if (literals->capacity == 0) {
|
|
320
|
+
for (uint32_t index = 0; index < literals->size; index++) {
|
|
321
|
+
pm_node_t *node = literals->nodes[index];
|
|
322
|
+
pm_static_literals_place(entries, mask, (pm_static_literals_entry_t) { .node = node, .hash = node_hash(parser, node) });
|
|
447
323
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
if (
|
|
451
|
-
|
|
324
|
+
} else {
|
|
325
|
+
for (uint32_t index = 0; index < literals->capacity; index++) {
|
|
326
|
+
if (literals->entries[index].node != NULL) {
|
|
327
|
+
pm_static_literals_place(entries, mask, literals->entries[index]);
|
|
328
|
+
}
|
|
452
329
|
}
|
|
330
|
+
|
|
331
|
+
xfree_sized(literals->entries, literals->capacity * sizeof(pm_static_literals_entry_t));
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
literals->entries = entries;
|
|
335
|
+
literals->capacity = capacity;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Add a node to the set of static literals. If an equal node is already in the
|
|
340
|
+
* set, it is returned (and replaced by the new node if replace is set).
|
|
341
|
+
* Otherwise the new node is inserted and NULL is returned. Nodes that are not
|
|
342
|
+
* static literals are ignored.
|
|
343
|
+
*/
|
|
344
|
+
pm_node_t *
|
|
345
|
+
pm_static_literals_add(const pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node, bool replace) {
|
|
346
|
+
switch (PM_NODE_TYPE(node)) {
|
|
347
|
+
case PM_FALSE_NODE:
|
|
348
|
+
case PM_FLOAT_NODE:
|
|
349
|
+
case PM_IMAGINARY_NODE:
|
|
350
|
+
case PM_INTEGER_NODE:
|
|
351
|
+
case PM_NIL_NODE:
|
|
352
|
+
case PM_RATIONAL_NODE:
|
|
353
|
+
case PM_REGULAR_EXPRESSION_NODE:
|
|
354
|
+
case PM_SOURCE_ENCODING_NODE:
|
|
355
|
+
case PM_SOURCE_FILE_NODE:
|
|
356
|
+
case PM_SOURCE_LINE_NODE:
|
|
357
|
+
case PM_STRING_NODE:
|
|
358
|
+
case PM_SYMBOL_NODE:
|
|
359
|
+
case PM_TRUE_NODE:
|
|
360
|
+
break;
|
|
453
361
|
default:
|
|
454
362
|
return NULL;
|
|
455
363
|
}
|
|
364
|
+
|
|
365
|
+
if (literals->capacity == 0) {
|
|
366
|
+
/* A set without a hash table compares the new node against each of
|
|
367
|
+
* its nodes directly. Comparing nodes of different types returns
|
|
368
|
+
* early, so this costs about as much as hashing the new node. */
|
|
369
|
+
for (uint32_t index = 0; index < literals->size; index++) {
|
|
370
|
+
pm_node_t *existing = literals->nodes[index];
|
|
371
|
+
|
|
372
|
+
if (compare_nodes(parser, existing, node) == 0) {
|
|
373
|
+
if (replace) literals->nodes[index] = node;
|
|
374
|
+
return existing;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (literals->size < PM_STATIC_LITERALS_INLINE_CAPACITY) {
|
|
379
|
+
literals->nodes[literals->size++] = node;
|
|
380
|
+
return NULL;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* The set is full, so its nodes move into a hash table. The table is
|
|
384
|
+
* sized to stay at most half full after this node is added, with room
|
|
385
|
+
* to spare before its first resize. */
|
|
386
|
+
pm_static_literals_resize(parser, literals, PM_STATIC_LITERALS_INLINE_CAPACITY * 4);
|
|
387
|
+
} else if (literals->size * 2 >= literals->capacity) {
|
|
388
|
+
/* If we are out of space, we need to resize the hash. */
|
|
389
|
+
pm_static_literals_resize(parser, literals, literals->capacity * 2);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
uint64_t hash = node_hash(parser, node);
|
|
393
|
+
uint32_t mask = literals->capacity - 1;
|
|
394
|
+
uint32_t index = (uint32_t) (hash & mask);
|
|
395
|
+
|
|
396
|
+
/* We use linear probing to resolve collisions. This means that if the
|
|
397
|
+
* current index is occupied, we will move to the next index and try again.
|
|
398
|
+
* We are guaranteed that this will eventually find an empty slot because we
|
|
399
|
+
* resize the hash when it gets too full. Entries whose hash differs cannot
|
|
400
|
+
* be equal, so only entries with a matching hash are compared. */
|
|
401
|
+
while (literals->entries[index].node != NULL) {
|
|
402
|
+
const pm_static_literals_entry_t *entry = &literals->entries[index];
|
|
403
|
+
if (entry->hash == hash && compare_nodes(parser, entry->node, node) == 0) break;
|
|
404
|
+
index = (index + 1) & mask;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/* If the current index is occupied, we need to return the node that was
|
|
408
|
+
* already in the hash. Otherwise, we can just increment the size and insert
|
|
409
|
+
* the new node. */
|
|
410
|
+
pm_node_t *result = literals->entries[index].node;
|
|
411
|
+
|
|
412
|
+
if (result == NULL) {
|
|
413
|
+
literals->size++;
|
|
414
|
+
literals->entries[index] = (pm_static_literals_entry_t) { .node = node, .hash = hash };
|
|
415
|
+
} else if (replace) {
|
|
416
|
+
literals->entries[index].node = node;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return result;
|
|
456
420
|
}
|
|
457
421
|
|
|
458
422
|
/**
|
|
@@ -460,12 +424,7 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line
|
|
|
460
424
|
*/
|
|
461
425
|
void
|
|
462
426
|
pm_static_literals_free(pm_static_literals_t *literals) {
|
|
463
|
-
|
|
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);
|
|
427
|
+
if (literals->capacity > 0) xfree_sized(literals->entries, literals->capacity * sizeof(pm_static_literals_entry_t));
|
|
469
428
|
}
|
|
470
429
|
|
|
471
430
|
/**
|
|
@@ -492,8 +451,8 @@ pm_static_literal_positive_p(const pm_node_t *node) {
|
|
|
492
451
|
/**
|
|
493
452
|
* Create a string-based representation of the given static literal.
|
|
494
453
|
*/
|
|
495
|
-
|
|
496
|
-
|
|
454
|
+
void
|
|
455
|
+
pm_static_literal_inspect(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *node) {
|
|
497
456
|
switch (PM_NODE_TYPE(node)) {
|
|
498
457
|
case PM_FALSE_NODE:
|
|
499
458
|
pm_buffer_append_string(buffer, "false", 5);
|
|
@@ -502,21 +461,21 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
|
|
|
502
461
|
const double value = ((const pm_float_node_t *) node)->value;
|
|
503
462
|
|
|
504
463
|
if (PRISM_ISINF(value)) {
|
|
505
|
-
if (
|
|
464
|
+
if (parser->start[node->location.start] == '-') {
|
|
506
465
|
pm_buffer_append_byte(buffer, '-');
|
|
507
466
|
}
|
|
508
467
|
pm_buffer_append_string(buffer, "Infinity", 8);
|
|
509
468
|
} else if (value == 0.0) {
|
|
510
|
-
if (
|
|
469
|
+
if (parser->start[node->location.start] == '-') {
|
|
511
470
|
pm_buffer_append_byte(buffer, '-');
|
|
512
471
|
}
|
|
513
472
|
pm_buffer_append_string(buffer, "0.0", 3);
|
|
514
473
|
} else {
|
|
515
474
|
pm_buffer_append_format(buffer, "%g", value);
|
|
516
475
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
476
|
+
/* %g will not insert a .0 for 1e100 (we'll get back 1e+100). So
|
|
477
|
+
* we check for the decimal point and add it in here if it's not
|
|
478
|
+
* present. */
|
|
520
479
|
if (pm_buffer_index(buffer, '.') == SIZE_MAX) {
|
|
521
480
|
size_t exponent_index = pm_buffer_index(buffer, 'e');
|
|
522
481
|
size_t index = exponent_index == SIZE_MAX ? pm_buffer_length(buffer) : exponent_index;
|
|
@@ -530,7 +489,7 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
|
|
|
530
489
|
const pm_node_t *numeric = ((const pm_imaginary_node_t *) node)->numeric;
|
|
531
490
|
pm_buffer_append_string(buffer, "(0", 2);
|
|
532
491
|
if (pm_static_literal_positive_p(numeric)) pm_buffer_append_byte(buffer, '+');
|
|
533
|
-
|
|
492
|
+
pm_static_literal_inspect(buffer, parser, numeric);
|
|
534
493
|
if (PM_NODE_TYPE_P(numeric, PM_RATIONAL_NODE)) {
|
|
535
494
|
pm_buffer_append_byte(buffer, '*');
|
|
536
495
|
}
|
|
@@ -566,7 +525,7 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
|
|
|
566
525
|
break;
|
|
567
526
|
}
|
|
568
527
|
case PM_SOURCE_ENCODING_NODE:
|
|
569
|
-
pm_buffer_append_format(buffer, "#<Encoding:%s>",
|
|
528
|
+
pm_buffer_append_format(buffer, "#<Encoding:%s>", parser->encoding->name);
|
|
570
529
|
break;
|
|
571
530
|
case PM_SOURCE_FILE_NODE: {
|
|
572
531
|
const pm_string_t *filepath = &((const pm_source_file_node_t *) node)->filepath;
|
|
@@ -576,7 +535,7 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
|
|
|
576
535
|
break;
|
|
577
536
|
}
|
|
578
537
|
case PM_SOURCE_LINE_NODE:
|
|
579
|
-
pm_buffer_append_format(buffer, "%
|
|
538
|
+
pm_buffer_append_format(buffer, "%" PRIi32, pm_line_offset_list_line_column(&parser->line_offsets, node->location.start, parser->start_line).line);
|
|
580
539
|
break;
|
|
581
540
|
case PM_STRING_NODE: {
|
|
582
541
|
const pm_string_t *unescaped = &((const pm_string_node_t *) node)->unescaped;
|
|
@@ -599,19 +558,3 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
|
|
|
599
558
|
break;
|
|
600
559
|
}
|
|
601
560
|
}
|
|
602
|
-
|
|
603
|
-
/**
|
|
604
|
-
* Create a string-based representation of the given static literal.
|
|
605
|
-
*/
|
|
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,
|
|
610
|
-
&(pm_static_literals_metadata_t) {
|
|
611
|
-
.newline_list = newline_list,
|
|
612
|
-
.start_line = start_line,
|
|
613
|
-
.encoding_name = encoding_name
|
|
614
|
-
},
|
|
615
|
-
node
|
|
616
|
-
);
|
|
617
|
-
}
|