BunnyBell

From Esolang
(Redirected from Bunnybell)
Jump to navigation Jump to search

Renaming to Bellbase and moving off the esolangs wiki. This page is still used for drafting.

Logo by User:PixelatedStarfish

BunnyBell is a programming language created by User:PixelatedStarfish in 2022 and it is designed to be a simple, practical language for learning to program. Given this, showcasing this language on the esolangs wiki is certainly ironic, but the esolangs wiki is convenient.

BunnyBell was initially created as a second major version of MacroBeep, designed to support subroutines. MacroBeep v2.0 diverged to the point where it was best renamed, as a new language entirely.

Alternate spellings are Bunnybell or Bunny Bell. The current version is version 1.2 and the language file extension is .bbe


Design Goals: Why Use BunnyBell?

This language is designed to learn by tinkering. It is designed for students to practice essential programming skills, such as tracing, debugging, and memory management.

Someone can learn to code with any programming language, but few are designed for learning, and fewer still are designed for an adult learner. Why is transitioning from a graphical, block-based language, to a text-based one so hard? Well, there are constructs and conventions to memorize. There might even be three different kinds of grouping symbols: (parentheses), [brackets], {braces}. A new programmer has a lot of questions. How do I install this? Which version am I using? How do I do this or that? What is this thing again? What does this error mean? BunnyBell is designed to ensure a new programmer can stop stressing, and focus on expressing their programs in code.

  • BunnyBell has a simplified syntax, for programs that are easier to write, organize, trace, and debug.
  • Lexemes are descriptive, such that the tokens they represent are clearly indicated.
  • Memory is always bound to function calls, which eliminates confusing design patterns while maintaining usability.

Factors in Design

Always write the simplest code you can. Never implement a complicated algorithm when a simple one will do.

On Languages Designed for Pedagogy: Thoughts on Stacking Blocks

Many languages with a pedagogical design philosophy are written for children. Such languages typically employ a form of graphics based coding in specialized IDEs (Integrated Development Environments) in which programming is accomplished by clicking and dragging colorful blocks. These blocks have statements written on them and fit together like puzzle pieces. In general, I believe these languages are a success. A child can enjoy programming a video game, or an animated story without dealing with abstract data structures and advanced concepts in object oriented programming. Instinctively, one can learn how to read source code and debug it; how to use loops, variables, and conditional statements. Then it comes time to get bolder, try out some data structures. Code gets long, hundreds, or even thousands of blocks long. The IDE starts to slow and lag. The programmer hits a ceiling, and it comes time to transition to a more efficient language, one that describes source with efficient, computer-friendly text. That is the hard part.

Most high-level languages are not based on stacking colorful blocks. They have keywords instead. In a language like Scratch, blocks are commands, or arguments for commands. Even when a block is completely isolated from your program, you can click on it and watch it do something. A cat goes left. A variable is set. A list is emptied. Not all keywords have this property. Many keywords modify groups of statements (which are also called blocks and opened and closed with braces. I call them groups here to avoid confusion.) The if keyword makes this block an if statement. The fn keyword means this group is a function. The private keyword modifies the fn keyword, indicating this function cannot be accessed outside the encapsulating class. Keywords can express abstract syntax, but many do not constitute a serviceable program in isolation. They do not do anything without context. This is one of a few factors that makes transitioning from Scratch to a C-style language hard.

BunnyBell commands are designed to be at a halfway point between Scratch blocks and C-style keywords. Most commands are programs that operate independent of other commands, in a manner similar to a Scratch block, or a function. However, BunnyBell commands are also versatile enough to describe functions, macros, and data structures, in a manner similar to a C-style language.

Clarity: Why no Braces?

Many people like braces. from __future__ import braces is a joke among Python programmers. Yes, braces are great for delineating blocks of statements, but they are easily disorganized and do not describe the blocks well. Consider this, often found at the end of a source file:

}
  }}
}

What are they enclosing? Which opening braces do they close? How does deleting one effect source? Nothing is clear about this syntax, a comment is needed for each brace. Something more descriptive is possible:

struct @foo
 #statements
_struct

Even if _struct is the only statement shown, it is still clear that it describes the end of a struct.

Influences: Shameless Theft

  • Structs, Macros, and Includes come from C.
  • The command syntax comes from assembly languages like ARM, and block-based languages like Scratch.
  • The call trace and data types are adapted from Java (and ultimately, C).
  • The format for error messages is adapted from Python.
  • Clojure influenced the syntax of functions, such that functions are first class, rather than second class.
  • BASIC, of course.

Acknowledgements

Thank you to the Skidmore College faculty, O'Connell, Dufour, Eckmann, Read, Reiley, and Prasad. I am glad to have my degree in computer science! I would also like to thank Bringas, Kravsky, and Lee for their tutelage, patience, and inspiration. BunnyBell would not exist without the esolangs wiki and the work of youtuber User:Truttle1.

Thanks to User:Gapples2 for their questions, comments, and insights. This language is better for them.

Of course I should thank my loved ones for their support.

Program Examples

Hello World

Outputs “Hello World” to the console.

func @main
 out "Hello World" 
 return 

Truth Machine

A problem by User:Keymaker to demonstrate usability. It takes a 0 or a 1. If it gets a 1 it outputs an endless sequence of ones. Otherwise it prints 0.

func @main
 int @c (:input "int")
 beq &c 0 Terminate
 label @Forever
  out 1
  goto Forever
 label @Terminate
  out 0
 return

Cat (Echo)

Outputs the input given.

func @main
 out "Feed the cat\n" 
 out (:input "string")
 return 

FizzBuzz

include lib/Math/Ops.bbe 
func @main char @c  int @i 0 label @loop beq ((:mod &i 15) 0) FizzBuzz beq ((:mod &i 5) 0) Buzz beq ((:mod &i 3) 0) Fizz goto nonCase
 label @Fizz   out "Fizz\n" goto loopEnd
 label @Buzz   out "Buzz\n" goto loopEnd
 label @FizzBuzz   out "FizzBuzz\n" goto loopEnd
 label @nonCase   out &i   out "\n"
 label @loopEnd   give &i 1   bleq &i 100 loop  return

Syntax

Statements

Statements are a single line of code that are interpreted to run a program. Each statement starts with a command, which can take arguments. The arguments for a command can be separated by tabs or spaces. A command can also have arbitrary spaces and tabs before it.

command arg1 arg2 arg3 #etc

Comments

Comments are not interpreted; they are for programmers, not computers. Comments are indicated with hashes.

#inline comment
## Multiple Line Comment ##

When writing a comment, it is better to explain why the code is there, not what it is for.

Blocks

Some commands define blocks of code that group multiple statements together. Here is an example of a data structure with attributes:

struct @foo
 int @a
 string @b
_struct

Any command that defines a block has a corresponding block ender, which is the starting command preceded by an underscore. The struct block ends with _struct. macro ends with _macro and a func can end with _func although it is more common to end a function with return.

Blocks cannot nest, nesting blocks throws an error.

Argument Tokens

