1use std::io::{self, IsTerminal, Write};
2use clap::Args;
3
4use crate::repl::{execute_bare_once, repl_loop, select_mode, ReplMode, ModeFlagOverride};
5
6#[derive(Args, Debug)]
7#[command(disable_help_flag = true)]
8pub struct ReplArgs {
9 #[arg(long = "bare", conflicts_with = "editor")]
11 pub bare: bool,
12
13 #[arg(long = "editor", conflicts_with = "bare")]
15 pub editor: bool,
16
17 #[arg(short = 'h', long = "help", action = clap::ArgAction::SetTrue)]
19 help: bool,
20}
21
22
23pub fn run(program: &str, help: bool, mode_flag: ModeFlagOverride) -> i32 {
25 if help {
26 usage_and_exit(program, 0);
27 }
28
29 let mode = match select_mode(mode_flag) {
31 Ok(m) => m,
32 Err(msg) => {
33 eprintln!("{program}: {msg}");
34 let _ = io::stderr().flush();
35 return 1;
36 }
37 };
38
39 if let Err(e) = ctrlc::set_handler(|| {
41 let _ = io::stdout().flush();
42 let _ = io::stderr().flush();
43 std::process::exit(0);
44 }) {
45 eprintln!("{program}: failed to set ctrl+c handler: {e}");
46 let _ = io::stderr().flush();
47 return 1;
48 }
49
50 match mode {
51 ReplMode::Editor => {
52 if io::stderr().is_terminal() {
54 eprintln!("Brainfuck REPL (interactive editor mode)");
55 eprintln!("Ctrl+d/Ctrl+z Enter (Windows) executes the current buffer. Press ctrl+c to exit");
56 let _ = io::stderr().flush();
57 }
58
59 if let Err(e) = repl_loop() {
60 eprintln!("{program}: REPL error: {e}");
61 let _ = io::stderr().flush();
62 return 1;
63 }
64
65 0
66 }
67 ReplMode::Bare => {
68 match execute_bare_once() {
70 Ok(_) => 0,
71 Err(e) => {
72 eprintln!("{program}: REPL error: {e}");
73 let _ = io::stderr().flush();
74 1
75 }
76 }
77 }
78 }
79}
80
81fn usage_and_exit(program: &str, code: i32) -> ! {
82 eprintln!(
83 r#"Usage:
84 {0} repl # Start a Brainfuck REPL (read-eval-print loop)
85
86Options:
87 --help, -h Show this help
88 --bare Force non-interactive bare mode
89 --editor Force interactive editor mode (errors if stdin is not a TTY)
90
91Description:
92 Starts a REPL where you can enter Brainfuck code and execute it live.
93
94Meta commands (line starts with ":")
95 :exit Exit immediately (code 0)
96 :help Show this help
97 :reset Clear current buffer (history is preserved)
98 :dump Print buffer (content → stdout; framing → stderr)
99 -n Include line numbers (stdout)
100 --stderr Send everything to stderr
101
102Notes:
103 - While editing, non-Brainfuck characters are ignored; only valid instructions are executed.
104 - Ctrl+D executes the current buffer on *nix/macOS.
105 - Ctrl+Z and Enter will execute the current buffer on Windows.
106 - Ctrl+C exits the REPL immediately.
107 - The REPL will print a newline after each execution for readability.
108 - Each execution starts with a fresh memory and pointer.
109 - The REPL will exit after a single execution if the environment variable `BF_REPL_ONCE` is set to `1`.
110 - Mode selection:
111 * Flags: --bare|--editor override environment and auto-detection.
112 * Env: BF_REPL_MODE=bare|editor overrides auto-detection (flags, when preset, will override env).
113 * Auto-detect: if stdin is a TTY, starts in interactive editor mode; otherwise, bare mode.
114 * Prompts/banners suppressed if stderr is not a TTY.
115
116"#,
117 program
118 );
119 let _ = io::stderr().flush();
120 std::process::exit(code);
121}