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