A command can take a few different tokens as arguments:

  • An IDENTIFIER identifies something. It is used to create a variable, function, or struct.
  • A FUNCTION CALL is a function name preceded by a colon. This call can be passed to a function, returned by a function, or evaluated.
  • FUNCTIONS are data and references can be made to them.
  • A VAR is an argument that refers to a cell with the refer operator (&). Any sequence of refer operators followed by an integer is a VAR.
  • A VALUE is any token that can be stored in a variable. These can be NUMBERS (longs, ints, floats, doubles, chars); COLLECTIONS (strings lists, and words); or STRUCTS (a data type defined in source.)
  • A LIST is a data type that can store values of multiple types. LISTS are COMPLEX COLLECTIONS, so are STRUCTS and FUNCTIONS.
  • A STRING is a sequence of printable characters framed by double quotes. Each string is one argument, even if it includes spaces. A STRING is a SIMPLE COLLECTION, because it can only store data of one type, characters.
  • LABELS organize source code.
  • PATHS point to files.
  • WORDS are the untyped argument. Every argument is a WORD.

Token Order

In a statement, tokens take the following order from left to right:

  • Commands
  • Identifiers
  • Function Calls
  • Variables and Structs
  • Numbers
  • Strings
  • Lists
  • Labels
  • Paths

Parentheses and Brackets

(parentheses) and [brackets] are useful for grouping tokens together, but they serve distinct functions in code:

  • Parentheses group calls, and the arguments those calls take together.
  • Brackets group arguments into a list.

To demonstrate syntactic distinction, let :foo be an arbitrary function that does the following:

  • When given a number argument, add 10 to that and return the result.
  • Otherwise, return 0.

Compare these two lines:

(:foo 10)
[:foo 10]

The top line is a call, and it evaluates to 20. The bottom one is a list, and it evaluates to [0 10]. An argument is accepted by the function on the top line, but not the bottom one.

Useful Operators

Some operators appear in multiple tables.

Argument Specifiers
Operation Name Desc.
& Refer Refers to a variable.
: Call Passing Call Passing; Passes a function call as an argument.
@ Identifying Operator For naming data.
" Quote Frames a String.
( Open Parenthesis Starts a parenthetical grouping, which evaluates first.
) Close Parenthesis Close Parenthesis; Closes a parenthetical grouping.
[ Open Bracket Open list.
] Close Bracket Close list.
! Bang Macro operator.
# Hash Starts inline comment. Everything to the right is a comment.



Compiler Operators
Operation Name Desc.
* Star Gets the contents of a directory and load files into the linker.
-> Right Arrow This is substituted with the path from bbe to the current file.
/ Subdirectory Delimiter Extends path to a subdirectory.
! Bang Macro operator.



Lexical Operators
Operation Name Desc.
\n Newline Character (ASCII) Terminates a command or inline comment.
; Semicolon Explicit newline, equivalent to a newline.
\ Escape Begins an escape sequence, which can represent newlines (\n), and tabs (\t).
. Dot Accesses library functions and structure attributes. (This is not a decimal point.)
_ Underscore When a command begins with this operator, a block of statements ends.
# Hash Start inline comment. Everything to the right is a comment.
## Double Hash Frames a multiple line comment.

Grammar in EBNF

Program := Header, Body
Header := {Include}, {(Macro | Struct)}
Body := {Func}
Macro := 'macro', Sp, Word, Lt | 'macro', {Statements}, '_macro', Lt
Struct := Stead, {Stine}, '_struct', Lt
Func := Fi, {Statement}, Re
Fi := 'func', Identifier, Lt
Re := ('return' | '_func'), {Arg}, Lt
Stead := 'struct' Identifier, Lt
Stine := Command, Identifier, Lt
Statement := Command, {Arg}, Lt
Command := {Sp}, Word, Sp
Arg := {(Number | Var | String | Identifier | Call | Word), Sp}
Call := ':', Word {'.', Word}
Var := (And, Word) | (Var '(', Var, ')') | (Var '(', Integer, ')') | Var, '.', Word
Identifier := '@', Words
Number := any number
Integer := any integer
Word := any set of non-white space characters
String := ' " ', (Word | Sp | Tab), ' " '
And := '&'
Sp := ' ', {' '}, Tab
Tab := a tab character
Lt := a newline char | ';' | '#'
Comment '#', {Word, Sp} | '##', {Word | Sp | Lt}, '##'
Key:
:= assignment, equivalence
| or
(group)
{repeated zero or more times}
[optional]
'string literal'
symbol
, symbol concatenation
; symbol terminator
(*comment*)

Variables

Data Types

Bunnybell stores data with variables. The following types are usable:

Types
Type Desc. Range
int A four byte integer. -2,147,483,648 to 2,147,483,647
float A four byte value with a decimal point. 3.4E +/- 38 (7 digits)
double An eight byte value with a decimal point. 1.7E +/- 308 (15 digits)
long A type of eight bytes. -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
char A printable character, of one byte. 0 to 255
string A printable string in quotes. none
list An array or list of items in brackets. Items can be of any type. none
path A path to a file. none
func A function, passed by reference. none

A programmer can define their own types with structures.

Declaring Variables: The Refer Operator &

Each type is a command. These commands can take a name and a value. The latter of which initializes the variable. All variables are referred to with ampersands. These are called refer operators, or refers.


Examples:

#use an identifier to create a var and a refer to refer to the var
int @i 20 
string @s "Hello"                   
# &i is 20
# &s is "Hello"

A variable's type cannot change after declaration, and two variables cannot have the same name. An uninitialized variable is 0 by default, including collections like strings, lists, and structures.

Null Data

0, the number, and null (no data here) are equivalent in this language. A variable of any type can be set to 0.

When a collection is null, it cannot have indexes; a null collection is distinct from an empty collection. When a structure is null it cannot have attributes; a null structure is distinct from a structure that has null attributes.

Assignment

Assignment, or setting a variable to a value, can be accomplished at initialization (shown above) or via the set command. Variables can be assigned values of their own type, but not values of a different type without casting.

set &i 21 
# &i is now 21

Casting

Numbers can be cast to other numbers, but collections cannot be cast to other collections.

Suppose a float is declared:

float @f 0 

To cast it to an int, let the int command take the variable &f. In this example, an integer is increased by the floor of f:

int @i
give &i (:int &f)


Casting with collections gives an unexpected type error.

Constants

In this line, a variable is set to a constant.

const &i #&i is now a constant

This variable cannot be modified after this point. It will have the same value for the duration of its life, and trying to modify it throws an error. If this variable is a structure with attributes, the attributes are also constants.

Lists, Indexing, and Nesting

Lists are divided into items. Each item in a list has an index. The leftmost index is 0. Each index refers to a variable in the list.

list @b [10 "Hello" 6.28]
# &b[0] is 10, &b[1] is "Hello" 

Lists can contain other lists to arbitrary depth. Indexes can also be nested to arbitrary depth.

list @b [10 [23 32] 34] # &b[1][1] is 32 

