1#![allow(rustdoc::private_intra_doc_links)]
6#![allow(rustdoc::broken_intra_doc_links)]
7#![allow(clippy::needless_range_loop)]
8
9pub mod agent;
10pub mod ai;
11pub mod ai_sugar;
12pub mod aop;
13pub mod aot;
14pub mod ast;
15pub mod builtins;
16pub mod bytecode;
17pub mod capture;
18pub mod cluster;
19pub mod compiler;
20pub mod controller;
21pub mod convert;
22mod crypt_util;
23pub mod data_section;
24pub mod debugger;
25pub mod deconvert;
26pub mod deparse;
27pub mod english;
28pub mod error;
29mod fib_like_tail;
30pub mod fmt;
31pub mod format;
32pub mod vm_helper;
33mod jit;
34mod jwt;
35pub mod lexer;
36pub mod list_builtins;
37pub mod lsp;
38mod map_grep_fast;
39mod map_stream;
40pub mod mcp;
41pub mod mro;
42mod nanbox;
43mod native_codec;
44pub mod native_data;
45pub mod pack;
46pub mod par_lines;
47mod par_list;
48pub mod par_pipeline;
49pub mod par_walk;
50pub mod parallel_trace;
51pub mod parser;
52pub mod pcache;
53pub mod pchannel;
54mod pending_destroy;
55pub mod perl_decode;
56pub mod perl_fs;
57pub mod perl_inc;
58#[cfg(unix)]
59pub mod perl_pty;
60mod perl_regex;
61pub mod perl_signal;
62pub mod pkg;
63mod pmap_progress;
64pub mod ppool;
65pub mod profiler;
66pub mod pwatch;
67pub mod remote_wire;
68pub mod rust_ffi;
69pub mod rust_sugar;
70pub mod scope;
71pub mod script_cache;
72pub mod secrets;
73mod sort_fast;
74pub mod special_vars;
75pub mod static_analysis;
76pub mod stress;
77pub mod token;
78pub mod value;
79pub mod vm;
80pub mod web;
81pub mod web_orm;
82
83pub use zsh::exec as shell_exec;
85pub use zsh::fds as shell_fds;
86pub use zsh::history as shell_history;
87pub use zsh::jobs as shell_jobs;
88pub use zsh::lexer as zsh_lex;
89pub use zsh::parser as shell_parse;
90pub use zsh::parser as zsh_parse;
91pub use zsh::signals as shell_signal;
92pub use zsh::tokens as zsh_tokens;
93pub use zsh::zle as shell_zle;
94pub use zsh::zwc as shell_zwc;
95
96pub use vm_helper::{
97 perl_bracket_version, FEAT_SAY, FEAT_STATE, FEAT_SWITCH, FEAT_UNICODE_STRINGS,
98};
99
100use error::{PerlError, PerlResult};
101use vm_helper::VMHelper;
102
103use std::sync::atomic::{AtomicBool, Ordering};
106
107static COMPAT_MODE: AtomicBool = AtomicBool::new(false);
111
112static NO_INTEROP_MODE: AtomicBool = AtomicBool::new(false);
116
117static BIGINT_PRAGMA: AtomicBool = AtomicBool::new(false);
122
123
124pub fn set_compat_mode(on: bool) {
126 COMPAT_MODE.store(on, Ordering::Relaxed);
127}
128
129#[inline]
131pub fn compat_mode() -> bool {
132 COMPAT_MODE.load(Ordering::Relaxed)
133}
134
135pub fn set_bigint_pragma(on: bool) {
138 BIGINT_PRAGMA.store(on, Ordering::Relaxed);
139}
140
141#[inline]
143pub fn bigint_pragma() -> bool {
144 BIGINT_PRAGMA.load(Ordering::Relaxed)
145}
146
147
148pub fn set_no_interop_mode(on: bool) {
150 NO_INTEROP_MODE.store(on, Ordering::Relaxed);
151}
152
153#[inline]
155pub fn no_interop_mode() -> bool {
156 NO_INTEROP_MODE.load(Ordering::Relaxed)
157}
158use value::PerlValue;
159
160pub fn format_program(p: &ast::Program) -> String {
163 fmt::format_program(p)
164}
165
166pub fn convert_to_stryke(p: &ast::Program) -> String {
168 convert::convert_program(p)
169}
170
171pub fn convert_to_stryke_with_options(p: &ast::Program, opts: &convert::ConvertOptions) -> String {
173 convert::convert_program_with_options(p, opts)
174}
175
176pub fn deconvert_to_perl(p: &ast::Program) -> String {
178 deconvert::deconvert_program(p)
179}
180
181pub fn deconvert_to_perl_with_options(
183 p: &ast::Program,
184 opts: &deconvert::DeconvertOptions,
185) -> String {
186 deconvert::deconvert_program_with_options(p, opts)
187}
188
189pub fn parse(code: &str) -> PerlResult<ast::Program> {
190 parse_with_file(code, "-e")
191}
192
193pub fn parse_with_file(code: &str, file: &str) -> PerlResult<ast::Program> {
196 parse_with_file_inner(code, file, false)
197}
198
199pub fn parse_module_with_file(code: &str, file: &str) -> PerlResult<ast::Program> {
202 parse_with_file_inner(code, file, true)
203}
204
205fn parse_with_file_inner(code: &str, file: &str, is_module: bool) -> PerlResult<ast::Program> {
206 let desugared = if compat_mode() {
210 code.to_string()
211 } else {
212 let s = rust_sugar::desugar_rust_blocks(code);
213 ai_sugar::desugar(&s)
214 };
215 let mut lexer = lexer::Lexer::new_with_file(&desugared, file);
216 let tokens = lexer.tokenize()?;
217 let mut parser = parser::Parser::new_with_file(tokens, file);
218 parser.parsing_module = is_module;
219 parser.parse_program()
220}
221
222pub fn parse_and_run_string(code: &str, interp: &mut VMHelper) -> PerlResult<PerlValue> {
226 let file = interp.file.clone();
227 parse_and_run_string_in_file(code, interp, &file)
228}
229
230pub fn parse_and_run_string_in_file(
233 code: &str,
234 interp: &mut VMHelper,
235 file: &str,
236) -> PerlResult<PerlValue> {
237 parse_and_run_string_in_file_inner(code, interp, file, false)
238}
239
240pub fn parse_and_run_module_in_file(
243 code: &str,
244 interp: &mut VMHelper,
245 file: &str,
246) -> PerlResult<PerlValue> {
247 parse_and_run_string_in_file_inner(code, interp, file, true)
248}
249
250fn parse_and_run_string_in_file_inner(
251 code: &str,
252 interp: &mut VMHelper,
253 file: &str,
254 is_module: bool,
255) -> PerlResult<PerlValue> {
256 let program = if is_module {
257 parse_module_with_file(code, file)?
258 } else {
259 parse_with_file(code, file)?
260 };
261 let saved = interp.file.clone();
262 interp.file = file.to_string();
263 let r = interp.execute(&program);
264 interp.file = saved;
265 let v = r?;
266 interp.drain_pending_destroys(0)?;
267 Ok(v)
268}
269
270pub fn vendor_perl_inc_path() -> std::path::PathBuf {
273 std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("vendor/perl")
274}
275
276pub fn run_lsp_stdio() -> i32 {
278 match lsp::run_stdio() {
279 Ok(()) => 0,
280 Err(e) => {
281 eprintln!("stryke --lsp: {e}");
282 1
283 }
284 }
285}
286
287pub fn run(code: &str) -> PerlResult<PerlValue> {
289 let program = parse(code)?;
290 let mut interp = VMHelper::new();
291 let v = interp.execute(&program)?;
292 interp.run_global_teardown()?;
293 Ok(v)
294}
295
296pub fn try_vm_execute(
304 program: &ast::Program,
305 interp: &mut VMHelper,
306) -> Option<PerlResult<PerlValue>> {
307 if let Err(e) = interp.prepare_program_top_level(program) {
308 return Some(Err(e));
309 }
310
311 if let Some(chunk) = interp.cached_chunk.take() {
314 return Some(run_compiled_chunk(chunk, interp));
315 }
316
317 let comp = compiler::Compiler::new()
322 .with_source_file(interp.file.clone())
323 .with_strict_vars(interp.strict_vars);
324 let chunk = match comp.compile_program(program) {
325 Ok(chunk) => chunk,
326 Err(compiler::CompileError::Frozen { line, detail }) => {
327 return Some(Err(PerlError::runtime(detail, line)));
328 }
329 Err(compiler::CompileError::Unsupported(reason)) => {
330 return Some(Err(PerlError::runtime(
331 format!("VM compile error (unsupported): {}", reason),
332 0,
333 )));
334 }
335 };
336
337 if let Some(path) = interp.cache_script_path.take() {
339 let _ = script_cache::try_save(&path, program, &chunk);
340 }
341 Some(run_compiled_chunk(chunk, interp))
342}
343
344fn run_compiled_chunk(chunk: bytecode::Chunk, interp: &mut VMHelper) -> PerlResult<PerlValue> {
348 interp.clear_flip_flop_state();
349 interp.prepare_flip_flop_vm_slots(chunk.flip_flop_slots);
350 if interp.disasm_bytecode {
351 eprintln!("{}", chunk.disassemble());
352 }
353 interp.clear_begin_end_blocks_after_vm_compile();
354 for def in &chunk.struct_defs {
355 interp
356 .struct_defs
357 .insert(def.name.clone(), std::sync::Arc::new(def.clone()));
358 }
359 for def in &chunk.enum_defs {
360 interp
361 .enum_defs
362 .insert(def.name.clone(), std::sync::Arc::new(def.clone()));
363 }
364 for def in &chunk.trait_defs {
366 interp
367 .trait_defs
368 .insert(def.name.clone(), std::sync::Arc::new(def.clone()));
369 }
370 for def in &chunk.class_defs {
371 let mut def = def.clone();
372 for parent_name in &def.extends.clone() {
374 if let Some(parent_def) = interp.class_defs.get(parent_name) {
375 if parent_def.is_final {
376 return Err(crate::error::PerlError::runtime(
377 format!("cannot extend final class `{}`", parent_name),
378 0,
379 ));
380 }
381 for m in &def.methods {
382 if let Some(parent_method) = parent_def.method(&m.name) {
383 if parent_method.is_final {
384 return Err(crate::error::PerlError::runtime(
385 format!(
386 "cannot override final method `{}` from class `{}`",
387 m.name, parent_name
388 ),
389 0,
390 ));
391 }
392 }
393 }
394 }
395 }
396 for trait_name in &def.implements.clone() {
398 if let Some(trait_def) = interp.trait_defs.get(trait_name) {
399 for required in trait_def.required_methods() {
400 let has_method = def.methods.iter().any(|m| m.name == required.name);
401 if !has_method {
402 return Err(crate::error::PerlError::runtime(
403 format!(
404 "class `{}` implements trait `{}` but does not define required method `{}`",
405 def.name, trait_name, required.name
406 ),
407 0,
408 ));
409 }
410 }
411 for tm in &trait_def.methods {
413 if tm.body.is_some() && !def.methods.iter().any(|m| m.name == tm.name) {
414 def.methods.push(tm.clone());
415 }
416 }
417 }
418 }
419 if !def.is_abstract {
422 for parent_name in &def.extends.clone() {
423 if let Some(parent_def) = interp.class_defs.get(parent_name) {
424 if parent_def.is_abstract {
425 for m in &parent_def.methods {
426 if m.body.is_none() && !def.methods.iter().any(|dm| dm.name == m.name) {
427 return Err(crate::error::PerlError::runtime(
428 format!(
429 "class `{}` must implement abstract method `{}` from `{}`",
430 def.name, m.name, parent_name
431 ),
432 0,
433 ));
434 }
435 }
436 }
437 }
438 }
439 }
440 for sf in &def.static_fields {
442 let val = if let Some(ref expr) = sf.default {
443 match interp.eval_expr(expr) {
444 Ok(v) => v,
445 Err(crate::vm_helper::FlowOrError::Error(e)) => return Err(e),
446 Err(_) => crate::value::PerlValue::UNDEF,
447 }
448 } else {
449 crate::value::PerlValue::UNDEF
450 };
451 let key = format!("{}::{}", def.name, sf.name);
452 interp.scope.declare_scalar(&key, val);
453 }
454 for m in &def.methods {
456 if let Some(ref body) = m.body {
457 let fq = format!("{}::{}", def.name, m.name);
458 let sub = std::sync::Arc::new(crate::value::PerlSub {
459 name: fq.clone(),
460 params: m.params.clone(),
461 body: body.clone(),
462 closure_env: None,
463 prototype: None,
464 fib_like: None,
465 });
466 interp.subs.insert(fq, sub);
467 }
468 }
469 if !def.extends.is_empty() {
471 let isa_key = format!("{}::ISA", def.name);
472 let parents: Vec<crate::value::PerlValue> = def
473 .extends
474 .iter()
475 .map(|p| crate::value::PerlValue::string(p.clone()))
476 .collect();
477 interp.scope.declare_array(&isa_key, parents);
478 }
479 interp
480 .class_defs
481 .insert(def.name.clone(), std::sync::Arc::new(def));
482 }
483 let vm_jit = interp.vm_jit_enabled && interp.profiler.is_none();
484 let mut vm = vm::VM::new(&chunk, interp);
485 vm.set_jit_enabled(vm_jit);
486 match vm.execute() {
487 Ok(val) => {
488 interp.drain_pending_destroys(0)?;
489 Ok(val)
490 }
491 Err(e)
496 if e.message.starts_with("VM: unimplemented op")
497 || e.message.starts_with("Unimplemented builtin") =>
498 {
499 Err(PerlError::runtime(e.message, 0))
500 }
501 Err(e) => Err(e),
502 }
503}
504
505pub fn compile_and_run_prelude(program: &ast::Program, interp: &mut VMHelper) -> PerlResult<()> {
508 interp.prepare_program_top_level(program)?;
509 let comp = compiler::Compiler::new()
510 .with_source_file(interp.file.clone())
511 .with_strict_vars(interp.strict_vars);
512 let mut chunk = match comp.compile_program(program) {
513 Ok(chunk) => chunk,
514 Err(compiler::CompileError::Frozen { line, detail }) => {
515 return Err(PerlError::runtime(detail, line));
516 }
517 Err(compiler::CompileError::Unsupported(reason)) => {
518 return Err(PerlError::runtime(
519 format!("VM compile error (unsupported): {}", reason),
520 0,
521 ));
522 }
523 };
524
525 interp.clear_flip_flop_state();
526 interp.prepare_flip_flop_vm_slots(chunk.flip_flop_slots);
527 if interp.disasm_bytecode {
528 eprintln!("{}", chunk.disassemble());
529 }
530 interp.clear_begin_end_blocks_after_vm_compile();
531 for def in &chunk.struct_defs {
532 interp
533 .struct_defs
534 .insert(def.name.clone(), std::sync::Arc::new(def.clone()));
535 }
536 for def in &chunk.enum_defs {
537 interp
538 .enum_defs
539 .insert(def.name.clone(), std::sync::Arc::new(def.clone()));
540 }
541 for def in &chunk.trait_defs {
542 interp
543 .trait_defs
544 .insert(def.name.clone(), std::sync::Arc::new(def.clone()));
545 }
546 for def in &chunk.class_defs {
547 interp
548 .class_defs
549 .insert(def.name.clone(), std::sync::Arc::new(def.clone()));
550 }
551 for def in &chunk.class_defs {
553 for m in &def.methods {
554 if let Some(ref body) = m.body {
555 let fq = format!("{}::{}", def.name, m.name);
556 let sub = std::sync::Arc::new(crate::value::PerlSub {
557 name: fq.clone(),
558 params: m.params.clone(),
559 body: body.clone(),
560 closure_env: None,
561 prototype: None,
562 fib_like: None,
563 });
564 interp.subs.insert(fq, sub);
565 }
566 }
567 }
568
569 let body_ip = chunk.body_start_ip;
570 if body_ip > 0 && body_ip < chunk.ops.len() {
571 let saved_op = chunk.ops[body_ip].clone();
573 chunk.ops[body_ip] = bytecode::Op::Halt;
574 let vm_jit = interp.vm_jit_enabled && interp.profiler.is_none();
575 let mut vm = vm::VM::new(&chunk, interp);
576 vm.set_jit_enabled(vm_jit);
577 let _ = vm.execute()?;
578 chunk.ops[body_ip] = saved_op;
579 }
580
581 interp.line_mode_chunk = Some(chunk);
582 Ok(())
583}
584
585pub fn run_line_body(
588 chunk: &bytecode::Chunk,
589 interp: &mut VMHelper,
590 line_str: &str,
591 is_last_input_line: bool,
592) -> PerlResult<Option<String>> {
593 interp.line_mode_eof_pending = is_last_input_line;
594 let result: PerlResult<Option<String>> = (|| {
595 interp.line_number += 1;
596 interp
597 .scope
598 .set_topic(value::PerlValue::string(line_str.to_string()));
599
600 if interp.auto_split {
601 let sep = interp.field_separator.as_deref().unwrap_or(" ");
602 let re = regex::Regex::new(sep).unwrap_or_else(|_| regex::Regex::new(" ").unwrap());
603 let fields: Vec<value::PerlValue> = re
604 .split(line_str)
605 .map(|s| value::PerlValue::string(s.to_string()))
606 .collect();
607 interp.scope.set_array("F", fields)?;
608 }
609
610 let vm_jit = interp.vm_jit_enabled && interp.profiler.is_none();
611 let mut vm = vm::VM::new(chunk, interp);
612 vm.set_jit_enabled(vm_jit);
613 vm.ip = chunk.body_start_ip;
614 let _ = vm.execute()?;
615
616 let mut out = interp.scope.get_scalar("_").to_string();
617 out.push_str(&interp.ors);
618 Ok(Some(out))
619 })();
620 interp.line_mode_eof_pending = false;
621 result
622}
623
624pub fn lint_program(program: &ast::Program, interp: &mut VMHelper) -> PerlResult<()> {
627 interp.prepare_program_top_level(program)?;
628 static_analysis::analyze_program(program, &interp.file)?;
629 if interp.strict_refs || interp.strict_subs || interp.strict_vars {
630 return Ok(());
631 }
632 let comp = compiler::Compiler::new().with_source_file(interp.file.clone());
633 match comp.compile_program(program) {
634 Ok(_) => Ok(()),
635 Err(e) => Err(compile_error_to_perl(e)),
636 }
637}
638
639fn compile_error_to_perl(e: compiler::CompileError) -> PerlError {
640 match e {
641 compiler::CompileError::Unsupported(msg) => {
642 PerlError::runtime(format!("compile: {}", msg), 0)
643 }
644 compiler::CompileError::Frozen { line, detail } => PerlError::runtime(detail, line),
645 }
646}
647
648#[cfg(test)]
649mod tests {
650 use super::*;
651
652 #[test]
653 fn run_executes_last_expression_value() {
654 let p = parse("2 + 2").expect("parse");
656 assert!(!p.statements.is_empty());
657 let _ = run("2 + 2").expect("run");
658 }
659
660 #[test]
661 fn run_propagates_parse_errors() {
662 assert!(run("sub f {").is_err());
663 }
664
665 #[test]
666 fn interpreter_scope_persists_global_scalar_across_execute_calls() {
667 let mut interp = VMHelper::new();
668 let assign = parse("$persist_test = 100").expect("parse assign");
669 interp.execute(&assign).expect("assign");
670 let read = parse("$persist_test").expect("parse read");
671 let v = interp.execute(&read).expect("read");
672 assert_eq!(v.to_int(), 100);
673 }
674
675 #[test]
676 fn parse_empty_program() {
677 let p = parse("").expect("empty input should parse");
678 assert!(p.statements.is_empty());
679 }
680
681 #[test]
682 fn parse_expression_statement() {
683 let p = parse("2 + 2").expect("parse");
684 assert!(!p.statements.is_empty());
685 }
686
687 #[test]
688 fn parse_semicolon_only_statements() {
689 parse(";;").expect("semicolons only");
690 }
691
692 #[test]
693 fn parse_if_with_block() {
694 parse("if (1) { 2 }").expect("if");
695 }
696
697 #[test]
698 fn parse_fails_on_invalid_syntax() {
699 assert!(parse("sub f {").is_err());
700 }
701
702 #[test]
703 fn parse_qw_word_list() {
704 parse("my @a = qw(x y z)").expect("qw list");
705 }
706
707 #[test]
708 fn parse_c_style_for_loop() {
709 parse("for (my $i = 0; $i < 3; $i = $i + 1) { 1; }").expect("c-style for");
710 }
711
712 #[test]
713 fn parse_package_statement() {
714 parse("package Foo::Bar; 1").expect("package");
715 }
716
717 #[test]
718 fn parse_unless_block() {
719 parse("unless (0) { 1; }").expect("unless");
720 }
721
722 #[test]
723 fn parse_if_elsif_else() {
724 parse("if (0) { 1; } elsif (1) { 2; } else { 3; }").expect("if elsif");
725 }
726
727 #[test]
728 fn parse_q_constructor() {
729 parse(r#"my $s = q{braces}"#).expect("q{}");
730 parse(r#"my $t = qq(double)"#).expect("qq()");
731 }
732
733 #[test]
734 fn parse_regex_literals() {
735 parse("m/foo/").expect("m//");
736 parse("s/foo/bar/g").expect("s///");
737 }
738
739 #[test]
740 fn parse_begin_and_end_blocks() {
741 parse("BEGIN { 1; }").expect("BEGIN");
742 parse("END { 1; }").expect("END");
743 }
744
745 #[test]
746 fn parse_transliterate_y() {
747 parse("$_ = 'a'; y/a/A/").expect("y//");
748 }
749
750 #[test]
751 fn parse_foreach_with_my_iterator() {
752 parse("foreach my $x (1, 2) { $x; }").expect("foreach my");
753 }
754
755 #[test]
756 fn parse_our_declaration() {
757 parse("our $g = 1").expect("our");
758 }
759
760 #[test]
761 fn parse_local_declaration() {
762 parse("local $x = 1").expect("local");
763 }
764
765 #[test]
766 fn parse_use_no_statements() {
767 parse("use strict").expect("use");
768 parse("no warnings").expect("no");
769 }
770
771 #[test]
772 fn parse_sub_with_prototype() {
773 parse("fn add2 ($$) { return $_0 + $_1; }").expect("fn prototype");
774 parse("fn try_block (&;@) { my ( $try, @code_refs ) = @_; }").expect("prototype @ slurpy");
775 }
776
777 #[test]
778 fn parse_list_expression_in_parentheses() {
779 parse("my @a = (1, 2, 3)").expect("list");
780 }
781
782 #[test]
783 fn parse_require_expression() {
784 parse("require strict").expect("require");
785 }
786
787 #[test]
788 fn parse_do_string_eval_form() {
789 parse(r#"do "foo.pl""#).expect("do string");
790 }
791
792 #[test]
793 fn parse_package_qualified_name() {
794 parse("package Foo::Bar::Baz").expect("package ::");
795 }
796
797 #[test]
798 fn parse_my_multiple_declarations() {
799 parse("my ($a, $b, $c)").expect("my list");
800 }
801
802 #[test]
803 fn parse_eval_block_statement() {
804 parse("eval { 1; }").expect("eval block");
805 }
806
807 #[test]
808 fn parse_p_statement() {
809 parse("p 42").expect("p");
810 }
811
812 #[test]
813 fn parse_chop_scalar() {
814 parse("chop $s").expect("chop");
815 }
816
817 #[test]
818 fn vendor_perl_inc_path_points_at_vendor_perl() {
819 let p = vendor_perl_inc_path();
820 assert!(
821 p.ends_with("vendor/perl"),
822 "unexpected vendor path: {}",
823 p.display()
824 );
825 }
826
827 #[test]
828 fn format_program_roundtrips_simple_expression() {
829 let p = parse("$x + 1").expect("parse");
830 let out = format_program(&p);
831 assert!(!out.trim().is_empty());
832 }
833}
834
835#[cfg(test)]
836mod builtins_extended_tests;
837
838#[cfg(test)]
839mod lib_api_extended_tests;
840
841#[cfg(test)]
842mod cache_bench;
843
844#[cfg(test)]
845mod parallel_api_tests;
846
847#[cfg(test)]
848mod parse_smoke_extended;
849
850#[cfg(test)]
851mod parse_smoke_batch2;
852
853#[cfg(test)]
854mod parse_smoke_batch3;
855
856#[cfg(test)]
857mod parse_smoke_batch4;
858
859#[cfg(test)]
860mod crate_api_tests;
861
862#[cfg(test)]
863mod parser_shape_tests;
864
865#[cfg(test)]
866mod interpreter_unit_tests;
867
868#[cfg(test)]
869mod run_semantics_tests;
870
871#[cfg(test)]
872mod run_semantics_more;
873
874#[cfg(test)]
875mod value_extra_tests;
876
877#[cfg(test)]
878mod lexer_extra_tests;
879
880#[cfg(test)]
881mod parser_extra_tests;
882
883#[cfg(test)]
884mod builtins_extra_tests;
885
886#[cfg(test)]
887mod thread_extra_tests;
888
889#[cfg(test)]
890mod error_extra_tests;
891
892#[cfg(test)]
893mod oo_extra_tests;
894
895#[cfg(test)]
896mod regex_extra_tests;
897
898#[cfg(test)]
899mod aot_extra_tests;