1use crate::error::set_runtime_error;
14use crate::seqstring::global_string;
15use crate::stack::{Stack, pop, push};
16use crate::value::Value;
17use std::sync::Arc;
18
19#[unsafe(no_mangle)]
28pub unsafe extern "C" fn patch_seq_string_split(stack: Stack) -> Stack {
29 use crate::value::VariantData;
30
31 assert!(!stack.is_null(), "string_split: stack is empty");
32
33 let (stack, delim_val) = unsafe { pop(stack) };
34 assert!(!stack.is_null(), "string_split: need two strings");
35 let (stack, str_val) = unsafe { pop(stack) };
36
37 match (str_val, delim_val) {
38 (Value::String(s), Value::String(d)) => {
39 let fields: Vec<Value> = s
41 .as_str()
42 .split(d.as_str())
43 .map(|part| Value::String(global_string(part.to_owned())))
44 .collect();
45
46 let variant = Value::Variant(Arc::new(VariantData::new(
48 global_string("List".to_string()),
49 fields,
50 )));
51
52 unsafe { push(stack, variant) }
53 }
54 _ => panic!("string_split: expected two strings on stack"),
55 }
56}
57
58#[unsafe(no_mangle)]
65pub unsafe extern "C" fn patch_seq_string_empty(stack: Stack) -> Stack {
66 assert!(!stack.is_null(), "string_empty: stack is empty");
67
68 let (stack, value) = unsafe { pop(stack) };
69
70 match value {
71 Value::String(s) => {
72 let is_empty = s.as_str().is_empty();
73 unsafe { push(stack, Value::Bool(is_empty)) }
74 }
75 _ => panic!("string_empty: expected String on stack"),
76 }
77}
78
79#[unsafe(no_mangle)]
86pub unsafe extern "C" fn patch_seq_string_contains(stack: Stack) -> Stack {
87 assert!(!stack.is_null(), "string_contains: stack is empty");
88
89 let (stack, substring_val) = unsafe { pop(stack) };
90 assert!(!stack.is_null(), "string_contains: need two strings");
91 let (stack, str_val) = unsafe { pop(stack) };
92
93 match (str_val, substring_val) {
94 (Value::String(s), Value::String(sub)) => {
95 let contains = s.as_str().contains(sub.as_str());
96 unsafe { push(stack, Value::Bool(contains)) }
97 }
98 _ => panic!("string_contains: expected two strings on stack"),
99 }
100}
101
102#[unsafe(no_mangle)]
109pub unsafe extern "C" fn patch_seq_string_starts_with(stack: Stack) -> Stack {
110 assert!(!stack.is_null(), "string_starts_with: stack is empty");
111
112 let (stack, prefix_val) = unsafe { pop(stack) };
113 assert!(!stack.is_null(), "string_starts_with: need two strings");
114 let (stack, str_val) = unsafe { pop(stack) };
115
116 match (str_val, prefix_val) {
117 (Value::String(s), Value::String(prefix)) => {
118 let starts = s.as_str().starts_with(prefix.as_str());
119 unsafe { push(stack, Value::Bool(starts)) }
120 }
121 _ => panic!("string_starts_with: expected two strings on stack"),
122 }
123}
124
125#[unsafe(no_mangle)]
132pub unsafe extern "C" fn patch_seq_string_concat(stack: Stack) -> Stack {
133 assert!(!stack.is_null(), "string_concat: stack is empty");
134
135 let (stack, str2_val) = unsafe { pop(stack) };
136 assert!(!stack.is_null(), "string_concat: need two strings");
137 let (stack, str1_val) = unsafe { pop(stack) };
138
139 match (str1_val, str2_val) {
140 (Value::String(s1), Value::String(s2)) => {
141 let result = format!("{}{}", s1.as_str(), s2.as_str());
142 unsafe { push(stack, Value::String(global_string(result))) }
143 }
144 _ => panic!("string_concat: expected two strings on stack"),
145 }
146}
147
148#[unsafe(no_mangle)]
158pub unsafe extern "C" fn patch_seq_string_length(stack: Stack) -> Stack {
159 assert!(!stack.is_null(), "string_length: stack is empty");
160
161 let (stack, str_val) = unsafe { pop(stack) };
162
163 match str_val {
164 Value::String(s) => {
165 let len = s.as_str().chars().count() as i64;
166 unsafe { push(stack, Value::Int(len)) }
167 }
168 _ => panic!("string_length: expected String on stack"),
169 }
170}
171
172#[unsafe(no_mangle)]
181pub unsafe extern "C" fn patch_seq_string_byte_length(stack: Stack) -> Stack {
182 assert!(!stack.is_null(), "string_byte_length: stack is empty");
183
184 let (stack, str_val) = unsafe { pop(stack) };
185
186 match str_val {
187 Value::String(s) => {
188 let len = s.as_str().len() as i64;
189 unsafe { push(stack, Value::Int(len)) }
190 }
191 _ => panic!("string_byte_length: expected String on stack"),
192 }
193}
194
195#[unsafe(no_mangle)]
205pub unsafe extern "C" fn patch_seq_string_char_at(stack: Stack) -> Stack {
206 assert!(!stack.is_null(), "string_char_at: stack is empty");
207
208 let (stack, index_val) = unsafe { pop(stack) };
209 assert!(!stack.is_null(), "string_char_at: need string and index");
210 let (stack, str_val) = unsafe { pop(stack) };
211
212 match (str_val, index_val) {
213 (Value::String(s), Value::Int(index)) => {
214 let result = if index < 0 {
215 -1
216 } else {
217 s.as_str()
218 .chars()
219 .nth(index as usize)
220 .map(|c| c as i64)
221 .unwrap_or(-1)
222 };
223 unsafe { push(stack, Value::Int(result)) }
224 }
225 _ => panic!("string_char_at: expected String and Int on stack"),
226 }
227}
228
229#[unsafe(no_mangle)]
245pub unsafe extern "C" fn patch_seq_string_substring(stack: Stack) -> Stack {
246 assert!(!stack.is_null(), "string_substring: stack is empty");
247
248 let (stack, len_val) = unsafe { pop(stack) };
249 assert!(
250 !stack.is_null(),
251 "string_substring: need string, start, len"
252 );
253 let (stack, start_val) = unsafe { pop(stack) };
254 assert!(
255 !stack.is_null(),
256 "string_substring: need string, start, len"
257 );
258 let (stack, str_val) = unsafe { pop(stack) };
259
260 match (str_val, start_val, len_val) {
261 (Value::String(s), Value::Int(start), Value::Int(len)) => {
262 let result = if start < 0 || len < 0 {
263 String::new()
264 } else {
265 s.as_str()
266 .chars()
267 .skip(start as usize)
268 .take(len as usize)
269 .collect()
270 };
271 unsafe { push(stack, Value::String(global_string(result))) }
272 }
273 _ => panic!("string_substring: expected String, Int, Int on stack"),
274 }
275}
276
277#[unsafe(no_mangle)]
287pub unsafe extern "C" fn patch_seq_char_to_string(stack: Stack) -> Stack {
288 assert!(!stack.is_null(), "char_to_string: stack is empty");
289
290 let (stack, code_point_val) = unsafe { pop(stack) };
291
292 match code_point_val {
293 Value::Int(code_point) => {
294 let result = if !(0..=0x10FFFF).contains(&code_point) {
295 String::new()
297 } else {
298 match char::from_u32(code_point as u32) {
299 Some(c) => c.to_string(),
300 None => String::new(), }
302 };
303 unsafe { push(stack, Value::String(global_string(result))) }
304 }
305 _ => panic!("char_to_string: expected Int on stack"),
306 }
307}
308
309#[unsafe(no_mangle)]
319pub unsafe extern "C" fn patch_seq_string_find(stack: Stack) -> Stack {
320 assert!(!stack.is_null(), "string_find: stack is empty");
321
322 let (stack, needle_val) = unsafe { pop(stack) };
323 assert!(!stack.is_null(), "string_find: need string and needle");
324 let (stack, str_val) = unsafe { pop(stack) };
325
326 match (str_val, needle_val) {
327 (Value::String(haystack), Value::String(needle)) => {
328 let haystack_str = haystack.as_str();
329 let needle_str = needle.as_str();
330
331 let result = match haystack_str.find(needle_str) {
333 Some(byte_pos) => {
334 haystack_str[..byte_pos].chars().count() as i64
336 }
337 None => -1,
338 };
339 unsafe { push(stack, Value::Int(result)) }
340 }
341 _ => panic!("string_find: expected two Strings on stack"),
342 }
343}
344
345#[unsafe(no_mangle)]
352pub unsafe extern "C" fn patch_seq_string_trim(stack: Stack) -> Stack {
353 assert!(!stack.is_null(), "string_trim: stack is empty");
354
355 let (stack, str_val) = unsafe { pop(stack) };
356
357 match str_val {
358 Value::String(s) => {
359 let trimmed = s.as_str().trim();
360 unsafe { push(stack, Value::String(global_string(trimmed.to_owned()))) }
361 }
362 _ => panic!("string_trim: expected String on stack"),
363 }
364}
365
366#[unsafe(no_mangle)]
373pub unsafe extern "C" fn patch_seq_string_to_upper(stack: Stack) -> Stack {
374 assert!(!stack.is_null(), "string_to_upper: stack is empty");
375
376 let (stack, str_val) = unsafe { pop(stack) };
377
378 match str_val {
379 Value::String(s) => {
380 let upper = s.as_str().to_uppercase();
381 unsafe { push(stack, Value::String(global_string(upper))) }
382 }
383 _ => panic!("string_to_upper: expected String on stack"),
384 }
385}
386
387#[unsafe(no_mangle)]
394pub unsafe extern "C" fn patch_seq_string_to_lower(stack: Stack) -> Stack {
395 assert!(!stack.is_null(), "string_to_lower: stack is empty");
396
397 let (stack, str_val) = unsafe { pop(stack) };
398
399 match str_val {
400 Value::String(s) => {
401 let lower = s.as_str().to_lowercase();
402 unsafe { push(stack, Value::String(global_string(lower))) }
403 }
404 _ => panic!("string_to_lower: expected String on stack"),
405 }
406}
407
408#[unsafe(no_mangle)]
415pub unsafe extern "C" fn patch_seq_string_equal(stack: Stack) -> Stack {
416 assert!(!stack.is_null(), "string_equal: stack is empty");
417
418 let (stack, str2_val) = unsafe { pop(stack) };
419 assert!(!stack.is_null(), "string_equal: need two strings");
420 let (stack, str1_val) = unsafe { pop(stack) };
421
422 match (str1_val, str2_val) {
423 (Value::String(s1), Value::String(s2)) => {
424 let equal = s1.as_str() == s2.as_str();
425 unsafe { push(stack, Value::Bool(equal)) }
426 }
427 _ => panic!("string_equal: expected two strings on stack"),
428 }
429}
430
431#[unsafe(no_mangle)]
441pub unsafe extern "C" fn patch_seq_symbol_equal(stack: Stack) -> Stack {
442 assert!(!stack.is_null(), "symbol_equal: stack is empty");
443
444 let (stack, sym2_val) = unsafe { pop(stack) };
445 assert!(!stack.is_null(), "symbol_equal: need two symbols");
446 let (stack, sym1_val) = unsafe { pop(stack) };
447
448 match (sym1_val, sym2_val) {
449 (Value::Symbol(s1), Value::Symbol(s2)) => {
450 let equal = if s1.is_interned() && s2.is_interned() {
452 s1.as_ptr() == s2.as_ptr()
453 } else {
454 s1.as_str() == s2.as_str()
456 };
457 unsafe { push(stack, Value::Bool(equal)) }
458 }
459 _ => panic!("symbol_equal: expected two symbols on stack"),
460 }
461}
462
463#[unsafe(no_mangle)]
480pub unsafe extern "C" fn patch_seq_json_escape(stack: Stack) -> Stack {
481 assert!(!stack.is_null(), "json_escape: stack is empty");
482
483 let (stack, value) = unsafe { pop(stack) };
484
485 match value {
486 Value::String(s) => {
487 let input = s.as_str();
488 let mut result = String::with_capacity(input.len() + 16);
489
490 for ch in input.chars() {
491 match ch {
492 '"' => result.push_str("\\\""),
493 '\\' => result.push_str("\\\\"),
494 '\n' => result.push_str("\\n"),
495 '\r' => result.push_str("\\r"),
496 '\t' => result.push_str("\\t"),
497 '\x08' => result.push_str("\\b"), '\x0C' => result.push_str("\\f"), c if c.is_control() => {
502 result.push_str(&format!("\\u{:04X}", c as u32));
503 }
504 c => result.push(c),
505 }
506 }
507
508 unsafe { push(stack, Value::String(global_string(result))) }
509 }
510 _ => panic!("json_escape: expected String on stack"),
511 }
512}
513
514#[unsafe(no_mangle)]
527pub unsafe extern "C" fn patch_seq_string_to_int(stack: Stack) -> Stack {
528 if stack.is_null() {
529 set_runtime_error("string->int: stack is empty");
530 return stack;
531 }
532 let (stack, val) = unsafe { pop(stack) };
533
534 match val {
535 Value::String(s) => match s.as_str().trim().parse::<i64>() {
536 Ok(i) => {
537 let stack = unsafe { push(stack, Value::Int(i)) };
538 unsafe { push(stack, Value::Bool(true)) }
539 }
540 Err(_) => {
541 let stack = unsafe { push(stack, Value::Int(0)) };
542 unsafe { push(stack, Value::Bool(false)) }
543 }
544 },
545 _ => {
546 set_runtime_error("string->int: expected String on stack");
547 let stack = unsafe { push(stack, Value::Int(0)) };
548 unsafe { push(stack, Value::Bool(false)) }
549 }
550 }
551}
552
553#[unsafe(no_mangle)]
563pub unsafe extern "C" fn patch_seq_string_chomp(stack: Stack) -> Stack {
564 assert!(!stack.is_null(), "string_chomp: stack is empty");
565
566 let (stack, str_val) = unsafe { pop(stack) };
567
568 match str_val {
569 Value::String(s) => {
570 let mut result = s.as_str().to_owned();
571 if result.ends_with('\n') {
572 result.pop();
573 if result.ends_with('\r') {
574 result.pop();
575 }
576 }
577 unsafe { push(stack, Value::String(global_string(result))) }
578 }
579 _ => panic!("string_chomp: expected String on stack"),
580 }
581}
582
583pub use patch_seq_char_to_string as char_to_string;
585pub use patch_seq_json_escape as json_escape;
586pub use patch_seq_string_byte_length as string_byte_length;
587pub use patch_seq_string_char_at as string_char_at;
588pub use patch_seq_string_chomp as string_chomp;
589pub use patch_seq_string_concat as string_concat;
590pub use patch_seq_string_contains as string_contains;
591pub use patch_seq_string_empty as string_empty;
592pub use patch_seq_string_equal as string_equal;
593pub use patch_seq_string_find as string_find;
594pub use patch_seq_string_length as string_length;
595pub use patch_seq_string_split as string_split;
596pub use patch_seq_string_starts_with as string_starts_with;
597pub use patch_seq_string_substring as string_substring;
598pub use patch_seq_string_to_int as string_to_int;
599pub use patch_seq_string_to_lower as string_to_lower;
600pub use patch_seq_string_to_upper as string_to_upper;
601pub use patch_seq_string_trim as string_trim;
602
603#[unsafe(no_mangle)]
631pub unsafe extern "C" fn patch_seq_string_to_cstring(stack: Stack, _out: *mut u8) -> *mut u8 {
632 assert!(!stack.is_null(), "string_to_cstring: stack is empty");
633
634 use crate::stack::peek;
635 use crate::value::Value;
636
637 let val = unsafe { peek(stack) };
639 let s = match &val {
640 Value::String(s) => s,
641 other => panic!(
642 "string_to_cstring: expected String on stack, got {:?}",
643 other
644 ),
645 };
646
647 let str_ptr = s.as_ptr();
648 let len = s.len();
649
650 let alloc_size = len.checked_add(1).unwrap_or_else(|| {
652 panic!(
653 "string_to_cstring: string too large for C conversion (len={})",
654 len
655 )
656 });
657
658 let ptr = unsafe { libc::malloc(alloc_size) as *mut u8 };
660 if ptr.is_null() {
661 panic!("string_to_cstring: malloc failed");
662 }
663
664 unsafe {
666 std::ptr::copy_nonoverlapping(str_ptr, ptr, len);
667 *ptr.add(len) = 0;
669 }
670
671 ptr
672}
673
674#[unsafe(no_mangle)]
683pub unsafe extern "C" fn patch_seq_cstring_to_string(stack: Stack, cstr: *const u8) -> Stack {
684 if cstr.is_null() {
685 return unsafe { push(stack, Value::String(global_string(String::new()))) };
687 }
688
689 let len = unsafe { libc::strlen(cstr as *const libc::c_char) };
691
692 let slice = unsafe { std::slice::from_raw_parts(cstr, len) };
694 let s = String::from_utf8_lossy(slice).into_owned();
695
696 unsafe { push(stack, Value::String(global_string(s))) }
697}
698
699#[cfg(test)]
700mod tests {
701 use super::*;
702
703 #[test]
704 fn test_string_split_simple() {
705 unsafe {
706 let stack = crate::stack::alloc_test_stack();
707 let stack = push(stack, Value::String(global_string("a b c".to_owned())));
708 let stack = push(stack, Value::String(global_string(" ".to_owned())));
709
710 let stack = string_split(stack);
711
712 let (_stack, result) = pop(stack);
714 match result {
715 Value::Variant(v) => {
716 assert_eq!(v.tag.as_str(), "List");
717 assert_eq!(v.fields.len(), 3);
718 assert_eq!(v.fields[0], Value::String(global_string("a".to_owned())));
719 assert_eq!(v.fields[1], Value::String(global_string("b".to_owned())));
720 assert_eq!(v.fields[2], Value::String(global_string("c".to_owned())));
721 }
722 _ => panic!("Expected Variant, got {:?}", result),
723 }
724 }
725 }
726
727 #[test]
728 fn test_string_split_empty() {
729 unsafe {
730 let stack = crate::stack::alloc_test_stack();
731 let stack = push(stack, Value::String(global_string("".to_owned())));
732 let stack = push(stack, Value::String(global_string(" ".to_owned())));
733
734 let stack = string_split(stack);
735
736 let (_stack, result) = pop(stack);
738 match result {
739 Value::Variant(v) => {
740 assert_eq!(v.tag.as_str(), "List");
741 assert_eq!(v.fields.len(), 1);
742 assert_eq!(v.fields[0], Value::String(global_string("".to_owned())));
743 }
744 _ => panic!("Expected Variant, got {:?}", result),
745 }
746 }
747 }
748
749 #[test]
750 fn test_string_empty_true() {
751 unsafe {
752 let stack = crate::stack::alloc_test_stack();
753 let stack = push(stack, Value::String(global_string("".to_owned())));
754
755 let stack = string_empty(stack);
756
757 let (_stack, result) = pop(stack);
758 assert_eq!(result, Value::Bool(true));
759 }
760 }
761
762 #[test]
763 fn test_string_empty_false() {
764 unsafe {
765 let stack = crate::stack::alloc_test_stack();
766 let stack = push(stack, Value::String(global_string("hello".to_owned())));
767
768 let stack = string_empty(stack);
769
770 let (_stack, result) = pop(stack);
771 assert_eq!(result, Value::Bool(false));
772 }
773 }
774
775 #[test]
776 fn test_string_contains_true() {
777 unsafe {
778 let stack = crate::stack::alloc_test_stack();
779 let stack = push(
780 stack,
781 Value::String(global_string("hello world".to_owned())),
782 );
783 let stack = push(stack, Value::String(global_string("world".to_owned())));
784
785 let stack = string_contains(stack);
786
787 let (_stack, result) = pop(stack);
788 assert_eq!(result, Value::Bool(true));
789 }
790 }
791
792 #[test]
793 fn test_string_contains_false() {
794 unsafe {
795 let stack = crate::stack::alloc_test_stack();
796 let stack = push(
797 stack,
798 Value::String(global_string("hello world".to_owned())),
799 );
800 let stack = push(stack, Value::String(global_string("foo".to_owned())));
801
802 let stack = string_contains(stack);
803
804 let (_stack, result) = pop(stack);
805 assert_eq!(result, Value::Bool(false));
806 }
807 }
808
809 #[test]
810 fn test_string_starts_with_true() {
811 unsafe {
812 let stack = crate::stack::alloc_test_stack();
813 let stack = push(
814 stack,
815 Value::String(global_string("hello world".to_owned())),
816 );
817 let stack = push(stack, Value::String(global_string("hello".to_owned())));
818
819 let stack = string_starts_with(stack);
820
821 let (_stack, result) = pop(stack);
822 assert_eq!(result, Value::Bool(true));
823 }
824 }
825
826 #[test]
827 fn test_string_starts_with_false() {
828 unsafe {
829 let stack = crate::stack::alloc_test_stack();
830 let stack = push(
831 stack,
832 Value::String(global_string("hello world".to_owned())),
833 );
834 let stack = push(stack, Value::String(global_string("world".to_owned())));
835
836 let stack = string_starts_with(stack);
837
838 let (_stack, result) = pop(stack);
839 assert_eq!(result, Value::Bool(false));
840 }
841 }
842
843 #[test]
844 fn test_http_request_line_parsing() {
845 unsafe {
847 let stack = crate::stack::alloc_test_stack();
848 let stack = push(
849 stack,
850 Value::String(global_string("GET /api/users HTTP/1.1".to_owned())),
851 );
852 let stack = push(stack, Value::String(global_string(" ".to_owned())));
853
854 let stack = string_split(stack);
855
856 let (_stack, result) = pop(stack);
858 match result {
859 Value::Variant(v) => {
860 assert_eq!(v.tag.as_str(), "List");
861 assert_eq!(v.fields.len(), 3);
862 assert_eq!(v.fields[0], Value::String(global_string("GET".to_owned())));
863 assert_eq!(
864 v.fields[1],
865 Value::String(global_string("/api/users".to_owned()))
866 );
867 assert_eq!(
868 v.fields[2],
869 Value::String(global_string("HTTP/1.1".to_owned()))
870 );
871 }
872 _ => panic!("Expected Variant, got {:?}", result),
873 }
874 }
875 }
876
877 #[test]
878 fn test_path_routing() {
879 unsafe {
881 let stack = crate::stack::alloc_test_stack();
882 let stack = push(stack, Value::String(global_string("/api/users".to_owned())));
883 let stack = push(stack, Value::String(global_string("/api/".to_owned())));
884
885 let stack = string_starts_with(stack);
886
887 let (_stack, result) = pop(stack);
888 assert_eq!(result, Value::Bool(true));
889 }
890 }
891
892 #[test]
893 fn test_string_concat() {
894 unsafe {
895 let stack = crate::stack::alloc_test_stack();
896 let stack = push(stack, Value::String(global_string("Hello, ".to_owned())));
897 let stack = push(stack, Value::String(global_string("World!".to_owned())));
898
899 let stack = string_concat(stack);
900
901 let (_stack, result) = pop(stack);
902 assert_eq!(
903 result,
904 Value::String(global_string("Hello, World!".to_owned()))
905 );
906 }
907 }
908
909 #[test]
910 fn test_string_length() {
911 unsafe {
912 let stack = crate::stack::alloc_test_stack();
913 let stack = push(stack, Value::String(global_string("Hello".to_owned())));
914
915 let stack = string_length(stack);
916
917 let (_stack, result) = pop(stack);
918 assert_eq!(result, Value::Int(5));
919 }
920 }
921
922 #[test]
923 fn test_string_length_empty() {
924 unsafe {
925 let stack = crate::stack::alloc_test_stack();
926 let stack = push(stack, Value::String(global_string("".to_owned())));
927
928 let stack = string_length(stack);
929
930 let (_stack, result) = pop(stack);
931 assert_eq!(result, Value::Int(0));
932 }
933 }
934
935 #[test]
936 fn test_string_trim() {
937 unsafe {
938 let stack = crate::stack::alloc_test_stack();
939 let stack = push(
940 stack,
941 Value::String(global_string(" Hello, World! ".to_owned())),
942 );
943
944 let stack = string_trim(stack);
945
946 let (_stack, result) = pop(stack);
947 assert_eq!(
948 result,
949 Value::String(global_string("Hello, World!".to_owned()))
950 );
951 }
952 }
953
954 #[test]
955 fn test_string_to_upper() {
956 unsafe {
957 let stack = crate::stack::alloc_test_stack();
958 let stack = push(
959 stack,
960 Value::String(global_string("Hello, World!".to_owned())),
961 );
962
963 let stack = string_to_upper(stack);
964
965 let (_stack, result) = pop(stack);
966 assert_eq!(
967 result,
968 Value::String(global_string("HELLO, WORLD!".to_owned()))
969 );
970 }
971 }
972
973 #[test]
974 fn test_string_to_lower() {
975 unsafe {
976 let stack = crate::stack::alloc_test_stack();
977 let stack = push(
978 stack,
979 Value::String(global_string("Hello, World!".to_owned())),
980 );
981
982 let stack = string_to_lower(stack);
983
984 let (_stack, result) = pop(stack);
985 assert_eq!(
986 result,
987 Value::String(global_string("hello, world!".to_owned()))
988 );
989 }
990 }
991
992 #[test]
993 fn test_http_header_content_length() {
994 unsafe {
996 let stack = crate::stack::alloc_test_stack();
997 let stack = push(
998 stack,
999 Value::String(global_string("Content-Length: ".to_owned())),
1000 );
1001 let stack = push(stack, Value::String(global_string("42".to_owned())));
1002
1003 let stack = string_concat(stack);
1004
1005 let (_stack, result) = pop(stack);
1006 assert_eq!(
1007 result,
1008 Value::String(global_string("Content-Length: 42".to_owned()))
1009 );
1010 }
1011 }
1012
1013 #[test]
1014 fn test_string_equal_true() {
1015 unsafe {
1016 let stack = crate::stack::alloc_test_stack();
1017 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1018 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1019
1020 let stack = string_equal(stack);
1021
1022 let (_stack, result) = pop(stack);
1023 assert_eq!(result, Value::Bool(true));
1024 }
1025 }
1026
1027 #[test]
1028 fn test_string_equal_false() {
1029 unsafe {
1030 let stack = crate::stack::alloc_test_stack();
1031 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1032 let stack = push(stack, Value::String(global_string("world".to_owned())));
1033
1034 let stack = string_equal(stack);
1035
1036 let (_stack, result) = pop(stack);
1037 assert_eq!(result, Value::Bool(false));
1038 }
1039 }
1040
1041 #[test]
1042 fn test_string_equal_empty_strings() {
1043 unsafe {
1044 let stack = crate::stack::alloc_test_stack();
1045 let stack = push(stack, Value::String(global_string("".to_owned())));
1046 let stack = push(stack, Value::String(global_string("".to_owned())));
1047
1048 let stack = string_equal(stack);
1049
1050 let (_stack, result) = pop(stack);
1051 assert_eq!(result, Value::Bool(true));
1052 }
1053 }
1054
1055 #[test]
1058 fn test_string_length_utf8() {
1059 unsafe {
1061 let stack = crate::stack::alloc_test_stack();
1062 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
1063
1064 let stack = string_length(stack);
1065
1066 let (_stack, result) = pop(stack);
1067 assert_eq!(result, Value::Int(5)); }
1069 }
1070
1071 #[test]
1072 fn test_string_length_emoji() {
1073 unsafe {
1075 let stack = crate::stack::alloc_test_stack();
1076 let stack = push(stack, Value::String(global_string("hi🎉".to_owned())));
1077
1078 let stack = string_length(stack);
1079
1080 let (_stack, result) = pop(stack);
1081 assert_eq!(result, Value::Int(3)); }
1083 }
1084
1085 #[test]
1086 fn test_string_byte_length_ascii() {
1087 unsafe {
1088 let stack = crate::stack::alloc_test_stack();
1089 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1090
1091 let stack = string_byte_length(stack);
1092
1093 let (_stack, result) = pop(stack);
1094 assert_eq!(result, Value::Int(5)); }
1096 }
1097
1098 #[test]
1099 fn test_string_byte_length_utf8() {
1100 unsafe {
1102 let stack = crate::stack::alloc_test_stack();
1103 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
1104
1105 let stack = string_byte_length(stack);
1106
1107 let (_stack, result) = pop(stack);
1108 assert_eq!(result, Value::Int(6)); }
1110 }
1111
1112 #[test]
1113 fn test_string_char_at_ascii() {
1114 unsafe {
1115 let stack = crate::stack::alloc_test_stack();
1116 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1117 let stack = push(stack, Value::Int(0));
1118
1119 let stack = string_char_at(stack);
1120
1121 let (_stack, result) = pop(stack);
1122 assert_eq!(result, Value::Int(104)); }
1124 }
1125
1126 #[test]
1127 fn test_string_char_at_utf8() {
1128 unsafe {
1130 let stack = crate::stack::alloc_test_stack();
1131 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
1132 let stack = push(stack, Value::Int(1));
1133
1134 let stack = string_char_at(stack);
1135
1136 let (_stack, result) = pop(stack);
1137 assert_eq!(result, Value::Int(233)); }
1139 }
1140
1141 #[test]
1142 fn test_string_char_at_out_of_bounds() {
1143 unsafe {
1144 let stack = crate::stack::alloc_test_stack();
1145 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1146 let stack = push(stack, Value::Int(10)); let stack = string_char_at(stack);
1149
1150 let (_stack, result) = pop(stack);
1151 assert_eq!(result, Value::Int(-1));
1152 }
1153 }
1154
1155 #[test]
1156 fn test_string_char_at_negative() {
1157 unsafe {
1158 let stack = crate::stack::alloc_test_stack();
1159 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1160 let stack = push(stack, Value::Int(-1));
1161
1162 let stack = string_char_at(stack);
1163
1164 let (_stack, result) = pop(stack);
1165 assert_eq!(result, Value::Int(-1));
1166 }
1167 }
1168
1169 #[test]
1170 fn test_string_substring_simple() {
1171 unsafe {
1172 let stack = crate::stack::alloc_test_stack();
1173 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1174 let stack = push(stack, Value::Int(1)); let stack = push(stack, Value::Int(3)); let stack = string_substring(stack);
1178
1179 let (_stack, result) = pop(stack);
1180 assert_eq!(result, Value::String(global_string("ell".to_owned())));
1181 }
1182 }
1183
1184 #[test]
1185 fn test_string_substring_utf8() {
1186 unsafe {
1188 let stack = crate::stack::alloc_test_stack();
1189 let stack = push(stack, Value::String(global_string("héllo".to_owned())));
1190 let stack = push(stack, Value::Int(1)); let stack = push(stack, Value::Int(3)); let stack = string_substring(stack);
1194
1195 let (_stack, result) = pop(stack);
1196 assert_eq!(result, Value::String(global_string("éll".to_owned())));
1197 }
1198 }
1199
1200 #[test]
1201 fn test_string_substring_clamp() {
1202 unsafe {
1204 let stack = crate::stack::alloc_test_stack();
1205 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1206 let stack = push(stack, Value::Int(2)); let stack = push(stack, Value::Int(100)); let stack = string_substring(stack);
1210
1211 let (_stack, result) = pop(stack);
1212 assert_eq!(result, Value::String(global_string("llo".to_owned())));
1213 }
1214 }
1215
1216 #[test]
1217 fn test_string_substring_beyond_end() {
1218 unsafe {
1220 let stack = crate::stack::alloc_test_stack();
1221 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1222 let stack = push(stack, Value::Int(10)); let stack = push(stack, Value::Int(3)); let stack = string_substring(stack);
1226
1227 let (_stack, result) = pop(stack);
1228 assert_eq!(result, Value::String(global_string("".to_owned())));
1229 }
1230 }
1231
1232 #[test]
1233 fn test_char_to_string_ascii() {
1234 unsafe {
1235 let stack = crate::stack::alloc_test_stack();
1236 let stack = push(stack, Value::Int(65)); let stack = char_to_string(stack);
1239
1240 let (_stack, result) = pop(stack);
1241 assert_eq!(result, Value::String(global_string("A".to_owned())));
1242 }
1243 }
1244
1245 #[test]
1246 fn test_char_to_string_utf8() {
1247 unsafe {
1248 let stack = crate::stack::alloc_test_stack();
1249 let stack = push(stack, Value::Int(233)); let stack = char_to_string(stack);
1252
1253 let (_stack, result) = pop(stack);
1254 assert_eq!(result, Value::String(global_string("é".to_owned())));
1255 }
1256 }
1257
1258 #[test]
1259 fn test_char_to_string_newline() {
1260 unsafe {
1261 let stack = crate::stack::alloc_test_stack();
1262 let stack = push(stack, Value::Int(10)); let stack = char_to_string(stack);
1265
1266 let (_stack, result) = pop(stack);
1267 assert_eq!(result, Value::String(global_string("\n".to_owned())));
1268 }
1269 }
1270
1271 #[test]
1272 fn test_char_to_string_invalid() {
1273 unsafe {
1274 let stack = crate::stack::alloc_test_stack();
1275 let stack = push(stack, Value::Int(-1)); let stack = char_to_string(stack);
1278
1279 let (_stack, result) = pop(stack);
1280 assert_eq!(result, Value::String(global_string("".to_owned())));
1281 }
1282 }
1283
1284 #[test]
1285 fn test_string_find_found() {
1286 unsafe {
1287 let stack = crate::stack::alloc_test_stack();
1288 let stack = push(
1289 stack,
1290 Value::String(global_string("hello world".to_owned())),
1291 );
1292 let stack = push(stack, Value::String(global_string("world".to_owned())));
1293
1294 let stack = string_find(stack);
1295
1296 let (_stack, result) = pop(stack);
1297 assert_eq!(result, Value::Int(6)); }
1299 }
1300
1301 #[test]
1302 fn test_string_find_not_found() {
1303 unsafe {
1304 let stack = crate::stack::alloc_test_stack();
1305 let stack = push(
1306 stack,
1307 Value::String(global_string("hello world".to_owned())),
1308 );
1309 let stack = push(stack, Value::String(global_string("xyz".to_owned())));
1310
1311 let stack = string_find(stack);
1312
1313 let (_stack, result) = pop(stack);
1314 assert_eq!(result, Value::Int(-1));
1315 }
1316 }
1317
1318 #[test]
1319 fn test_string_find_first_match() {
1320 unsafe {
1322 let stack = crate::stack::alloc_test_stack();
1323 let stack = push(stack, Value::String(global_string("hello".to_owned())));
1324 let stack = push(stack, Value::String(global_string("l".to_owned())));
1325
1326 let stack = string_find(stack);
1327
1328 let (_stack, result) = pop(stack);
1329 assert_eq!(result, Value::Int(2)); }
1331 }
1332
1333 #[test]
1334 fn test_string_find_utf8() {
1335 unsafe {
1337 let stack = crate::stack::alloc_test_stack();
1338 let stack = push(
1339 stack,
1340 Value::String(global_string("héllo wörld".to_owned())),
1341 );
1342 let stack = push(stack, Value::String(global_string("wörld".to_owned())));
1343
1344 let stack = string_find(stack);
1345
1346 let (_stack, result) = pop(stack);
1347 assert_eq!(result, Value::Int(6)); }
1349 }
1350
1351 #[test]
1354 fn test_json_escape_quotes() {
1355 unsafe {
1356 let stack = crate::stack::alloc_test_stack();
1357 let stack = push(
1358 stack,
1359 Value::String(global_string("hello \"world\"".to_owned())),
1360 );
1361
1362 let stack = json_escape(stack);
1363
1364 let (_stack, result) = pop(stack);
1365 assert_eq!(
1366 result,
1367 Value::String(global_string("hello \\\"world\\\"".to_owned()))
1368 );
1369 }
1370 }
1371
1372 #[test]
1373 fn test_json_escape_backslash() {
1374 unsafe {
1375 let stack = crate::stack::alloc_test_stack();
1376 let stack = push(
1377 stack,
1378 Value::String(global_string("path\\to\\file".to_owned())),
1379 );
1380
1381 let stack = json_escape(stack);
1382
1383 let (_stack, result) = pop(stack);
1384 assert_eq!(
1385 result,
1386 Value::String(global_string("path\\\\to\\\\file".to_owned()))
1387 );
1388 }
1389 }
1390
1391 #[test]
1392 fn test_json_escape_newline_tab() {
1393 unsafe {
1394 let stack = crate::stack::alloc_test_stack();
1395 let stack = push(
1396 stack,
1397 Value::String(global_string("line1\nline2\ttabbed".to_owned())),
1398 );
1399
1400 let stack = json_escape(stack);
1401
1402 let (_stack, result) = pop(stack);
1403 assert_eq!(
1404 result,
1405 Value::String(global_string("line1\\nline2\\ttabbed".to_owned()))
1406 );
1407 }
1408 }
1409
1410 #[test]
1411 fn test_json_escape_carriage_return() {
1412 unsafe {
1413 let stack = crate::stack::alloc_test_stack();
1414 let stack = push(
1415 stack,
1416 Value::String(global_string("line1\r\nline2".to_owned())),
1417 );
1418
1419 let stack = json_escape(stack);
1420
1421 let (_stack, result) = pop(stack);
1422 assert_eq!(
1423 result,
1424 Value::String(global_string("line1\\r\\nline2".to_owned()))
1425 );
1426 }
1427 }
1428
1429 #[test]
1430 fn test_json_escape_control_chars() {
1431 unsafe {
1432 let stack = crate::stack::alloc_test_stack();
1433 let stack = push(
1435 stack,
1436 Value::String(global_string("a\x08b\x0Cc".to_owned())),
1437 );
1438
1439 let stack = json_escape(stack);
1440
1441 let (_stack, result) = pop(stack);
1442 assert_eq!(result, Value::String(global_string("a\\bb\\fc".to_owned())));
1443 }
1444 }
1445
1446 #[test]
1447 fn test_json_escape_unicode_control() {
1448 unsafe {
1449 let stack = crate::stack::alloc_test_stack();
1450 let stack = push(stack, Value::String(global_string("a\x00b".to_owned())));
1452
1453 let stack = json_escape(stack);
1454
1455 let (_stack, result) = pop(stack);
1456 assert_eq!(result, Value::String(global_string("a\\u0000b".to_owned())));
1457 }
1458 }
1459
1460 #[test]
1461 fn test_json_escape_mixed_special_chars() {
1462 unsafe {
1464 let stack = crate::stack::alloc_test_stack();
1465 let stack = push(
1466 stack,
1467 Value::String(global_string("Line 1\nLine \"2\"\ttab\r\n".to_owned())),
1468 );
1469
1470 let stack = json_escape(stack);
1471
1472 let (_stack, result) = pop(stack);
1473 assert_eq!(
1474 result,
1475 Value::String(global_string(
1476 "Line 1\\nLine \\\"2\\\"\\ttab\\r\\n".to_owned()
1477 ))
1478 );
1479 }
1480 }
1481
1482 #[test]
1483 fn test_json_escape_no_change() {
1484 unsafe {
1486 let stack = crate::stack::alloc_test_stack();
1487 let stack = push(
1488 stack,
1489 Value::String(global_string("Hello, World!".to_owned())),
1490 );
1491
1492 let stack = json_escape(stack);
1493
1494 let (_stack, result) = pop(stack);
1495 assert_eq!(
1496 result,
1497 Value::String(global_string("Hello, World!".to_owned()))
1498 );
1499 }
1500 }
1501
1502 #[test]
1503 fn test_json_escape_empty_string() {
1504 unsafe {
1505 let stack = crate::stack::alloc_test_stack();
1506 let stack = push(stack, Value::String(global_string("".to_owned())));
1507
1508 let stack = json_escape(stack);
1509
1510 let (_stack, result) = pop(stack);
1511 assert_eq!(result, Value::String(global_string("".to_owned())));
1512 }
1513 }
1514
1515 #[test]
1518 fn test_string_to_int_success() {
1519 unsafe {
1520 let stack = crate::stack::alloc_test_stack();
1521 let stack = push(stack, Value::String(global_string("42".to_owned())));
1522
1523 let stack = string_to_int(stack);
1524
1525 let (stack, success) = pop(stack);
1526 let (_stack, value) = pop(stack);
1527 assert_eq!(success, Value::Bool(true));
1528 assert_eq!(value, Value::Int(42));
1529 }
1530 }
1531
1532 #[test]
1533 fn test_string_to_int_negative() {
1534 unsafe {
1535 let stack = crate::stack::alloc_test_stack();
1536 let stack = push(stack, Value::String(global_string("-99".to_owned())));
1537
1538 let stack = string_to_int(stack);
1539
1540 let (stack, success) = pop(stack);
1541 let (_stack, value) = pop(stack);
1542 assert_eq!(success, Value::Bool(true));
1543 assert_eq!(value, Value::Int(-99));
1544 }
1545 }
1546
1547 #[test]
1548 fn test_string_to_int_with_whitespace() {
1549 unsafe {
1550 let stack = crate::stack::alloc_test_stack();
1551 let stack = push(stack, Value::String(global_string(" 123 ".to_owned())));
1552
1553 let stack = string_to_int(stack);
1554
1555 let (stack, success) = pop(stack);
1556 let (_stack, value) = pop(stack);
1557 assert_eq!(success, Value::Bool(true));
1558 assert_eq!(value, Value::Int(123));
1559 }
1560 }
1561
1562 #[test]
1563 fn test_string_to_int_failure() {
1564 unsafe {
1565 let stack = crate::stack::alloc_test_stack();
1566 let stack = push(
1567 stack,
1568 Value::String(global_string("not a number".to_owned())),
1569 );
1570
1571 let stack = string_to_int(stack);
1572
1573 let (stack, success) = pop(stack);
1574 let (_stack, value) = pop(stack);
1575 assert_eq!(success, Value::Bool(false));
1576 assert_eq!(value, Value::Int(0));
1577 }
1578 }
1579
1580 #[test]
1581 fn test_string_to_int_empty() {
1582 unsafe {
1583 let stack = crate::stack::alloc_test_stack();
1584 let stack = push(stack, Value::String(global_string("".to_owned())));
1585
1586 let stack = string_to_int(stack);
1587
1588 let (stack, success) = pop(stack);
1589 let (_stack, value) = pop(stack);
1590 assert_eq!(success, Value::Bool(false));
1591 assert_eq!(value, Value::Int(0));
1592 }
1593 }
1594
1595 #[test]
1596 fn test_string_to_int_leading_zeros() {
1597 unsafe {
1598 let stack = crate::stack::alloc_test_stack();
1599 let stack = push(stack, Value::String(global_string("007".to_owned())));
1600
1601 let stack = string_to_int(stack);
1602
1603 let (stack, success) = pop(stack);
1604 let (_stack, value) = pop(stack);
1605 assert_eq!(success, Value::Bool(true));
1606 assert_eq!(value, Value::Int(7));
1607 }
1608 }
1609
1610 #[test]
1611 fn test_string_to_int_type_error() {
1612 unsafe {
1613 crate::error::clear_runtime_error();
1614
1615 let stack = crate::stack::alloc_test_stack();
1616 let stack = push(stack, Value::Int(42)); let stack = string_to_int(stack);
1619
1620 assert!(crate::error::has_runtime_error());
1622 let error = crate::error::take_runtime_error().unwrap();
1623 assert!(error.contains("expected String"));
1624
1625 let (stack, success) = pop(stack);
1627 assert_eq!(success, Value::Bool(false));
1628 let (_stack, value) = pop(stack);
1629 assert_eq!(value, Value::Int(0));
1630 }
1631 }
1632}