Esoteric - Bf - Macro language cheat sheet

by admin, 2020-02-10 23:09:44

Glossary

name a variable name, A-z0-9
decimal a positive decimal integer, 0-9
index a variable name surrounded by square brackets
string a constant string (single or double quoted)
... any text or BF code

Keywords and constructs

declare ( name , decimal )
Does not produce any code by itself. Declares and maps a variable on the tape of name name and size decimal. Size in bytes.
Example (will refer to these same variables later on):
define(foo, 3)
define(bar, 7)
define(x, 1)

will result in the next memory layout:
0 0 0 0 0 0 0 0 0 0 0 0 0 ...
foo bar x


define ( name , decimal )
Behaves a little bit like `declare`, but this time, it not only allocates the space in the memory tape, it also initializes the variable. Defines a variable on the tape of name name with its value decimal. Size is determined automatically.

offset name|decimal { ... }
Does not produce any code by itself. Specifies the offset for `define` statements, so that you can map simple structures on tape.
Example:
offset foo {
define (z, 1)
define (k, 1)
}

`z` will have the same address as `foo` does.
Or you can also use it for creating relative maps, just instead of a variable name specify offset number of bytes:
offset NIL {
declare (First, 1)
declare (Second, 1)
}



macro ( name ) { ... }
Defines a macro substitution section. Anything under the curly braces won't be rendered immediately, but instead, a `place` operator should be used.

place ( name )
Puts in place a previously defined macro substitution.
Example:
macro (Test) { XXX }
place (Test)
place (Test)

will yield:
XXX
XXX


while ( name|index ) { ... }
Creates while-not-zero loop around a defined variable on tape. It can also start a loop over an indexed cell.
Example:
while (foo) { ... }
will result in the next code:
><>< ... ><

using name|index { ... }
References a defined variable on tape. Whatever is in between the curly braces, is executed relative to a variable name or indexed cell index.
Example:
using k { ... }
will give us:
>> ... <<

if ( name|index ) { ... }
Generates conditional block of code. It is basically a macro for a combination of comparison and while.

ifnot ( name|index ) { ... }
Same as if, except it has an embedded negate.
Preserves the variable or index under question.

index ( name, decimal )
Initializes a pointer, relative to a variable name.
An index is a way of organizing the tape so that the BF virtual machine can quickly access some arbitrary cell or a group of cells, whose location is determined at runtime.
Think of it as pointers in C. Basically, it splits the tape, providing flag cells between the data cells or elements.
Consider this layout:
... Z X F X F X F ...
         bar

Here X - some user data (1 or more cells), F - pointer flags,
Z - begin marker.
Each data element is assigned with a pointer flag to the right. Cell Z is always zero. X can be anything we don't care about. Size of X (in bytes) is specified as second parameter to `index`. F is usually 0 and only set to 1 on an indexed element.
`index` command always produces the same code, which clears beginning marker and puts pointer flag at the first data element.
Example:
index (bar, <sizeof X>)
will give us the code which makes our `bar` look like this:
... 0 X 1 X F X F ...
         bar

Now the first pointer flag points at the first element.

right ( name , decimal )
Clears current pointer flag and sets the one decimal
elements to the right.
Example:
right (bar, 2)
resulting code will make this happen:
... 0 X 0 X 0 X 1 ...
        bar


left ( name , decimal )
Clears current pointer flag and sets the one decimal
elements to the left.
Example:
left (bar, 1)
resulting code will make this happen:
... 0 X 0 X 1 X 0 ...
        bar


Note: keep in mind that moving out past indexed area left or right
makes our program do unpredictable things.

seek ( name , decimal )
Clears current pointer flag and sets the one decimal
elements from the beginning of the index. Keep in mind that decimal value is limited by VM implementation (which is usually 255).

# ...
Allows for bypassing of keywords. A line comment.

let name|index = string|name|decimal|index
               +name|decimal|index
               -name|decimal|index
               *name|decimal
               /name|decimal
               %name|decimal
               ?name|decimal|index

Performs some simple arithmetic expressions on variables and consts. Preserves contents of all the operands on the RHS.
Examples:
let x = x + 1
let k = x * 3 + z
let x = 0
let k = x
let z = x
let bar = 'Hello!'
let bar = "Hello!"

but you can not do this:
let x = 1 - x # RHS operand must not be same as LHS
# except for the first one
let x = x * z # you can only multiply by const (for now)

String literal in double quotes does not only initialize the string, but it also assigns an index to a variable, so that the string can be traversed.

input ( name|index , decimal )
Reads text string from input to buffer name or buffer at index until decimal character is met. The end-of-line character is trimmed. Some commonly used characters have default aliases, like:
EOL = 10
NIL = 0


output ( name|index|decimal|string , ... )
Prints text. It can be supplied either from static variable, or from an indexed cells, or as a constant string or as a decimal character code as well. You can specify as many parameters as you wish.
Example:
output('bar contains "', bar, '"', EOL)

negate ( name|index )
Logically negates value, stored in specified location. Any non-zero number becomes 0. 0 becomes 1.


pause
Inserts breakpoint.


Program example: 99 bottles of beer

define (count, 99)
define (Lbob, ' bottles of beer')
define (Lwall, ' on the wall')
define (Ltake, 'Take one down, pass it around.')
define (Lnomore, 'No more bottles of beer on the wall')

offset Lbob
{
  define (Lnil, 1)
  define (Ltens, 1)
  define (Lones, 1)
}

macro (IntToChars)
{
  let Ltens = count / 10 + 48
  let Lones = count % 10 + 48
}

let count = 99
let Lbob = '   bottles of beer'
let Lwall = ' on the wall'
let Ltake = 'Take one down, pass it around.'

while (count)
{
  place (IntToChars)
  output (Lbob, Lwall, EOL)
  output (Lbob, EOL)
  output (Ltake, EOL)
  let count = count - 1
  place (IntToChars)
  if (count) {
    output (Lbob, Lwall, EOL, EOL)
  }
  ifnot (count) {
    output ('No more bottles of beer on the wall.', EOL)
  }
}