1mod builtins;
2mod bytecode;
3mod compiler;
4mod context;
5mod serializer;
6mod value;
7mod vm;
8
9pub use bytecode::{ByteCode, Instruction};
10pub use compiler::Compiler;
11pub use context::Context;
12pub use serializer::{deserialize_bytecode, serialize_bytecode};
13pub use value::{DictKey, ExceptionType, Module, Value};
14
15use clap::{Parser, Subcommand};
16
17#[cfg(not(test))]
18use std::process;
19
20#[derive(Parser)]
21#[command(name = "quickpython")]
22#[command(about = "A simple Python interpreter", long_about = None)]
23struct Cli {
24 #[command(subcommand)]
25 command: Commands,
26}
27
28#[derive(Subcommand)]
29enum Commands {
30 Run { file: String },
32 Compile {
34 file: String,
35 #[arg(short, long)]
36 output: Option<String>,
37 },
38}
39
40#[cfg(not(test))]
41#[allow(dead_code)]
42fn main() {
43 let cli = Cli::parse();
44
45 match cli.command {
46 Commands::Run { file } => {
47 if file.ends_with(".pyq") {
49 let data = match std::fs::read(&file) {
51 Ok(d) => d,
52 Err(e) => {
53 eprintln!("Error reading file '{}': {}", file, e);
54 process::exit(1);
55 }
56 };
57
58 let bytecode = match deserialize_bytecode(&data) {
59 Ok(bc) => bc,
60 Err(e) => {
61 eprintln!("Error deserializing bytecode: {}", e);
62 process::exit(1);
63 }
64 };
65
66 let mut vm = vm::VM::new();
67 let mut globals = std::collections::HashMap::new();
68 match vm.execute(&bytecode, &mut globals) {
69 Ok(result) => {
70 if let Some(i) = result.as_int() {
71 println!("{}", i);
72 }
73 }
74 Err(e) => {
75 eprintln!("Runtime error: {:?}", e);
76 process::exit(1);
77 }
78 }
79 } else {
80 let source = match std::fs::read_to_string(&file) {
82 Ok(s) => s,
83 Err(e) => {
84 eprintln!("Error reading file '{}': {}", file, e);
85 process::exit(1);
86 }
87 };
88
89 let mut ctx = Context::new();
90 match ctx.eval(&source) {
91 Ok(result) => {
92 if let Some(i) = result.as_int() {
93 println!("{}", i);
94 }
95 }
96 Err(e) => {
97 eprintln!("Error: {}", e);
98 process::exit(1);
99 }
100 }
101 }
102 }
103 Commands::Compile { file, output } => {
104 let source = match std::fs::read_to_string(&file) {
106 Ok(s) => s,
107 Err(e) => {
108 eprintln!("Error reading file '{}': {}", file, e);
109 process::exit(1);
110 }
111 };
112
113 let bytecode = match Compiler::compile(&source) {
115 Ok(bc) => bc,
116 Err(e) => {
117 eprintln!("Compile error: {}", e);
118 process::exit(1);
119 }
120 };
121
122 let data = match serialize_bytecode(&bytecode) {
124 Ok(d) => d,
125 Err(e) => {
126 eprintln!("Serialization error: {}", e);
127 process::exit(1);
128 }
129 };
130
131 let output_file = output
133 .unwrap_or_else(|| file.strip_suffix(".py").unwrap_or(&file).to_string() + ".pyq");
134
135 match std::fs::write(&output_file, data) {
137 Ok(_) => {
138 println!("Compiled successfully: {}", output_file);
139 }
140 Err(e) => {
141 eprintln!("Error writing file '{}': {}", output_file, e);
142 process::exit(1);
143 }
144 }
145 }
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn test_simple_add() {
155 let mut ctx = Context::new();
156 let result = ctx.eval("1 + 2").unwrap();
157 assert_eq!(result.as_int(), Some(3));
158 }
159
160 #[test]
161 fn test_simple_mul() {
162 let mut ctx = Context::new();
163 let result = ctx.eval("10 * 5").unwrap();
164 assert_eq!(result.as_int(), Some(50));
165 }
166
167 #[test]
168 fn test_simple_div() {
169 let mut ctx = Context::new();
170 let result = ctx.eval("100 / 4").unwrap();
171 assert_eq!(result.as_int(), Some(25));
172 }
173
174 #[test]
175 fn test_simple_sub() {
176 let mut ctx = Context::new();
177 let result = ctx.eval("7 - 3").unwrap();
178 assert_eq!(result.as_int(), Some(4));
179 }
180
181 #[test]
182 fn test_complex_expr() {
183 let mut ctx = Context::new();
184 let result = ctx.eval("(10 + 5) * 2").unwrap();
185 assert_eq!(result.as_int(), Some(30));
186 }
187
188 #[test]
189 fn test_another_complex_expr() {
190 let mut ctx = Context::new();
191 let result = ctx.eval("(1 + 2) * 3").unwrap();
192 assert_eq!(result.as_int(), Some(9));
193 }
194
195 #[test]
196 fn test_division_by_zero() {
197 let mut ctx = Context::new();
198 let result = ctx.eval("10 / 0");
199 assert!(result.is_err());
200 }
201
202 #[test]
203 fn test_variable_assignment() {
204 let mut ctx = Context::new();
205 ctx.eval("x = 42").unwrap();
206 let x = ctx.eval("x").unwrap();
207 assert_eq!(x.as_int(), Some(42));
208 }
209
210 #[test]
211 fn test_variable_expr() {
212 let mut ctx = Context::new();
213 ctx.eval("x = 10").unwrap();
214 ctx.eval("y = x * 2").unwrap();
215 let y = ctx.eval("y").unwrap();
216 assert_eq!(y.as_int(), Some(20));
217 }
218
219 #[test]
220 fn test_get_set_api() {
221 let mut ctx = Context::new();
222 ctx.set("z", Value::Int(100));
223 let z = ctx.get("z").unwrap();
224 assert_eq!(z.as_int(), Some(100));
225 }
226
227 #[test]
228 fn test_undefined_variable() {
229 let mut ctx = Context::new();
230 let result = ctx.eval("undefined_var");
231 assert!(result.is_err());
232 }
233
234 #[test]
235 fn test_function_def_and_call() {
236 let mut ctx = Context::new();
237 ctx.eval("def add(a, b):\n return a + b").unwrap();
238 let result = ctx.eval("add(1, 2)").unwrap();
239 assert_eq!(result.as_int(), Some(3));
240 }
241
242 #[test]
243 fn test_factorial() {
244 let mut ctx = Context::new();
245 ctx.eval(
246 r#"
247def factorial(n):
248 if n <= 1:
249 return 1
250 return n * factorial(n - 1)
251 "#,
252 )
253 .unwrap();
254
255 let result = ctx.eval("factorial(5)").unwrap();
256 assert_eq!(result.as_int(), Some(120));
257 }
258
259 #[test]
260 fn test_if_else() {
261 let mut ctx = Context::new();
262 ctx.eval(
263 r#"
264def max(a, b):
265 if a > b:
266 return a
267 else:
268 return b
269 "#,
270 )
271 .unwrap();
272
273 let result = ctx.eval("max(10, 5)").unwrap();
274 assert_eq!(result.as_int(), Some(10));
275
276 let result = ctx.eval("max(3, 8)").unwrap();
277 assert_eq!(result.as_int(), Some(8));
278 }
279
280 #[test]
281 fn test_comparison_operators() {
282 let mut ctx = Context::new();
283
284 let result = ctx.eval("5 > 3").unwrap();
285 assert_eq!(result.as_bool(), Some(true));
286
287 let result = ctx.eval("5 < 3").unwrap();
288 assert_eq!(result.as_bool(), Some(false));
289
290 let result = ctx.eval("5 == 5").unwrap();
291 assert_eq!(result.as_bool(), Some(true));
292 }
293
294 #[test]
295 fn test_while_loop() {
296 let mut ctx = Context::new();
297 ctx.eval(
298 r#"
299i = 0
300sum = 0
301while i < 10:
302 sum = sum + i
303 i = i + 1
304 "#,
305 )
306 .unwrap();
307
308 let sum = ctx.get("sum").unwrap();
309 assert_eq!(sum.as_int(), Some(45));
310 }
311
312 #[test]
313 fn test_fibonacci_iterative() {
314 let mut ctx = Context::new();
315 ctx.eval(
316 r#"
317def fib(n):
318 if n <= 1:
319 return n
320 a = 0
321 b = 1
322 i = 2
323 while i <= n:
324 temp = a + b
325 a = b
326 b = temp
327 i = i + 1
328 return b
329 "#,
330 )
331 .unwrap();
332
333 let result = ctx.eval("fib(10)").unwrap();
334 assert_eq!(result.as_int(), Some(55));
335 }
336
337 #[test]
338 fn test_string_literal() {
339 let mut ctx = Context::new();
340 let result = ctx.eval(r#""hello""#).unwrap();
341 assert_eq!(result.as_string(), Some("hello"));
342 }
343
344 #[test]
345 fn test_string_concatenation() {
346 let mut ctx = Context::new();
347 let result = ctx.eval(r#""hello" + " " + "world""#).unwrap();
348 assert_eq!(result.as_string(), Some("hello world"));
349 }
350
351 #[test]
352 fn test_string_variable() {
353 let mut ctx = Context::new();
354 ctx.eval(r#"s = "test""#).unwrap();
355 let result = ctx.eval("s").unwrap();
356 assert_eq!(result.as_string(), Some("test"));
357 }
358
359 #[test]
360 fn test_print_string() {
361 let mut ctx = Context::new();
362 let result = ctx.eval(r#"print("hello")"#).unwrap();
363 assert_eq!(result, Value::None);
364 }
365
366 #[test]
367 fn test_print_int() {
368 let mut ctx = Context::new();
369 let result = ctx.eval("print(42)").unwrap();
370 assert_eq!(result, Value::None);
371 }
372
373 #[test]
374 fn test_float_literal() {
375 let mut ctx = Context::new();
376 let result = ctx.eval("3.15").unwrap();
377 assert_eq!(result.as_float(), Some(3.15));
378 }
379
380 #[test]
381 fn test_float_arithmetic() {
382 let mut ctx = Context::new();
383 let result = ctx.eval("3.15 * 2.0").unwrap();
384 assert_eq!(result.as_float(), Some(6.3));
385 }
386
387 #[test]
388 fn test_mixed_int_float() {
389 let mut ctx = Context::new();
390 let result = ctx.eval("10 + 3.5").unwrap();
391 assert_eq!(result.as_float(), Some(13.5));
392
393 let result = ctx.eval("10 / 3.0").unwrap();
394 assert!((result.as_float().unwrap() - 3.333333333333333).abs() < 0.0001);
395 }
396
397 #[test]
398 fn test_int_conversion() {
399 let mut ctx = Context::new();
400 ctx.eval("x = 3.14").unwrap();
401 let result = ctx.eval("int(x)").unwrap();
402 assert_eq!(result.as_int(), Some(3));
403
404 let result = ctx.eval("int(3.9)").unwrap();
405 assert_eq!(result.as_int(), Some(3));
406 }
407
408 #[test]
409 fn test_float_conversion() {
410 let mut ctx = Context::new();
411 let result = ctx.eval("float(42)").unwrap();
412 assert_eq!(result.as_float(), Some(42.0));
413
414 ctx.eval("x = 10").unwrap();
415 let result = ctx.eval("float(x)").unwrap();
416 assert_eq!(result.as_float(), Some(10.0));
417 }
418
419 #[test]
420 fn test_list_literal() {
421 let mut ctx = Context::new();
422 ctx.eval("numbers = [1, 2, 3, 4, 5]").unwrap();
423 let numbers = ctx.get("numbers").unwrap();
424 let list = numbers.as_list().unwrap();
425 assert_eq!(list.borrow().items.len(), 5);
426 }
427
428 #[test]
429 fn test_list_index() {
430 let mut ctx = Context::new();
431 ctx.eval("numbers = [10, 20, 30]").unwrap();
432 let result = ctx.eval("numbers[0]").unwrap();
433 assert_eq!(result.as_int(), Some(10));
434
435 let result = ctx.eval("numbers[2]").unwrap();
436 assert_eq!(result.as_int(), Some(30));
437 }
438
439 #[test]
440 fn test_list_index_assignment() {
441 let mut ctx = Context::new();
442 ctx.eval("numbers = [1, 2, 3]").unwrap();
443 ctx.eval("numbers[1] = 99").unwrap();
444 let result = ctx.eval("numbers[1]").unwrap();
445 assert_eq!(result.as_int(), Some(99));
446 }
447
448 #[test]
449 fn test_list_append() {
450 let mut ctx = Context::new();
451 ctx.eval("numbers = [1, 2, 3]").unwrap();
452 ctx.eval("numbers.append(4)").unwrap();
453 let numbers = ctx.get("numbers").unwrap();
454 let list = numbers.as_list().unwrap();
455 assert_eq!(list.borrow().items.len(), 4);
456 assert_eq!(list.borrow().items[3].as_int(), Some(4));
457 }
458
459 #[test]
460 fn test_list_pop() {
461 let mut ctx = Context::new();
462 ctx.eval("numbers = [1, 2, 3]").unwrap();
463 let result = ctx.eval("numbers.pop()").unwrap();
464 assert_eq!(result.as_int(), Some(3));
465
466 let numbers = ctx.get("numbers").unwrap();
467 let list = numbers.as_list().unwrap();
468 assert_eq!(list.borrow().items.len(), 2);
469 }
470
471 #[test]
472 fn test_dict_literal_string_keys() {
473 let mut ctx = Context::new();
474 ctx.eval(r#"person = {"name": "Alice", "age": 30}"#)
475 .unwrap();
476 let result = ctx.eval(r#"person["name"]"#).unwrap();
477 assert_eq!(result.as_string(), Some("Alice"));
478 }
479
480 #[test]
481 fn test_dict_literal_int_keys() {
482 let mut ctx = Context::new();
483 ctx.eval("scores = {1: 100, 2: 95, 3: 88}").unwrap();
484 let result = ctx.eval("scores[1]").unwrap();
485 assert_eq!(result.as_int(), Some(100));
486 }
487
488 #[test]
489 fn test_dict_assignment() {
490 let mut ctx = Context::new();
491 ctx.eval(r#"person = {"name": "Bob"}"#).unwrap();
492 ctx.eval(r#"person["age"] = 25"#).unwrap();
493 let result = ctx.eval(r#"person["age"]"#).unwrap();
494 assert_eq!(result.as_int(), Some(25));
495 }
496
497 #[test]
498 fn test_dict_keys() {
499 let mut ctx = Context::new();
500 ctx.eval(r#"person = {"name": "Alice", "age": 30}"#)
501 .unwrap();
502 let result = ctx.eval("person.keys()").unwrap();
503 let keys = result.as_list().unwrap();
504 assert_eq!(keys.borrow().items.len(), 2);
505 }
506
507 #[test]
508 fn test_len_function() {
509 let mut ctx = Context::new();
510
511 ctx.eval("numbers = [1, 2, 3, 4, 5]").unwrap();
512 let result = ctx.eval("len(numbers)").unwrap();
513 assert_eq!(result.as_int(), Some(5));
514
515 ctx.eval(r#"person = {"name": "Alice", "age": 30}"#)
516 .unwrap();
517 let result = ctx.eval("len(person)").unwrap();
518 assert_eq!(result.as_int(), Some(2));
519
520 let result = ctx.eval(r#"len("hello")"#).unwrap();
521 assert_eq!(result.as_int(), Some(5));
522 }
523
524 #[test]
525 fn test_for_range_simple() {
526 let mut ctx = Context::new();
527 ctx.eval(
528 r#"
529sum = 0
530for i in range(10):
531 sum = sum + i
532 "#,
533 )
534 .unwrap();
535
536 let sum = ctx.get("sum").unwrap();
537 assert_eq!(sum.as_int(), Some(45));
538 }
539
540 #[test]
541 fn test_for_range_start_stop() {
542 let mut ctx = Context::new();
543 ctx.eval(
544 r#"
545sum = 0
546for i in range(5, 10):
547 sum = sum + i
548 "#,
549 )
550 .unwrap();
551
552 let sum = ctx.get("sum").unwrap();
553 assert_eq!(sum.as_int(), Some(35)); }
555
556 #[test]
557 fn test_for_range_with_step() {
558 let mut ctx = Context::new();
559 ctx.eval(
560 r#"
561sum = 0
562for i in range(0, 10, 2):
563 sum = sum + i
564 "#,
565 )
566 .unwrap();
567
568 let sum = ctx.get("sum").unwrap();
569 assert_eq!(sum.as_int(), Some(20)); }
571
572 #[test]
573 fn test_for_list_iteration() {
574 let mut ctx = Context::new();
575 ctx.eval(
576 r#"
577numbers = [10, 20, 30, 40]
578sum = 0
579for num in numbers:
580 sum = sum + num
581 "#,
582 )
583 .unwrap();
584
585 let sum = ctx.get("sum").unwrap();
586 assert_eq!(sum.as_int(), Some(100));
587 }
588
589 #[test]
590 fn test_for_dict_iteration() {
591 let mut ctx = Context::new();
592 ctx.eval(
593 r#"
594scores = {1: 100, 2: 95, 3: 88}
595sum = 0
596for key in scores:
597 sum = sum + key
598 "#,
599 )
600 .unwrap();
601
602 let sum = ctx.get("sum").unwrap();
603 assert_eq!(sum.as_int(), Some(6)); }
605
606 #[test]
607 fn test_nested_for_loops() {
608 let mut ctx = Context::new();
609 ctx.eval(
610 r#"
611sum = 0
612for i in range(3):
613 for j in range(3):
614 sum = sum + i * j
615 "#,
616 )
617 .unwrap();
618
619 let sum = ctx.get("sum").unwrap();
620 assert_eq!(sum.as_int(), Some(9)); }
622
623 #[test]
624 fn test_for_with_function() {
625 let mut ctx = Context::new();
626 ctx.eval(
627 r#"
628def sum_range(n):
629 total = 0
630 for i in range(n):
631 total = total + i
632 return total
633 "#,
634 )
635 .unwrap();
636
637 let result = ctx.eval("sum_range(10)").unwrap();
638 assert_eq!(result.as_int(), Some(45));
639 }
640
641 #[test]
642 fn test_exception_creation() {
643 use crate::value::ExceptionType;
644
645 let exc = Value::error(ExceptionType::ValueError, "test error");
646 assert!(exc.is_exception());
647
648 let exc_value = exc.as_exception().unwrap();
649 assert_eq!(exc_value.exception_type, ExceptionType::ValueError);
650 assert_eq!(exc_value.message, "test error");
651 }
652
653 #[test]
654 fn test_exception_equality() {
655 use crate::value::ExceptionType;
656
657 let exc1 = Value::error(ExceptionType::ValueError, "test");
658 let exc2 = Value::error(ExceptionType::ValueError, "test");
659 let exc3 = Value::error(ExceptionType::TypeError, "test");
660
661 assert_eq!(exc1, exc2);
662 assert_ne!(exc1, exc3);
663 }
664
665 #[test]
666 fn test_raise_value_error() {
667 let mut ctx = Context::new();
668 let result = ctx.eval(r#"raise ValueError("test error")"#);
669 assert!(result.is_err());
670
671 let err = result.unwrap_err();
672 assert!(err.contains("ValueError"));
673 assert!(err.contains("test error"));
674 }
675
676 #[test]
677 fn test_division_by_zero_exception() {
678 let mut ctx = Context::new();
679 let result = ctx.eval("x = 1 / 0");
680 assert!(result.is_err());
681
682 let err = result.unwrap_err();
683 assert!(err.contains("ZeroDivisionError"));
684 }
685
686 #[test]
687 fn test_index_error_exception() {
688 let mut ctx = Context::new();
689 let result = ctx.eval(
690 r#"
691my_list = [1, 2, 3]
692x = my_list[10]
693 "#,
694 );
695 assert!(result.is_err());
696
697 let err = result.unwrap_err();
698 assert!(err.contains("IndexError"));
699 }
700
701 #[test]
702 fn test_key_error_exception() {
703 let mut ctx = Context::new();
704 let result = ctx.eval(
705 r#"
706my_dict = {"a": 1}
707x = my_dict["b"]
708 "#,
709 );
710 assert!(result.is_err());
711
712 let err = result.unwrap_err();
713 assert!(err.contains("KeyError"));
714 }
715
716 #[test]
717 fn test_try_except_basic() {
718 let mut ctx = Context::new();
719 ctx.eval(
720 r#"
721result = "ok"
722try:
723 x = 1 / 0
724except ZeroDivisionError:
725 result = "caught"
726 "#,
727 )
728 .unwrap();
729
730 let result = ctx.get("result").unwrap();
731 assert_eq!(result.as_string(), Some("caught"));
732 }
733
734 #[test]
735 fn test_try_except_with_binding() {
736 let mut ctx = Context::new();
737 ctx.eval(
738 r#"
739msg = ""
740try:
741 raise ValueError("test error")
742except ValueError as e:
743 msg = "caught"
744 "#,
745 )
746 .unwrap();
747
748 let msg = ctx.get("msg").unwrap();
749 assert_eq!(msg.as_string(), Some("caught"));
750 }
751
752 #[test]
753 fn test_try_except_multiple() {
754 let mut ctx = Context::new();
755 ctx.eval(
756 r#"
757result = ""
758try:
759 x = 1 / 0
760except ValueError:
761 result = "value"
762except ZeroDivisionError:
763 result = "zero"
764 "#,
765 )
766 .unwrap();
767
768 let result = ctx.get("result").unwrap();
769 assert_eq!(result.as_string(), Some("zero"));
770 }
771
772 #[test]
773 fn test_try_except_no_match() {
774 let mut ctx = Context::new();
775 let result = ctx.eval(
776 r#"
777try:
778 x = 1 / 0
779except ValueError:
780 pass
781 "#,
782 );
783
784 assert!(result.is_err());
785 let err = result.unwrap_err();
786 assert!(err.contains("ZeroDivisionError"));
787 }
788
789 #[test]
790 fn test_break_in_while() {
791 let mut ctx = Context::new();
792 ctx.eval(
793 r#"
794i = 0
795sum = 0
796while i < 100:
797 if i == 5:
798 break
799 sum = sum + i
800 i = i + 1
801 "#,
802 )
803 .unwrap();
804
805 let sum = ctx.get("sum").unwrap();
806 assert_eq!(sum.as_int(), Some(10)); let i = ctx.get("i").unwrap();
809 assert_eq!(i.as_int(), Some(5));
810 }
811
812 #[test]
813 fn test_continue_in_while() {
814 let mut ctx = Context::new();
815 ctx.eval(
816 r#"
817i = 0
818sum = 0
819while i < 10:
820 i = i + 1
821 if i == 5:
822 continue
823 sum = sum + i
824 "#,
825 )
826 .unwrap();
827
828 let sum = ctx.get("sum").unwrap();
829 assert_eq!(sum.as_int(), Some(50)); }
831
832 #[test]
833 fn test_break_in_for() {
834 let mut ctx = Context::new();
835 ctx.eval(
836 r#"
837sum = 0
838for i in range(100):
839 if i == 5:
840 break
841 sum = sum + i
842 "#,
843 )
844 .unwrap();
845
846 let sum = ctx.get("sum").unwrap();
847 assert_eq!(sum.as_int(), Some(10)); }
849
850 #[test]
851 fn test_continue_in_for() {
852 let mut ctx = Context::new();
853 ctx.eval(
854 r#"
855sum = 0
856for i in range(10):
857 if i == 5:
858 continue
859 sum = sum + i
860 "#,
861 )
862 .unwrap();
863
864 let sum = ctx.get("sum").unwrap();
865 assert_eq!(sum.as_int(), Some(40)); }
867
868 #[test]
869 fn test_break_in_nested_loop() {
870 let mut ctx = Context::new();
871 ctx.eval(
872 r#"
873outer_count = 0
874inner_count = 0
875for i in range(3):
876 outer_count = outer_count + 1
877 for j in range(5):
878 if j == 2:
879 break
880 inner_count = inner_count + 1
881 "#,
882 )
883 .unwrap();
884
885 let outer_count = ctx.get("outer_count").unwrap();
886 assert_eq!(outer_count.as_int(), Some(3)); let inner_count = ctx.get("inner_count").unwrap();
889 assert_eq!(inner_count.as_int(), Some(6)); }
891
892 #[test]
893 fn test_continue_in_nested_loop() {
894 let mut ctx = Context::new();
895 ctx.eval(
896 r#"
897count = 0
898for i in range(3):
899 for j in range(5):
900 if j == 2:
901 continue
902 count = count + 1
903 "#,
904 )
905 .unwrap();
906
907 let count = ctx.get("count").unwrap();
908 assert_eq!(count.as_int(), Some(12)); }
910
911 #[test]
912 fn test_break_outside_loop() {
913 let mut ctx = Context::new();
914 let result = ctx.eval("break");
915
916 assert!(result.is_err());
917 let err = result.unwrap_err();
918 assert!(err.contains("break") && err.contains("outside loop"));
919 }
920
921 #[test]
922 fn test_continue_outside_loop() {
923 let mut ctx = Context::new();
924 let result = ctx.eval("continue");
925
926 assert!(result.is_err());
927 let err = result.unwrap_err();
928 assert!(err.contains("continue") && err.contains("outside loop"));
929 }
930
931 #[test]
932 fn test_break_with_list_iteration() {
933 let mut ctx = Context::new();
934 ctx.eval(
935 r#"
936numbers = [10, 20, 30, 40, 50]
937sum = 0
938for num in numbers:
939 if num == 30:
940 break
941 sum = sum + num
942 "#,
943 )
944 .unwrap();
945
946 let sum = ctx.get("sum").unwrap();
947 assert_eq!(sum.as_int(), Some(30)); }
949
950 #[test]
951 fn test_continue_with_list_iteration() {
952 let mut ctx = Context::new();
953 ctx.eval(
954 r#"
955numbers = [10, 20, 30, 40, 50]
956sum = 0
957for num in numbers:
958 if num == 30:
959 continue
960 sum = sum + num
961 "#,
962 )
963 .unwrap();
964
965 let sum = ctx.get("sum").unwrap();
966 assert_eq!(sum.as_int(), Some(120)); }
968
969 #[test]
970 fn test_break_in_function() {
971 let mut ctx = Context::new();
972 ctx.eval(
973 r#"
974def find_first_even(numbers):
975 result = 0
976 for num in numbers:
977 if num == 0:
978 break
979 if num / 2 * 2 == num:
980 result = num
981 break
982 return result
983 "#,
984 )
985 .unwrap();
986
987 let result = ctx.eval("find_first_even([1, 3, 5, 8, 10])").unwrap();
988 assert_eq!(result.as_int(), Some(8));
989 }
990
991 #[test]
992 fn test_continue_skip_odds() {
993 let mut ctx = Context::new();
994 ctx.eval(
995 r#"
996sum = 0
997for i in range(10):
998 if i / 2 * 2 != i:
999 continue
1000 sum = sum + i
1001 "#,
1002 )
1003 .unwrap();
1004
1005 let sum = ctx.get("sum").unwrap();
1006 assert_eq!(sum.as_int(), Some(20)); }
1008
1009 #[test]
1010 fn test_try_finally_basic() {
1011 let mut ctx = Context::new();
1012 ctx.eval(
1013 r#"
1014executed = []
1015try:
1016 executed.append(1)
1017finally:
1018 executed.append(2)
1019 "#,
1020 )
1021 .unwrap();
1022
1023 let executed = ctx.get("executed").unwrap().as_list().unwrap();
1024 assert_eq!(executed.borrow().items.len(), 2);
1025 }
1026
1027 #[test]
1028 fn test_try_except_finally() {
1029 let mut ctx = Context::new();
1030 ctx.eval(
1031 r#"
1032result = ""
1033try:
1034 x = 1 / 0
1035except ZeroDivisionError:
1036 result = "caught"
1037finally:
1038 result = result + " finally"
1039 "#,
1040 )
1041 .unwrap();
1042
1043 let result = ctx.get("result").unwrap();
1044 assert_eq!(result.as_string(), Some("caught finally"));
1045 }
1046
1047 #[test]
1048 fn test_iterator_modification_append() {
1049 let mut ctx = Context::new();
1050 let result = ctx.eval(
1051 r#"
1052numbers = [1, 2, 3]
1053for n in numbers:
1054 numbers.append(10)
1055 "#,
1056 );
1057
1058 assert!(result.is_err());
1059 let err = result.unwrap_err();
1060 assert!(err.contains("IteratorError"));
1061 }
1062
1063 #[test]
1064 fn test_iterator_modification_pop() {
1065 let mut ctx = Context::new();
1066 let result = ctx.eval(
1067 r#"
1068numbers = [1, 2, 3]
1069for n in numbers:
1070 numbers.pop()
1071 "#,
1072 );
1073
1074 assert!(result.is_err());
1075 let err = result.unwrap_err();
1076 assert!(err.contains("IteratorError"));
1077 }
1078
1079 #[test]
1080 fn test_iterator_modification_after_loop_ok() {
1081 let mut ctx = Context::new();
1082 ctx.eval(
1083 r#"
1084numbers = [1, 2, 3]
1085for n in numbers:
1086 pass
1087numbers.append(4)
1088 "#,
1089 )
1090 .unwrap();
1091
1092 let numbers = ctx.get("numbers").unwrap().as_list().unwrap();
1093 assert_eq!(numbers.borrow().items.len(), 4);
1094 }
1095
1096 #[test]
1099 fn test_float_comparison() {
1100 let mut ctx = Context::new();
1101 ctx.eval("x = 3.14").unwrap();
1102 ctx.eval("y = 2.71").unwrap();
1103
1104 let result = ctx.eval("x > y").unwrap();
1105 assert_eq!(result.as_bool(), Some(true));
1106
1107 let result = ctx.eval("x == y").unwrap();
1108 assert_eq!(result.as_bool(), Some(false));
1109
1110 let result = ctx.eval("x != y").unwrap();
1111 assert_eq!(result.as_bool(), Some(true));
1112 }
1113
1114 #[test]
1115 fn test_mixed_int_float_comparison() {
1116 let mut ctx = Context::new();
1117 ctx.eval("score = 87.5").unwrap();
1118
1119 let result = ctx.eval("score >= 90").unwrap();
1120 assert_eq!(result.as_bool(), Some(false));
1121
1122 let result = ctx.eval("score >= 80").unwrap();
1123 assert_eq!(result.as_bool(), Some(true));
1124
1125 let result = ctx.eval("10 == 10.0").unwrap();
1126 assert_eq!(result.as_bool(), Some(true));
1127
1128 let result = ctx.eval("5 < 5.5").unwrap();
1129 assert_eq!(result.as_bool(), Some(true));
1130 }
1131
1132 #[test]
1133 fn test_string_comparison() {
1134 let mut ctx = Context::new();
1135
1136 let result = ctx.eval(r#""hello" == "hello""#).unwrap();
1138 assert_eq!(result.as_bool(), Some(true));
1139
1140 let result = ctx.eval(r#""hello" == "world""#).unwrap();
1141 assert_eq!(result.as_bool(), Some(false));
1142
1143 let result = ctx.eval(r#""hello" != "world""#).unwrap();
1144 assert_eq!(result.as_bool(), Some(true));
1145
1146 let result = ctx.eval(r#""apple" < "banana""#).unwrap();
1148 assert_eq!(result.as_bool(), Some(true));
1149
1150 let result = ctx.eval(r#""zebra" > "apple""#).unwrap();
1151 assert_eq!(result.as_bool(), Some(true));
1152 }
1153
1154 #[test]
1155 fn test_bool_comparison() {
1156 let mut ctx = Context::new();
1157
1158 let result = ctx.eval("True == True").unwrap();
1159 assert_eq!(result.as_bool(), Some(true));
1160
1161 let result = ctx.eval("True == False").unwrap();
1162 assert_eq!(result.as_bool(), Some(false));
1163
1164 let result = ctx.eval("False != True").unwrap();
1165 assert_eq!(result.as_bool(), Some(true));
1166 }
1167
1168 #[test]
1169 fn test_none_comparison() {
1170 let mut ctx = Context::new();
1171 ctx.eval("x = None").unwrap();
1172
1173 let result = ctx.eval("x == None").unwrap();
1174 assert_eq!(result.as_bool(), Some(true));
1175
1176 let result = ctx.eval("x != None").unwrap();
1177 assert_eq!(result.as_bool(), Some(false));
1178 }
1179
1180 #[test]
1181 fn test_different_types_equality() {
1182 let mut ctx = Context::new();
1183
1184 let result = ctx.eval(r#""hello" == 5"#).unwrap();
1186 assert_eq!(result.as_bool(), Some(false));
1187
1188 let result = ctx.eval(r#"True == 5"#).unwrap();
1189 assert_eq!(result.as_bool(), Some(false));
1190 }
1191
1192 #[test]
1193 fn test_different_types_ordering_error() {
1194 let mut ctx = Context::new();
1195
1196 let result = ctx.eval(r#""hello" < 5"#);
1198 assert!(result.is_err());
1199 let err = result.unwrap_err();
1200 assert!(err.contains("TypeError"));
1201 assert!(err.contains("unsupported operand types"));
1202 }
1203
1204 #[test]
1205 fn test_grade_function_with_float() {
1206 let mut ctx = Context::new();
1207 ctx.eval(
1208 r#"
1209def get_grade(score):
1210 if score >= 90.0:
1211 return "A"
1212 else:
1213 if score >= 80.0:
1214 return "B"
1215 else:
1216 if score >= 70.0:
1217 return "C"
1218 else:
1219 return "F"
1220
1221grade = get_grade(85.5)
1222 "#,
1223 )
1224 .unwrap();
1225
1226 let grade = ctx.get("grade").unwrap();
1227 assert_eq!(grade.as_string(), Some("B"));
1228 }
1229
1230 #[test]
1231 fn test_import_json() {
1232 let mut ctx = Context::new();
1233 ctx.eval("import json").unwrap();
1234 }
1235
1236 #[test]
1237 fn test_json_loads() {
1238 let mut ctx = Context::new();
1239 let result = ctx
1240 .eval(
1241 r#"
1242import json
1243data = json.loads('{"x": 1, "y": 2}')
1244data["x"]
1245 "#,
1246 )
1247 .unwrap();
1248 assert_eq!(result.as_int(), Some(1));
1249 }
1250
1251 #[test]
1252 fn test_json_dumps() {
1253 let mut ctx = Context::new();
1254 let result = ctx
1255 .eval(
1256 r#"
1257import json
1258obj = {"x": 1, "y": 2}
1259json.dumps(obj)
1260 "#,
1261 )
1262 .unwrap();
1263
1264 let json_str = result.as_string().unwrap();
1265 assert!(json_str.contains("\"x\""));
1266 assert!(json_str.contains("1"));
1267 }
1268
1269 #[test]
1270 fn test_from_import() {
1271 let mut ctx = Context::new();
1272 let result = ctx
1273 .eval(
1274 r#"
1275from json import loads
1276data = loads('{"value": 42}')
1277data["value"]
1278 "#,
1279 )
1280 .unwrap();
1281 assert_eq!(result.as_int(), Some(42));
1282 }
1283
1284 #[test]
1285 fn test_import_as() {
1286 let mut ctx = Context::new();
1287 let result = ctx
1288 .eval(
1289 r#"
1290import json as j
1291data = j.loads('{"test": true}')
1292data["test"]
1293 "#,
1294 )
1295 .unwrap();
1296 assert_eq!(result.as_bool(), Some(true));
1297 }
1298
1299 #[test]
1300 fn test_json_array() {
1301 let mut ctx = Context::new();
1302 let result = ctx
1303 .eval(
1304 r#"
1305import json
1306arr = json.loads('[1, 2, 3, 4, 5]')
1307len(arr)
1308 "#,
1309 )
1310 .unwrap();
1311 assert_eq!(result.as_int(), Some(5));
1312 }
1313
1314 #[test]
1315 fn test_json_nested() {
1316 let mut ctx = Context::new();
1317 let result = ctx
1318 .eval(
1319 r#"
1320import json
1321data = json.loads('{"user": {"name": "Bob", "id": 123}}')
1322data["user"]["name"]
1323 "#,
1324 )
1325 .unwrap();
1326 assert_eq!(result.as_string(), Some("Bob"));
1327 }
1328
1329 #[test]
1330 fn test_module_not_found() {
1331 let mut ctx = Context::new();
1332 let result = ctx.eval("import nonexistent");
1333 assert!(result.is_err());
1334 }
1335
1336 #[test]
1337 fn test_os_getcwd() {
1338 let mut ctx = Context::new();
1339 let result = ctx
1340 .eval(
1341 r#"
1342import os
1343cwd = os.getcwd()
1344len(cwd) > 0
1345 "#,
1346 )
1347 .unwrap();
1348 assert_eq!(result.as_bool(), Some(true));
1349 }
1350
1351 #[test]
1352 fn test_os_listdir() {
1353 let mut ctx = Context::new();
1354 let result = ctx
1355 .eval(
1356 r#"
1357import os
1358files = os.listdir(".")
1359len(files) > 0
1360 "#,
1361 )
1362 .unwrap();
1363 assert_eq!(result.as_bool(), Some(true));
1364 }
1365
1366 #[test]
1367 fn test_os_path_exists() {
1368 let mut ctx = Context::new();
1369 let result = ctx
1370 .eval(
1371 r#"
1372import os
1373os.path.exists("Cargo.toml")
1374 "#,
1375 )
1376 .unwrap();
1377 assert_eq!(result.as_bool(), Some(true));
1378 }
1379
1380 #[test]
1381 fn test_os_path_join() {
1382 let mut ctx = Context::new();
1383 let result = ctx
1384 .eval(
1385 r#"
1386import os
1387path = os.path.join("dir", "subdir", "file.txt")
1388len(path) > 0
1389 "#,
1390 )
1391 .unwrap();
1392 assert_eq!(result.as_bool(), Some(true));
1393 }
1394
1395 #[test]
1396 fn test_os_path_basename() {
1397 let mut ctx = Context::new();
1398 let result = ctx
1399 .eval(
1400 r#"
1401import os
1402os.path.basename("/path/to/file.txt")
1403 "#,
1404 )
1405 .unwrap();
1406 assert_eq!(result.as_string(), Some("file.txt"));
1407 }
1408
1409 #[test]
1410 fn test_os_path_dirname() {
1411 let mut ctx = Context::new();
1412 let result = ctx
1413 .eval(
1414 r#"
1415import os
1416os.path.dirname("/path/to/file.txt")
1417 "#,
1418 )
1419 .unwrap();
1420 assert_eq!(result.as_string(), Some("/path/to"));
1421 }
1422
1423 #[test]
1424 fn test_os_getenv() {
1425 let mut ctx = Context::new();
1426
1427 unsafe {
1428 std::env::set_var("TEST_VAR_OS", "test_value");
1429 }
1430
1431 let result = ctx
1432 .eval(
1433 r#"
1434import os
1435os.getenv("TEST_VAR_OS")
1436 "#,
1437 )
1438 .unwrap();
1439 assert_eq!(result.as_string(), Some("test_value"));
1440
1441 let result = ctx
1442 .eval(
1443 r#"
1444import os
1445os.getenv("NONEXISTENT_VAR_OS", "default")
1446 "#,
1447 )
1448 .unwrap();
1449 assert_eq!(result.as_string(), Some("default"));
1450 }
1451
1452 #[test]
1453 fn test_os_name() {
1454 let mut ctx = Context::new();
1455 let result = ctx
1456 .eval(
1457 r#"
1458import os
1459os.name
1460 "#,
1461 )
1462 .unwrap();
1463
1464 let name = result.as_string().unwrap();
1465 assert!(name == "posix" || name == "nt");
1466 }
1467
1468 #[test]
1469 fn test_os_mkdir_rmdir() {
1470 let mut ctx = Context::new();
1471 let result = ctx
1472 .eval(
1473 r#"
1474import os
1475os.mkdir("test_dir_quickpy")
1476exists = os.path.exists("test_dir_quickpy")
1477os.rmdir("test_dir_quickpy")
1478exists
1479 "#,
1480 )
1481 .unwrap();
1482 assert_eq!(result.as_bool(), Some(true));
1483 }
1484
1485 #[test]
1486 fn test_re_match() {
1487 let mut ctx = Context::new();
1488 let result = ctx
1489 .eval(
1490 r#"
1491import re
1492m = re.match(r"hello", "hello world")
1493m.group(0)
1494 "#,
1495 )
1496 .unwrap();
1497 assert_eq!(result.as_string(), Some("hello"));
1498 }
1499
1500 #[test]
1501 fn test_re_match_no_match() {
1502 let mut ctx = Context::new();
1503 let result = ctx
1504 .eval(
1505 r#"
1506import re
1507m = re.match(r"world", "hello world")
1508m
1509 "#,
1510 )
1511 .unwrap();
1512 assert_eq!(result, Value::None);
1513 }
1514
1515 #[test]
1516 fn test_re_search() {
1517 let mut ctx = Context::new();
1518 let result = ctx
1519 .eval(
1520 r#"
1521import re
1522m = re.search(r"world", "hello world")
1523m.group(0)
1524 "#,
1525 )
1526 .unwrap();
1527 assert_eq!(result.as_string(), Some("world"));
1528 }
1529
1530 #[test]
1531 fn test_re_findall() {
1532 let mut ctx = Context::new();
1533 let result = ctx
1534 .eval(
1535 r#"
1536import re
1537matches = re.findall(r"\d+", "abc 123 def 456 ghi")
1538len(matches)
1539 "#,
1540 )
1541 .unwrap();
1542 assert_eq!(result.as_int(), Some(2));
1543 }
1544
1545 #[test]
1546 fn test_re_sub() {
1547 let mut ctx = Context::new();
1548 let result = ctx
1549 .eval(
1550 r#"
1551import re
1552result = re.sub(r"\d+", "X", "abc 123 def 456")
1553result
1554 "#,
1555 )
1556 .unwrap();
1557 assert_eq!(result.as_string(), Some("abc X def X"));
1558 }
1559
1560 #[test]
1561 fn test_re_split() {
1562 let mut ctx = Context::new();
1563 let result = ctx
1564 .eval(
1565 r#"
1566import re
1567parts = re.split(r"\s+", "hello world test")
1568len(parts)
1569 "#,
1570 )
1571 .unwrap();
1572 assert_eq!(result.as_int(), Some(3));
1573 }
1574
1575 #[test]
1576 fn test_re_groups() {
1577 let mut ctx = Context::new();
1578 let result = ctx
1579 .eval(
1580 r#"
1581import re
1582m = re.search(r"(\d+)-(\d+)", "Phone: 123-456")
1583m.group(1)
1584 "#,
1585 )
1586 .unwrap();
1587 assert_eq!(result.as_string(), Some("123"));
1588 }
1589
1590 #[test]
1591 fn test_re_match_span() {
1592 let mut ctx = Context::new();
1593 let result = ctx
1594 .eval(
1595 r#"
1596import re
1597m = re.search(r"world", "hello world")
1598span = m.span()
1599span[0]
1600 "#,
1601 )
1602 .unwrap();
1603 assert_eq!(result.as_int(), Some(6));
1604 }
1605
1606 #[test]
1609 fn test_compile_serialize_execute() {
1610 let source = "x = 1 + 2";
1611 let bytecode = Compiler::compile(source).unwrap();
1612 let bytes = serialize_bytecode(&bytecode).unwrap();
1613 let restored = deserialize_bytecode(&bytes).unwrap();
1614
1615 let mut ctx = Context::new();
1616 ctx.eval_bytecode(&restored).unwrap();
1617 assert_eq!(ctx.get("x"), Some(Value::Int(3)));
1618 }
1619
1620 #[test]
1621 fn test_compile_serialize_roundtrip() {
1622 let source = r#"
1623x = 10 + 20
1624y = x * 2
1625"#;
1626 let bytecode = Compiler::compile(source).unwrap();
1627 let bytes = serialize_bytecode(&bytecode).unwrap();
1628 let restored = deserialize_bytecode(&bytes).unwrap();
1629 assert_eq!(bytecode, restored);
1630 }
1631
1632 #[test]
1633 fn test_serialize_error_on_function() {
1634 let source = "def foo(): return 1";
1635 let bytecode = Compiler::compile(source).unwrap();
1636 assert!(serialize_bytecode(&bytecode).is_err());
1637 }
1638
1639 #[test]
1640 fn test_serialize_error_on_list() {
1641 let source = "x = [1, 2, 3]";
1642 let bytecode = Compiler::compile(source).unwrap();
1643 assert!(serialize_bytecode(&bytecode).is_err());
1644 }
1645
1646 #[test]
1647 fn test_eval_bytecode_with_string() {
1648 let source = r#"name = "hello""#;
1649 let bytecode = Compiler::compile(source).unwrap();
1650 let bytes = serialize_bytecode(&bytecode).unwrap();
1651 let restored = deserialize_bytecode(&bytes).unwrap();
1652
1653 let mut ctx = Context::new();
1654 ctx.eval_bytecode(&restored).unwrap();
1655 assert_eq!(ctx.get("name"), Some(Value::String("hello".to_string())));
1656 }
1657
1658 #[test]
1659 fn test_eval_bytecode_with_float() {
1660 let source = "pi = 3.15";
1661 let bytecode = Compiler::compile(source).unwrap();
1662 let bytes = serialize_bytecode(&bytecode).unwrap();
1663 let restored = deserialize_bytecode(&bytes).unwrap();
1664
1665 let mut ctx = Context::new();
1666 ctx.eval_bytecode(&restored).unwrap();
1667 assert_eq!(ctx.get("pi"), Some(Value::Float(3.15)));
1668 }
1669
1670 #[test]
1671 fn test_unary_minus_int() {
1672 let mut ctx = Context::new();
1673 let result = ctx.eval("-17").unwrap();
1674 assert_eq!(result, Value::Int(-17));
1675 }
1676
1677 #[test]
1678 fn test_unary_minus_float() {
1679 let mut ctx = Context::new();
1680 let result = ctx.eval("-3.15").unwrap();
1681 assert_eq!(result, Value::Float(-3.15));
1682 }
1683
1684 #[test]
1685 fn test_unary_minus_variable() {
1686 let mut ctx = Context::new();
1687 ctx.eval("x = 42").unwrap();
1688 let result = ctx.eval("-x").unwrap();
1689 assert_eq!(result, Value::Int(-42));
1690 }
1691
1692 #[test]
1693 fn test_unary_plus() {
1694 let mut ctx = Context::new();
1695 let result = ctx.eval("+17").unwrap();
1696 assert_eq!(result, Value::Int(17));
1697 }
1698
1699 #[test]
1700 fn test_double_negative() {
1701 let mut ctx = Context::new();
1702 let result = ctx.eval("--17").unwrap();
1703 assert_eq!(result, Value::Int(17));
1704 }
1705
1706 #[test]
1707 fn test_unary_minus_in_expression() {
1708 let mut ctx = Context::new();
1709 let result = ctx.eval("10 + -5").unwrap();
1710 assert_eq!(result, Value::Int(5));
1711 }
1712
1713 #[test]
1714 fn test_unary_minus_serialization() {
1715 let source = "-42";
1716 let bytecode = Compiler::compile(source).unwrap();
1717 let bytes = serialize_bytecode(&bytecode).unwrap();
1718 let restored = deserialize_bytecode(&bytes).unwrap();
1719
1720 let mut ctx = Context::new();
1721 let result = ctx.eval_bytecode(&restored).unwrap();
1722 assert_eq!(result, Value::Int(-42));
1723 }
1724
1725 #[test]
1726 fn test_modulo_int() {
1727 let mut ctx = Context::new();
1728 let result = ctx.eval("10 % 3").unwrap();
1729 assert_eq!(result, Value::Int(1));
1730 }
1731
1732 #[test]
1733 fn test_modulo_even_check() {
1734 let mut ctx = Context::new();
1735 let result = ctx.eval("10 % 2").unwrap();
1736 assert_eq!(result, Value::Int(0));
1737 }
1738
1739 #[test]
1740 fn test_modulo_float() {
1741 let mut ctx = Context::new();
1742 let result = ctx.eval("10.5 % 3.0").unwrap();
1743 assert_eq!(result, Value::Float(1.5));
1744 }
1745
1746 #[test]
1747 fn test_modulo_mixed() {
1748 let mut ctx = Context::new();
1749 let result = ctx.eval("10 % 3.0").unwrap();
1750 assert_eq!(result, Value::Float(1.0));
1751 }
1752
1753 #[test]
1754 fn test_modulo_zero_error() {
1755 let mut ctx = Context::new();
1756 let result = ctx.eval("10 % 0");
1757 assert!(result.is_err());
1758 }
1759
1760 #[test]
1761 fn test_exception_hierarchy_exception_catches_valueerror() {
1762 let mut ctx = Context::new();
1763 let result = ctx
1764 .eval(
1765 r#"
1766try:
1767 raise ValueError("test")
1768except Exception:
1769 x = "caught"
1770x
1771"#,
1772 )
1773 .unwrap();
1774 assert_eq!(result, Value::String("caught".to_string()));
1775 }
1776
1777 #[test]
1778 fn test_exception_hierarchy_exception_catches_all() {
1779 let mut ctx = Context::new();
1780 let result = ctx
1781 .eval(
1782 r#"
1783count = 0
1784try:
1785 raise ValueError("test")
1786except Exception:
1787 count = count + 1
1788try:
1789 raise TypeError("test")
1790except Exception:
1791 count = count + 1
1792try:
1793 raise IndexError("test")
1794except Exception:
1795 count = count + 1
1796count
1797"#,
1798 )
1799 .unwrap();
1800 assert_eq!(result, Value::Int(3));
1801 }
1802
1803 #[test]
1804 fn test_exception_hierarchy_specific_handler_works() {
1805 let mut ctx = Context::new();
1806 let result = ctx
1807 .eval(
1808 r#"
1809try:
1810 raise ValueError("test")
1811except ValueError:
1812 x = "caught ValueError"
1813x
1814"#,
1815 )
1816 .unwrap();
1817 assert_eq!(result, Value::String("caught ValueError".to_string()));
1818 }
1819
1820 #[test]
1821 fn test_exception_hierarchy_wrong_handler_doesnt_catch() {
1822 let mut ctx = Context::new();
1823 let result = ctx
1824 .eval(
1825 r#"
1826try:
1827 raise ValueError("test")
1828except TypeError:
1829 x = "caught TypeError"
1830except ValueError:
1831 x = "caught ValueError"
1832x
1833"#,
1834 )
1835 .unwrap();
1836 assert_eq!(result, Value::String("caught ValueError".to_string()));
1837 }
1838
1839 #[test]
1841 fn test_augmented_add() {
1842 let mut ctx = Context::new();
1843 let result = ctx.eval("x = 10\nx += 5\nx").unwrap();
1844 assert_eq!(result, Value::Int(15));
1845 }
1846
1847 #[test]
1848 fn test_augmented_sub() {
1849 let mut ctx = Context::new();
1850 let result = ctx.eval("x = 10\nx -= 3\nx").unwrap();
1851 assert_eq!(result, Value::Int(7));
1852 }
1853
1854 #[test]
1855 fn test_augmented_mul() {
1856 let mut ctx = Context::new();
1857 let result = ctx.eval("x = 5\nx *= 3\nx").unwrap();
1858 assert_eq!(result, Value::Int(15));
1859 }
1860
1861 #[test]
1862 fn test_augmented_div() {
1863 let mut ctx = Context::new();
1864 let result = ctx.eval("x = 20\nx /= 4\nx").unwrap();
1865 assert_eq!(result, Value::Int(5));
1866 }
1867
1868 #[test]
1869 fn test_augmented_mod() {
1870 let mut ctx = Context::new();
1871 let result = ctx.eval("x = 17\nx %= 5\nx").unwrap();
1872 assert_eq!(result, Value::Int(2));
1873 }
1874
1875 #[test]
1876 fn test_augmented_float() {
1877 let mut ctx = Context::new();
1878 let result = ctx.eval("x = 10.0\nx += 2.5\nx").unwrap();
1879 assert_eq!(result, Value::Float(12.5));
1880 }
1881
1882 #[test]
1883 fn test_augmented_string() {
1884 let mut ctx = Context::new();
1885 let result = ctx.eval("s = \"Hello\"\ns += \" World\"\ns").unwrap();
1886 assert_eq!(result, Value::String("Hello World".to_string()));
1887 }
1888
1889 #[test]
1890 fn test_augmented_in_loop() {
1891 let mut ctx = Context::new();
1892 let result = ctx
1893 .eval(
1894 r#"
1895total = 0
1896for i in range(5):
1897 total += i
1898total
1899"#,
1900 )
1901 .unwrap();
1902 assert_eq!(result, Value::Int(10));
1903 }
1904
1905 #[test]
1907 fn test_logical_and_both_true() {
1908 let mut ctx = Context::new();
1909 let result = ctx.eval("True and True").unwrap();
1910 assert_eq!(result, Value::Bool(true));
1911 }
1912
1913 #[test]
1914 fn test_logical_and_first_false() {
1915 let mut ctx = Context::new();
1916 let result = ctx.eval("False and True").unwrap();
1917 assert_eq!(result, Value::Bool(false));
1918 }
1919
1920 #[test]
1921 fn test_logical_and_second_false() {
1922 let mut ctx = Context::new();
1923 let result = ctx.eval("True and False").unwrap();
1924 assert_eq!(result, Value::Bool(false));
1925 }
1926
1927 #[test]
1928 fn test_logical_or_both_false() {
1929 let mut ctx = Context::new();
1930 let result = ctx.eval("False or False").unwrap();
1931 assert_eq!(result, Value::Bool(false));
1932 }
1933
1934 #[test]
1935 fn test_logical_or_first_true() {
1936 let mut ctx = Context::new();
1937 let result = ctx.eval("True or False").unwrap();
1938 assert_eq!(result, Value::Bool(true));
1939 }
1940
1941 #[test]
1942 fn test_logical_or_second_true() {
1943 let mut ctx = Context::new();
1944 let result = ctx.eval("False or True").unwrap();
1945 assert_eq!(result, Value::Bool(true));
1946 }
1947
1948 #[test]
1949 fn test_logical_not_true() {
1950 let mut ctx = Context::new();
1951 let result = ctx.eval("not True").unwrap();
1952 assert_eq!(result, Value::Bool(false));
1953 }
1954
1955 #[test]
1956 fn test_logical_not_false() {
1957 let mut ctx = Context::new();
1958 let result = ctx.eval("not False").unwrap();
1959 assert_eq!(result, Value::Bool(true));
1960 }
1961
1962 #[test]
1963 fn test_logical_and_returns_value() {
1964 let mut ctx = Context::new();
1965 let result = ctx.eval("1 and 2").unwrap();
1967 assert_eq!(result, Value::Int(2));
1968
1969 let result = ctx.eval("0 and 5").unwrap();
1970 assert_eq!(result, Value::Int(0));
1971 }
1972
1973 #[test]
1974 fn test_logical_or_returns_value() {
1975 let mut ctx = Context::new();
1976 let result = ctx.eval("1 or 2").unwrap();
1978 assert_eq!(result, Value::Int(1));
1979
1980 let result = ctx.eval("0 or 5").unwrap();
1981 assert_eq!(result, Value::Int(5));
1982 }
1983
1984 #[test]
1985 fn test_logical_truthiness() {
1986 let mut ctx = Context::new();
1987 assert_eq!(ctx.eval("not 0").unwrap(), Value::Bool(true));
1989 assert_eq!(ctx.eval("not 1").unwrap(), Value::Bool(false));
1990 assert_eq!(ctx.eval("not \"\"").unwrap(), Value::Bool(true));
1991 assert_eq!(ctx.eval("not \"hello\"").unwrap(), Value::Bool(false));
1992 assert_eq!(ctx.eval("not []").unwrap(), Value::Bool(true));
1993 assert_eq!(ctx.eval("not [1]").unwrap(), Value::Bool(false));
1994 }
1995
1996 #[test]
1997 fn test_logical_in_condition() {
1998 let mut ctx = Context::new();
1999 let result = ctx
2000 .eval(
2001 r#"
2002x = 5
2003if x > 0 and x < 10:
2004 result = "in range"
2005else:
2006 result = "out of range"
2007result
2008"#,
2009 )
2010 .unwrap();
2011 assert_eq!(result, Value::String("in range".to_string()));
2012 }
2013
2014 #[test]
2015 fn test_logical_chaining() {
2016 let mut ctx = Context::new();
2017 let result = ctx.eval("True and True and True").unwrap();
2018 assert_eq!(result, Value::Bool(true));
2019
2020 let result = ctx.eval("False or False or True").unwrap();
2021 assert_eq!(result, Value::Bool(true));
2022 }
2023
2024 #[test]
2026 fn test_in_list_found() {
2027 let mut ctx = Context::new();
2028 let result = ctx.eval("2 in [1, 2, 3]").unwrap();
2029 assert_eq!(result, Value::Bool(true));
2030 }
2031
2032 #[test]
2033 fn test_in_list_not_found() {
2034 let mut ctx = Context::new();
2035 let result = ctx.eval("4 in [1, 2, 3]").unwrap();
2036 assert_eq!(result, Value::Bool(false));
2037 }
2038
2039 #[test]
2040 fn test_not_in_list() {
2041 let mut ctx = Context::new();
2042 let result = ctx.eval("4 not in [1, 2, 3]").unwrap();
2043 assert_eq!(result, Value::Bool(true));
2044 }
2045
2046 #[test]
2047 fn test_in_string() {
2048 let mut ctx = Context::new();
2049 let result = ctx.eval("\"world\" in \"hello world\"").unwrap();
2050 assert_eq!(result, Value::Bool(true));
2051
2052 let result = ctx.eval("\"xyz\" in \"hello world\"").unwrap();
2053 assert_eq!(result, Value::Bool(false));
2054 }
2055
2056 #[test]
2057 fn test_in_dict() {
2058 let mut ctx = Context::new();
2059 let result = ctx
2060 .eval(
2061 r#"
2062d = {"a": 1, "b": 2}
2063result = "a" in d
2064result
2065"#,
2066 )
2067 .unwrap();
2068 assert_eq!(result, Value::Bool(true));
2069
2070 let result = ctx
2071 .eval(
2072 r#"
2073d = {"a": 1, "b": 2}
2074result = "c" in d
2075result
2076"#,
2077 )
2078 .unwrap();
2079 assert_eq!(result, Value::Bool(false));
2080 }
2081
2082 #[test]
2083 fn test_in_empty_containers() {
2084 let mut ctx = Context::new();
2085 assert_eq!(ctx.eval("1 in []").unwrap(), Value::Bool(false));
2086 assert_eq!(ctx.eval("\"x\" in {}").unwrap(), Value::Bool(false));
2087 assert_eq!(ctx.eval("\"x\" in \"\"").unwrap(), Value::Bool(false));
2088 }
2089
2090 #[test]
2091 fn test_in_condition() {
2092 let mut ctx = Context::new();
2093 let result = ctx
2094 .eval(
2095 r#"
2096numbers = [1, 2, 3, 4, 5]
2097if 3 in numbers:
2098 result = "found"
2099else:
2100 result = "not found"
2101result
2102"#,
2103 )
2104 .unwrap();
2105 assert_eq!(result, Value::String("found".to_string()));
2106 }
2107
2108 #[test]
2109 fn test_in_loop() {
2110 let mut ctx = Context::new();
2111 let result = ctx
2112 .eval(
2113 r#"
2114count = 0
2115for i in range(10):
2116 if i in [2, 4, 6, 8]:
2117 count += 1
2118count
2119"#,
2120 )
2121 .unwrap();
2122 assert_eq!(result, Value::Int(4));
2123 }
2124
2125 #[test]
2127 fn test_string_split_whitespace() {
2128 let mut ctx = Context::new();
2129 let result = ctx
2130 .eval(
2131 r#"
2132s = "hello world python"
2133words = s.split()
2134len(words)
2135"#,
2136 )
2137 .unwrap();
2138 assert_eq!(result, Value::Int(3));
2139 }
2140
2141 #[test]
2142 fn test_string_split_separator() {
2143 let mut ctx = Context::new();
2144 let result = ctx
2145 .eval(
2146 r#"
2147s = "a,b,c"
2148parts = s.split(",")
2149len(parts)
2150"#,
2151 )
2152 .unwrap();
2153 assert_eq!(result, Value::Int(3));
2154 }
2155
2156 #[test]
2157 fn test_string_strip() {
2158 let mut ctx = Context::new();
2159 let result = ctx.eval(r#"" hello ".strip()"#).unwrap();
2160 assert_eq!(result, Value::String("hello".to_string()));
2161 }
2162
2163 #[test]
2164 fn test_string_startswith() {
2165 let mut ctx = Context::new();
2166 let result = ctx.eval(r#""hello world".startswith("hello")"#).unwrap();
2167 assert_eq!(result, Value::Bool(true));
2168
2169 let result = ctx.eval(r#""hello world".startswith("world")"#).unwrap();
2170 assert_eq!(result, Value::Bool(false));
2171 }
2172
2173 #[test]
2174 fn test_string_endswith() {
2175 let mut ctx = Context::new();
2176 let result = ctx.eval(r#""hello world".endswith("world")"#).unwrap();
2177 assert_eq!(result, Value::Bool(true));
2178
2179 let result = ctx.eval(r#""hello world".endswith("hello")"#).unwrap();
2180 assert_eq!(result, Value::Bool(false));
2181 }
2182
2183 #[test]
2184 fn test_string_lower() {
2185 let mut ctx = Context::new();
2186 let result = ctx.eval(r#""Hello World".lower()"#).unwrap();
2187 assert_eq!(result, Value::String("hello world".to_string()));
2188 }
2189
2190 #[test]
2191 fn test_string_upper() {
2192 let mut ctx = Context::new();
2193 let result = ctx.eval(r#""Hello World".upper()"#).unwrap();
2194 assert_eq!(result, Value::String("HELLO WORLD".to_string()));
2195 }
2196
2197 #[test]
2198 fn test_string_replace() {
2199 let mut ctx = Context::new();
2200 let result = ctx
2201 .eval(r#""hello world".replace("world", "python")"#)
2202 .unwrap();
2203 assert_eq!(result, Value::String("hello python".to_string()));
2204 }
2205
2206 #[test]
2207 fn test_string_join() {
2208 let mut ctx = Context::new();
2209 let result = ctx
2210 .eval(
2211 r#"
2212words = ["hello", "world"]
2213" ".join(words)
2214"#,
2215 )
2216 .unwrap();
2217 assert_eq!(result, Value::String("hello world".to_string()));
2218
2219 let result = ctx
2220 .eval(
2221 r#"
2222words = ["hello", "world"]
2223",".join(words)
2224"#,
2225 )
2226 .unwrap();
2227 assert_eq!(result, Value::String("hello,world".to_string()));
2228 }
2229
2230 #[test]
2231 fn test_string_method_chaining() {
2232 let mut ctx = Context::new();
2233 let result = ctx.eval(r#"" HELLO WORLD ".strip().lower()"#).unwrap();
2234 assert_eq!(result, Value::String("hello world".to_string()));
2235 }
2236
2237 #[test]
2238 fn test_string_split_empty() {
2239 let mut ctx = Context::new();
2240 let result = ctx
2241 .eval(
2242 r#"
2243parts = "".split()
2244len(parts)
2245"#,
2246 )
2247 .unwrap();
2248 assert_eq!(result, Value::Int(0));
2249 }
2250
2251 #[test]
2253 fn test_dict_get_existing_key() {
2254 let mut ctx = Context::new();
2255 let result = ctx
2256 .eval(
2257 r#"
2258d = {"a": 1, "b": 2}
2259d.get("a")
2260"#,
2261 )
2262 .unwrap();
2263 assert_eq!(result, Value::Int(1));
2264 }
2265
2266 #[test]
2267 fn test_dict_get_missing_key_no_default() {
2268 let mut ctx = Context::new();
2269 let result = ctx
2270 .eval(
2271 r#"
2272d = {"a": 1}
2273d.get("b")
2274"#,
2275 )
2276 .unwrap();
2277 assert_eq!(result, Value::None);
2278 }
2279
2280 #[test]
2281 fn test_dict_get_missing_key_with_default() {
2282 let mut ctx = Context::new();
2283 let result = ctx
2284 .eval(
2285 r#"
2286d = {"a": 1}
2287d.get("b", 0)
2288"#,
2289 )
2290 .unwrap();
2291 assert_eq!(result, Value::Int(0));
2292
2293 let result = ctx
2294 .eval(
2295 r#"
2296d = {"a": 1}
2297d.get("c", "default")
2298"#,
2299 )
2300 .unwrap();
2301 assert_eq!(result, Value::String("default".to_string()));
2302 }
2303
2304 #[test]
2305 fn test_dict_get_doesnt_modify() {
2306 let mut ctx = Context::new();
2307 ctx.eval(
2308 r#"
2309d = {"a": 1}
2310result = d.get("b", 0)
2311"#,
2312 )
2313 .unwrap();
2314
2315 let result = ctx.eval(r#""b" in d"#).unwrap();
2316 assert_eq!(result, Value::Bool(false));
2317 }
2318
2319 #[test]
2320 fn test_dict_get_different_key_types() {
2321 let mut ctx = Context::new();
2322 let result = ctx
2323 .eval(
2324 r#"
2325d = {1: "one", "two": 2}
2326d.get(1)
2327"#,
2328 )
2329 .unwrap();
2330 assert_eq!(result, Value::String("one".to_string()));
2331
2332 let result = ctx
2333 .eval(
2334 r#"
2335d = {1: "one", "two": 2}
2336d.get("two")
2337"#,
2338 )
2339 .unwrap();
2340 assert_eq!(result, Value::Int(2));
2341
2342 let result = ctx
2343 .eval(
2344 r#"
2345d = {1: "one", "two": 2}
2346d.get(3, "missing")
2347"#,
2348 )
2349 .unwrap();
2350 assert_eq!(result, Value::String("missing".to_string()));
2351 }
2352
2353 #[test]
2354 fn test_dict_get_real_usage() {
2355 let mut ctx = Context::new();
2356 ctx.eval(
2357 r#"
2358config = {"host": "localhost", "port": 8080}
2359host = config.get("host", "0.0.0.0")
2360timeout = config.get("timeout", 30)
2361"#,
2362 )
2363 .unwrap();
2364
2365 let host = ctx.get("host").unwrap();
2366 assert_eq!(host, Value::String("localhost".to_string()));
2367
2368 let timeout = ctx.get("timeout").unwrap();
2369 assert_eq!(timeout, Value::Int(30));
2370 }
2371
2372 #[test]
2373 fn test_dict_get_no_args_error() {
2374 let mut ctx = Context::new();
2375 let result = ctx.eval(
2376 r#"
2377d = {"a": 1}
2378d.get()
2379"#,
2380 );
2381 assert!(result.is_err());
2382 let err = result.unwrap_err();
2383 assert!(err.contains("TypeError"));
2384 }
2385
2386 #[test]
2388 fn test_tuple_unpacking_basic() {
2389 let mut ctx = Context::new();
2390 ctx.eval("a, b = 1, 2").unwrap();
2391 assert_eq!(ctx.get("a"), Some(Value::Int(1)));
2392 assert_eq!(ctx.get("b"), Some(Value::Int(2)));
2393 }
2394
2395 #[test]
2396 fn test_tuple_unpacking_from_list() {
2397 let mut ctx = Context::new();
2398 ctx.eval("x, y = [10, 20]").unwrap();
2399 assert_eq!(ctx.get("x"), Some(Value::Int(10)));
2400 assert_eq!(ctx.get("y"), Some(Value::Int(20)));
2401 }
2402
2403 #[test]
2404 fn test_tuple_unpacking_from_tuple() {
2405 let mut ctx = Context::new();
2406 ctx.eval("p, q = (100, 200)").unwrap();
2407 assert_eq!(ctx.get("p"), Some(Value::Int(100)));
2408 assert_eq!(ctx.get("q"), Some(Value::Int(200)));
2409 }
2410
2411 #[test]
2412 fn test_tuple_swap_variables() {
2413 let mut ctx = Context::new();
2414 ctx.eval(
2415 r#"
2416a = 5
2417b = 10
2418a, b = b, a
2419"#,
2420 )
2421 .unwrap();
2422 assert_eq!(ctx.get("a"), Some(Value::Int(10)));
2423 assert_eq!(ctx.get("b"), Some(Value::Int(5)));
2424 }
2425
2426 #[test]
2427 fn test_tuple_multiple_values() {
2428 let mut ctx = Context::new();
2429 ctx.eval("a, b, c = 1, 2, 3").unwrap();
2430 assert_eq!(ctx.get("a"), Some(Value::Int(1)));
2431 assert_eq!(ctx.get("b"), Some(Value::Int(2)));
2432 assert_eq!(ctx.get("c"), Some(Value::Int(3)));
2433 }
2434
2435 #[test]
2436 fn test_tuple_function_return() {
2437 let mut ctx = Context::new();
2438 ctx.eval(
2439 r#"
2440def get_coords():
2441 return 3, 4
2442
2443x, y = get_coords()
2444"#,
2445 )
2446 .unwrap();
2447 assert_eq!(ctx.get("x"), Some(Value::Int(3)));
2448 assert_eq!(ctx.get("y"), Some(Value::Int(4)));
2449 }
2450
2451 #[test]
2452 fn test_tuple_unpacking_too_many_values() {
2453 let mut ctx = Context::new();
2454 let result = ctx.eval("a, b = [1, 2, 3]");
2455 assert!(result.is_err());
2456 let err = result.unwrap_err();
2457 assert!(err.contains("ValueError") || err.contains("too many values"));
2458 }
2459
2460 #[test]
2461 fn test_tuple_unpacking_too_few_values() {
2462 let mut ctx = Context::new();
2463 let result = ctx.eval("a, b, c = [1, 2]");
2464 assert!(result.is_err());
2465 let err = result.unwrap_err();
2466 assert!(err.contains("ValueError") || err.contains("expected 3"));
2467 }
2468
2469 #[test]
2470 fn test_tuple_literal() {
2471 let mut ctx = Context::new();
2472 let result = ctx.eval("t = (1, 2, 3)").unwrap();
2473 assert_eq!(result, Value::None);
2474 }
2476
2477 #[test]
2478 fn test_tuple_in_expression() {
2479 let mut ctx = Context::new();
2480 let result = ctx.eval("(1, 2)").unwrap();
2481 match result {
2483 Value::Tuple(t) => {
2484 assert_eq!(t.len(), 2);
2485 assert_eq!(t[0], Value::Int(1));
2486 assert_eq!(t[1], Value::Int(2));
2487 }
2488 _ => panic!("Expected tuple"),
2489 }
2490 }
2491
2492 #[test]
2494 fn test_str_int() {
2495 let mut ctx = Context::new();
2496 let result = ctx.eval("str(123)").unwrap();
2497 assert_eq!(result, Value::String("123".to_string()));
2498 }
2499
2500 #[test]
2501 fn test_str_float() {
2502 let mut ctx = Context::new();
2503 let result = ctx.eval("str(3.14)").unwrap();
2504 assert_eq!(result, Value::String("3.14".to_string()));
2505 }
2506
2507 #[test]
2508 fn test_str_bool() {
2509 let mut ctx = Context::new();
2510 let result = ctx.eval("str(True)").unwrap();
2511 assert_eq!(result, Value::String("True".to_string()));
2512
2513 let result = ctx.eval("str(False)").unwrap();
2514 assert_eq!(result, Value::String("False".to_string()));
2515 }
2516
2517 #[test]
2518 fn test_str_none() {
2519 let mut ctx = Context::new();
2520 let result = ctx.eval("str(None)").unwrap();
2521 assert_eq!(result, Value::String("None".to_string()));
2522 }
2523
2524 #[test]
2525 fn test_str_string() {
2526 let mut ctx = Context::new();
2527 let result = ctx.eval(r#"str("hello")"#).unwrap();
2528 assert_eq!(result, Value::String("hello".to_string()));
2529 }
2530
2531 #[test]
2532 fn test_str_list() {
2533 let mut ctx = Context::new();
2534 let result = ctx.eval("str([1, 2, 3])").unwrap();
2535 assert_eq!(result, Value::String("[1, 2, 3]".to_string()));
2536 }
2537
2538 #[test]
2539 fn test_str_tuple() {
2540 let mut ctx = Context::new();
2541 let result = ctx.eval("str((1, 2))").unwrap();
2542 assert_eq!(result, Value::String("(1, 2)".to_string()));
2543 }
2544
2545 #[test]
2546 fn test_str_dict() {
2547 let mut ctx = Context::new();
2548 ctx.eval(r#"d = {"a": 1}"#).unwrap();
2549 let result = ctx.eval("str(d)").unwrap();
2550 let s = result.as_string().unwrap();
2552 assert!(s.contains("'a'"));
2553 assert!(s.contains("1"));
2554 }
2555
2556 #[test]
2557 fn test_str_concatenation() {
2558 let mut ctx = Context::new();
2559 let result = ctx.eval(r#""Number: " + str(42)"#).unwrap();
2560 assert_eq!(result, Value::String("Number: 42".to_string()));
2561 }
2562
2563 #[test]
2565 fn test_fstring_simple_variable() {
2566 let mut ctx = Context::new();
2567 ctx.eval(r#"name = "Alice""#).unwrap();
2568 let result = ctx.eval(r#"f"Hello {name}""#).unwrap();
2569 assert_eq!(result, Value::String("Hello Alice".to_string()));
2570 }
2571
2572 #[test]
2573 fn test_fstring_multiple_variables() {
2574 let mut ctx = Context::new();
2575 ctx.eval("x = 10").unwrap();
2576 ctx.eval("y = 20").unwrap();
2577 let result = ctx.eval(r#"f"{x} + {y} = {x + y}""#).unwrap();
2578 assert_eq!(result, Value::String("10 + 20 = 30".to_string()));
2579 }
2580
2581 #[test]
2582 fn test_fstring_expressions() {
2583 let mut ctx = Context::new();
2584 ctx.eval("n = 5").unwrap();
2585 let result = ctx.eval(r#"f"Square of {n} is {n * n}""#).unwrap();
2586 assert_eq!(result, Value::String("Square of 5 is 25".to_string()));
2587 }
2588
2589 #[test]
2590 fn test_fstring_different_types() {
2591 let mut ctx = Context::new();
2592 ctx.eval("age = 30").unwrap();
2593 ctx.eval("height = 5.9").unwrap();
2594 let result = ctx.eval(r#"f"Age: {age}, Height: {height}""#).unwrap();
2595 assert_eq!(result, Value::String("Age: 30, Height: 5.9".to_string()));
2596 }
2597
2598 #[test]
2599 fn test_fstring_no_interpolation() {
2600 let mut ctx = Context::new();
2601 let result = ctx.eval(r#"f"Hello World""#).unwrap();
2602 assert_eq!(result, Value::String("Hello World".to_string()));
2603 }
2604
2605 #[test]
2606 fn test_fstring_only_expression() {
2607 let mut ctx = Context::new();
2608 ctx.eval("x = 42").unwrap();
2609 let result = ctx.eval(r#"f"{x}""#).unwrap();
2610 assert_eq!(result, Value::String("42".to_string()));
2611 }
2612
2613 #[test]
2614 fn test_fstring_function_call() {
2615 let mut ctx = Context::new();
2616 ctx.eval(
2617 r#"
2618def get_name():
2619 return "Bob"
2620"#,
2621 )
2622 .unwrap();
2623 let result = ctx.eval(r#"f"Hello {get_name()}""#).unwrap();
2624 assert_eq!(result, Value::String("Hello Bob".to_string()));
2625 }
2626
2627 #[test]
2628 fn test_fstring_nested_expression() {
2629 let mut ctx = Context::new();
2630 ctx.eval("items = [1, 2, 3]").unwrap();
2631 let result = ctx.eval(r#"f"Length: {len(items)}""#).unwrap();
2632 assert_eq!(result, Value::String("Length: 3".to_string()));
2633 }
2634
2635 #[test]
2637 fn test_list_slice_basic() {
2638 let mut ctx = Context::new();
2639 ctx.eval("items = [0, 1, 2, 3, 4]").unwrap();
2640 let result = ctx.eval("items[1:3]").unwrap();
2641 let expected = ctx.eval("[1, 2]").unwrap();
2642 assert_eq!(result, expected);
2643 }
2644
2645 #[test]
2646 fn test_list_slice_start_only() {
2647 let mut ctx = Context::new();
2648 ctx.eval("items = [0, 1, 2, 3, 4]").unwrap();
2649 let result = ctx.eval("items[2:]").unwrap();
2650 let expected = ctx.eval("[2, 3, 4]").unwrap();
2651 assert_eq!(result, expected);
2652 }
2653
2654 #[test]
2655 fn test_list_slice_stop_only() {
2656 let mut ctx = Context::new();
2657 ctx.eval("items = [0, 1, 2, 3, 4]").unwrap();
2658 let result = ctx.eval("items[:3]").unwrap();
2659 let expected = ctx.eval("[0, 1, 2]").unwrap();
2660 assert_eq!(result, expected);
2661 }
2662
2663 #[test]
2664 fn test_list_slice_full() {
2665 let mut ctx = Context::new();
2666 ctx.eval("items = [0, 1, 2, 3, 4]").unwrap();
2667 let result = ctx.eval("items[:]").unwrap();
2668 let expected = ctx.eval("[0, 1, 2, 3, 4]").unwrap();
2669 assert_eq!(result, expected);
2670 }
2671
2672 #[test]
2673 fn test_list_slice_step() {
2674 let mut ctx = Context::new();
2675 ctx.eval("items = [0, 1, 2, 3, 4, 5, 6]").unwrap();
2676 let result = ctx.eval("items[::2]").unwrap();
2677 let expected = ctx.eval("[0, 2, 4, 6]").unwrap();
2678 assert_eq!(result, expected);
2679 }
2680
2681 #[test]
2682 fn test_list_slice_negative_indices() {
2683 let mut ctx = Context::new();
2684 ctx.eval("items = [0, 1, 2, 3, 4]").unwrap();
2685 let result = ctx.eval("items[-3:-1]").unwrap();
2686 let expected = ctx.eval("[2, 3]").unwrap();
2687 assert_eq!(result, expected);
2688 }
2689
2690 #[test]
2691 fn test_list_slice_negative_step() {
2692 let mut ctx = Context::new();
2693 ctx.eval("items = [0, 1, 2, 3, 4]").unwrap();
2694 let result = ctx.eval("items[::-1]").unwrap();
2695 let expected = ctx.eval("[4, 3, 2, 1, 0]").unwrap();
2696 assert_eq!(result, expected);
2697 }
2698
2699 #[test]
2700 fn test_string_slice_basic() {
2701 let mut ctx = Context::new();
2702 let result = ctx.eval(r#""hello"[1:4]"#).unwrap();
2703 assert_eq!(result, Value::String("ell".to_string()));
2704 }
2705
2706 #[test]
2707 fn test_string_slice_start_only() {
2708 let mut ctx = Context::new();
2709 let result = ctx.eval(r#""hello"[2:]"#).unwrap();
2710 assert_eq!(result, Value::String("llo".to_string()));
2711 }
2712
2713 #[test]
2714 fn test_string_slice_stop_only() {
2715 let mut ctx = Context::new();
2716 let result = ctx.eval(r#""hello"[:3]"#).unwrap();
2717 assert_eq!(result, Value::String("hel".to_string()));
2718 }
2719
2720 #[test]
2721 fn test_string_slice_step() {
2722 let mut ctx = Context::new();
2723 let result = ctx.eval(r#""hello"[::2]"#).unwrap();
2724 assert_eq!(result, Value::String("hlo".to_string()));
2725 }
2726
2727 #[test]
2728 fn test_string_slice_negative_step() {
2729 let mut ctx = Context::new();
2730 let result = ctx.eval(r#""hello"[::-1]"#).unwrap();
2731 assert_eq!(result, Value::String("olleh".to_string()));
2732 }
2733
2734 #[test]
2735 fn test_tuple_slice_basic() {
2736 let mut ctx = Context::new();
2737 ctx.eval("t = (0, 1, 2, 3, 4)").unwrap();
2738 let result = ctx.eval("t[1:3]").unwrap();
2739 let expected = ctx.eval("(1, 2)").unwrap();
2740 assert_eq!(result, expected);
2741 }
2742
2743 #[test]
2744 fn test_slice_empty_result() {
2745 let mut ctx = Context::new();
2746 ctx.eval("items = [1, 2, 3]").unwrap();
2747 let result = ctx.eval("items[5:10]").unwrap();
2748 let expected = ctx.eval("[]").unwrap();
2749 assert_eq!(result, expected);
2750 }
2751
2752 #[test]
2754 fn test_isinstance_int() {
2755 let mut ctx = Context::new();
2756 ctx.eval("x = 123").unwrap();
2757 let result = ctx.eval("isinstance(x, int)").unwrap();
2758 assert_eq!(result, Value::Bool(true));
2759 let result = ctx.eval("isinstance(x, str)").unwrap();
2760 assert_eq!(result, Value::Bool(false));
2761 }
2762
2763 #[test]
2764 fn test_isinstance_float() {
2765 let mut ctx = Context::new();
2766 ctx.eval("y = 3.14").unwrap();
2767 let result = ctx.eval("isinstance(y, float)").unwrap();
2768 assert_eq!(result, Value::Bool(true));
2769 let result = ctx.eval("isinstance(y, int)").unwrap();
2770 assert_eq!(result, Value::Bool(false));
2771 }
2772
2773 #[test]
2774 fn test_isinstance_string() {
2775 let mut ctx = Context::new();
2776 ctx.eval("s = 'hello'").unwrap();
2777 let result = ctx.eval("isinstance(s, str)").unwrap();
2778 assert_eq!(result, Value::Bool(true));
2779 let result = ctx.eval("isinstance(s, list)").unwrap();
2780 assert_eq!(result, Value::Bool(false));
2781 }
2782
2783 #[test]
2784 fn test_isinstance_bool() {
2785 let mut ctx = Context::new();
2786 ctx.eval("b = True").unwrap();
2787 let result = ctx.eval("isinstance(b, bool)").unwrap();
2788 assert_eq!(result, Value::Bool(true));
2789 }
2790
2791 #[test]
2792 fn test_isinstance_list() {
2793 let mut ctx = Context::new();
2794 ctx.eval("lst = [1, 2, 3]").unwrap();
2795 let result = ctx.eval("isinstance(lst, list)").unwrap();
2796 assert_eq!(result, Value::Bool(true));
2797 let result = ctx.eval("isinstance(lst, dict)").unwrap();
2798 assert_eq!(result, Value::Bool(false));
2799 }
2800
2801 #[test]
2802 fn test_isinstance_dict() {
2803 let mut ctx = Context::new();
2804 ctx.eval("d = {'a': 1}").unwrap();
2805 let result = ctx.eval("isinstance(d, dict)").unwrap();
2806 assert_eq!(result, Value::Bool(true));
2807 let result = ctx.eval("isinstance(d, list)").unwrap();
2808 assert_eq!(result, Value::Bool(false));
2809 }
2810
2811 #[test]
2812 fn test_isinstance_tuple() {
2813 let mut ctx = Context::new();
2814 ctx.eval("t = (1, 2, 3)").unwrap();
2815 let result = ctx.eval("isinstance(t, tuple)").unwrap();
2816 assert_eq!(result, Value::Bool(true));
2817 let result = ctx.eval("isinstance(t, list)").unwrap();
2818 assert_eq!(result, Value::Bool(false));
2819 }
2820
2821 #[test]
2822 fn test_isinstance_in_condition() {
2823 let mut ctx = Context::new();
2824 ctx.eval(
2825 r#"
2826def process(value):
2827 if isinstance(value, int):
2828 return value * 2
2829 elif isinstance(value, str):
2830 return value.upper()
2831 else:
2832 return None
2833"#,
2834 )
2835 .unwrap();
2836 let result = ctx.eval("process(5)").unwrap();
2837 assert_eq!(result, Value::Int(10));
2838 let result = ctx.eval("process('hi')").unwrap();
2839 assert_eq!(result, Value::String("HI".to_string()));
2840 }
2841
2842 #[test]
2843 fn test_isinstance_wrong_arg_count() {
2844 let mut ctx = Context::new();
2845 let result = ctx.eval("isinstance(1)");
2846 assert!(result.is_err());
2847 }
2848
2849 #[test]
2850 fn test_isinstance_second_arg_not_type() {
2851 let mut ctx = Context::new();
2852 let result = ctx.eval("isinstance(1, 'int')");
2853 assert!(result.is_err());
2854 }
2855
2856 #[test]
2858 fn test_listcomp_basic() {
2859 let mut ctx = Context::new();
2860 let result = ctx.eval("[x*x for x in range(5)]").unwrap();
2861 let expected = ctx.eval("[0, 1, 4, 9, 16]").unwrap();
2862 assert_eq!(result, expected);
2863 }
2864
2865 #[test]
2866 fn test_listcomp_with_filter() {
2867 let mut ctx = Context::new();
2868 let result = ctx.eval("[x for x in range(10) if x % 2 == 0]").unwrap();
2869 let expected = ctx.eval("[0, 2, 4, 6, 8]").unwrap();
2870 assert_eq!(result, expected);
2871 }
2872
2873 #[test]
2874 fn test_listcomp_string_transformation() {
2875 let mut ctx = Context::new();
2876 ctx.eval("words = ['hello', 'world']").unwrap();
2877 let result = ctx.eval("[w.upper() for w in words]").unwrap();
2878 let expected = ctx.eval("['HELLO', 'WORLD']").unwrap();
2879 assert_eq!(result, expected);
2880 }
2881
2882 #[test]
2883 fn test_listcomp_expression() {
2884 let mut ctx = Context::new();
2885 ctx.eval("nums = [1, 2, 3]").unwrap();
2886 let result = ctx.eval("[n * 2 for n in nums]").unwrap();
2887 let expected = ctx.eval("[2, 4, 6]").unwrap();
2888 assert_eq!(result, expected);
2889 }
2890
2891 #[test]
2892 fn test_listcomp_empty_result() {
2893 let mut ctx = Context::new();
2894 let result = ctx.eval("[x for x in range(5) if x > 10]").unwrap();
2895 let expected = ctx.eval("[]").unwrap();
2896 assert_eq!(result, expected);
2897 }
2898
2899 #[test]
2900 fn test_listcomp_multiple_filters() {
2901 let mut ctx = Context::new();
2902 let result = ctx
2903 .eval("[x for x in range(20) if x % 2 == 0 if x % 3 == 0]")
2904 .unwrap();
2905 let expected = ctx.eval("[0, 6, 12, 18]").unwrap();
2906 assert_eq!(result, expected);
2907 }
2908
2909 #[test]
2910 fn test_listcomp_with_variable() {
2911 let mut ctx = Context::new();
2912 ctx.eval("n = 5").unwrap();
2913 let result = ctx.eval("[i for i in range(n)]").unwrap();
2914 let expected = ctx.eval("[0, 1, 2, 3, 4]").unwrap();
2915 assert_eq!(result, expected);
2916 }
2917
2918 #[test]
2919 fn test_listcomp_complex_expression() {
2920 let mut ctx = Context::new();
2921 let result = ctx.eval("[x*2 + 1 for x in range(5)]").unwrap();
2922 let expected = ctx.eval("[1, 3, 5, 7, 9]").unwrap();
2923 assert_eq!(result, expected);
2924 }
2925
2926 #[test]
2927 fn test_listcomp_from_list() {
2928 let mut ctx = Context::new();
2929 ctx.eval("items = [1, 2, 3, 4, 5]").unwrap();
2930 let result = ctx.eval("[x*x for x in items if x > 2]").unwrap();
2931 let expected = ctx.eval("[9, 16, 25]").unwrap();
2932 assert_eq!(result, expected);
2933 }
2934
2935 #[test]
2936 fn test_listcomp_string_iteration() {
2937 let mut ctx = Context::new();
2938 let result = ctx.eval("[c for c in 'hello']").unwrap();
2939 let expected = ctx.eval("['h', 'e', 'l', 'l', 'o']").unwrap();
2940 assert_eq!(result, expected);
2941 }
2942
2943 #[test]
2944 fn test_string_iteration_for_loop() {
2945 let mut ctx = Context::new();
2946 ctx.eval(
2947 r#"
2948result = []
2949for c in "abc":
2950 result.append(c)
2951"#,
2952 )
2953 .unwrap();
2954 let result = ctx.eval("result").unwrap();
2955 let expected = ctx.eval("['a', 'b', 'c']").unwrap();
2956 assert_eq!(result, expected);
2957 }
2958
2959 #[test]
2961 fn test_async_function_basic() {
2962 let mut ctx = Context::new();
2963 ctx.eval(
2964 r#"
2965async def greet(name):
2966 return "Hello, " + name
2967
2968result = await greet("World")
2969"#,
2970 )
2971 .unwrap();
2972 let result = ctx.eval("result").unwrap();
2973 assert_eq!(result, Value::String("Hello, World".to_string()));
2974 }
2975
2976 #[test]
2977 fn test_async_function_with_computation() {
2978 let mut ctx = Context::new();
2979 ctx.eval(
2980 r#"
2981async def compute(x, y):
2982 return x * y + 10
2983
2984result = await compute(5, 3)
2985"#,
2986 )
2987 .unwrap();
2988 let result = ctx.eval("result").unwrap();
2989 assert_eq!(result, Value::Int(25));
2990 }
2991
2992 #[test]
2993 fn test_async_function_returns_coroutine() {
2994 let mut ctx = Context::new();
2995 ctx.eval(
2996 r#"
2997async def get_value():
2998 return 42
2999
3000coro = get_value()
3001"#,
3002 )
3003 .unwrap();
3004 let result = ctx.eval("coro").unwrap();
3005 match result {
3007 Value::Coroutine(_, _) => {} _ => panic!("Expected coroutine, got {:?}", result),
3009 }
3010 }
3011
3012 #[test]
3013 fn test_await_coroutine() {
3014 let mut ctx = Context::new();
3015 ctx.eval(
3016 r#"
3017async def get_value():
3018 return 42
3019
3020coro = get_value()
3021result = await coro
3022"#,
3023 )
3024 .unwrap();
3025 let result = ctx.eval("result").unwrap();
3026 assert_eq!(result, Value::Int(42));
3027 }
3028
3029 #[test]
3030 fn test_async_function_with_multiple_statements() {
3031 let mut ctx = Context::new();
3032 ctx.eval(
3033 r#"
3034async def process(x):
3035 y = x * 2
3036 z = y + 5
3037 return z
3038
3039result = await process(10)
3040"#,
3041 )
3042 .unwrap();
3043 let result = ctx.eval("result").unwrap();
3044 assert_eq!(result, Value::Int(25));
3045 }
3046
3047 #[test]
3048 fn test_await_non_coroutine_error() {
3049 let mut ctx = Context::new();
3050 let result = ctx.eval("await 42");
3051 assert!(result.is_err());
3052 let err = result.unwrap_err();
3053 assert!(err.contains("TypeError"));
3054 assert!(err.contains("cannot be awaited"));
3055 }
3056
3057 #[test]
3058 fn test_async_function_with_conditionals() {
3059 let mut ctx = Context::new();
3060 ctx.eval(
3061 r#"
3062async def check(x):
3063 if x > 10:
3064 return "big"
3065 else:
3066 return "small"
3067
3068result1 = await check(15)
3069result2 = await check(5)
3070"#,
3071 )
3072 .unwrap();
3073 let result1 = ctx.eval("result1").unwrap();
3074 let result2 = ctx.eval("result2").unwrap();
3075 assert_eq!(result1, Value::String("big".to_string()));
3076 assert_eq!(result2, Value::String("small".to_string()));
3077 }
3078
3079 #[test]
3080 fn test_async_function_nested_calls() {
3081 let mut ctx = Context::new();
3082 ctx.eval(
3083 r#"
3084async def inner(x):
3085 return x * 2
3086
3087async def outer(x):
3088 y = await inner(x)
3089 return y + 1
3090
3091result = await outer(5)
3092"#,
3093 )
3094 .unwrap();
3095 let result = ctx.eval("result").unwrap();
3096 assert_eq!(result, Value::Int(11));
3097 }
3098
3099 #[test]
3101 fn test_asyncio_sleep_basic() {
3102 let mut ctx = Context::new();
3103 let start = std::time::Instant::now();
3104 ctx.eval(
3105 r#"
3106import asyncio
3107
3108async def test():
3109 await asyncio.sleep(0.1)
3110 return "done"
3111
3112result = await test()
3113"#,
3114 )
3115 .unwrap();
3116 let elapsed = start.elapsed();
3117 let result = ctx.eval("result").unwrap();
3118 assert_eq!(result, Value::String("done".to_string()));
3119 assert!(elapsed.as_millis() >= 100);
3121 }
3122
3123 #[test]
3124 fn test_asyncio_sleep_with_int() {
3125 let mut ctx = Context::new();
3126 let start = std::time::Instant::now();
3127 ctx.eval(
3128 r#"
3129import asyncio
3130await asyncio.sleep(0)
3131"#,
3132 )
3133 .unwrap();
3134 let elapsed = start.elapsed();
3135 assert!(elapsed.as_millis() < 100);
3137 }
3138
3139 #[test]
3140 fn test_asyncio_sleep_with_float() {
3141 let mut ctx = Context::new();
3142 let start = std::time::Instant::now();
3143 ctx.eval(
3144 r#"
3145import asyncio
3146await asyncio.sleep(0.05)
3147"#,
3148 )
3149 .unwrap();
3150 let elapsed = start.elapsed();
3151 assert!(elapsed.as_millis() >= 50);
3153 }
3154
3155 #[test]
3156 fn test_asyncio_sleep_negative_error() {
3157 let mut ctx = Context::new();
3158 let result = ctx.eval(
3159 r#"
3160import asyncio
3161await asyncio.sleep(-1)
3162"#,
3163 );
3164 assert!(result.is_err());
3165 let err = result.unwrap_err();
3166 assert!(err.contains("ValueError"));
3167 assert!(err.contains("non-negative"));
3168 }
3169
3170 #[test]
3171 fn test_asyncio_sleep_wrong_type_error() {
3172 let mut ctx = Context::new();
3173 let result = ctx.eval(
3174 r#"
3175import asyncio
3176await asyncio.sleep("hello")
3177"#,
3178 );
3179 assert!(result.is_err());
3180 let err = result.unwrap_err();
3181 assert!(err.contains("TypeError"));
3182 assert!(err.contains("must be a number"));
3183 }
3184
3185 #[test]
3186 fn test_asyncio_sleep_in_async_function() {
3187 let mut ctx = Context::new();
3188 let start = std::time::Instant::now();
3189 ctx.eval(
3190 r#"
3191import asyncio
3192
3193async def delayed_add(x, y):
3194 await asyncio.sleep(0.1)
3195 return x + y
3196
3197result = await delayed_add(3, 4)
3198"#,
3199 )
3200 .unwrap();
3201 let elapsed = start.elapsed();
3202 let result = ctx.eval("result").unwrap();
3203 assert_eq!(result, Value::Int(7));
3204 assert!(elapsed.as_millis() >= 100);
3205 }
3206
3207 #[test]
3208 fn test_asyncio_sleep_multiple_awaits() {
3209 let mut ctx = Context::new();
3210 let start = std::time::Instant::now();
3211 ctx.eval(
3212 r#"
3213import asyncio
3214
3215async def multi_sleep():
3216 await asyncio.sleep(0.05)
3217 await asyncio.sleep(0.05)
3218 return "done"
3219
3220result = await multi_sleep()
3221"#,
3222 )
3223 .unwrap();
3224 let elapsed = start.elapsed();
3225 let result = ctx.eval("result").unwrap();
3226 assert_eq!(result, Value::String("done".to_string()));
3227 assert!(elapsed.as_millis() >= 100);
3229 }
3230}