Brainfunct
Paradigm(s) | functional |
---|---|
Designed by | User:Koen |
Appeared in | 2012 |
Memory system | Cell-based |
Dimensions | one-dimensional |
Computational class | see Computational Class |
Major implementations | see Implementations |
Influenced by | Brainfuck |
Influenced | brainFn |
File extension(s) | .b .bf |
Brainfunct is a functional esoteric programming language based on brainfuck, created by User:Koen in 2012 when he observed a lack of functional languages among the plethora of Brainfuck derivatives. A program in brainfunct consists in a succession of declaration of functions. Functions have no name but have a number, corresponding to the order in which they are declared. Functions operates on a tape similar to brainfuck's. The commands are:
Command | Description |
---|---|
>
|
Move the pointer to the right |
<
|
Move the pointer to the left |
+
|
Increment the memory cell under the pointer |
-
|
Decrement the memory cell under the pointer |
.
|
Output the character signified by the cell at the pointer |
,
|
Input a character and store it in the cell at the pointer |
@
|
Call the function signified by the cell under the pointer |
Function declaration
There are two versions of Brainfunct. The only difference is the syntax for function declaration.
Brainfunct-slash
In brainfunct-slash, functions are simply separated by the symbol /
. The first function is number 1. The last function is the main function; it cannot be called by other functions, but it is called when the execution starts.
Brainfunct-nested
The great addition of brainfunct-nested is the use of parenthesizing instead of the simple /
. This allows for nested functions. Nested functions cannot be called from out of the function where they are declared. The number of a function is the smallest number available within their scope, starting with less deep functions. All commands not included in a function are part of the main function. The following is an example of how functions are numbered in this version:
(1) (2) (((7)6)3) (4) (((8)6)(7)5) main
Sample programs
Truth-machine
////////////////////////////////////////////////.@/,.@
In this program the first 48 functions are no-ops, and the 49th (49 being the ASCII code for '1') is an infinite loop of printing character '1'.
Cat program
>,.<@/+@
Doesn't handle end of file. The implementation below doesn't either, though.
Syntactic sugar
To avoid "flooding" a program with the symbol /
, the body of a function may be preceded by the function number. The function number must be written in octal; using this notation, the truth-machine above would become:
61.@/,.@
Implementations
Ocaml
Note: This interpreter supports brainfunct-slash only.
let tape_size = 100;; (* ideally the tape would be unbounded *) let n_functions = 260;; (* ideally the number of functions would be unbounded *) let tape = Array.make tape_size 0;; let ptr = ref 0;; let f = Array.make n_functions "";; let make_functions s = let i = ref 0 and j = ref 1 and l = String.length s in for k = 0 to (l - 1) do if s.[k] = '/' then begin f.(!j) <- String.sub s !i (k - !i); i := k+1; incr j end done; f.(0) <- String.sub s !i (l - !i);; exception Unknown_command of char;; let rec mtch c = match c with | '>' -> incr ptr | '<' -> decr ptr | '+' -> tape.(!ptr) <- succ tape.(!ptr) | '-' -> tape.(!ptr) <- pred tape.(!ptr) | '.' -> print_char (char_of_int tape.(!ptr)); flush(stdout) | ',' -> tape.(!ptr) <- (try int_of_char (input_char stdin) with | End_of_file -> -1) | '@' -> interpret tape.(!ptr) false | x -> raise (Unknown_command x) and interpret fn first_time = if (fn > 0 || first_time) then let l = String.length f.(fn) - 2 in for k = 0 to l do mtch f.(fn).[k] done; mtch f.(fn).[l + 1];; make_functions Sys.argv.(1);; interpret 0 true;;
Haskell
Supports both slashes and parentheses, including in weird nested combinations, essentially desugaring the former to the latter. Does not support the function number syntactic sugar.
import Control.Monad.State (StateT, evalStateT, gets, liftIO, modify) import Data.Functor ((<$>), (<$)) import System.Environment (getArgs) import System.IO (hFlush, hIsEOF, hPutStr, stderr, stdin, stdout) import Text.Parsec (Parsec, between, char, choice, eof, many, parse, sepBy1, spaces, (<|>)) data Tape = Tape { left, right :: [Int] } data CmdOrFun = Cmd (FunList -> BF) | Fun (FunList -> BF) type BF = StateT Tape IO () type FunList = [BF] op :: Char -> Parsec [Char] () () op c = char c >> spaces cmdOrSubfun :: Parsec [Char] () CmdOrFun cmdOrSubfun = Fun <$> between (op '(') (op ')') function <|> Cmd (\funList -> findFun funList =<< getCurrent) <$ op '@' <|> Cmd . const <$> choice [ modify (\(Tape ls (cur:rs)) -> Tape (cur:ls) rs) <$ op '>', modify (\(Tape (l:ls) rs) -> Tape ls (l:rs)) <$ op '<', modify (\(Tape ls (cur:rs)) -> Tape ls (cur+1:rs)) <$ op '+', modify (\(Tape ls (cur:rs)) -> Tape ls (cur-1:rs)) <$ op '-', (getCurrent >>= \c -> liftIO $ putChar (toEnum c) >> hFlush stdout) <$ op '.', (do c <- liftIO $ do e <- hIsEOF stdin if e then return (-1) else fromEnum <$> getChar modify (\(Tape ls (_:rs)) -> Tape ls (c:rs))) <$ op ',' ] where getCurrent = gets (head . right) findFun fs n | n > 0, f:_ <- drop (n-1) fs = f | otherwise = return () function :: Parsec [Char] () (FunList -> BF) function = makeFunction . unslash <$> sepBy1 (many cmdOrSubfun) (op '/') where unslash blocks = map (Fun . makeFunction) (init blocks) ++ last blocks makeFunction cfs funList = sequence_ [cmd funList' | Cmd cmd <- cfs] where funList' = funList ++ [fun funList' | Fun fun <- cfs] runBF :: String -> String -> IO () runBF sourceName s = case parse (between spaces eof function) sourceName s of Left err -> hPutStr stderr $ "Parse error: " ++ show err Right f -> evalStateT (f []) $ Tape (repeat 0) (repeat 0) main = runBF "command line" . unwords =<< getArgs
Computational Class
- Note: The Computational Class of this language hasn't been proven yet, so please take the following information with a grain of salt.
The computational class of the two versions of brainfunct are currently unknown. However the conjectured computational classes are that brainfunct-slashes is Turing Incomplete, while brainfunct-nested is Turing Complete.