cataract 0.2.5 → 0.3.0

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.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cataract
4
- VERSION = '0.2.5'
4
+ VERSION = '0.3.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cataract
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Cook
@@ -56,7 +56,6 @@ files:
56
56
  - ext/cataract/css_parser.c
57
57
  - ext/cataract/extconf.rb
58
58
  - ext/cataract/flatten.c
59
- - ext/cataract/import_scanner.c
60
59
  - ext/cataract/shorthand_expander.c
61
60
  - ext/cataract/specificity.c
62
61
  - ext/cataract/value_splitter.c
@@ -67,16 +66,6 @@ files:
67
66
  - ext/cataract_color/color_conversion_named.c
68
67
  - ext/cataract_color/color_conversion_oklab.c
69
68
  - ext/cataract_color/extconf.rb
70
- - ext/cataract_old/cataract.c
71
- - ext/cataract_old/cataract.h
72
- - ext/cataract_old/css_parser.c
73
- - ext/cataract_old/extconf.rb
74
- - ext/cataract_old/import_scanner.c
75
- - ext/cataract_old/merge.c
76
- - ext/cataract_old/shorthand_expander.c
77
- - ext/cataract_old/specificity.c
78
- - ext/cataract_old/stylesheet.c
79
- - ext/cataract_old/value_splitter.c
80
69
  - lib/cataract.rb
81
70
  - lib/cataract/at_rule.rb
82
71
  - lib/cataract/color_conversion.rb
@@ -122,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
111
  - !ruby/object:Gem::Version
123
112
  version: '0'
124
113
  requirements: []
125
- rubygems_version: 3.6.9
114
+ rubygems_version: 4.0.10
126
115
  specification_version: 4
127
116
  summary: High-performance CSS parser with C extensions
128
117
  test_files: []