Syntactically, indexes are variables and should be interpreted as such. Referring to an index that does not exist gives a bad reference error.

Strings and Escapes

Strings are interpreted literally, as text. A string is framed by quotes and can include white space. A string is always one argument, even one with spaces in it.

For example, consider this line.

out "Hello World &s" 

It outputs Hello World &s to the console. The entire string, including spaces, is one argument, output by the command.

Escape Sequences

These are character codes for representing specific characters inside a string, indicated by a backslash and a letter.

Escapes
Escape Desc.
\n Newline
\r Carriage return
\t Tab
\" Quote
\# Hash
\\ Backslash

Example:

out "Hello\nWorld" 

Output:

Hello
World

Character Access

String characters can be accessed via the indexing syntax

This example prints the first character of a string:

func @main
 string @s "dog"
 out &s[0] #outputs d
return

Identifiers: How to Use the Identifying Operator @

These are used when naming a construct, like a function, label, variable, or struct. This practice enforces readability and prevents abuses of syntax. In earlier versions, untyped words were used for naming, but this was problematic.

Identifying a variable:

int @i 10

Identifying a function:

func @foo (:int @a) #a is an integer.

Identifying a struct:

struct @tree

Identifying a label:

label @foo

The identifying operator (@) is not part of a name, it indicates a name.

Labels and Conditionals

Goto statements change the control flow of a program by moving the program counter from one line to another. In a program with no goto statements or calls, lines are executed from the first to the last in order, top to bottom. These programs cannot perform logic, parse input, or make decisions. The program counter needs to be able to jump in order for computation to be possible.

Goto statements make the program counter jump. Labels are locations to which the program counter jumps. In combination, they can evaluate conditions and execute loops. Finally, note that labels are stored at compile-time, so a goto can accept a label that is declared on a later line.

Conditionals and Loops

A conditional is a statement that can be true or false if a condition is met. When a conditional is true, the program counter goes to the label specified. Otherwise, the program counter increments to the next statement.

Loops begin with a label and end with a goto. They can run until a condition is met, or they can run forever. Count-controlled loops increment a number until it is of a specific value. Sentinel-controlled loops run until a condition is met.

Commands and Examples

  • See the "Conditionals and Loops" section of Commands for information on commands that go to labels.
  • For an example of conditionals with an infinite, sentinel-controlled loop, see Truth Machine in Program Examples.
  • For an example of conditionals with a count-controlled loop, see FizzBuzz in Program Examples.

Logic and Truth Values

Relations

A truth value can be one of two states, true or false; these are also called 1 and 0. Logical relations operate on truth values:

  • NOT inverts true and false.
  • AND takes two truth values and outputs true if both inputs are true. Otherwise, output is false.
  • OR takes two truth values and outputs true if either are true. Otherwise, output is false.
  • XOR (exclusive or) takes two truth values and outputs true if the values are opposite. Otherwise, output is false.

Standard Macros for Truth Values

  • !true equals 1.
  • !false equals 0.
  • !null equals 0.

These macros are part of the standard library and can be used at anytime.

Structs: Defining a Data Type

What is a Structure?

Structures are data structures defined in source.

struct @thing
 int @i
 int @j
 int @k
_struct


Variables in structures, called attributes, are not initialized. They are accessed and modified by functions, such as in the example here:

func @main
 thing @s
 set &s.k 10
 out &s.k
return
#outputs 10


Structures can have attributes of their own type. This is useful for defining data structures. This example defines a binary tree.

struct @binary_tree
 node @root
_struct
 
#node
struct @node
 int @data
 node @left   #child one
 node @right  #child two 
_struct

Initialization

An uninitialized variable of a structure type has no attributes at all, it is not a structure with null attributes, but a variable with no data at all. To initialize this variable, that is, give it data, a set block or list is used:


struct @binary_tree
 node @root
_struct
 
#node
struct @node
 int @data
 node @left   #child one
 node @right  #child two 
_struct
func @main
 set (:binary_tree @t) [0] #evaluates to &t [0] where &t is a tree with a null root
 set &t.root
  int 10
  node &left  0 
  node &right 0 
 _set
return

Notes

  • Note that referring to an attribute of a null structure throws a bad reference error.
  • Note that structures cannot be defined within a function.
  • Note that structure definitions cannot nest.

Functions

All memory in this language is bound to functions (or subroutines, if you prefer that term). Calls to functions are stored in the function tree. The main call is the root of this tree, and it branches each time a call is made.

The Basics: Functions and Calls

Functions let a programmer define their own commands as subprograms, and they can be called to perform a task. A function call executes the sub program defined by a function. Calls cannot run include statements, but they can take input, store information in memory, and output to calling (parent) function calls. After processing include statements, all BunnyBell programs call the main function implicitly. Every function called from the main function is a child of the main function call.

The ‘func’ command starts a function definition. It takes a function name and input arguments for the function. Functions end with a ‘return’ command, which can output to the parent call.

When a function is called, execution stops for the parent call, and resumes when the child call completes execution. The program ends when the main call completes or when a halt command executes.

Specifying Function Input Types

Sometimes a programmer needs to specify what type of arguments their function can take:

Typings
Example Desc.
@foo foo is an identifier. Many commands take these.
:foo foo is a call.
(:t @foo) foo is a value the type specified; t is generic for an int, float, double, long, char, string, list, path, or struct.

Function Calls as Arguments: The Call Passing Operator :

Function calls are first class in this language. They can be assigned to a cell, passed to a function, or returned from a function.

foo :bar #passes what bar returns to foo.
foo (:bar @i 10 20) #bar can take arguments.

Overloading

A function is said to be overloaded when two function definitions share the same name.

func @foo (:string @a)
 out &a
 return
func @foo (:int @a)
 give &a 1
 out &a
 return

The function foo can output a string, or increment an integer by one and output. Overloaded functions can differ by the number of arguments they take and the types of arguments they take. However functions cannot share name, number of arguments, and types of arguments. Here is an example of what not to do.

func @foo
 out "Hi"
 return
func @foo
 out "Bye"
 return

This overloaded function is ambiguous and produces a collision. If foo were called, the output of that call is indeterminate. The two definitions of foo collide.

Returning

A return outputs from a function call to its parent. In the case where a call is an argument of a call or command, the argument is evaluated first. Let &d be 2 in this example.

bneq (:give 3 &d) 0 # -> bneq 5 0

The arrow is not an operation, it demonstrates evaluation.

Referring to Functions

Recall the table of data types. Functions and calls are data types, and they can be referenced with the refer operator. A function reference takes an identifier, and a function (header and body). It is a function definition. These are in a scope such that all functions can reference other functions.

A command called call can run a call passed by reference. (:foo 1 2) is equivalent to:

call &foo [1 2]

Functions as Attributes of Structures

A structure can reference a function as an attribute, shown here:

struct @foo
 func &bar
_struct

Such a structure can accept and run any function it references, in general this is useful for binding data to a function call.

Macros

Macros are substitutions performed by the compiler before runtime.

Use as Constants

Consider this example with no macros:

