Ruby 4.1.0dev (2026-09-07 revision 11ce3778c6eacc10897729314e5ab3bed7830971)
string_query.c
2
3#include "prism/internal/char.h"
4#include "prism/internal/encoding.h"
5
6#include <assert.h>
7#include <string.h>
8
10typedef enum {
12 PM_SLICE_TYPE_ERROR = -1,
13
15 PM_SLICE_TYPE_NONE,
16
18 PM_SLICE_TYPE_LOCAL,
19
21 PM_SLICE_TYPE_CONSTANT,
22
24 PM_SLICE_TYPE_METHOD_NAME
25} pm_slice_type_t;
26
30static pm_slice_type_t
31pm_slice_type(const uint8_t *source, size_t length, const char *encoding_name) {
32 // first, get the right encoding object
33 const pm_encoding_t *encoding = pm_encoding_find((const uint8_t *) encoding_name, (const uint8_t *) (encoding_name + strlen(encoding_name)));
34 if (encoding == NULL) return PM_SLICE_TYPE_ERROR;
35
36 // check that there is at least one character
37 if (length == 0) return PM_SLICE_TYPE_NONE;
38
39 size_t width;
40 if ((width = encoding->alpha_char(source, (ptrdiff_t) length)) != 0) {
41 // valid because alphabetical
42 } else if (*source == '_') {
43 // valid because underscore
44 width = 1;
45 } else if ((*source >= 0x80) && ((width = encoding->char_width(source, (ptrdiff_t) length)) > 0)) {
46 // valid because multibyte
47 } else {
48 // invalid because no match
49 return PM_SLICE_TYPE_NONE;
50 }
51
52 // determine the type of the slice based on the first character
53 const uint8_t *end = source + length;
54 pm_slice_type_t result = encoding->isupper_char(source, end - source) ? PM_SLICE_TYPE_CONSTANT : PM_SLICE_TYPE_LOCAL;
55
56 // next, iterate through all of the bytes of the string to ensure that they
57 // are all valid identifier characters
58 source += width;
59
60 while (source < end) {
61 if ((width = encoding->alnum_char(source, end - source)) != 0) {
62 // valid because alphanumeric
63 source += width;
64 } else if (*source == '_') {
65 // valid because underscore
66 source++;
67 } else if ((*source >= 0x80) && ((width = encoding->char_width(source, end - source)) > 0)) {
68 // valid because multibyte
69 source += width;
70 } else {
71 // invalid because no match
72 break;
73 }
74 }
75
76 // accept a ! or ? at the end of the slice as a method name
77 if (*source == '!' || *source == '?' || *source == '=') {
78 source++;
79 result = PM_SLICE_TYPE_METHOD_NAME;
80 }
81
82 // valid if we are at the end of the slice
83 return source == end ? result : PM_SLICE_TYPE_NONE;
84}
85
90pm_string_query_local(const uint8_t *source, size_t length, const char *encoding_name) {
91 switch (pm_slice_type(source, length, encoding_name)) {
92 case PM_SLICE_TYPE_ERROR:
94 case PM_SLICE_TYPE_NONE:
95 case PM_SLICE_TYPE_CONSTANT:
96 case PM_SLICE_TYPE_METHOD_NAME:
98 case PM_SLICE_TYPE_LOCAL:
100 }
101
102 assert(false && "unreachable");
104}
105
110pm_string_query_constant(const uint8_t *source, size_t length, const char *encoding_name) {
111 switch (pm_slice_type(source, length, encoding_name)) {
112 case PM_SLICE_TYPE_ERROR:
114 case PM_SLICE_TYPE_NONE:
115 case PM_SLICE_TYPE_LOCAL:
116 case PM_SLICE_TYPE_METHOD_NAME:
118 case PM_SLICE_TYPE_CONSTANT:
120 }
121
122 assert(false && "unreachable");
124}
125
130pm_string_query_method_name(const uint8_t *source, size_t length, const char *encoding_name) {
131#define B(p) ((p) ? PM_STRING_QUERY_TRUE : PM_STRING_QUERY_FALSE)
132#define C1(c) (*source == c)
133#define C2(s) (memcmp(source, s, 2) == 0)
134#define C3(s) (memcmp(source, s, 3) == 0)
135
136 switch (pm_slice_type(source, length, encoding_name)) {
137 case PM_SLICE_TYPE_ERROR:
139 case PM_SLICE_TYPE_NONE:
140 break;
141 case PM_SLICE_TYPE_LOCAL:
142 // numbered parameters are not valid method names
143 return B((length != 2) || (source[0] != '_') || (source[1] == '0') || !pm_char_is_decimal_digit(source[1]));
144 case PM_SLICE_TYPE_CONSTANT:
145 // all constants are valid method names
146 case PM_SLICE_TYPE_METHOD_NAME:
147 // all method names are valid method names
149 }
150
151 switch (length) {
152 case 1:
153 return B(C1('&') || C1('`') || C1('!') || C1('^') || C1('>') || C1('<') || C1('-') || C1('%') || C1('|') || C1('+') || C1('/') || C1('*') || C1('~'));
154 case 2:
155 return B(C2("!=") || C2("!~") || C2("[]") || C2("==") || C2("=~") || C2(">=") || C2(">>") || C2("<=") || C2("<<") || C2("**"));
156 case 3:
157 return B(C3("===") || C3("<=>") || C3("[]="));
158 default:
160 }
161
162#undef B
163#undef C1
164#undef C2
165#undef C3
166}
Functions for querying properties of strings, such as whether they are valid local variable names,...
pm_string_query_t
Represents the results of a slice query.
@ PM_STRING_QUERY_TRUE
Returned if the result of the slice query is true.
@ PM_STRING_QUERY_ERROR
Returned if the encoding given to a slice query was invalid.
@ PM_STRING_QUERY_FALSE
Returned if the result of the slice query is false.