@@ -1,174 +0,0 @@
1
- #include <ruby.h>
2
- #include <ctype.h>
3
- #include <string.h>
4
- #include "cataract.h"
5
-
6
- /*
7
- * Scan CSS for @import statements
8
- *
9
- * Matches patterns:
10
- * @import url("path");
11
- * @import url('path');
12
- * @import "path";
13
- * @import 'path';
14
- * @import url("path") print; (with media query)
15
- *
16
- * Returns array of hashes: [{url: "...", media: "...", full_match: "..."}]
17
- */
18
- VALUE extract_imports(VALUE self, VALUE css_string) {
19
- Check_Type(css_string, T_STRING);
20
-
21
- const char *css = RSTRING_PTR(css_string);
22
- long css_len = RSTRING_LEN(css_string);
23
-
24
- VALUE imports = rb_ary_new();
25
-
26
- const char *p = css;
27
- const char *end = css + css_len;
28
-
29
- while (p < end) {
30
- // Skip whitespace and comments
31
- while (p < end) {
32
- if (IS_WHITESPACE(*p)) {
33
- p++;
34
- } else if (p + 2 <= end && p[0] == '/' && p[1] == '*') {
35
- // Skip /* */ comment
36
- p += 2;
37
- while (p + 1 < end && !(p[0] == '*' && p[1] == '/')) {
38
- p++;
39
- }
40
- if (p + 1 < end) p += 2; // Skip */
41
- } else {
42
- break;
43
- }
44
- }
45
-
46
- // Check for @import
47
- if (p + 7 <= end && strncasecmp(p, "@import", 7) == 0) {
48
- const char *import_start = p;
49
- p += 7;
50
-
51
- // Skip whitespace after @import
52
- while (p < end && IS_WHITESPACE(*p)) p++;
53
-
54
- // Check for optional url(
55
- int has_url_function = 0;
56
- if (p + 4 <= end && strncasecmp(p, "url(", 4) == 0) {
57
- has_url_function = 1;
58
- p += 4;
59
- while (p < end && IS_WHITESPACE(*p)) p++;
60
- }
61
-
62
- // Find opening quote
63
- if (p >= end || (*p != '"' && *p != '\'')) {
64
- // Invalid @import, skip to next semicolon
65
- while (p < end && *p != ';') p++;
66
- if (p < end) p++; // Skip semicolon
67
- continue;
68
- }
69
-
70
- char quote_char = *p;
71
- p++; // Skip opening quote
72
-
73
- const char *url_start = p;
74
-
75
- // Find closing quote (handle escaped quotes)
76
- while (p < end && *p != quote_char) {
77
- if (*p == '\\' && p + 1 < end) {
78
- p += 2; // Skip escaped character
79
- } else {
80
- p++;
81
- }
82
- }
83
-
84
- if (p >= end) {
85
- // Unterminated string
86
- break;
87
- }
88
-
89
- const char *url_end = p;
90
- p++; // Skip closing quote
91
-
92
- // Skip closing paren if we had url(
93
- if (has_url_function) {
94
- while (p < end && IS_WHITESPACE(*p)) p++;
95
- if (p < end && *p == ')') {
96
- p++;
97
- }
98
- }
99
-
100
- // Skip whitespace before optional media query or semicolon
101
- while (p < end && IS_WHITESPACE(*p)) p++;
102
-
103
- // Check for optional media query (everything until semicolon)
104
- const char *media_start = NULL;
105
- const char *media_end = NULL;
106
-
107
- if (p < end && *p != ';') {
108
- media_start = p;
109
-
110
- // Find semicolon
111
- while (p < end && *p != ';') p++;
112
-
113
- media_end = p;
114
-
115
- // Trim trailing whitespace from media query
116
- while (media_end > media_start && IS_WHITESPACE(*(media_end - 1))) {
117
- media_end--;
118
- }
119
- }
120
-
121
- // Skip semicolon
122
- if (p < end && *p == ';') p++;
123
-
124
- const char *import_end = p;
125
-
126
- // Build result hash
127
- VALUE import_hash = rb_hash_new();
128
-
129
- // Extract URL
130
- VALUE url = rb_str_new(url_start, url_end - url_start);
131
- rb_hash_aset(import_hash, ID2SYM(rb_intern("url")), url);
132
-
133
- // Extract media query (or nil)
134
- VALUE media = Qnil;
135
- if (media_start && media_end > media_start) {
136
- media = rb_str_new(media_start, media_end - media_start);
137
- }
138
- rb_hash_aset(import_hash, ID2SYM(rb_intern("media")), media);
139
-
140
- // Extract full match
141
- VALUE full_match = rb_str_new(import_start, import_end - import_start);
142
- rb_hash_aset(import_hash, ID2SYM(rb_intern("full_match")), full_match);
143
-
144
- rb_ary_push(imports, import_hash);
145
-
146
- RB_GC_GUARD(url);
147
- RB_GC_GUARD(media);
148
- RB_GC_GUARD(full_match);
149
- RB_GC_GUARD(import_hash);
150
- } else {
151
- // Not an @import, skip to next line or rule
152
- // Once we hit a non-@import rule (except @charset), stop looking
153
- // Per CSS spec, @import must be at the top
154
-
155
- // Skip @charset if present
156
- if (p + 8 <= end && strncasecmp(p, "@charset", 8) == 0) {
157
- // Skip to semicolon
158
- while (p < end && *p != ';') p++;
159
- if (p < end) p++; // Skip semicolon
160
- continue;
161
- }
162
-
163
- // If we hit any other content, stop scanning for imports
164
- if (p < end && !IS_WHITESPACE(*p)) {
165
- break;
166
- }
167
-
168
- p++;
169
- }
170
- }
171
-
172
- RB_GC_GUARD(imports);
173
- return imports;
174
- }
@@ -1,393 +0,0 @@
1
- #include <ruby.h>
2
- #include <stdio.h>
3
- #include "cataract.h"
4
-
5
- // Global struct class definitions (declared extern in cataract.h)
6
- VALUE cDeclaration;
7
- VALUE cRule;
8
-
9
- // Error class definitions (declared extern in cataract.h)
10
- VALUE eCataractError;
11
- VALUE eParseError;
12
- VALUE eDepthError;
13
- VALUE eSizeError;
14
-
15
- // ============================================================================
16
- // Ruby Bindings and Public API
17
- // ============================================================================
18
-
19
- static VALUE parse_css_internal(VALUE self, VALUE css_string, int depth) {
20
- // Check recursion depth to prevent stack overflow and memory exhaustion
21
- if (depth > MAX_PARSE_DEPTH) {
22
- rb_raise(eDepthError,
23
- "CSS nesting too deep: exceeded maximum depth of %d",
24
- MAX_PARSE_DEPTH);
25
- }
26
-
27
- Check_Type(css_string, T_STRING);
28
-
29
- // Extract @charset if present (must be at very start per W3C spec)
30
- // Handled separately because @charset must be at the absolute start
31
- // and can be processed with simple string operations
32
- VALUE charset = Qnil;
33
- const char *css_start = RSTRING_PTR(css_string);
34
- long css_len = RSTRING_LEN(css_string);
35
-
36
- // Check for @charset at very start: @charset "UTF-8";
37
- // Per spec: exact syntax with double quotes required
38
- if (css_len > 10 && strncmp(css_start, "@charset ", 9) == 0) {
39
- // Find opening quote
40
- char *quote_start = strchr(css_start + 9, '"');
41
- if (quote_start != NULL) {
42
- // Find closing quote and semicolon
43
- char *quote_end = strchr(quote_start + 1, '"');
44
- if (quote_end != NULL) {
45
- char *semicolon = quote_end + 1;
46
- // Skip whitespace between quote and semicolon
47
- while (semicolon < css_start + css_len && IS_WHITESPACE(*semicolon)) {
48
- semicolon++;
49
- }
50
- if (semicolon < css_start + css_len && *semicolon == ';') {
51
- // Valid @charset rule found
52
- charset = rb_str_new(quote_start + 1, quote_end - quote_start - 1);
53
- DEBUG_PRINTF("[@charset] Extracted: '%s'\n", RSTRING_PTR(charset));
54
- }
55
- }
56
- }
57
- }
58
-
59
- // Parse CSS using our C parser implementation
60
- // Returns hash: {query_string => [rules]} already grouped
61
- VALUE rules_by_media = parse_css_impl(css_string, depth, Qnil);
62
-
63
- // GC Guard: Protect Ruby objects from garbage collection
64
- RB_GC_GUARD(css_string);
65
- RB_GC_GUARD(rules_by_media);
66
- RB_GC_GUARD(charset);
67
-
68
- // At depth 0 (top-level parse), return hash with rules and charset
69
- // Nested parses (depth > 0) return the hash directly
70
- if (depth == 0) {
71
- VALUE result = rb_hash_new();
72
- rb_hash_aset(result, ID2SYM(rb_intern("rules")), rules_by_media);
73
- rb_hash_aset(result, ID2SYM(rb_intern("charset")), charset);
74
- return result;
75
- }
76
- return rules_by_media;
77
- }
78
-
79
- /*
80
- * Ruby-facing wrapper for parse_declarations
81
- *
82
- * @param declarations_string [String] CSS declarations like "color: red; margin: 10px"
83
- * @return [Array<Declaration>] Array of parsed declaration structs
84
- */
85
- static VALUE parse_declarations(VALUE self, VALUE declarations_string) {
86
- Check_Type(declarations_string, T_STRING);
87
-
88
- const char *input = RSTRING_PTR(declarations_string);
89
- long input_len = RSTRING_LEN(declarations_string);
90
-
91
- // Strip outer braces and whitespace (css_parser compatibility)
92
- const char *start = input;
93
- const char *end = input + input_len;
94
-
95
- while (start < end && (IS_WHITESPACE(*start) || *start == '{')) start++;
96
- while (end > start && (IS_WHITESPACE(*(end-1)) || *(end-1) == '}')) end--;
97
-
98
- VALUE result = parse_declarations_string(start, end);
99
-
100
- RB_GC_GUARD(result);
101
- return result;
102
- }
103
-
104
- // Public wrapper for Ruby - starts at depth 0
105
- static VALUE parse_css(VALUE self, VALUE css_string) {
106
- // Verify that cRule was initialized in Init_cataract
107
- if (cRule == Qnil || cRule == 0) {
108
- rb_raise(rb_eRuntimeError, "cRule struct class not initialized - Init_cataract may have failed");
109
- }
110
- return parse_css_internal(self, css_string, 0);
111
- }
112
-
113
- /*
114
- * Convert array of Rule structs to full CSS string
115
- * Format: "selector { prop: value; }\nselector2 { prop: value; }"
116
- */
117
- static VALUE rules_to_s(VALUE self, VALUE rules_array) {
118
- Check_Type(rules_array, T_ARRAY);
119
-
120
- long len = RARRAY_LEN(rules_array);
121
- if (len == 0) {
122
- return rb_str_new_cstr("");
123
- }
124
-
125
- // Estimate: ~100 chars per rule (selector + declarations)
126
- VALUE result = rb_str_buf_new(len * 100);
127
-
128
- for (long i = 0; i < len; i++) {
129
- VALUE rule = rb_ary_entry(rules_array, i);
130
-
131
- // Validate this is a Rule struct
132
- if (!RB_TYPE_P(rule, T_STRUCT)) {
133
- rb_raise(rb_eTypeError,
134
- "Expected array of Rule structs, got %s at index %ld",
135
- rb_obj_classname(rule), i);
136
- }
137
-
138
- // Extract: selector, declarations, specificity, media_query
139
- VALUE selector = rb_struct_aref(rule, INT2FIX(RULE_SELECTOR));
140
- VALUE declarations = rb_struct_aref(rule, INT2FIX(RULE_DECLARATIONS));
141
-
142
- // Append selector
143
- rb_str_buf_append(result, selector);
144
- rb_str_buf_cat2(result, " { ");
145
-
146
- // Serialize each declaration
147
- long decl_len = RARRAY_LEN(declarations);
148
- for (long j = 0; j < decl_len; j++) {
149
- VALUE decl = rb_ary_entry(declarations, j);
150
-
151
- VALUE property = rb_struct_aref(decl, INT2FIX(DECL_PROPERTY));
152
- VALUE value = rb_struct_aref(decl, INT2FIX(DECL_VALUE));
153
- VALUE important = rb_struct_aref(decl, INT2FIX(DECL_IMPORTANT));
154
-
155
- rb_str_buf_append(result, property);
156
- rb_str_buf_cat2(result, ": ");
157
- rb_str_buf_append(result, value);
158
-
159
- if (RTEST(important)) {
160
- rb_str_buf_cat2(result, " !important");
161
- }
162
-
163
- rb_str_buf_cat2(result, "; ");
164
- }
165
-
166
- rb_str_buf_cat2(result, "}\n");
167
-
168
- RB_GC_GUARD(rule);
169
- RB_GC_GUARD(selector);
170
- RB_GC_GUARD(declarations);
171
- }
172
-
173
- RB_GC_GUARD(result);
174
- return result;
175
- }
176
-
177
- /*
178
- * Convert array of Declaration structs to CSS string
179
- * Format: "prop: value; prop2: value2 !important; "
180
- *
181
- * This is the core serialization logic used by both:
182
- * - Declarations#to_s (instance method)
183
- * - Internal C serialization (stylesheet.c)
184
- *
185
- * Exported (non-static) so stylesheet.c can call it
186
- */
187
- VALUE declarations_array_to_s(VALUE declarations_array) {
188
- Check_Type(declarations_array, T_ARRAY);
189
-
190
- long len = RARRAY_LEN(declarations_array);
191
- if (len == 0) {
192
- return rb_str_new_cstr("");
193
- }
194
-
195
- // Use rb_str_buf_new for efficient string building
196
- VALUE result = rb_str_buf_new(len * 32); // Estimate 32 chars per declaration
197
-
198
- for (long i = 0; i < len; i++) {
199
- VALUE decl = rb_ary_entry(declarations_array, i);
200
-
201
- // Validate this is a Declaration struct
202
- if (!RB_TYPE_P(decl, T_STRUCT) || rb_obj_class(decl) != cDeclaration) {
203
- rb_raise(rb_eTypeError,
204
- "Expected array of Declaration structs, got %s at index %ld",
205
- rb_obj_classname(decl), i);
206
- }
207
-
208
- // Extract struct fields
209
- VALUE property = rb_struct_aref(decl, INT2FIX(DECL_PROPERTY));
210
- VALUE value = rb_struct_aref(decl, INT2FIX(DECL_VALUE));
211
- VALUE important = rb_struct_aref(decl, INT2FIX(DECL_IMPORTANT));
212
-
213
- // Append: "property: value"
214
- rb_str_buf_append(result, property);
215
- rb_str_buf_cat2(result, ": ");
216
- rb_str_buf_append(result, value);
217
-
218
- // Append " !important" if needed
219
- if (RTEST(important)) {
220
- rb_str_buf_cat2(result, " !important");
221
- }
222
-
223
- rb_str_buf_cat2(result, "; ");
224
-
225
- RB_GC_GUARD(decl);
226
- RB_GC_GUARD(property);
227
- RB_GC_GUARD(value);
228
- RB_GC_GUARD(important);
229
- }
230
-
231
- // Strip trailing space
232
- rb_str_set_len(result, RSTRING_LEN(result) - 1);
233
-
234
- RB_GC_GUARD(result);
235
- return result;
236
- }
237
-
238
- /*
239
- * Instance method: Declarations#to_s
240
- * Converts declarations to CSS string
241
- *
242
- * @return [String] CSS declarations like "color: red; margin: 10px !important;"
243
- */
244
- static VALUE declarations_to_s_method(VALUE self) {
245
- // Get @values instance variable (array of Declaration structs)
246
- VALUE values = rb_ivar_get(self, rb_intern("@values"));
247
-
248
- // Call core serialization function
249
- return declarations_array_to_s(values);
250
- }
251
-
252
- void Init_cataract() {
253
- VALUE module = rb_define_module("Cataract");
254
-
255
- // Initialize merge constants (cached strings and symbol IDs)
256
- init_merge_constants();
257
-
258
- // Define error class hierarchy
259
- eCataractError = rb_define_class_under(module, "Error", rb_eStandardError);
260
- eParseError = rb_define_class_under(module, "ParseError", eCataractError);
261
- eDepthError = rb_define_class_under(module, "DepthError", eCataractError);
262
- eSizeError = rb_define_class_under(module, "SizeError", eCataractError);
263
-
264
- // Define Cataract::Declarations class (Ruby side will add methods)
265
- VALUE cDeclarations = rb_define_class_under(module, "Declarations", rb_cObject);
266
-
267
- // Define Cataract::Declaration = Struct.new(:property, :value, :important)
268
- cDeclaration = rb_struct_define_under(
269
- module,
270
- "Declaration",
271
- "property",
272
- "value",
273
- "important",
274
- NULL
275
- );
276
-
277
- // Add methods to Declarations class
278
- rb_define_method(cDeclarations, "to_s", declarations_to_s_method, 0);
279
-
280
- // Define Cataract::Rule = Struct.new(:selector, :declarations, :specificity)
281
- // Note: media_query removed - media info now stored at group level in hash structure
282
- cRule = rb_struct_define_under(
283
- module,
284
- "Rule",
285
- "selector",
286
- "declarations",
287
- "specificity",
288
- NULL
289
- );
290
-
291
- // Define Cataract::Stylesheet class (Ruby side will reopen and add methods)
292
- rb_define_class_under(module, "Stylesheet", rb_cObject);
293
-
294
- rb_define_module_function(module, "parse_css", parse_css, 1);
295
- rb_define_module_function(module, "parse_declarations", parse_declarations, 1);
296
- rb_define_module_function(module, "calculate_specificity", calculate_specificity, 1);
297
- rb_define_module_function(module, "merge_rules", cataract_merge_wrapper, 1);
298
- rb_define_module_function(module, "apply_cascade", cataract_merge_wrapper, 1); // Alias with better name
299
- /* @api private */
300
- rb_define_module_function(module, "_rules_to_s", rules_to_s, 1);
301
-
302
- /* @api private */
303
- rb_define_module_function(module, "_split_value", cataract_split_value, 1);
304
- /* @api private */
305
- rb_define_module_function(module, "_expand_margin", cataract_expand_margin, 1);
306
- /* @api private */
307
- rb_define_module_function(module, "_expand_padding", cataract_expand_padding, 1);
308
- /* @api private */
309
- rb_define_module_function(module, "_expand_border_color", cataract_expand_border_color, 1);
310
- /* @api private */
311
- rb_define_module_function(module, "_expand_border_style", cataract_expand_border_style, 1);
312
- /* @api private */
313
- rb_define_module_function(module, "_expand_border_width", cataract_expand_border_width, 1);
314
- /* @api private */
315
- rb_define_module_function(module, "_expand_border", cataract_expand_border, 1);
316
- /* @api private */
317
- rb_define_module_function(module, "_expand_border_side", cataract_expand_border_side, 2);
318
- /* @api private */
319
- rb_define_module_function(module, "_expand_font", cataract_expand_font, 1);
320
- /* @api private */
321
- rb_define_module_function(module, "_expand_list_style", cataract_expand_list_style, 1);
322
- /* @api private */
323
- rb_define_module_function(module, "_expand_background", cataract_expand_background, 1);
324
-
325
- // Shorthand creation (inverse of expansion)
326
- /* @api private */
327
- rb_define_module_function(module, "_create_margin_shorthand", cataract_create_margin_shorthand, 1);
328
- /* @api private */
329
- rb_define_module_function(module, "_create_padding_shorthand", cataract_create_padding_shorthand, 1);
330
- /* @api private */
331
- rb_define_module_function(module, "_create_border_width_shorthand", cataract_create_border_width_shorthand, 1);
332
- /* @api private */
333
- rb_define_module_function(module, "_create_border_style_shorthand", cataract_create_border_style_shorthand, 1);
334
- /* @api private */
335
- rb_define_module_function(module, "_create_border_color_shorthand", cataract_create_border_color_shorthand, 1);
336
- /* @api private */
337
- rb_define_module_function(module, "_create_border_shorthand", cataract_create_border_shorthand, 1);
338
- /* @api private */
339
- rb_define_module_function(module, "_create_background_shorthand", cataract_create_background_shorthand, 1);
340
- /* @api private */
341
- rb_define_module_function(module, "_create_font_shorthand", cataract_create_font_shorthand, 1);
342
- /* @api private */
343
- rb_define_module_function(module, "_create_list_style_shorthand", cataract_create_list_style_shorthand, 1);
344
-
345
- // Serialization
346
- /* @api private */
347
- rb_define_module_function(module, "_stylesheet_to_s_c", stylesheet_to_s_c, 2);
348
- /* @api private */
349
- rb_define_module_function(module, "_stylesheet_to_formatted_s_c", stylesheet_to_formatted_s_c, 2);
350
-
351
- // Import scanning
352
- rb_define_module_function(module, "extract_imports", extract_imports, 1);
353
-
354
- // Export string allocation mode as a constant for verification in benchmarks
355
- #ifdef DISABLE_STR_BUF_OPTIMIZATION
356
- rb_define_const(module, "STRING_ALLOC_MODE", ID2SYM(rb_intern("dynamic")));
357
- #else
358
- rb_define_const(module, "STRING_ALLOC_MODE", ID2SYM(rb_intern("buffer")));
359
- #endif
360
-
361
- // Export compile-time optimization flags as a hash for runtime introspection
362
- VALUE compile_flags = rb_hash_new();
363
-
364
- #ifdef DISABLE_STR_BUF_OPTIMIZATION
365
- rb_hash_aset(compile_flags, ID2SYM(rb_intern("str_buf_optimization")), Qfalse);
366
- #else
367
- rb_hash_aset(compile_flags, ID2SYM(rb_intern("str_buf_optimization")), Qtrue);
368
- #endif
369
-
370
- #ifdef CATARACT_DEBUG
371
- rb_hash_aset(compile_flags, ID2SYM(rb_intern("debug")), Qtrue);
372
- #else
373
- rb_hash_aset(compile_flags, ID2SYM(rb_intern("debug")), Qfalse);
374
- #endif
375
-
376
- #ifdef DISABLE_LOOP_UNROLL
377
- rb_hash_aset(compile_flags, ID2SYM(rb_intern("loop_unroll")), Qfalse);
378
- #else
379
- rb_hash_aset(compile_flags, ID2SYM(rb_intern("loop_unroll")), Qtrue);
380
- #endif
381
-
382
- // Note: Compiler flags like -O3, -march=native, -funroll-loops don't have
383
- // preprocessor defines, so we can't detect them at runtime. They're purely
384
- // compiler optimizations that affect the generated code.
385
-
386
- rb_define_const(module, "COMPILE_FLAGS", compile_flags);
387
-
388
- // NOTE: Color conversion is now a separate extension (cataract_color)
389
- // It's initialized when you require 'cataract/color_conversion'
390
- }
391
-
392
- // NOTE: shorthand_expander.c and value_splitter.c are now compiled separately (not included)
393
-