func @main
 list @b [9 8 7] 
 give &b[0] 10
 give &b[1] 10 
 give &b[2] 10 
return


To swap 10 for 11, three lines need to be changed.

Now consider this example with macros:

macro !foo 11
func @main
 list @b [9 8 7] 
 give &b[0] !foo
 give &b[1] !foo 
 give &b[2] !foo 
return

Only one line needs to be changed. It's nicer.

Blocks

Macros can be multiple lines long, which is great for consolidating repeated code and simplifying source.

Files

Note this excerpt from Commands:

out
     This prints the value given. Standard output is default, 
     but the second argument specifies a file for writing.  
     The file content is not overwritten. Data is appended.
     Args: VALUE, [PATH]
read
    Loads file content into memory; takes a file path and returns a string.
    Args: PATH
ovwr
    Overwrite file (arg2), with content (arg1)
    Args: STRING, PATH
make
    Make a file (0) or directory (1) at the specified path.
    Args: INTEGER, PATH
del
    Delete file or directory (recursively) at path.
    Args: PATH

There actually is not much to say about files. Directories are folders, which contain files, which contain data that can be read into a program, overwritten, or given new data to add to the file somewhere.

Errors

Error Codes

Syntax Error [Command Doc] [Int Range] ::: (1)
File Not Found ::::::::::::::::::::::::::: (2)
End Of File :::::::::::::::::::::::::::::: (3)
Undefined Variable ::::::::::::::::::::::: (5)
Undefined Function ::::::::::::::::::::::: (4)
Undefined Label :::::::::::::::::::::::::: (5)
Conflicting Identifiers :::::::::::::::::: (6)
Conflicting Function Definitions ::::::::: (7)   
Conflicting Labels ::::::::::::::::::::::: (8)
Unexpected Argument Type ::::::::::::::::: (9)
More Arguments Expected ::::::::::::::::: (10)
Missing Main Function ::::::::::::::::::: (11)
Unmatched Parenthesis ::::::::::::::::::: (12)
Unmatched Bracket ::::::::::::::::::::::: (13)
Compiler Error; Check Statements :::::::: (14)
Cannot Compile in Function :::::::::::::: (15) 
Undefined Macro ::::::::::::::::::::::::: (16) 
Runtime Error ::::::::::::::::::::::::::: (17)
Stack Overflow :::::::::::::::::::::::::: (18)
Out of Memory ::::::::::::::::::::::::::: (19)
Bad Argument :::::::::::::::::::::::::::: (20)
Bad Reference ::::::::::::::::::::::::::: (21)
Cannot Modify Constant :::::::::::::::::: (22)
Cannot Nest Blocks :::::::::::::::::::::: (23)
Error ::::::::::::::::::::::::::::::::::: (24)

To clarify the use of adjectives: Undefined means that the error is thrown by an argument that requires a definition in source. Conflict occurs when the next command to execute cannot be determined because of a shared name or header. Bad means that the erroneous code is incomputable, like a negative wait time, or a reference to data that is not there.

Whenever possible, an error should describe the function and instruction at which it occurred. A trace of calls should be printed whenever an error is thrown; each call should list all of its arguments on one line if applicable.

Whenever it is applicable, a doc string should be printed describing the syntax of the erroneous command. If this command is defined by the user, the function source will be printed

Example of Call Trace

foo 213
bar
main

Example of Command Doc String

bell 
     Don’t you know? A bell goes ding! 
     It makes sound!
     Args: [HERTZ, [DURATION IN MILLIS]]

Example of Function Print

func @cat
   out "Feed the cat\n"
   out (:input 2)
   doc "This function asks for input and prints that."
return

Writing a Doc String

The doc command can accept a string or a block of text. A one line doc string is shown above, but a block of text does not require quotes at all. All text in this block is literal. The interpreter skips this block entirely.

doc
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
_doc 


func @main
 out "Hello World" 
 return 

Throwing, Catching, and Handling Errors

Errors can be caught and handled inside handle blocks. A handle block catches an error and executes a goto statement. Errors can be thrown by passing an error code to the error command. Like all block statements, handles cannot nest. Here is an excerpt from the Commands section:

handle 
     Go to the label if any of the listed errors occurs in a handle block.
     Args: LABEL, LIST OF ERRCODES
_handle
     Ends the handle block above.
error 
     Throws the error given.
     Args: INTEGER.

Commands

(Note that arguments listed in [brackets] are optional)

Functions

func
     Defines a function with or without arguments
     Args: IDENTIFIER, [FUNCTION ARGUMENTS]
func
     Accepts a reference to a function, typically as a struct attribute.
     Args: FUNCTION REFERENCE (A VALUE)
return
     Ends a function and returns arguments given.
     If there is nothing to return, 0 is returned.
     Args: [VALUE]
_func
     Equivalent to a return command. 
     Args: [VALUE]
call
     Runs a call when given a function reference.
     Args: FUNCTION REFERENCE [LIST OF ARGUMENTS]
doc
    This command defines a doc string for a function.
    Doc strings explains what a function does and how 
    it should be used.
    The doc string starts on the next line and
    execution skips to the end of the doc string.
_doc
    End doc string; resume execution.

Input and Output

out
     This prints the value given. Standard output is default, 
     but the second argument specifies a file for writing.  
     The file content is not overwritten. Data is appended.
     Args: VALUE, [PATH]
input
     Takes input of the data type given 
     (with the exclusion of lists and structs).
     Args: STRING
read
    Loads file into memory; takes a file path and returns a string.
    Args: PATH
ovwr
    Overwrite file (arg2), with content (arg1)
    Args: STRING, PATH
bell 
     Don’t you know? A bell goes ding! 
     It makes sound!
     Args: [HERTZ, [DURATION IN MILLIS]]

Conditionals and Loops

label
     A label to go to. These are not global, 
     instead they are defined locally in a function. 
     Args: IDENTIFIER
beq
     Branch to LABEL if args are equal.
     Args: NUMBER, NUMBER, LABEL  
bneq 
     Branch to LABEL if args are not equal.
     Args: NUMBER, NUMBER, LABEL 
bgr 
     Branch to LABEL 
     if arg1 is greater than arg2.
     Args: NUMBER, NUMBER, LABEL    
bleq 
     Branch to LABEL 
     if arg1 is less than or equal to arg2.
     Args: NUMBER, NUMBER, LABEL   
goto
     Go to label specified
     Args: LABEL

Relational Operators

eq
     Return 1 if args are equal.
     Otherwise return 0.
     Args: NUMBER, NUMBER
neq 
     Return 1 if args are not equal.
     Otherwise return 0.
     Args: NUMBER, NUMBER
gr 
     Return 1 if arg1 is greater than arg2. 
     Otherwise return 0.
     Args: NUMBER, NUMBER
leq 
     Return 1 if arg1 is less than or equal to arg2. 
     Otherwise return 0.
     Args: NUMBER, NUMBER
not  
     Return 1 if given 0.
     Otherwise return 1.
     Args: NUMBER
