1#![allow(rustdoc::private_intra_doc_links)]
6#![allow(rustdoc::broken_intra_doc_links)]
7#![allow(clippy::needless_range_loop)]
8pub mod agent;
10pub mod ai;
12pub mod ai_sugar;
14pub mod aop;
16pub mod aot;
18pub mod ast;
20pub mod banner;
22pub mod builtins;
24pub mod builtins_bio_geom_markov;
26pub mod builtins_bits_music_stats;
28pub mod builtins_combin_audio_physics;
30pub mod builtins_const;
32pub mod builtins_data;
34pub mod builtins_games_ml_chem;
36pub mod builtins_geom;
38pub mod builtins_github;
40pub mod builtins_iter;
42pub mod builtins_linalg_graph_date;
44pub mod builtins_mathx;
46pub mod builtins_misc;
48pub mod builtins_misc2;
50pub mod builtins_net;
52pub mod builtins_phonetic_geo_codec;
54pub mod builtins_quant;
56pub mod builtins_ratings_geom_units;
58pub mod builtins_sync;
60pub mod builtins_validate;
62pub mod builtins_vision_ir_algorithms;
64pub mod bytecode;
66pub mod fusevm_bridge;
69pub mod capture;
71pub mod cli_runners;
73pub mod cluster;
75pub mod compiler;
77pub mod controller;
79pub mod convert;
81mod crypt_util;
82pub mod dap;
84pub mod data_section;
86pub mod debugger;
88pub mod deconvert;
90pub mod deparse;
92pub mod doc_render;
94pub mod docs;
96pub mod english;
98pub mod error;
100mod fib_like_tail;
101pub mod fmt;
103pub mod format;
105pub mod getopts;
107pub mod god;
109mod jit;
110mod jwt;
111pub mod kvstore;
113pub mod lexer;
115pub mod list_builtins;
117pub mod lsp;
119pub mod lsp_docs_domains;
121pub mod lsp_extras;
123pub mod lsp_symbols;
125mod map_grep_fast;
126mod map_stream;
127pub mod mcp;
129pub mod minify;
131pub mod mro;
133mod nanbox;
134pub mod nat_punch;
136mod native_codec;
137pub mod native_data;
139pub mod pack;
141pub mod par_lines;
143mod par_list;
144pub mod par_pipeline;
146pub mod par_walk;
148pub mod parallel_trace;
150pub mod parser;
152pub mod pcache;
154pub mod pchannel;
156mod pending_destroy;
157pub mod perf_recorder;
159pub mod perl_decode;
161pub mod perl_fs;
163pub mod perl_inc;
165#[cfg(unix)]
167pub mod perl_pty;
168mod perl_regex;
169pub mod perl_signal;
171pub mod pkg;
173mod pmap_progress;
174pub mod ppool;
176pub mod profiler;
178pub mod provenance;
180pub mod pwatch;
182pub mod remote_wire;
184pub mod rust_ffi;
186pub mod rust_sugar;
188pub mod scope;
190pub mod script_cache;
192pub mod secrets;
194pub mod serialize_normalize;
196pub mod sketches;
198mod sort_fast;
199pub mod special_vars;
201pub mod static_analysis;
203pub mod stego;
205pub mod stress;
207pub mod stryke_log;
209pub mod teleport;
211pub mod token;
213pub mod turn_client;
215pub mod turnbuckle;
217pub mod udp_sockets;
219pub mod value;
221pub mod vm;
223pub mod vm_helper;
225pub mod web;
227pub mod web_orm;
229
230pub use zsh::exec as shell_exec;
232pub use zsh::fds as shell_fds;
233pub use zsh::history as shell_history;
234pub use zsh::jobs as shell_jobs;
235pub use zsh::lex as zsh_lex;
236pub use zsh::parse as shell_parse;
237pub use zsh::parse as zsh_parse;
238pub use zsh::signals as shell_signal;
239pub use zsh::tokens as zsh_tokens;
240pub use zsh::utils::errflag as zsh_errflag;
241pub use zsh::zle as shell_zle;
242pub use zsh::zsh_h::ERRFLAG_ERROR;
243pub use zsh::zwc as shell_zwc;
244
245pub use vm_helper::{
246 perl_bracket_version, FEAT_SAY, FEAT_STATE, FEAT_SWITCH, FEAT_UNICODE_STRINGS,
247};
248
249use error::{StrykeError, StrykeResult};
250use vm_helper::VMHelper;
251
252use std::cell::Cell;
255use std::sync::atomic::{AtomicBool, Ordering};
256
257static COMPAT_MODE: AtomicBool = AtomicBool::new(false);
261
262static NO_INTEROP_DEFAULT: AtomicBool = AtomicBool::new(false);
265
266thread_local! {
267 static NO_INTEROP_TLS: Cell<Option<bool>> = const { Cell::new(None) };
273}
274
275static BIGINT_PRAGMA: AtomicBool = AtomicBool::new(false);
280
281pub fn set_compat_mode(on: bool) {
283 COMPAT_MODE.store(on, Ordering::Relaxed);
284}
285
286#[inline]
288pub fn compat_mode() -> bool {
289 COMPAT_MODE.load(Ordering::Relaxed)
290}
291
292pub fn set_bigint_pragma(on: bool) {
295 BIGINT_PRAGMA.store(on, Ordering::Relaxed);
296}
297
298#[inline]
300pub fn bigint_pragma() -> bool {
301 BIGINT_PRAGMA.load(Ordering::Relaxed)
302}
303
304pub fn set_no_interop_mode(on: bool) {
307 NO_INTEROP_DEFAULT.store(on, Ordering::Relaxed);
308}
309
310pub fn set_no_interop_mode_tls(value: Option<bool>) {
314 NO_INTEROP_TLS.with(|c| c.set(value));
315}
316
317pub fn no_interop_mode_tls() -> Option<bool> {
320 NO_INTEROP_TLS.with(|c| c.get())
321}
322
323#[inline]
326pub fn no_interop_mode() -> bool {
327 if let Some(v) = NO_INTEROP_TLS.with(|c| c.get()) {
328 return v;
329 }
330 NO_INTEROP_DEFAULT.load(Ordering::Relaxed)
331}
332use value::StrykeValue;
333
334pub fn format_program(p: &ast::Program) -> String {
337 fmt::format_program(p)
338}
339
340pub fn convert_to_stryke(p: &ast::Program) -> String {
342 convert::convert_program(p)
343}
344
345pub fn convert_to_stryke_with_options(p: &ast::Program, opts: &convert::ConvertOptions) -> String {
347 convert::convert_program_with_options(p, opts)
348}
349
350pub fn deconvert_to_perl(p: &ast::Program) -> String {
352 deconvert::deconvert_program(p)
353}
354
355pub fn deconvert_to_perl_with_options(
357 p: &ast::Program,
358 opts: &deconvert::DeconvertOptions,
359) -> String {
360 deconvert::deconvert_program_with_options(p, opts)
361}
362pub fn parse(code: &str) -> StrykeResult<ast::Program> {
364 parse_with_file(code, "-e")
365}
366
367pub fn parse_with_file(code: &str, file: &str) -> StrykeResult<ast::Program> {
370 parse_with_file_inner(code, file, false)
371}
372
373pub fn parse_module_with_file(code: &str, file: &str) -> StrykeResult<ast::Program> {
376 parse_with_file_inner(code, file, true)
377}
378
379fn parse_with_file_inner(code: &str, file: &str, is_module: bool) -> StrykeResult<ast::Program> {
380 let desugared = if compat_mode() {
384 code.to_string()
385 } else {
386 let s = rust_sugar::desugar_rust_blocks(code);
387 ai_sugar::desugar(&s)
388 };
389 let mut lexer = lexer::Lexer::new_with_file(&desugared, file);
390 let tokens = lexer.tokenize()?;
391 let bare_positional_indices = std::mem::take(&mut lexer.bare_positional_indices);
392 let mut parser = parser::Parser::new_with_file(tokens, file);
393 parser.bare_positional_indices = bare_positional_indices;
394 parser.parsing_module = is_module;
395 parser.parse_program()
396}
397
398pub fn parse_and_run_string(code: &str, interp: &mut VMHelper) -> StrykeResult<StrykeValue> {
402 let file = interp.file.clone();
403 parse_and_run_string_in_file(code, interp, &file)
404}
405
406pub fn parse_and_run_string_in_file(
409 code: &str,
410 interp: &mut VMHelper,
411 file: &str,
412) -> StrykeResult<StrykeValue> {
413 parse_and_run_string_in_file_inner(code, interp, file, false)
414}
415
416pub fn parse_and_run_module_in_file(
419 code: &str,
420 interp: &mut VMHelper,
421 file: &str,
422) -> StrykeResult<StrykeValue> {
423 parse_and_run_string_in_file_inner(code, interp, file, true)
424}
425
426fn parse_and_run_string_in_file_inner(
427 code: &str,
428 interp: &mut VMHelper,
429 file: &str,
430 is_module: bool,
431) -> StrykeResult<StrykeValue> {
432 let program = if is_module {
433 parse_module_with_file(code, file)?
434 } else {
435 parse_with_file(code, file)?
436 };
437 let saved = interp.file.clone();
438 interp.file = file.to_string();
439 let r = interp.execute(&program);
440 interp.file = saved;
441 let v = r?;
442 interp.drain_pending_destroys(0)?;
443 Ok(v)
444}
445
446pub fn vendor_perl_inc_path() -> std::path::PathBuf {
449 std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("vendor/perl")
450}
451
452pub fn run_lsp_stdio() -> i32 {
454 match lsp::run_stdio() {
455 Ok(()) => 0,
456 Err(e) => {
457 eprintln!("stryke --lsp: {e}");
458 1
459 }
460 }
461}
462
463pub fn run(code: &str) -> StrykeResult<StrykeValue> {
465 let program = parse(code)?;
466 let mut interp = VMHelper::new();
467 let v = interp.execute(&program)?;
468 interp.run_global_teardown()?;
469 Ok(v)
470}
471
472pub fn try_vm_execute(
480 program: &ast::Program,
481 interp: &mut VMHelper,
482) -> Option<StrykeResult<StrykeValue>> {
483 if let Err(e) = interp.prepare_program_top_level(program) {
484 return Some(Err(e));
485 }
486
487 if let Some(chunk) = interp.cached_chunk.take() {
490 return Some(run_compiled_chunk(chunk, interp));
491 }
492
493 let comp = compiler::Compiler::new()
498 .with_source_file(interp.file.clone())
499 .with_strict_vars(interp.strict_vars);
500 let chunk = match comp.compile_program(program) {
501 Ok(chunk) => chunk,
502 Err(compiler::CompileError::Frozen { line, detail }) => {
503 let err = if detail.starts_with("Global symbol") {
504 StrykeError::syntax(detail, line)
505 } else {
506 StrykeError::runtime(detail, line)
507 };
508 return Some(Err(err));
509 }
510 Err(compiler::CompileError::Unsupported(reason)) => {
511 return Some(Err(StrykeError::runtime(
512 format!("VM compile error (unsupported): {}", reason),
513 0,
514 )));
515 }
516 };
517
518 if let Some(path) = interp.cache_script_path.take() {
520 let _ = script_cache::try_save(&path, program, &chunk);
521 }
522 Some(run_compiled_chunk(chunk, interp))
523}
524
525fn run_compiled_chunk(chunk: bytecode::Chunk, interp: &mut VMHelper) -> StrykeResult<StrykeValue> {
529 interp.clear_flip_flop_state();
530 interp.prepare_flip_flop_vm_slots(chunk.flip_flop_slots);
531 if interp.disasm_bytecode {
532 eprintln!("{}", chunk.disassemble());
533 }
534 interp.clear_begin_end_blocks_after_vm_compile();
535 for def in &chunk.struct_defs {
536 interp
537 .struct_defs
538 .insert(def.name.clone(), std::sync::Arc::new(def.clone()));
539 }
540 for def in &chunk.enum_defs {
541 interp
542 .enum_defs
543 .insert(def.name.clone(), std::sync::Arc::new(def.clone()));
544 }
545 for def in &chunk.trait_defs {
547 interp
548 .trait_defs
549 .insert(def.name.clone(), std::sync::Arc::new(def.clone()));
550 }
551 for def in &chunk.class_defs {
552 let mut def = def.clone();
553 for parent_name in &def.extends.clone() {
555 if let Some(parent_def) = interp.class_defs.get(parent_name) {
556 if parent_def.is_final {
557 return Err(crate::error::StrykeError::runtime(
558 format!("cannot extend final class `{}`", parent_name),
559 0,
560 ));
561 }
562 for m in &def.methods {
563 if let Some(parent_method) = parent_def.method(&m.name) {
564 if parent_method.is_final {
565 return Err(crate::error::StrykeError::runtime(
566 format!(
567 "cannot override final method `{}` from class `{}`",
568 m.name, parent_name
569 ),
570 0,
571 ));
572 }
573 }
574 }
575 }
576 }
577 for trait_name in &def.implements.clone() {
579 if let Some(trait_def) = interp.trait_defs.get(trait_name) {
580 for required in trait_def.required_methods() {
581 let has_method = def.methods.iter().any(|m| m.name == required.name);
582 if !has_method {
583 return Err(crate::error::StrykeError::runtime(
584 format!(
585 "class `{}` implements trait `{}` but does not define required method `{}`",
586 def.name, trait_name, required.name
587 ),
588 0,
589 ));
590 }
591 }
592 for tm in &trait_def.methods {
594 if tm.body.is_some() && !def.methods.iter().any(|m| m.name == tm.name) {
595 def.methods.push(tm.clone());
596 }
597 }
598 }
599 }
600 if !def.is_abstract {
603 for parent_name in &def.extends.clone() {
604 if let Some(parent_def) = interp.class_defs.get(parent_name) {
605 if parent_def.is_abstract {
606 for m in &parent_def.methods {
607 if m.body.is_none() && !def.methods.iter().any(|dm| dm.name == m.name) {
608 return Err(crate::error::StrykeError::runtime(
609 format!(
610 "class `{}` must implement abstract method `{}` from `{}`",
611 def.name, m.name, parent_name
612 ),
613 0,
614 ));
615 }
616 }
617 }
618 }
619 }
620 }
621 for sf in &def.static_fields {
623 let val = if let Some(ref expr) = sf.default {
624 match interp.eval_expr(expr) {
625 Ok(v) => v,
626 Err(crate::vm_helper::FlowOrError::Error(e)) => return Err(e),
627 Err(_) => crate::value::StrykeValue::UNDEF,
628 }
629 } else {
630 crate::value::StrykeValue::UNDEF
631 };
632 let key = format!("{}::{}", def.name, sf.name);
633 interp.scope.declare_scalar(&key, val);
634 }
635 for m in &def.methods {
637 if let Some(ref body) = m.body {
638 let fq = format!("{}::{}", def.name, m.name);
639 let sub = std::sync::Arc::new(crate::value::StrykeSub {
640 name: fq.clone(),
641 params: m.params.clone(),
642 body: body.clone(),
643 closure_env: None,
644 prototype: None,
645 fib_like: None,
646 });
647 interp.subs.insert(fq, sub);
648 }
649 }
650 if !def.extends.is_empty() {
652 let isa_key = format!("{}::ISA", def.name);
653 let parents: Vec<crate::value::StrykeValue> = def
654 .extends
655 .iter()
656 .map(|p| crate::value::StrykeValue::string(p.clone()))
657 .collect();
658 interp.scope.declare_array(&isa_key, parents);
659 }
660 interp
661 .class_defs
662 .insert(def.name.clone(), std::sync::Arc::new(def));
663 }
664 let vm_jit = interp.vm_jit_enabled && interp.profiler.is_none();
665 let mut vm = vm::VM::new(&chunk, interp);
666 vm.set_jit_enabled(vm_jit);
667 match vm.execute() {
668 Ok(val) => {
669 interp.drain_pending_destroys(0)?;
670 Ok(val)
671 }
672 Err(e)
677 if e.message.starts_with("VM: unimplemented op")
678 || e.message.starts_with("Unimplemented builtin") =>
679 {
680 Err(StrykeError::runtime(e.message, 0))
681 }
682 Err(e) => Err(e),
683 }
684}
685
686pub fn compile_and_run_prelude(program: &ast::Program, interp: &mut VMHelper) -> StrykeResult<()> {
689 interp.prepare_program_top_level(program)?;
690 let comp = compiler::Compiler::new()
691 .with_source_file(interp.file.clone())
692 .with_strict_vars(interp.strict_vars);
693 let mut chunk = match comp.compile_program(program) {
694 Ok(chunk) => chunk,
695 Err(compiler::CompileError::Frozen { line, detail }) => {
696 let err = if detail.starts_with("Global symbol") {
697 StrykeError::syntax(detail, line)
698 } else {
699 StrykeError::runtime(detail, line)
700 };
701 return Err(err);
702 }
703 Err(compiler::CompileError::Unsupported(reason)) => {
704 return Err(StrykeError::runtime(
705 format!("VM compile error (unsupported): {}", reason),
706 0,
707 ));
708 }
709 };
710
711 interp.clear_flip_flop_state();
712 interp.prepare_flip_flop_vm_slots(chunk.flip_flop_slots);
713 if interp.disasm_bytecode {
714 eprintln!("{}", chunk.disassemble());
715 }
716 interp.clear_begin_end_blocks_after_vm_compile();
717 for def in &chunk.struct_defs {
718 interp
719 .struct_defs
720 .insert(def.name.clone(), std::sync::Arc::new(def.clone()));
721 }
722 for def in &chunk.enum_defs {
723 interp
724 .enum_defs
725 .insert(def.name.clone(), std::sync::Arc::new(def.clone()));
726 }
727 for def in &chunk.trait_defs {
728 interp
729 .trait_defs
730 .insert(def.name.clone(), std::sync::Arc::new(def.clone()));
731 }
732 for def in &chunk.class_defs {
733 interp
734 .class_defs
735 .insert(def.name.clone(), std::sync::Arc::new(def.clone()));
736 }
737 for def in &chunk.class_defs {
739 for m in &def.methods {
740 if let Some(ref body) = m.body {
741 let fq = format!("{}::{}", def.name, m.name);
742 let sub = std::sync::Arc::new(crate::value::StrykeSub {
743 name: fq.clone(),
744 params: m.params.clone(),
745 body: body.clone(),
746 closure_env: None,
747 prototype: None,
748 fib_like: None,
749 });
750 interp.subs.insert(fq, sub);
751 }
752 }
753 }
754
755 let body_ip = chunk.body_start_ip;
756 if body_ip > 0 && body_ip < chunk.ops.len() {
757 let saved_op = chunk.ops[body_ip].clone();
759 chunk.ops[body_ip] = bytecode::Op::Halt;
760 let vm_jit = interp.vm_jit_enabled && interp.profiler.is_none();
761 let mut vm = vm::VM::new(&chunk, interp);
762 vm.set_jit_enabled(vm_jit);
763 let _ = vm.execute()?;
764 chunk.ops[body_ip] = saved_op;
765 }
766
767 interp.line_mode_chunk = Some(chunk);
768 Ok(())
769}
770
771pub fn run_line_body(
774 chunk: &bytecode::Chunk,
775 interp: &mut VMHelper,
776 line_str: &str,
777 is_last_input_line: bool,
778) -> StrykeResult<Option<String>> {
779 interp.line_mode_eof_pending = is_last_input_line;
780 let result: StrykeResult<Option<String>> = (|| {
781 interp.line_number += 1;
782 interp
783 .scope
784 .set_topic(value::StrykeValue::string(line_str.to_string()));
785
786 if interp.auto_split {
787 let sep = interp.field_separator.as_deref().unwrap_or(" ");
788 let re = regex::Regex::new(sep).unwrap_or_else(|_| regex::Regex::new(" ").unwrap());
789 let fields: Vec<value::StrykeValue> = re
790 .split(line_str)
791 .map(|s| value::StrykeValue::string(s.to_string()))
792 .collect();
793 interp.scope.set_array("F", fields)?;
794 }
795
796 let vm_jit = interp.vm_jit_enabled && interp.profiler.is_none();
797 let mut vm = vm::VM::new(chunk, interp);
798 vm.set_jit_enabled(vm_jit);
799 vm.ip = chunk.body_start_ip;
800 let _ = vm.execute()?;
801
802 let mut out = interp.scope.get_scalar("_").to_string();
803 out.push_str(&interp.ors);
804 Ok(Some(out))
805 })();
806 interp.line_mode_eof_pending = false;
807 result
808}
809
810pub fn lint_program(program: &ast::Program, interp: &mut VMHelper) -> StrykeResult<()> {
813 interp.prepare_program_top_level(program)?;
814 static_analysis::analyze_program_with_strict(program, &interp.file, interp.strict_vars)?;
820 if interp.strict_refs || interp.strict_subs || interp.strict_vars {
821 return Ok(());
822 }
823 let comp = compiler::Compiler::new().with_source_file(interp.file.clone());
824 match comp.compile_program(program) {
825 Ok(_) => Ok(()),
826 Err(e) => Err(compile_error_to_perl(e)),
827 }
828}
829
830fn compile_error_to_perl(e: compiler::CompileError) -> StrykeError {
831 match e {
832 compiler::CompileError::Unsupported(msg) => {
833 StrykeError::runtime(format!("compile: {}", msg), 0)
834 }
835 compiler::CompileError::Frozen { line, detail } => {
836 if detail.starts_with("Global symbol") {
841 StrykeError::syntax(detail, line)
842 } else {
843 StrykeError::runtime(detail, line)
844 }
845 }
846 }
847}
848
849#[cfg(test)]
850mod tests {
851 use super::*;
852
853 #[test]
854 fn run_executes_last_expression_value() {
855 let p = parse("2 + 2").expect("parse");
857 assert!(!p.statements.is_empty());
858 let _ = run("2 + 2").expect("run");
859 }
860
861 #[test]
862 fn run_propagates_parse_errors() {
863 assert!(run("sub f {").is_err());
864 }
865
866 #[test]
867 fn interpreter_scope_persists_global_scalar_across_execute_calls() {
868 let mut interp = VMHelper::new();
869 let assign = parse("$persist_test = 100").expect("parse assign");
870 interp.execute(&assign).expect("assign");
871 let read = parse("$persist_test").expect("parse read");
872 let v = interp.execute(&read).expect("read");
873 assert_eq!(v.to_int(), 100);
874 }
875
876 #[test]
877 fn parse_empty_program() {
878 let p = parse("").expect("empty input should parse");
879 assert!(p.statements.is_empty());
880 }
881
882 #[test]
883 fn parse_expression_statement() {
884 let p = parse("2 + 2").expect("parse");
885 assert!(!p.statements.is_empty());
886 }
887
888 #[test]
889 fn parse_semicolon_only_statements() {
890 parse(";;").expect("semicolons only");
891 }
892
893 #[test]
894 fn parse_if_with_block() {
895 parse("if (1) { 2 }").expect("if");
896 }
897
898 #[test]
899 fn parse_fails_on_invalid_syntax() {
900 assert!(parse("sub f {").is_err());
901 }
902
903 #[test]
904 fn parse_qw_word_list() {
905 parse("my @a = qw(x y z)").expect("qw list");
906 }
907
908 #[test]
909 fn parse_c_style_for_loop() {
910 parse("for (my $i = 0; $i < 3; $i = $i + 1) { 1; }").expect("c-style for");
911 }
912
913 #[test]
914 fn parse_package_statement() {
915 parse("package Foo::Bar; 1").expect("package");
916 }
917
918 #[test]
919 fn parse_unless_block() {
920 parse("unless (0) { 1; }").expect("unless");
921 }
922
923 #[test]
924 fn parse_if_elsif_else() {
925 parse("if (0) { 1; } elsif (1) { 2; } else { 3; }").expect("if elsif");
926 }
927
928 #[test]
929 fn parse_q_constructor() {
930 parse(r#"my $s = q{braces}"#).expect("q{}");
931 parse(r#"my $t = qq(double)"#).expect("qq()");
932 }
933
934 #[test]
935 fn parse_regex_literals() {
936 parse("m/foo/").expect("m//");
937 parse("s/foo/bar/g").expect("s///");
938 }
939
940 #[test]
941 fn parse_begin_and_end_blocks() {
942 parse("BEGIN { 1; }").expect("BEGIN");
943 parse("END { 1; }").expect("END");
944 }
945
946 #[test]
947 fn parse_transliterate_y() {
948 parse("$_ = 'a'; y/a/A/").expect("y//");
949 }
950
951 #[test]
952 fn parse_foreach_with_my_iterator() {
953 parse("foreach my $x (1, 2) { $x; }").expect("foreach my");
954 }
955
956 #[test]
957 fn parse_our_declaration() {
958 parse("our $g = 1").expect("our");
959 }
960
961 #[test]
962 fn parse_local_declaration() {
963 parse("local $x = 1").expect("local");
964 }
965
966 #[test]
967 fn parse_use_no_statements() {
968 parse("use strict").expect("use");
969 parse("no warnings").expect("no");
970 }
971
972 #[test]
973 fn parse_sub_with_prototype() {
974 parse("fn add2 ($$) { return $_0 + $_1; }").expect("fn prototype");
975 parse("fn try_block (&;@) { my ( $try, @code_refs ) = @_; }").expect("prototype @ slurpy");
976 }
977
978 #[test]
979 fn parse_list_expression_in_parentheses() {
980 parse("my @a = (1, 2, 3)").expect("list");
981 }
982
983 #[test]
984 fn parse_require_expression() {
985 parse("require strict").expect("require");
986 }
987
988 #[test]
989 fn parse_do_string_eval_form() {
990 parse(r#"do "foo.pl""#).expect("do string");
991 }
992
993 #[test]
994 fn parse_package_qualified_name() {
995 parse("package Foo::Bar::Baz").expect("package ::");
996 }
997
998 #[test]
999 fn parse_my_multiple_declarations() {
1000 parse("my ($a, $b, $c)").expect("my list");
1001 }
1002
1003 #[test]
1004 fn parse_eval_block_statement() {
1005 parse("eval { 1; }").expect("eval block");
1006 }
1007
1008 #[test]
1009 fn parse_p_statement() {
1010 parse("p 42").expect("p");
1011 }
1012
1013 #[test]
1014 fn parse_chop_scalar() {
1015 parse("chop $s").expect("chop");
1016 }
1017
1018 #[test]
1019 fn vendor_perl_inc_path_points_at_vendor_perl() {
1020 let p = vendor_perl_inc_path();
1021 assert!(
1022 p.ends_with("vendor/perl"),
1023 "unexpected vendor path: {}",
1024 p.display()
1025 );
1026 }
1027
1028 #[test]
1029 fn format_program_roundtrips_simple_expression() {
1030 let p = parse("$x + 1").expect("parse");
1031 let out = format_program(&p);
1032 assert!(!out.trim().is_empty());
1033 }
1034}
1035
1036#[cfg(test)]
1037mod builtins_extended_tests;
1038
1039#[cfg(test)]
1040mod lib_api_extended_tests;
1041
1042#[cfg(test)]
1043mod parallel_api_tests;
1044
1045#[cfg(test)]
1046mod parse_smoke_extended;
1047
1048#[cfg(test)]
1049mod parse_smoke_batch2;
1050
1051#[cfg(test)]
1052mod parse_smoke_batch3;
1053
1054#[cfg(test)]
1055mod parse_smoke_batch4;
1056
1057#[cfg(test)]
1058mod crate_api_tests;
1059
1060#[cfg(test)]
1061mod parser_shape_tests;
1062
1063#[cfg(test)]
1064mod interpreter_unit_tests;
1065
1066#[cfg(test)]
1067mod run_semantics_tests;
1068
1069#[cfg(test)]
1070mod run_semantics_more;
1071
1072#[cfg(test)]
1073mod value_extra_tests;
1074
1075#[cfg(test)]
1076mod lexer_extra_tests;
1077
1078#[cfg(test)]
1079mod parser_extra_tests;
1080
1081#[cfg(test)]
1082mod builtins_extra_tests;
1083
1084#[cfg(test)]
1085mod keywords_hash_tests;
1086
1087#[cfg(test)]
1088mod thread_extra_tests;
1089
1090#[cfg(test)]
1091mod error_extra_tests;
1092
1093#[cfg(test)]
1094mod oo_extra_tests;
1095
1096#[cfg(test)]
1097mod regex_extra_tests;
1098
1099#[cfg(test)]
1100mod aot_extra_tests;
1101
1102#[cfg(test)]
1103mod builtins_sync_tests;