Ruby 4.1.0dev (2026-10-26 revision 6b15825df0fce73e868b99f98c689755e46cfc8c)
string_query.c
2
3#include "prism/internal/char.h"
4#include "prism/internal/encoding.h"
5#include "prism/internal/parser.h"
6
7#include <assert.h>
8#include <string.h>
9
11typedef enum {
13 PM_SLICE_TYPE_ERROR = -1,
14
16 PM_SLICE_TYPE_NONE,
17
19 PM_SLICE_TYPE_LOCAL,
20
22 PM_SLICE_TYPE_CONSTANT,
23
25 PM_SLICE_TYPE_METHOD_NAME
26} pm_slice_type_t;
27
31static pm_slice_type_t
32pm_slice_type(const uint8_t *source, size_t length, const char *encoding_name) {
33 // first, get the right encoding object
34 const pm_encoding_t *encoding = pm_encoding_find((const uint8_t *) encoding_name, (const uint8_t *) (encoding_name + strlen(encoding_name)));
35 if (encoding == NULL) return PM_SLICE_TYPE_ERROR;
36
37 // check that there is at least one character
38 if (length == 0) return PM_SLICE_TYPE_NONE;
39
40 size_t width;
41 if ((width = encoding->alpha_char(source, (ptrdiff_t) length)) != 0) {
42 // valid because alphabetical
43 } else if (*source == '_') {
44 // valid because underscore
45 width = 1;
46 } else if ((*source >= 0x80) && ((width = encoding->char_width(source, (ptrdiff_t) length)) > 0)) {
47 // valid because multibyte
48 } else {
49 // invalid because no match
50 return PM_SLICE_TYPE_NONE;
51 }
52
53 // determine the type of the slice based on the first character
54 const uint8_t *end = source + length;
55 pm_slice_type_t result = encoding->isupper_char(source, end - source) ? PM_SLICE_TYPE_CONSTANT : PM_SLICE_TYPE_LOCAL;
56
57 // next, iterate through all of the bytes of the string to ensure that they
58 // are all valid identifier characters
59 source += width;
60
61 while (source < end) {
62 if ((width = encoding->alnum_char(source, end - source)) != 0) {
63 // valid because alphanumeric
64 source += width;
65 } else if (*source == '_') {
66 // valid because underscore
67 source++;
68 } else if ((*source >= 0x80) && ((width = encoding->char_width(source, end - source)) > 0)) {
69 // valid because multibyte
70 source += width;
71 } else {
72 // invalid because no match
73 break;
74 }
75 }
76
77 // accept a ! or ? at the end of the slice as a method name
78 if (*source == '!' || *source == '?' || *source == '=') {
79 source++;
80 result = PM_SLICE_TYPE_METHOD_NAME;
81 }
82
83 // valid if we are at the end of the slice
84 return source == end ? result : PM_SLICE_TYPE_NONE;
85}
86
91pm_string_query_local(const uint8_t *source, size_t length, const char *encoding_name) {
92 switch (pm_slice_type(source, length, encoding_name)) {
93 case PM_SLICE_TYPE_ERROR:
95 case PM_SLICE_TYPE_NONE:
96 case PM_SLICE_TYPE_CONSTANT:
97 case PM_SLICE_TYPE_METHOD_NAME:
99 case PM_SLICE_TYPE_LOCAL:
101 }
102
103 assert(false && "unreachable");
105}
106
111pm_string_query_constant(const uint8_t *source, size_t length, const char *encoding_name) {
112 switch (pm_slice_type(source, length, encoding_name)) {
113 case PM_SLICE_TYPE_ERROR:
115 case PM_SLICE_TYPE_NONE:
116 case PM_SLICE_TYPE_LOCAL:
117 case PM_SLICE_TYPE_METHOD_NAME:
119 case PM_SLICE_TYPE_CONSTANT:
121 }
122
123 assert(false && "unreachable");
125}
126
131pm_string_query_method_name(const uint8_t *source, size_t length, const char *encoding_name) {
132#define B(p) ((p) ? PM_STRING_QUERY_TRUE : PM_STRING_QUERY_FALSE)
133#define C1(c) (*source == c)
134#define C2(s) (memcmp(source, s, 2) == 0)
135#define C3(s) (memcmp(source, s, 3) == 0)
136
137 switch (pm_slice_type(source, length, encoding_name)) {
138 case PM_SLICE_TYPE_ERROR:
140 case PM_SLICE_TYPE_NONE:
141 break;
142 case PM_SLICE_TYPE_LOCAL:
143 // numbered parameters are not valid method names
144 return B((length != 2) || (source[0] != '_') || (source[1] == '0') || !pm_char_is_decimal_digit(source[1]));
145 case PM_SLICE_TYPE_CONSTANT:
146 // all constants are valid method names
147 case PM_SLICE_TYPE_METHOD_NAME:
148 // all method names are valid method names
150 }
151
152 switch (length) {
153 case 1:
154 return B(C1('&') || C1('`') || C1('!') || C1('^') || C1('>') || C1('<') || C1('-') || C1('%') || C1('|') || C1('+') || C1('/') || C1('*') || C1('~'));
155 case 2:
156 return B(C2("!=") || C2("!~") || C2("[]") || C2("==") || C2("=~") || C2(">=") || C2(">>") || C2("<=") || C2("<<") || C2("**"));
157 case 3:
158 return B(C3("===") || C3("<=>") || C3("[]="));
159 default:
161 }
162
163#undef B
164#undef C1
165#undef C2
166#undef C3
167}
168
174pm_string_query_keyword(const uint8_t *source, size_t length, const char *encoding_name) {
175 if (pm_encoding_find((const uint8_t *) encoding_name, (const uint8_t *) (encoding_name + strlen(encoding_name))) == NULL) {
177 }
178
179 return pm_keyword_p(source, source + length) ? PM_STRING_QUERY_TRUE : PM_STRING_QUERY_FALSE;
180}
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.