BFC

From Esolang
Jump to navigation Jump to search

BFC or Brainfuck Condensed is a low level abstraction layer on top of Brainfuck code, aiming at reducing the code length of Brainfuck code while making it faster to run on a BFC interpreter, promoting good (ie. efficient) codestyle and making it better readable. It is trivial to convert between BF and BFC.

Motivation

Brainfuck programmers often attempt to find the shortest code possible for a given task, because they deem that better. That's wrong. If the task is just to print the digit 0, then using loops to save code bytes (ie. ++++[->++++<]>[-<+++]<.) makes the program a lot slower and harder to read than the straightforward solution (+++++ +++++ +++++ +++++ +++++ +++++ +++++ +++++ +++++ +++.). Additionally, the long and simple version can be compressed by a good interpreter/compiler into a single atomic data operation. The solution is to use a syntax that translates to Brainfuck and in which the shortest possible code is also the fastest and best readable.

Implementation

BFC consists of multiple layers of increasing abstraction. BFC transpilers can step by step from one to the next layer. Raw Brainfuck can be considered Layer 0.

Layer 1

Layer 1 is small runtime optimisations. This currently includes only two features: Quantifiers and the Zero operator. A BFC interpreter can directly execute BFC Layer 1 code without further conversion, which is more performant than the equivalent BF code.

Quantifiers are numbers that can be written in front of any instruction, except the loop instructions ([ and ]). For example the BFC code 5+ would be equivalent to the BF code +++++. The quantified instruction is repeated as many times as the quantifier says. The format for quantifiers is either decimals or hex numbers (using either uppercase or case insensitive letters A-F); the interpreter/compiler should explicitly document which format is used. A number can have any amount of digits. A BFC interpreter can implement some quantified operations as single atomic operations. A quantifier in front of a loop or loop end is invalid, similarly to how unmatching rectangular brackets are invalid in Brainfuck. A number that is not immediately followed by an instruction is ignored, since it could be a comment.

The Zero Operator, written as underscore (_) is equivalent to either of the two Brainfuck snippets [-] or [+]. It sets the current cell to zero. A BFC interpreter can implement this as a single atomic operation.

Layer 2

This layer adds some metaprogramming features, namely groups, macros and templates. Layer 2 is usually not executed directly, but first compiled down to Layer 1.

Groups are created by wrapping arbitrary code in braces. This code would be converted to its wrapped code just as without the braces, but any quantifiers in front of the group affect the whole group. For example the BFC code 3{.>} compiles to .>.>.>. Groups contain nested groups. Groups can contain invalid code, if it becomes valid after unpacking the group; eg. 2{3}+. is valid (and compiles to 33+.) even though the group contains a quantifier without instruction, which is invalid.

A Macro is basically a named group that can be included in the code later. It is defined with the syntax {name:code}. The code is not immediately included at the current position, but it can be included at any later point with the syntax: {name}. The name can be any string consisting only of the 26 ASCII letters and it is case insensitive. Macros can also contain groups and other macros that were previously defined. Recursion in macros is impossible.

Macros can be defined with parameters. These are called Templates. The syntax is: {name:param1:param2:code}. A template can have any number of parameters. Parameter names follow the same format as macro names. Access to the parameters happens as if there was a macro with the same name as the parameter. When including the template, all parameters have to be defined and can contain arbitrary BFC code, using the syntax: {name:param1:param2}

A BFC Layer 2 compiler should implement one or both of the following two features:

  • Template parameters may have default values. These are defined with the syntax paramname=defaultvalue. A call of the template may omit parameters with defaults. All parameters with defaults must be the last parameters of the template.
  • There may be multiple templates with the same name, but a different amount of parameters. The compiler must figure out which template matches the number of parameters of a template call.

Note that the first variant can easily be converted into the second.

Example: Prints "Hello World" (using hex quantifiers) {print:c:_{c}+._} {print:48} {print:65} {print:6C} {print:6C} {print:6F} {print:20} {print:57} {print:6F} {print:72} {print:6C} {print:64}

Brainfuck Compatibility

Both layers 1 and 2 are a superset of Brainfuck, which means any Brainfuck program can also be run as a BFC program with the same effect, except for comments - see below.

Writing Code Comments

Comments remain mostly the same as in BF, with a few exceptions:

  • Numbers immediately preceding an instruction are interpreted as quantifier and affect program behavior.
  • The underscore character (_) is an instruction and should not be used in comments.
  • Comments should not include opening braces ({); although this is safe in most situations.