WhatLang
WhatLang is a stack-based programming language created by User:YufangTSTSU (Yufang). Written in TypeScript, its first interpreter can be installed as a private (not published on npm) plugin for Koishi, a bot framework for QQ and other instant messaging platforms, and used by invoking the bot command whatlang
or simply sending the code prefixed with a '¿
'. Currently there exist web ports of the WhatLang interpreter that can be used in a browser; however, these ports do not include Koishi related functions, and currently do not provide other means of reading input from the user yet.
Mechanics
WhatLang has four types of values: String, Number (64-bit float including NaN), Array, and Undefined. If two occurences of Strings have the same content, they are the same String. Arrays are mutable, have a variable length, and can hold mixed types of values.
The virtual machine has an internal stack of stacks, known as the Frame Stack. The topmost stack on the Frame Stack, aka. the Stack, is the one that your code usually interacts with. Values are pushed to and read / popped from the top of the Stack. When using the [
or |
instruction to create / edit an Array, the Array is pushed onto the Frame Stack, temporarily becoming the Stack; upon executing the ]
instruction, it is popped from the Frame Stack and pushed back onto the previous Stack as an Array. The conversion from an Array to a Stack, or vice versa, does not make it a new object, which means that if you duplicate the reference to an Array, edit it with | ]
, changes are reflected in other references to the Array.
The virtual machine also has an internal dict which stores named variables.
In the Koishi runtime, there also exists an internal Output Stack, which can hold fragments of text, attachments, and message reply (quote) references. Each time something is printed, it is pushed onto the Output Stack. send@
, sends@
or sendsto@
can then be used to pop items from the Output Stack and send them immediately. When the program ends, all remaining items on the Output Stack are sent in the current channel, in the order they were pushed.
For the purpose of this documentation:
- Popping from the Stack when it is already empty yields Undefined.
- When popping multiple values, they are listed from bottom to top.
- To return something means to push it onto the Stack.
- To execute a String func with a value argument, create a shallow copy of the current Stack called tempstack; push argument and func onto it, and run the
@
instruction on it. Then, the Result of execution is the topmost value in tempstack, or Undefined if it is now empty. - If something "must" satisfy a certain constraint, the interpreter's behavior is undefined when it does not.
Instructions
Instruction | Name | Description |
---|---|---|
0
|
Zero | Returns 0. |
A 1-9 digit followed by zero or more 0-9 digits | Positive integer literal | Returns the literal Number. |
An ASCII letter, followed by zero or more ASCII alphanumerics and/or underscores | Identifier string literal | Returns the identifier as a String, converted to lower case. |
' followed by one UTF-16 unit
|
Char literal | Returns the char as a 1-length String. |
" delimited text
|
Quoted string literal | Returns the text as a String.
Line feeds and character tabulations can be escaped as |
` delimited text
|
Literal print | Similar to a " delimited string, but prints the string without doing anything with the Stack.
|
+
|
Add or string concatenation | Pops a and b from the Stack and returns their sum.
a and b must not be Arrays. If one of a and b is a String, the other must not be Undefined; it is coerced into a String, and the result is the two Strings concatenated into one. Otherwise, if at least one of a and b is Undefined or NaN, the result is NaN; otherwise the operands are added numerically. |
- * / %
|
Other arithmatic operations | Pops a and b from the Stack, coerced into Numbers, and returns a <operator> b. |
?
|
Compare | Pops a and b from the Stack. If they are loosely equal (== in JavaScript; two occurences of Arrays need to be the same Array to be considered equal), returns 0; if a is greater than b, returns 1; if a is less than b, returns -1; otherwise, returns NaN.
|
~
|
Logical not | Pops 1 value from the Stack. If it is the empty String, 0 or Undefined[1], returns 1; otherwise returns 0. |
[
|
New Stack | Starts a new empty Stack. |
|
|
Open Stack | Pops 1 value from the Stack: an error is thrown if it is not an Array; otherwise makes it the Stack. |
]
|
Close Stack | Turns the current Stack into an Array, goes back to the previous Stack and returns the Array.
If already at the initial Stack, the behavior is undefined. |
() delimited text (may include nested parens)
|
Parenthesized string literal | Returns the literal contents of the parens as a String. |
.
|
Prints the element at the top of the Stack without popping it.
If the stack is empty, ' | |
\
|
Swap | Swaps the topmost two elements in the Stack.
Does nothing if the Stack contains less than 2 values. |
:
|
Duplicate | Pushes the Stack's topmost element onto the Stack again.
Does not copy Arrays; a reference to the same Array is pushed. Does nothing if the Stack is empty. |
&
|
Bury | Pops 1 value from the Stack, then inserts it at the bottom of the Stack.
Does nothing if the Stack is empty. |
_
|
Pop | Pops and discards the topmost value from the Stack. |
=
|
Set named variable | Pops 1 value from the Stack, which must be a String. Sets the value of the variable named this string to the topmost remaining element of the Stack, or Undefined if there is none left. |
^
|
Get named variable | Pops 1 value from the Stack, which must be a String. If a variable named this string exists, returns its value. Otherwise, if the string is the name of a builtin function, returns the string plus "@" . Otherwise, returns Undefined.
|
@
|
Call or eval | Pops 1 value from the Stack, which must be a String. If the string is the name of a builtin function, calls the function. Otherwise, if the string is the name of an existing variable[2], executes the value as WhatLang code (the value must be a String). Otherwise, executes the string as WhatLang code. |
>
|
Gather | Pops n from the Stack, coerced into an integer. Removes the topmost n elements from the Stack, then returns a new Array containing the removed elements.
If n is not zero or negative, gathers all but the bottommost |n| elements instead. Inf and -Inf are treated like 0. |
<
|
Spread | Pops 1 value from the Stack, coerced into an Array. Pushes each of its elements onto the Stack. |
{
|
While loop start | Pops 1 value from the Stack. If it is the empty String, 0 or Undefined[1], jumps to the corresponding } .
|
}
|
While loop end | Pops 1 value from the Stack. If it is not the empty String, 0 or Undefined[1], jumps to the corresponding { .
|
One or more consecutive ! s
|
Break, return or halt | If inside of a {} loop and the number of ! s is less than or equal to the current level of nested braces, breaks out of [number of ! s] levels of loops and continues after the corresponding } . Otherwise, if running inside the @ or # instruction, ends the execution of the current String of code (for # , continues with the next iteration, if any); otherwise halts the program.
|
#
|
Map Array | Pops func from the Stack, which must be a String. The remaining topmost value in the Stack must be an Array; for each item in this Array, executes function with item. Returns an Array containing the Results of each execution. |
,
|
Get Array item | Pops n from the Stack, coerced into an integer. Returns the item at index n of the remaining topmost element a in the Stack.
If a is a String, it is treated like an Array with each UTF-16 unit in it as a 1-length String element; otherwise it must be an Array. A negative index counts backwards from the end of the Array, with -1 being the last element. If the index is out of bounds, the result is Undefined. |
;
|
Set Array item | Pops n and value from the stack. The remaining topmost value in the Stack must be an Array. Sets its item at index n to value.
If n is NaN or is equal to the length of the Array, value is pushed to the end of the Array, increasing the Array's length by one; otherwise n is coerced into an integer and used as the index. A negative index counts backwards from the end of the Array, with -1 being the last element. If the index is out of bounds, the operation silently fails. |
$
|
Delete Array item | Pops n from the stack, coerced into an integer. The remaining topmost value in the Stack must be an Array. Removes the item at index n from the Array.
A negative index counts backwards from the end of the Array, with -1 being the last element. If the index is out of bounds, the operation silently fails. All items following a removed item are moved forward by one cell. |
Conversion and coercing
To format a value into a String:
Value | Result |
---|---|
String | Surround the string content with double quotes and escape backslashes, double quotes, line feeds (\n ) and character tabulations (\t ) in it.
|
Undefined | "undef"
|
NaN | "NaN"
|
Inf | "Inf"
|
-Inf | "-Inf"
|
finite Number | as done natively by JavaScript |
Array | Format each element to a String; join the results with ", " and surround that with "[" and "]" . When a circular Array reference is detected, replace it with "[...]" .
|
To convert (coerce) a value into a String, if it isn't already one, format it into a String.
To convert (coerce) a value into a Number, if it isn't already one:
Value | Result |
---|---|
String | as done natively by JavaScript; possibly resulting in NaN if the String cannot be interpreted as a number |
Undefined | NaN |
Array | 0, if the Array is empty; the result of converting its element at index 0 into a Number, if its length equals 1; NaN otherwise |
To coerce a value into an integer, convert it into a Number; then, if it is NaN, result in 0; otherwise, truncate the Number's fractional part if it is finite. Leave Inf or -Inf as is.
To convert (coerce) a value into an Array, it must be either a String or an Array. If it is a String, the result is a new Array containing each character (or unpaired UTF-16 surrogate) in it as a separate String; if it is an Array, the result is a shallow copy of it.
Builtin functions
WhatLang has core builtin functions and Koishi runtime specific builtin functions.
Core
Function | Description |
---|---|
num@
|
Pops 1 value, converts it to a Number and returns the result. |
str@
|
Pops 1 value, converts it to a String and returns the result. |
repr@
|
Pops 1 value, and returns a string that tries to recreate the value when executed as WhatLang code. |
arr@
|
Pops 1 value, converts it to an Array and returns the result. |
pow@
|
Pops a and b, coerced into Numbers, and returns the result of a ** b. |
band@ bor@ bxor@
|
Popss 2 values, coerced into signed 32-bit integers, and returns the result of performing bitwise AND, OR, or XOR between them, respectively. |
bnot@
|
Pops 1 value, coerced into a signed 32-bit integer, and returns the result of performing bitwise NOT on it. |
rand@
|
Returns a random number between 0 and 1. |
randint@
|
Pops 2 values, which must be Numbers. Returns a random number between them, rounded down to an integer. |
flr@
|
Pops 1 value, coerced into a Number. Returns the result of rounding it down to an integer. |
range@
|
Pops n, coerced into an integer. Returns a new Array containing every integer from 0 to n.
Throws an error if n is negative or greater than 4294967295. |
len@
|
Returns the length of the topmost value in the Stack.
If the value is a Number, the result is Undefined. If the value is Undefined or the stack is empty, an error is thrown. |
split@
|
Pops string and separator, coerced into Strings. Returns the result of spliting string into an Array of Strings at each occurrence of separator, or an Array containing every UTF-16 unit in string if separator is empty. |
join@
|
Pops separator, coerced into a String. Returns the result of joining the remaining topmost element in the Stack (coreced into an Array) into a String, with the items coerced into Strings, sepatared by separator. |
reverse@
|
Returns a shallow copy of the topmost value in the Stack, converted to an Array, with its items reversed in order. |
in@
|
Pops value. The remaining topmost element in the Stack is converted into an Array. If value is in the Array, returns the index of its first occurence. Returns -1 otherwise. |
filter@
|
Pops func. The remaining topmost element in the Stack is converted into an Array. For each item in it, execute func with item. Returns a new Array containing the items for which the Result was not the empty String, 0, or Undefined. |
chr@
|
Pops 1 value, which can be a single Unicode codepoint number or an Array of codepoint Numbers, and returns a String composed of the characters specified by the codepoint(s).
Items in the input Array, or the input value itself if it is not an Array, are coerced into Numbers. If any given Number is negative or greater than 1114111, an error is thrown. |
ord@
|
Pops 1 value, coerced into a String. Returns an Array containing each codepoint (or unpaired UTF-16 surrogaate) in the String as a Number. |
and@
|
Pops a and b. If a is the empty String, 0 or undefined, returns a; otherwise returns b. |
or@
|
Pops a and b. If a is the empty String, 0 or undefined, returns b; otherwise returns a. |
nan@
|
Returns NaN. |
undef@
|
Returns Undefined. |
inf@
|
Returns Inf. |
ninf@
|
Returns -Inf. |
eq@
|
Pops 2 values. If they are strictly equal (=== in JavaScript), returns 1; otherwise returns 0.
|
stak@
|
Returns the Stack as an Array. Note that this makes the Stack contain itself. |
stack@
|
Returns a shallow copy of the Stack as an Array. |
try@
|
Like the @ instruction, but returns a new Array containing the error name and message if a runtime error occurs while executing. If no error occurs, returns an Array containing 2 Undefined's.
|
throw@
|
Pops 1 value, which must be either a String or a Number. Throws an error with this value as the message and "Error" as the name.
|
match@
|
Pops string and expression; executes the JavaScript Regular Expression specified by expression and returns the result as an Array of Strings, or an empty Array if no match is found.
An error is thrown if string is not a String. expression must be either a String, a Number (coerced into a String) or an Array. If it is an Array, it is the arguments to JavaScript's RegExp constructor - pattern, and optionally flags; otherwise it is the pattern. An error is thrown if the expression is invalid. If the expression has the g-flag, the resulting Array contains the whole matched strings of each match; Other it contains the whole matched string and the substrings matched by each capture group. |
repl@
|
Pops string, pattern and replacement; substitutes each occurrence of pattern in string with replacement and returns the modified String. pattern can be a literal String or an Array specifying a JavaScript Regular Expression like in match@ .
An error is thrown if string is not a String; pattern must be either a String, a Number (coerced into a String) or an Array, and replacement must be either a String or a Number (coerced into a String). Backreferences are replaced in the replacement string; to avoid this, replace any dollar sign in replacement with two dollar signs. |
time@
|
Returns the current system time in epoch milliseconds. |
type@
|
Pops 1 value; returns its type (one of "String" , "Number" , "Array" , "Undefined" ).
|
Koishi runtime specific
Some of these functions involve messages received by the bot; message contents are Strings in Koishi's XML format.
Function | Description |
---|---|
help@
|
Help function.
Pop 1 value, which must be either a String or Undefined, and returns the contents of the associated help topic as a String.
If the value is the empty String or Undefined, the result is a brief introduction to the language and some instructions on using the |
helpall@
|
Prints a list of (almost) all builtin functions as an image. |
pr@
|
Returns the contents of the next message from the current user in the current channel.
On timeout, returns Undefined. |
propt@
|
Pops userid. Returns the contents of the next message from any of the specified user(s) in the current channel.
If userid is falsey, messages from all users are accepted. Otherwise it should be one or more platform-defined user IDs. |
prompt@
|
Pops channelid and func. Listens for messages in specified channel(s), and for each message received, executes func with an Array containing the details of the message (see me@ ) until the Result is truthy, then returns the message details Array.
On timeout, returns Undefined. |
me@
|
Returns the details of the message triggering the interpreter, as an Array, which contains:
|
outimg@ outaudio@ outvideo@ outfile@
|
Pops 1 value, which must be a String, and prints an instance of the corresponding type of attachment with this value as its src URL. |
outquote@
|
Pops id, which must be a String, and prints a reference to the message with this ID. |
outat@
|
Pops id, which must be a String, and prints a @mention of the user with this platform-defined ID. |
outimag@
|
Pops 1 value, coerced into a String, renders its content text with a monospaced font and prints the result as an image. |
outksq@
|
Pops 1 value, coerced into a String, renders its content text with the Kreative Square font and prints the result as an image. |
outsvg@
|
Pops 1 value, which must be an Array, and prints the result of rendering the SVG image defined by this Array.
The first 2 items in the Array are the
|
nout@
|
Pops and discards the last printed element from the Output Stack. |
nouts@
|
Pops n, coerced into an integer. Removes and discards the top (latest) n elements from the Output Stack.
If n is zero or negative, removes all but the bottom (oldest) |n| elements instead. Inf and -Inf are treated like zero. |
nsend@
|
Pops messageid, which must be a String, and deletes (recalls) the message with this platform-defined ID in the current channel. |
send@
|
Pops 1 element from the Output Stack and sends it immediately. Returns an Array containing the platform-defined ID String of the message sent. |
sends@
|
Pops n, coerced into an integer. Pops the top (latest) n elements from the Output Stack and sends them immediately. Returns an Array containing platform-defined ID Strings of the message(s) sent (usually only one, but there may be more if the platform does not allow the content to fit in one message).
If n is zero or negative, sends all but the bottom (oldest) |n| elements instead. Inf and -Inf are treated like zero. |
sendsto@
|
Pops channelid, which must be a String, and n, coerced into an integer. Pops elements from the Output Stack as in sends@ and sends them in the channel with platform-defined ID channelid. Return an Array containing platform-defined ID Strings of the message(s) sent.
|
cat@
|
Pops url, which must be a String. Performs an HTTP GET request to this URL and returns the response body text as a String.
If the response has an error status code or there is a network error, an error is thrown. |
reesc@
|
Pops 1 value, which must be a Strin, and returns the result of escaping all JavaScript Regular Expression metacharacters in this String with backslash sequences. |
getmsg@ [1]
|
Pops func. Reads historical messages in the current channel, and for each message read, executes func with an Array containing the details of the message (see me@ ) until the Result is truthy, then returns the details Array for the last message read.
Can read up to the platform-specific limit or the end of the message history. If still nothing has been returned, returns Undefined. |
msgbyid@ [1]
|
Pops id, which must be a String, and returns the details of the message with this ID as an Array (see me@ ).
|
sleep@
|
Pops n, coerced into a Number, and sleeps for n seconds.
n must be between 0 and 2147483.647, inclusive. |
guildmem@
|
Pops guildid, which must be a String, and returns an Array of members of the current guild, each represented by an Array containing their username and platform-defined ID as Strings.
The bot needs to have received at least one message from a user for them to appear in the results. |
WhatNoter
This is a feature that provides a simple data storage based on Koishi-defined user IDs. Each user ID has a public note, a protected note and a private note. It is not required that a user with the given ID exists for the public note of that ID to be accessed. Once one of the notes of some user ID is created, other types of note belonging to the same user ID are initialized as the empty String; reading any note of an ID whose notes have never been written to yields Undefined.
Function | Description |
---|---|
notewc@
|
Pops uid, which must be a natrual Number, and str, which must be a String. Sets the public note content of user id uid to str. |
notewd@
|
Pops str, which must be a String. Sets the protected note content of the user triggering the interpreter to str. |
notewe@
|
Pops str, which must be a String. Sets the private note content of the user triggering the interpreter to str. |
noterc@
|
Pops uid, which must be a natrual Number. Returns the public note content of that user id. |
noterd@
|
Pops uid, which must be a natrual Number. Returns the protected note content of that user id. |
notere@
|
Returns the private note content of the user triggering the interpreter. |
WhatCommands
This is a feature that allows you to define custom commands using WhatLang. Each command has a name, a piece of code, a description String and a manual String. Commands can be invoked using the '¿¿
' prefix, taking a single String as input, which can be found at the top of the Stack.
Function | Description |
---|---|
cmdset@
|
Pops code and name, which must be Strings, and sets the code of the command named name to code. |
cmdall@
|
Returns an Array containing the names of all currently defined commands. |
cmdsethelp@
|
Pops str and name, which must be Strings, and sets the manual of the command named name to str. |
cmdseth@
|
Pops str and name, which must be Strings, and sets the description of the command named name to str. |
cmddel@
|
Pops name, which must be a String, and deletes the command with that name. |
cmdget@
|
Pops name, which must be a String, and returns the code of the command with that name, or Undefined if it does not exist. |
cmdgethelp@
|
Pops name, which must be a String, and returns the manual of the command with that name, or Undefined if it does not exist. |
cmdgeth@
|
Pops name, which must be a String, and returns the description of the command with that name, or Undefined if it does not exist. |
cmd@
|
Pops input and name, where name must be a String, and executes the code of the command named name with input and returns the Result. Note that commands do not automatically print their Results when invoked directly with '¿¿ '.
|
Practices and idioms
- To initialize a named variable with a literal value:
value name=_
- Pop (
_
) is used because you usually don't need the value in the stack since you have assigned it to your variable. - To define a custom function, simply enclose the code with parenthesis so it is understood as a string, and define it as a variable. The code can optionally pop arguments from the top of the Stack and optionally return one or more values. The function can be invoked by simply writing
functionname@
- Pop (
- To get negative one (or likewise any negative integer):
01-
- To get any real Number, for example, 3×10100:
(3e100)num@
- Represent the number with a String and then use
num@
to convert it into a Number.
- Represent the number with a String and then use
- If block:
condition{ statements !}
- A while loop with a break at the end.
- If-else block:
condition c={ true-branch !} c^~{ false-branch !}
- Alternatively using no variables:
1{ condition{ true-branch !!} false-branch !}
- Alternatively:
condition~[(true-branch)(false-branch)]\,\_@
- Alternatively using no variables:
- Comparison of two values (returns a truthy value when the condition satisfies)
- Loose equal (cmp(a, b) == 0):
?~
- Greater than (cmp(a, b) == 1):
?1?~
- Less than:
?(-1)?~
or\?1?~
- Less than or equal to (cmp(a, b) < 1):
?1\?1?~
- Greater than or equal to (cmp(b, a) < 1):
\?1\?1?~
- Remove the
~
for a negated condition. - Alternatively,
?[equal greater less]\,\_
can be used to implement these comparisons by replacing equal, greater and less with 0's and 1's (or any other value) depending on what you want the result to be in each case, for example,?[1:0]\,\_
for "greater than or equal". Note that when the values are not comparable, it is treated as if they were equal.- To deal with the incomparable, you can add a
2+
in it:?2+[for-incomparable for-less for-equal for-greater]\,\_
- To deal with the incomparable, you can add a
- Loose equal (cmp(a, b) == 0):
- Push value into Array (assuming Array at top of Stack):
len@ value ;
ornan@ value ;
- Comment:
0{ comment }
or(comment)_
Example programs
Programs here are prefixed with ¿
since that's how you usually invoke the interpreter bot on a messaging platform.
¿`Hello, world!`
¿(`¿(`.`) `.) `¿(`.`) `.
A shorter one using the eval instruction:
¿(`¿(`.`):@`):@
A cheating quine:
¿me@0,.
Another one, translated from Underload code
((¿)S:aSS)(¿)S:aSS
¿((¿)._:repr@._._)(¿)._:repr@._._
Cat programGet random cat image from TheCatAPI
¿(https://api.thecatapi.com/v1/images/search) cat@ ("url":"(.+?)") match@1, outimg@
This utilizes a regular expression and Koishi runtime specific builtin functions.
- Repeat the user's next message — arguably the equivalent of a Cat program in the Koishi runtime
¿pr@ [(<) g](<)repl@ [(>) g](>)repl@ [(&) g](&)repl@ .
This unescapes <>&
in the input before outputting it. Note that it produces unexpected results when the user's message is / contains something like an image or a platform-specific emoji, because those are represented as XML tags.
- Roll a dice (made by Yufang)
¿[ (000,010,000) (001,000,100) (100,010,001) (101,000,101) (101,010,101) (101,101,101) ]( ',split@( [(0)g]' repl@ [(1)g]61496chr@repl@ )#"│\n│"join@ "╭───╮\n│"\+ "│\n╰───╯\n"+ )# 0 6 randint@, outksq@
This results in one of the following:
- Get current datetime
¿ (2>|:&&:&\/flr@:&*-]<)divmod=_ (2>| 3600000*+ 946684800000- 86400000divmod@& 146097divmod@ :5+7%1+& :59- 36524/ flr@ :0?1?~{+0!}_ 36525divmod@ 1461divmod@ :59- 365/ flr@ :0?1?~{+0!}_ 366divmod@ 1\ 1{ :31-:0?(-1)?~{!!} \_\1+\ :29-:0?(-1)?~{!!} \_\1+\ :31-:0?(-1)?~{!!} \_\1+\ :30-:0?(-1)?~{!!} \_\1+\ :31-:0?(-1)?~{!!} \_\1+\ :30-:0?(-1)?~{!!} \_\1+\ :31-:0?(-1)?~{!!} \_\1+\ :31-:0?(-1)?~{!!} \_\1+\ :30-:0?(-1)?~{!!} \_\1+\ :31-:0?(-1)?~{!!} \_\1+\ :30-:0?(-1)?~{!!} \_\1+\ :31-:0?(-1)?~{!!} \_\1+\ 0!}_ 1+ && \4*+ \100*+ \400*+ 2000+& 3600000divmod@ 60000divmod@ 1000divmod@ ])datetime=_ time@ 8 datetime@.
This defines two custom functions, divmod@
and datetime@
, and then calls datetime@
with the current timestamp and a time zone offset (in this case GMT+8). It returns something like [2024, 9, 13, 5, 6, 55, 38, 92]
(the fourth number represents the day of week, where 1 - 7 stand for Monday to Sunday respectively; the last number is the milliseconds), which is then printed.
- maybe a Brainfuck interpreter
¿(code)[ (p^,1+256%p^\;) (p^,1-256%p^\;) (p^1+p=_) (p^1-p=_) (p^,{) (p^,}) (g^len@~{ _`Please input...`send@_pr@ord@reverse@\_g= !} 01-,\|_]_p^\;) (p^,._) ]c=_ arr@( "+-><[],."\in@c^\, )#\_ ""join@\_(999range@(0)#t=\_500p=_[]g=_)\+@
You should replace code
on the first line with your Brainfuck code, and it requires a input to be send as a message whenever it meets ",".
Another one, it wastes memory.
0a=b=[0]l=[0]r=(put code here)arr@((+-<>[].,)\in@[(l^a^l^a^,\_1+255band@;)(l^a^l^a^,\_1-255band@;)(b^1+b=r^len@0;r^b^l^a^,\_;l^a^0;a^1-:0?1+~~*a=)(a^1+a=l^len@0;l^a^r^b^,\_;r^b^0;b^1-:0?1+~~*b=)(l^a^,{)(l^a^,})(l^a^,\_chr@.)(l^a^pr@ord@len@~[(0,\_)(_0)]\,\_@;_)()]\,\_)#()join@@