you can go to https://tio.run/#quarterstaff to run quarterstaff programs online. however, the TIO BF implementation seems to have limited cells, according to the documentation

The title is rather descriptive, except that it doesn't describe what Quarterstaff and BF are. For this challenge, I have created two languages: Quarterstaff, and a dialect of BF that probably already existed in its exact form, but I wanted to be certain, especially with the I/O format, that it would work, called QuarterBF. In this challenge, you will compile a program written Quarterstaff to QuarterBF (but the underlying conversion should work for any generic BF dialect with byte cells and an infinite tape and such).

I have created interpreters for both of these. However, should you find the spec for one of them here conflicts with the interpreter, the spec is prioritised for the challenge (the interpreters should just be helping you out here)

First, the QuarterBF spec, since it will be easier to explain some of the Quarterstaff concepts afterwards (I cribbed some of this from the esolangs page for Brainf***):

BF spec

Brainfuck operates on an array of memory cells, also referred to as the tape, each initially set to zero. These memory cells can each hold an integer between 0 and 255, inclusive, and the tape extends infinitely to the right (some implementations will use a limited number of cells, hence I made my own to be sure / make an appropriate interpreter easy to find). There is a pointer, initially pointing to the first memory cell. There are no cells to the left of the first memory cell, and attempting to move further left is undefined behaviour. The commands are:

> : move the pointer to the right
< : move the pointer to the left
+ : increment the cell under the pointer, unless the cell is 255, in which case set cell to 0
- : decrement the cell under the pointer, unless the cell is 0, in which case set cell to 255
, : input a character code into the cell under the pointer (described more below)
. : output the character that the integer in the cell under the pointer represents
[ : Jump past the matching ] if the cell under the pointer is 0
] : Jump back to the matching [ if the cell under the pointer is nonzero

In QuarterBF input is interactive. The exact mechanism this works by could be gleaned by reading the source if you wanted, but it's not totally necessary. The important points: it sets a cell to the character code of an input character, sets to 0 on EOF, and to 10 for end of line newlines (which is the newline charcode)

Either way, you don't have to worry so much about how input works, because the values of ? and ! translate essentially perfectly to the values , and . anyway.

Quarterstaff spec:

Quarterstaff's memory consists of registers. one of these registers is called "value", and the others are named variables. variables are case sensitive. Variables can be referenced before assignment and are initially zero (alternately, referencing a variable before assignment does nothing, because if it hasn't been assigned yet, it means it has a value of zero, therefore adding zero has no effect).

[a string of digits] : add the number those digits represent to the value
[a string of letters not preceded by >] : add the variable that the name represents to value
[a string of letters preceded by >] : set the variable to the current value, set value to 0
. : set value to zero
- : multiply value by -1
? : add to value the amount that the underlying bf implementation would set a cell to when , is executed
! : output the character with the charcode corresponding to value.
[ : Jump past the matching ] if value is 0
] : Jump back to the matching [ if value is nonzero
([...] is a while loop)
{ : Jump past the matching } if value is nonzero
} : Jump back to the matching { if value is 0
({...} is a while-not loop)
(, |, ): part of an if else construct...
[any characters not mentioned here] separator between commands; doesn't execute anything, but separates variables or numbers

Explanation of if:

if-style 1:

when coming to this paren, check if value is zero or not
v
(...)
^ if value was nonzero, execute the commands within
either way, then continue execution after the )

if-style 2:

when coming to this paren, check if value is zero or not
v
(...|...)
A B
if the value is nonzero, execute the subprogram designated A. else execute the one designated B.
then, continue execution after the closing )

(...) is equivalent to (...|)

The challenge

Taking a quarterstaff program as input, compile it to BF; output a program which functions the same, but in BF.

i.e.

it halts if and only if the inputted program halts

outputs the exact same text as it.

if it doesn't halt, it should print the text that the original program prints in finite time, in finite time (it shouldn't put output into a buffer and then print it when it stops executing or something)

Allowances and some restrictions:

you may assume that input to the outputted program is always in printable ASCII and ASCII whitespace (and EOF. Basically, no need to worry about any characters >255 overflowing or whatever), and that the quarterstaff program will never try to output anything other than printable ASCII and ASCII whitespace.

you may assume the inputted program is only ascii printables and ascii whitespace

you may not assume that the variables and values only go up to 255. you need to implement infinite variables and accumulator

basically, you don't get to limit anything (except possible i/o characters). your bf spec has infinite cells, you can use them!

you may assume that the Quarterstaff program has fully matched brackets, and does not invoke undefined behaviour (e.g. trying to output non-ASCII like I mentioned earlier)

you should not invoke undefined behaviour in your outputted bf program. just because I included an interpreter doesn't mean you get to exploit any bugs or lack of exceptions for undefined behaviour. When the spec in this question and implementation differ, follow the question spec. This includes when the interpreter technically could suffer from recursing too deep. doesn't mean you get to assume that a program never recurses that much.

Scoring is by the length of the program, shorter is better; code-golf (this is not metagolf, the length of your bf programs does not matter, as long as they work)

test cases:

prime tester (slightly different from my answer on the prime challenge; a space was replaced with a newline):

10-?[-38a a a a a a a a a a>a10-?]a>b
b[>b>d a>c c[1>c1d>d b d(.>e|>d1>e)c]e f>f1b]2-f(.|1)48!

Your Answer

If this is an answer to a challenge…

…Be sure to follow the challenge specification. However, please refrain from exploiting obvious loopholes. Answers abusing any of the standard loopholes are considered invalid. If you think a specification is unclear or underspecified, comment on the question instead.

…Try to optimize your score. For instance, answers to code-golf challenges should attempt to be as short as possible. You can always include a readable version of the code in addition to the competitive one.
Explanations of your answer make it more interesting to read and are very much encouraged.

…Include a short header which indicates the language(s) of your code and its score, as defined by the challenge.

More generally…

…Please make sure to answer the question and provide sufficient detail.

…Avoid asking for help, clarification or responding to other answers (use comments instead).