#Incorrect behavior on piped input

20 messages · Page 1 of 1 (latest)

hardy hamlet
#

I was making a Brainf*ck interpreter, and while I was testing the entire thing for correctness, I found that everything worked flawlessly, except piped input. For some reason, whenever I pipe the input, it goes nuts, but whenever I copy-paste the same Brainf*ck program with interactive input or by specifying a file, it works flawlessly.

I spent several days trying to debug the issue without any success. I suspect that it's the way that I handle IO, but I am not sure where I did wrong.

The only places I do IO in are main and lib::engine::Engine::run
https://gitlab.com/alexmozaidze/brainfuck-rs/-/tree/dev/src?ref_type=heads

glass nebula
#
  1. You're reading stdin until EOF to read the program.
  2. Your Instruction::Read also reads stdin.
    Therefore, if stdin is is a pipe (or redirected file!), your program will always read to EOF of the file or pipe, take that as the BF instructions, and nothing will be left.
#

And your Instruction::Read, if quit_on_eof is not set, will proceed as if every read produces a 0 byte

hardy hamlet
#

If the user decides to not input anything (Ctrl-D/EOF), current cell must be set to 0, this is intentional. But I don't understand how are the two reads interconnected.

I read the stdin to get the program into code variable successfully, it gets parsed and passed to Engine::run, but then it starts to freak out, why is that?

glass nebula
#

Well, what do you mean by "freak out"?

#

how are the two reads interconnected
They're both reads from stdin.

#

If you write cat program.bf | cargo run then stdin is the program file, so all reads looking for user input will fail with EOF.

hardy hamlet
#

I modified the Instruction::Read clause to the following:

if !settings.should_flush {
    stdout.flush().unwrap();
}

let mut input_char: [u8; 1] = [0];

match stdin.read_exact(&mut input_char) {
    Ok(_) => {},
    Err(e) if settings.quit_on_eof && e.kind() == ErrorKind::UnexpectedEof => return,
    Err(e) if !settings.quit_on_eof && e.kind() == ErrorKind::UnexpectedEof => {},
    Err(e) => panic!("unexpected error: {e}"),
}

dbg!(input_char[0]); // Basically added this line
self.tape[self.pointer] = Wrapping(input_char[0]);

And I found that only, and only when the input is piped, it starts spamming:

[src/lib/engine.rs:132] input_char[0] = 0

It seems to be trying to read a character over and over again, without waiting for user input.

glass nebula
#

Yes

#

Because stdin is not attached to the terminal!

#

It is attached to the pipe, which you already read everything from

#

So you are hitting this branch of your match:

    Err(e) if !settings.quit_on_eof && e.kind() == ErrorKind::UnexpectedEof => {},
#

which falls through and reads the 0 that you initialized your buffer with here:

let mut input_char: [u8; 1] = [0];
hardy hamlet
#

Oooh, so it basically treats the pipe as if it was the user's input, even after the EOF

#

I understand now

hardy hamlet
#

I changed the interface of my program to treat piped data as input to the program, and programs can only be loaded through files. That'll allow using Brainf*ck programs as scripts and is easier to implement. Thank you for helping!

glass nebula
#

stdin is not user's input

#

stdin is 'the input' to your program

#

for programs started from an interactive shell, it will be connected to the terminal by default; in any other circumstance it is something else