and
     Return 1 if both args are 1. Otherwise return 0.
     Args: NUMBER, NUMBER
or
     Return 1 if either arg is 1. Otherwise return 0.
     Args: NUMBER, NUMBER
xor
     Return 1 if args are opposite each other. Otherwise return 0.
     Args: NUMBER, NUMBER

Data

var 
     Initializes a variable of the given type. 
     Note that var is a genericized placeholder for a data type.
     Pass it a value to initialize the variable.
     Args: IDENTIFIER, [VALUE]   
var
     To cast, pass a variable.
     Args: VAR
set
     Set variable to value.
     Args: VAR, VALUE
set
     Takes a variable that is a structure, 
     and sets attributes in a list.
     Args: VAR, LIST
set
     Takes a variable that is a structure, 
     and sets attributes in a block.
     Args: VAR
_set
     End set block.
const
     The specified variable is now a constant.
millis 
     Get the time since midnight of January 1, 1970, 
     in milliseconds. Returns a long.

Numeric Modifiers

give
     The first argument is set to the sum of itself and the second argument.
     Args: VAR, NUMBER
take
     The first argument is set to the difference of itself and the second argument.
     Args: VAR, NUMBER

Error Handling

handle 
     Go to the label if any of the listed errors occurs in a handle block.
     Args: LABEL, LIST OF ERRCODES
_handle
     Ends the handle block above.
error 
     Throws the error given.
     Args: INTEGER.

File Management

For File I/O see Input and Output

make
    Make a file (0) or directory (1) at the specified path.
    Args: INTEGER, PATH
del
    Delete file or directory (recursively) at path.
    Args: PATH

Program Interrupters (Excluding input)

wait
     Wait for a number of milliseconds 
     (1000 if not specified).
     Args: [TIME TO WAIT IN MILLIS]
halt   
     End program.

Compiled

struct 
     Defines a data type as a block of variables.
     Args: IDENTIFIER
_struct 
     Ends a struct.
include
     Include contents of specified file.
     or directory
     Args: PATH
macro
     These are substitutions handled by the linker.
     Argument one is the macro name
     which always start with a bang (!).
     !arg1 is replaced with arg2 at all points in source.
     Args: MACRO_NAME, REPLACEMENT
_macro
     If the second arg of a macro is a semi-colon, the macro can continue over multiple lines. 
     This command ends the macro.
impl 
     Short for implements, this is a command designed for interpreter extensibility.
     It takes a path to a .bbim file in the extensions directory. 
     This enables the interpreter to interpret commands defined in .bbim files.

Running a BunnyBell Program

The interpreter runs in a shell and takes command line arguments:

bbe file_to_run.bbe inputs_for_main_func -flag
  • inputs_for_main_func is a set of arguments for the main function, separated by spaces.
  • The -flag sets the interpreter to mute, debug, or both.

Flagging

The flag can be in four states

 -m mute the bell
 -d debugger on
-dm mute the bell and debugger on
-md mute the bell and debugger on

Muting

When mute, the bell should not make sound and it should not output text. It is a no-op.

The Debugger

Every BunnyBell interpreter should have a debugger that prints the following at each step:

  • The current function call, with arguments.
  • The current instruction in full.
  • Value stored at the last variable accessed.
  • Any program output at that step.

Example Debug Output:

Current Function: messages
Current Instruction: out "Here are your messages."
Last Variable Modified: &v
Variable State: 9
Output:
Here are your messages.

Inputs

When a program is waiting for user input it should output a right angle bracket and a space:

> Lorem Ipsum

The Linker: Include Commands

How to use Includes

The linker formats source code so that it can run appropriately. All leading whitespace before an instruction is removed. Additionally, all include commands are processed. The contents of each included file are compiled for use.

Note that source files are run from a run directory. Here are examples of include commands:

include run\foo\bar\hello.bbe
include ->dog\cat\*

As explained in Useful Operations, the right arrow is substituted with the path from run to the current file, and star includes the entire contents of a directory.

Using a Library

Libraries are directories that contain source files. These should be stored in a directory called lib. This directory should be stored in the same location as run.

Example:

include lib\Math.bbe

On Cyclic File References

TL;DR detecting these is an operationally expensive way to do a whole lot of nothing. If a programmer has one of these by mistake, they probably also have an infinite recursion anyway, and they will get a stack trace that alerts them to the bug.

Consider this example:

  1. File A.bbe has the statement include B.bbe
  2. File B.bbe has the statement include A.bbe

These are generally difficult to handle efficiently. While they can be detected by generating a graph of linked files, and checking for a Hamiltonian cycle. This would be done in polynomial time and slow the interpreter down. Even internationally recognized, professionally implemented languages, like C and Python, are often not implemented in ways that explicitly deal with cyclic references. Explicit handling of cyclic include references is, in general, too costly to bother with.

The more common, and simpler case, is infinite recursions on the call stack. These are easily demonstrated via tracing and printing the call stack in linear time. Such a case is relevant to cyclic file references because files linked in a cycle are likely to make function calls that produce an infinite recursion. Ie, file A calls a function in file B. Then file B calls a function in file A. In general, if there is an unintentional cyclic file reference, it is probably going to be discovered by means of an infinite recursion anyway, so having an explicit way to detect cycling files is not necessary.

Libraries

The following libraries are included in a BunnyBell interpreter.

Standard

This is the library that is always included by default. It describes all standard commands and macros.

Math

Ops

This library has functions that operate on two numbers. Each returns an integer.

add
   adds stuff
sub
   subtracts stuff
mul
   multiplies stuff
div
   integer division
mod
   modulo 
pow
   raise arg1 to arg2
root
   takes the arg2 root of arg1
random
   returns a random value between 0 and 1
randint 
   returns a random integer between arg1 and arg2 inclusive.
floor
   returns the floor of a float or double as an int.
ceiling
   returns the ceiling of a float or double as an int.
abs
   returns the absolute value of a number.
sin
   returns the sine of a number.
cos
   returns the cosine of a number.
tan
   returns the tangent of a number.

Consts

pi 
   returns pi as a double 
e
   returns e as a double 
phi
   returns phi as a double

Data

Collect

Operations on collections.

size
   Get the number of items in a collection.
   Args: COLLECTION
   Ret:  INTEGER
count
   Counts the occurrences of a value.
   Args: COLLECTION, VALUE 
   Ret:  INTEGER
cat
   Concatenates strings into one string. 
   Args: STRING, STRING
   Ret:  STRING
cat
   Concatenates lists into one list. 
   Args: list, list
   Ret:  list
make_empty_list
   Makes an empty list (of null data) of a given size.
   Args: INTEGER
   Ret:  list
   Err: Bad Argument if Negative integer
append 
  To use with a list. 
  The list size grows by 1 and the item becomes the last index in the list.
  Args: list, VALUE
remove 
  Remove the index given from the list.
  Args: list, VAR
  Err: If the index is not found, that’s an undefined var.
insert
  Insert item at index of list.
  Args: list, INDEX, VALUE
  Err: Not a list or index

Consts

maxInt
  returns maximum integer value
