Full Code
For your copy-pasting convinience, here is the full file for the bf interpreter as implemented in this book:
// sets a value to a specific value by zeroing it before writing
[@SET Aaddr Vval] [
ZERO Aaddr;
INCR Aaddr Vval;
]
// copies the content of a cell whilst keeping the source (conservatively)
[@COPC Asrc Adst sp] [
ALIS Atmp sp;
COPY Asrc Adst Atmp;
ADDP Asrc Atmp;
]
// if not equal conditional
[@IFNE Aaddr Vval [scp] sp] [
ALIS Atmp1 sp;
ALIS Atmp2 sp+1;
ALIS sp sp+2;
COPY Aaddr Atmp1 Atmp2;
ADDP Aaddr Atmp2;
WHNE Atmp1 Vval [
ZERO Atmp1;
INLN [scp];
INCR Atmp1 Vval;
];
ZERO Atmp1;
]
// if equal conditional
[@IFEQ Aaddr Vval [scp] sp] [
ALIS Aflag sp;
ALIS sp sp+1;
ALIS Vnot_equal 1;
IFNE Aaddr Vval [
INCR Aflag Vnot_equal;
] sp;
IFNE Aflag Vnot_equal [scp] sp;
ZERO Aflag;
]
// moves the value of the cell at Aarray[value of Aindex] to Adst
[@GETD Aarray Aindex Adst] [
ADDP Aarray+3 Aindex;
BBOX Aarray+1;
ASUM 0;
ALIS Aswap 0;
ALIS Areturn 1;
ALIS Aindex 2;
ALIS Aelement 3;
WHNE Aindex 0 [
DECR Aindex 1;
INCR Areturn 1;
ADDP Aswap Aelement;
ADDP Aindex+1 Aindex;
ADDP Areturn+1 Areturn;
BBOX 1;
ASUM 0;
];
ALIS Acell Aindex;
ADDP Acell Aelement;
ALIS Aswap Aelement;
ALIS Aelement 0;
WHNE Areturn 0 [
DECR Areturn 1;
ADDP Areturn-1 Areturn;
ADDP Acell-1 Acell;
BBOX 0;
ASUM 1;
ADDP Aswap Aelement;
];
BBOX Aelement;
ASUM Aarray+1;
ADDP Adst Aarray+3;
]
// adds the value of the cell at Asrc to the Aarray[value of Aindex] cell
[@ADDD Aarray Aindex Asrc] [
ADDP Aarray+3 Asrc;
ADDP Aarray+2 Aindex;
BBOX Aarray;
ASUM 0;
ALIS Aswap 0;
ALIS Areturn 1;
ALIS Aindex 2;
ALIS Acell 3;
ALIS Aelement 4;
WHNE Aindex 0 [
DECR Aindex 1;
INCR Areturn 1;
ADDP Aswap Aelement;
ADDP Acell+1 Acell;
ADDP Aindex+1 Aindex;
ADDP Areturn+1 Areturn;
BBOX 1;
ASUM 0;
];
ADDP Aelement Acell;
ALIS Aswap Aelement;
ALIS Aelement 0;
WHNE Areturn 0 [
DECR Areturn 1;
ADDP Areturn-1 Areturn;
BBOX 0;
ASUM 1;
ADDP Aswap Aelement;
];
BBOX Aelement;
ASUM Aarray;
]
[@init_input Aarray] [
BBOX Aarray+4;
ASUM 0;
ALIS Aflag 1;
ALIS Vappended 1;
ALIS Vexit 2;
ALIS sp 2;
// we can still use sp, since we know the cells will be zeroed
WHNE Aflag Vexit [
IN 0;
// -- mapping operators --
IFEQ 0 '+' [
SET 0 1;
INCR Aflag Vappended;
] sp;
IFEQ 0 '-' [
SET 0 2;
INCR Aflag Vappended;
] sp;
IFEQ 0 '>' [
SET 0 3;
INCR Aflag Vappended;
] sp;
IFEQ 0 '<' [
SET 0 4;
INCR Aflag Vappended;
] sp;
IFEQ 0 '[' [
SET 0 5;
INCR Aflag Vappended;
] sp;
IFEQ 0 ']' [
SET 0 6;
INCR Aflag Vappended;
] sp;
IFEQ 0 ',' [
SET 0 7;
INCR Aflag Vappended;
] sp;
IFEQ 0 '.' [
SET 0 8;
INCR Aflag Vappended;
] sp;
// check for end
IFEQ 0 '!' [
ZERO 0;
SET Aflag Vexit;
] sp;
// if we added something, we need to move up one
IFEQ Aflag Vappended [
ZERO Aflag;
BBOX 1;
ASUM 0;
] sp;
// we don't clear the last character, even if it was invalid,
// as the next IN will overwrite the cell
];
// NEVER forget cleanup
ZERO Aflag;
// -- going back --
// because the last char will be '!', we need to offset by at least 1
BBOX 0;
ASUM 1;
// moves back until we reached the zeroed cells of the parking
WHNE 0 0 [
BBOX 0;
ASUM 1;
];
ASUM Aarray+3;
]
[main] [
ALIS Voperating_memory 16;
ALIS Aprog_pointer 0;
ALIS Amem_pointer 1;
ALIS Aflag 2;
ALIS Vexit 1;
ALIS Vbracket 2;
ALIS Aoperator 3;
ALIS Acell 4;
ALIS Abracket_jump 5;
ALIS sp 6;
ALIS Vprog_array_cap 256;
ALIS Aprog Voperating_memory;
ALIS Amem Aprog+Vprog_array_cap+4;
// get program from user
init_input Aprog;
WHNE Aflag Vexit [
ALIS Atmp sp;
ALIS sp sp+1;
// 1. load in operator
COPC Aprog_pointer Atmp sp;
GETD Aprog Atmp Aoperator;
// 2. operator specific logic
// + + + + +
IFEQ Aoperator 1 [
INCR Acell 1;
] sp;
// - - - - -
IFEQ Aoperator 2 [
DECR Acell 1;
] sp;
// > > > > >
IFEQ Aoperator 3 [
// store the old cell
COPC Amem_pointer Atmp sp;
ADDD Amem Atmp Acell;
// get the new cell
INCR Amem_pointer 1;
COPC Amem_pointer Atmp sp;
GETD Amem Atmp Acell;
] sp;
// < < < < <
IFEQ Aoperator 4 [
// store the old cell
COPC Amem_pointer Atmp sp;
ADDD Amem Atmp Acell;
// get the new cell
// NOTE: this DECR may underflow the tape pointer
DECR Amem_pointer 1;
COPC Amem_pointer Atmp sp;
GETD Amem Atmp Acell;
] sp;
// [ [ [ [ [
IFEQ Aoperator 5 [
IFEQ Acell 0 [
ALIS Acounter sp;
INCR Acounter 1; // 1.
ALIS Asearch_op sp+1;
ALIS sp sp+2;
// 2.
// we don't need to define our search counter here,
// we can use Abracket_jump directly
COPC Aprog_pointer Abracket_jump sp;
WHNE Acounter 0 [
// 3.
INCR Abracket_jump 1;
// 4.
COPC Abracket_jump Atmp sp;
GETD Aprog Atmp Asearch_op;
// 5.
// if op == '['
IFEQ Asearch_op 5 [
INCR Acounter 1;
] sp;
// if op == ']'
IFEQ Asearch_op 6 [
DECR Acounter 1;
] sp;
// 6.
COPC Abracket_jump Atmp sp;
ADDD Aprog Atmp Asearch_op;
]; // 7.
INCR Aflag Vbracket;
] sp;
] sp;
// ] ] ] ] ]
IFEQ Aoperator 6 [
IFNE Acell 0 [
ALIS Acounter sp;
INCR Acounter 1; // 1.
ALIS Asearch_op sp+1;
ALIS sp sp+2;
// 2.
COPC Aprog_pointer Abracket_jump sp;
WHNE Acounter 0 [
// 3.
DECR Abracket_jump 1;
// 4.
COPC Abracket_jump Atmp sp;
GETD Aprog Atmp Asearch_op;
// 5.
// if op == '['
IFEQ Asearch_op 5 [
DECR Acounter 1;
] sp;
// if op == ']'
IFEQ Asearch_op 6 [
INCR Acounter 1;
] sp;
// 6.
COPC Abracket_jump Atmp sp;
ADDD Aprog Atmp Asearch_op;
]; // 7.
INCR Aflag Vbracket;
] sp;
] sp;
// , , , , ,
IFEQ Aoperator 7 [
IN Acell;
] sp;
// . . . . .
IFEQ Aoperator 8 [
OUT Acell;
] sp;
// check for the end
IFEQ Aoperator 0 [
INCR Aflag Vexit;
] sp;
// set the operator back
COPC Aprog_pointer Atmp sp;
ADDD Aprog Atmp Aoperator;
IFEQ Aflag Vbracket [
ZERO Aprog_pointer;
ADDP Aprog_pointer Abracket_jump;
ZERO Aflag;
] sp;
// increase program pointer
INCR Aprog_pointer 1;
];
]