minInt
  returns minimum integer value
maxLong
  returns maximum long value
minLong
  returns minimum long value
maxFloat
  returns maximum float value
minFloat
  returns minimum float value
maxDouble
  returns maximum double value
minDouble
  returns minimum double value

Compare

This is a library for comparing collections. Collections are converted to integers and evaluated.

How collections are compared:

  • Strings are a sum of characters.
  • Lists are a sum of items.
  • Structs are a sum of attributes.
  • Functions are always 0, as they are not generally comparable.

Two sums A and B, are calculated then B - A is returned. Equal values return 0. B > A returns a positive value. A < B returns a negative value.

compare
  Compares two strings.
compare
  Compare two lists recursively. 
compare
  Compare two structs by attributes recursively. 

The convention for naming library files is to write the name of the library and bbe. The Collect file is Collect.bbe in the directory lib\Data

Proof of Turing Completeness

Proof by Translation to bf

It is trivial to define a function that can simulate a bf interpreter. Bf stores memory as cells on a tape. Cells are modified by a movable pointer, exactly like a Turing machine. A list of characters is sufficient to implement the tape.

func @bf
 list @tape (:make_empty_list 30000)
 int @pointer 0 #points to a char

##
translated bf goes here ##
return
Translation Table
bf BunnyBell
> give &pointer 1;
< take &pointer 1;
+ give &tape[&pointer] 1;
- give &tape[&pointer] -1;
. out &tape[&pointer];
, input &tape[&pointer] "char";
[ beq &tape[&pointer] 0 @closeBracket; label @openBracket;
] bneq &tape[&pointer] 0 @openBracket; label @closeBracket;

(The semicolon is an explicit newline.)

Given bf is Turing complete, and Bunnybell can simulate a bf interpreter. Bunnybell must also be Turing complete. QED

Syntax Highlighting

Syntax highlighting is a practice to improve readability by coloring tokens. Keep in mind that a readable source code is best achieved in a language with appropriate syntax. Readable code does not require highlights at all.

As a general rule of thumb: tokens that require specialized parsers have hue, and tokens are otherwise black or white. The color scheme below is not a mandate, but an attempt.

Colors

  • ERRORS range from red to orange; if preferred, black or white is also acceptable.
  • IDENTIFIERS range from red to yellow
  • VARIABLES and NUMBERS range from yellow to blue
  • MACROS range from green to blue
  • STRINGS and DOC STRINGS range from blue to purple.
  • CALLS and COMMANDS range from purple to pink.
  • COMMENTS are gray.
  • PARENTHESES, BRACKETS, SEMI-COLONS, DOTS, LABELS, and PATHS are black or white.
  • BLACK is hex code #031930
  • WHITE is hex code #fce6cf

This scheme should be modified as needed for colorblindness, theme, mode, or whim. Two colorblind adapted palettes are available. Note that a dark reader may interfere with the rendering of palettes. Also note that as implied in the palettes, the operators are highlighted according to token color.

General Palettes

Lakeside palette:

error
@identifier
number
&variable
!macro
"string"
command
:call
#comment
()[];.
label
path
error

Forest palette:

error
@identifier
number
&variable
!macro
"string"
command
:call
#comment
()[];.
label
path
error

Plateau palette (This is used in documentation for light and dark reading.)

error
@identifier
number
&variable
!macro
"string"
command
:call
#comment
()[];.
label
path
error

Adapted Palettes

IBM Design Library:

error
@identifier
number
&variable
!macro
"string"
command
:call
#comment
()[];.
label
path
error

Wong, Points of View: Color Blindness. Nature Methods:

error
@identifier
number
&variable
!macro
"string"
command
:call
#comment
()[];.
label
path
error

Extensibility

The interpreter should be implemented such that it can accept command modules, such as .bbim files that can define additional commands for the interpreter. These files would be located in a directory in bbe called ext. These extended commands would be implemented in source via a compiler command. impl. These .bbim are called extensions.

Defining Commands

Files would describe commands with command blocks. The command takes an identifier as a name such as comm @foo. The body of the command would run a run command that one, takes a path to a file that is written in the same language as the Bunnybell interpreter, such that it can be run via the language that implements Bunnybell; and two, takes a reference (variable) that describes a function in the file described by argument one.

The file described by the run command (let's call it A) would be a file that includes necessary resources, and wraps the code that describes the extended command in a function. The .bbim file would be interpreted such that when the commands defined in the .bbim are executed by the Bunnybell interpreter, the relevant function in file A is run for each command.

Here is an example:

comm @foo
 run path_to_A &function_in_A
_comm

Defining Errors

To throw an error, simply have the run command return the error code needed. 0 indicates that there is no error. Codes 1 to 24 are error codes. 25 and above are equivalent to 24. Negative values are interpreted as absolute values, such that the positive error code is returned.

Implementation

By Specification

Bunnybell makes use of a compiler and an interpreter to run programs. Compiling happens at compile time. Then interpreting happens at run time. All compiled statements are processed outside of a function, like includes, structs, and macros. All interpreted statements are executed inside of a function.

The compiler does the following:

  • Uses a linker to read all included files into memory and concatenates them together for interpretation.
  • Processes all macros and performs replacements in memory.
  • Analyzes all structs and generates the associated data types for use in interpretation.
  • Analyzes all functions, storing labels, and binding calls to their respective functions.
  • Parses the source code with lexical and syntactic analysis.
  • Throws any relevant errors.

The interpreter does the following:

  • Executes compiled code starting at the main function.
  • Executes commands in order, until the program completes.
  • Throws an error if needed, and stops execution.
  • Outputs information for debugging.

By Translation

An alternative is to convert BunnyBell source into source for an implemented language, such as a C style language, and then running that code. This may or may not require that a BunnyBell compiler run first, depending on preference.

Minibell

This is a minimized dialect of BunnyBell featuring only the essential commands, errors and operations for computation. Implementing this dialect is the first step to a successful implementation of BunnyBell.

Commands:

  • func
  • return
  • char
  • give
  • take
  • beq
  • label
  • out

Operations:

  • Refer
  • Call Passing
  • Identifying
  • Parentheses (Open and Close)
  • Newline

Errors:

  • Error
  • Missing Main
  • Unmatched Parenthesis
  • Nested Function
  • Unexpected Argument

Test Cases

Parser Tests

  • Test each type of comment.
  • Test strings and docs strings.
  • Test for parenthesis and brackets
  • Test each operation.

Command Tests

Note that each command should be tested with no arguments, expected arguments, and unexpected arguments.

Error Tests

Each error should be tested.

Library Tests

Each function in the library described should be tested.

Function Tests

  • One (non-main) function, one call, no arguments. (Simplest Case)
  • One (non-main) function, one call, with value arguments.
  • One (non-main) function, one call, with variable arguments.
  • One (non-main) function, one call, with functions as arguments.
  • One (non-main) function, one call, with mixed type arguments.
  • One (non-main) function, one call, with unexpected arguments.
  • One overloaded (non-main) function, one call, with arguments.
  • One overloaded (non-main) function, multiple calls, with arguments.
  • One overloaded (non-main) function, multiple calls, with arguments, such that each call runs the same subroutine with different inputs.

Compiler Tests

  • Macro definition test.
  • Structure definition and use test.
  • Include one file in same directory.
  • Include one file in another directory.
  • Include file in subdirectory.
  • Include multiple files from various directories.
  • Include entire directory.
  • Include files in file to be included (accomplished via the right arrow >).
  • Circular include reference.

Index

Much of the stuff here is explained in the docs or rigorously defined elsewhere. This is not a textbook and the definitions here are for those interested in getting to the point.

Algorithm

A procedure to do something or make a decision.

ASCII Table

A table in which textual characters are assigned to a number. It was specifically designed for teleprinters in the 1960s. A teleprinter is essentially a typewriter that can be operated remotely. So, the ASCII table features a lot of specific control signals for operating the recipient's teleprinter. (7 rings a bell, which is fun.) The ASCII table is essentially the ancestor of all text encodings. Unless you are using some kind of esoteric keyboard, the computer you are using supports ASCII (including all of those old teleprinter signals).

Assignment

Sets a variable to a value. If x is assigned 5 , then x is now 5.

Arguments

These are not the emotional kind, or the kind of an interlocutor. They are inputs for a command or function.

Array

This is an ordered grouping of items. A list of stuff, usually contiguous in memory. lists are arrays.

Bell

This goes ding or beep. If you find yourself running a program for hours, you can use these to alert you to the program’s status while you are doing laundry.

If you prefer to run such a program while you sleep, you can mute the bell. Just know that if you ignore the computer for too long, it may crash out of spite, and you will wake up to a friendly error message, and you have to plan your evening around the needs of an old computer, and you will question your life choices.

Bf

A polite name for a language created by Urban Müller in 1993. This language is designed to be the simplest language possible, such that it has the smallest compiler possible. It runs a Turing machine on a tape of bytes and is proven to be Turing complete.

Bit

The fundamental unit of information. A bit can be in one of two states. You can think of it as a lightbulb. A bit can be on or off, 0 or 1, true or false. A contiguous set of bits is a word. A word of 2 bits has four states. 3 bits have 8 states. One more bit increases the number of states to the next power of two.

Bug

This is the chaotic code that broke your metaphoric vase. You probably wrote it to do something, but you made a mistake and now it does something else. On the origin of the term, I offer this quote from the biography of Rear Admiral Grace Murray Hopper:

"In 1946, when Hopper was released from active duty, she joined the Harvard Faculty at the Computation Laboratory where she continued her work on the Mark II and Mark III. She traced an error in the Mark II to a moth trapped in a relay, coining the term bug. This bug was carefully removed and taped to the log book. [Needless cruelty!] Stemming from the first bug, today we call errors or glitch's in a program a bug."

One day in 1946 they found a literal bug inside some very large, punch-card monstrosity of a machine. The bug crawled in looking for warmth and a cozy shelter, which is understandable. The bug was a bug, and it could not comprehend the consequences of its actions. It was making the computer do strange things. The computer needed debugging, hence the origin of a new term in the field. Hopefully, the bug was freed from its tape prison to go about its bug life uninterrupted.

Byte

8 bits. Range of 0 to 255 (unsigned).

Call

This runs a function with inputs. These can have parents and children. A parent call calls a child call.

Child

Opposite of a parent. This call finishes execution within parent scope.

Command

This is what you use to tell the computer to do a thing.

Comment

This is for people to read; it clarifies. The interpreter cannot read these. As a rule, it is better to explain why the code is written, not what it does, when writing a comment.

Compiler

In computation, a compiler is a program that reads source code and converts it into a form that can be interpreted.

Console

This runs programs, takes input, and displays output.

Constant

Data that cannot change state over time.

Directory

A folder. It stores files.

EBNF

This is Extended Backus-Naur Form. It is a meta-syntax that describes the syntax of programming languages.

Error

Errors are the seatbelt that keeps your computer safe. In this analogy, your program is a car and your computer is the driver. Errors protect your computer when your program does something that is unexpected. Errors are distinguished by error codes.

Esolang

An esolang is an esoteric programming language. These are usually designed by hobbyists to experiment with concepts in programming and computation. BunnyBell is the least esoteric programming language in the history of the esolangs wiki, and it is documented there out of convenience. If that upsets you, perhaps you should consider if being upset over such trivial things is contributing positively to your life.

File

A happy little home for data.

First Class

Something is considered first class when it has the following properties:

  • It can be assigned or stored in program memory (RAM).
  • It can be passed to a function.
  • It can be returned from a function.

If these three criteria are not satisfied, the construct is second-class.

Function

A program that is run by another program. A subprogram. Takes input and returns output. The function is defined as source code.

Heap

This is a data structure that behaves like a large pile of legos. You can use the legos to build houses and spaceships and whatever else you like. (Just make sure they go back in the box when you are done.) In this language, the legos are variables, and the return command cleans everything up.

Initialization

Uninitialized variables are null. The first time a variable is assigned data, the variable undergoes initialization.

Input

Data to be processed by a program.

Interpreter

Interprets and executes source code line by line.

Library

A library is a set of files that contain useful functions for programmers.

Nesting

Putting a thing inside another thing. It has nothing to do with the 2011 movie Inception.

Null

This means "no data here". 0 is equivalent to null in ASCII.

Output

Data generated by a program.

Overloading

A function is said to be overloaded when there are two or more function definitions of the same name, but differing arguments. The number of arguments may be different, their typing may be different, or the number and typing of the arguments may vary between definitions.

Parent

Calls the current call, when the current call is finished execution returns to the parent.

Program

A to-do list for a computer. Completing the list is called running or execution.

Program Counter

This keeps track of what to do next. It will keep incrementing after each statement, until the program ends.

Source Code

This is what programs are written in.

Stack

Think of a stack of pancakes, which is what I call the yummy, pan-fried, tasties, though you might call them flapjacks or hot cakes. Pancakes get on the stack from the top, and the ones at the bottom get eaten last.

Sadly, function calls are not pancakes, the last call on the stack creates the next call to go on the stack.

Stack Overflow

Have you ever tried to make an endless stack of pancakes? You use up all the batter and the stack topples over. This error is thrown before toppling happens. It stops someone from creating a stack with an irresponsible amount of pancakes.

Syntax

This tells you how to read and write stuff in a language.

Token

A token is the shortest portion of source an interpreter can understand.

Truth Value

A value that is true or false.

Turing Complete

Something that is Turing complete is capable of doing any solvable math problem or deciding any decidable problem. It can run any algorithm that is runnable. Anything Turing complete can simulate anything else that is Turing complete.

Turing Machine

A theoretical device that can be programmed to read and write on an endless tape. The tape is divided into cells that can store one bit. The machine is capable of solving any solvable problem and is foundational in computing. It was invented by Alan Turing.

Variable

Data that can change state over time.

ASCII Table

On mobile phones, many binary values listed here will be interpreted as phone numbers. This gives them a blue, clickable link.

Binary Decimal Hex Character
0000000 0 00 NULL
0000001 1 01 Start of Heading
0000010 2 02 Start of Text
0000011 3 03 End of Text
0000100 4 04 End of Transmission
0000101 5 05 Enquiry
0000110 6 06 Acknowledgement
0000111 7 07 Bell
0001000 8 08 Backspace
0001001 9 09 Horizontal Tab
0001010 10 0A New Line
0001011 11 0B Vertical Tab
0001100 12 0C Form Feed
0001101 13 0D Carriage Return
0001110 14 0E Shift Out
0001111 15 0F Shift In
0010000 16 10 Data Link Escape
0010001 17 11 Device Control 1
0010010 18 12 Device Control 2
0010011 19 13 Device Control 3
0010100 20 14 Device Control 4
0010101 21 15 Negative Acknowledgement
0010110 22 16 Synchronous Idle
0010111 23 17 End of Transmission Block
0011000 24 18 Cancel
0011001 25 19 End of Medium
0011010 26 1A Substitute
0011011 27 1B Escape
0011100 28 1C File Separator
0011101 29 1D Group Separator
0011110 30 1E Record Separator
0011111 31 1F Unit Separator
00100000 32 20 Space
00100001 33 21 !
00100010 34 22 "
00100011 35 23 #
00100100 36 24 $
00100101 37 25 %
00100110 38 26 &
00100111 39 27 '
00101000 40 28 (
00101001 41 29 )
00101010 42 2A *
00101011 43 2B +
00101100 44 2C ,
00101101 45 2D -
00101110 46 2E .
00101111 47 2F /
00110000 48 30 0
00110001 49 31 1
00110010 50 32 2
00110011 51 33 3
00110100 52 34 4
00110101 53 35 5
00110110 54 36 6
00110111 55 37 7
00111000 56 38 8
00111001 57 39 9
00111010 58 3A :
00111011 59 3B ;
00111100 60 3C <
00111101 61 3D =
00111110 62 3E >
00111111 63 3F ?
01000000 64 40 @
01000001 65 41 A
01000010 66 42 B
01000011 67 43 C
01000100 68 44 D
01000101 69 45 E
01000110 70 46 F
01000111 71 47 G
01001000 72 48 H
01001001 73 49 I
01001010 74 4A J
01001011 75 4B K
01001100 76 4C L
01001101 77 4D M
01001110 78 4E N
01001111 79 4F O
01010000 80 50 P
01010001 81 51 Q
01010010 82 52 R
01010011 83 53 S
01010100 84 54 T
01010101 85 55 U
01010110 86 56 V
01010111 87 57 W
01011000 88 58 X
01011001 89 59 Y
01011010 90 5A Z
01011011 91 5B [
01011100 92 5C \
01011101 93 5D ]
01011110 94 5E ^
01011111 95 5F _
01100000 96 60 `
01100001 97 61 a
01100010 98 62 b
01100011 99 63 c
01100100 100 64 d
01100101 101 65 e
01100110 102 66 f
01100111 103 67 g
01101000 104 68 h
01101001 105 69 i
01101010 106 6A j
01101011 107 6B k
01101100 108 6C l
01101101 109 6D m
01101110 110 6E n
01101111 111 6F o
01110000 112 70 p
01110001 113 71 q
01110010 114 72 r
01110011 115 73 s
01110100 116 74 t
01110101 117 75 u
01110110 118 76 v
01110111 119 77 w
01111000 120 78 x
01111001 121 79 y
01111010 122 7A z
01111011 123 7B {
01111100 124 7C |
01111101 125 7D }
01111110 126 7E ~
01111111 127 7F Delete

Via the esolangs wiki.

Sources

@atelierbram, Syntax Highlighting Color Schemes: Atelier Schemes. (n.d.) Retrieved July 16, 2022 from 
https://atelierbram.github.io/syntax-highlighting/atelier-schemes/
-
ASCII table. ASCII Table - ASCII Character Codes, HTML, Octal, Hex,
Decimal. (n.d.). Retrieved January 16, 2022, 
from https://www.asciitable.com/
-
Bf. Esolang. (n.d.). Retrieved January 16, 2022, 
from https://esolangs.org/wiki/Bf
(Swear word removed.)
-
BunnyBell. Esolang. (n.d.). Retrieved June 4, 2022, 
https://esolangs.org/wiki/BunnyBell_Documentation
-
Contributor, T. T. (2020, December 16). What is Logic Gate (and, or, XOR, not, Nand, nor and xnor)? 
A definition from whatis.com. WhatIs.com. 
Retrieved September 4, 2022, from https://www.techtarget.com/whatis/definition/logic-gate-AND-OR-XOR-NOT-NAND-NOR-and-XNOR 
-
Davis, S. A. (n.d.). Rear Admiral Grace Murray Hopper.  Retrieved June 12, 2022, from 
https://ei.cs.vt.edu/~history/Hopper.Danis.html 
-
Encyclopedia Britannica, inc. (n.d.). ASCII. Encyclopedia Britannica. Retrieved June 3, 2022, from 
https://www.britannica.com/topic/ASCII
-
Java Data Types. Java data types. (n.d.). Retrieved June 3, 2022, from https://www.w3schools.com/java/java_data_types.asp
-
Nichols Davis, Coloring for Colorblindness. (n.d.) 
Retrieved July 16, 2022 from https://davidmathlogic.com/colorblind/#%23D81B60-%231E88E5-%23FFC107-%23004D40
-
Truth-machine. Esolang. (n.d.). Retrieved February 2, 2022, 
from https://esolangs.org/wiki/Truth-machine
-
Wikimedia Foundation. (2021, December 25). Extended backus-naur form. 
Wikipedia. Retrieved January 17, 2022, 
from https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form#Table_of_Symbols
-
Wikimedia Foundation. (2022, January 4). Turing completeness. Wikipedia. 
Retrieved January 16, 2022, 
from https://en.wikipedia.org/wiki/Turing_completeness
-
Wikimedia Foundation. (2022, January 14). Turing machine. Wikipedia. 
Retrieved January 16, 2022, 
from https://en.wikipedia.org/wiki/Turing_machine

External Links

Homepage

Language Specification on GitHub

Notes

A possible method of implementation is to convert BunnyBell to C. This would simplify extensibility; commands could be defined in C. No interpreter would be used, instead, a compiler is used. Pros: no interpreter. Cons: user needs a C compiler. Java is another option. Then BunnyBell would run on the JVM. Although C++ is better, it has gotos. C sharp could be an option, because it can dynamically compile.


To start transpiling a minimized form of bbe can be defined (minibell) in functions each get a tape of cells, allowing for defining cells, allocating cells, io, performing addition and subtraction, and set a cell to a cell.

  • if statements?
  • functions to evaluate a condition (eq returns one or zero)
  • else?
  • for and while loops?
  • true, false, and null values (as numbers)

The LLVM/ELVM tool chain is also useful.

How would the language change if nesting blocks was allowed?