# Fool

Fool is an esoteric programming language created by User:DigitalDetective47. Its name comes from the common abbreviations func and bool, which are the only concepts that the language defines. It also references the fact that anyone who would attempt use this language for anything productive is a fool.

## Memory

Data is stored on a tape of bits that extends infinitely in both directions. The tape is initially filled with 0s.

## Program structure

A program is read as a list of function definitions separated by newlines. Programs must not include a trailing newline. The syntax for a function declaration is:

<function name>:<function code>


Any function name that does not contain any of &().:| or newlines is valid. This means that the empty string is a valid function name. A function name may not be declared multiple times within a single program. Every function takes a bit as input and returns a bit as output. There are three built‐in functions in Fool. First is <, which moves the tape head one cell to the left. It returns its input as its output. Second is >, which move the tape head one cell to the right, copying its input to its output. Third is *. If its input is 1, it flips the bit pointed to by the tape head. It then returns the value of the bit pointed to (after flipping), regardless of its input. There are three special operators that combine two functions together. All operators of the same precedence bind right to left. Most binding is ., which is a function composition. It takes the input to its expression and passes it to the expression bound on the right. It then passes the output from the expression bound on the right and passes it as the input to the expression bound on the left. It then returns the output of the expression bound on the left. (h:g.f defines ${\displaystyle h\left(x\right)}$ as ${\displaystyle g\left(f\left(x\right)\right)}$.) Less binding are & and |, which act as a binary AND and OR, respectively. Both sides are given the expression's input, and they both use lazy evaluation right‐to‐left. For example, define h:g&f. The process for evaluating ${\displaystyle h\left(x\right)}$ is:

1. Evaluate ${\displaystyle f\left(x\right)}$ and store the result as ${\displaystyle a}$.
2. If ${\displaystyle a=0}$, return 0. Otherwise, evaluate and return ${\displaystyle g\left(x\right)}$.

Parentheses can be used to change the order of binding. As an example of the default operator binding, a.b|c.d&e.f|g.h is parsed as (a.b)|((c.d)&((e.f)|(g.h))). To get ((a.b)|(c.d))&((e.f)|(g.h)), the simplest form would be (a.b|c.d)&e.f|g.h. To get (a.b)|((c.d)&(e.f)|(g.h), the simplest form would be a.b|(c.d&e.f)|g.h. When the program is run, the main function is called with an input of 1, and its output is ignored. A program without a function called main is invalid. All implementations must reject invalid programs.

## Examples

### Hello, world!

Writes the ASCII representation of the string onto the tape.

H:>.>.>.>.*.>.>.>.*.>
e:>.*.>.>.*.>.>.>.*.>.*.>
l:>.>.>.*.>.*.>.>.*.>.*.>
o:>.*.>.*.>.*.>.*.>.>.*.>.*.>
,:>.>.>.*.>.*.>.>.*.>.>
:>.>.>.>.>.>.*.>.>
w:>.*.>.*.>.*.>.>.*.>.*.>.*.>
r:>.>.*.>.>.>.*.>.*.>.*.>
d:>.>.>.*.>.>.>.*.>.*.>
!noshift:*.>.>.>.>.>.*.>.>
main:!noshift.d.l.r.o.w. .,.o.l.l.e.H


### Truth-machine

To pass a 0 as input, run the program as‐is. To pass a 1 as input, append .* to the last line to set cell 0 to 1.

stepLeft:*.<.*.>|stepLeft&*.<
glideLeft:glideRight&stepLeft
stepRight:*.>.*.<|stepRight&*.>
glideRight:glideLeft&stepRight
main:*.(glideRight.<.*.*.>.*|*)


### Infinite loop

#### Trivial recursive version

main:main


#### Golfed version

:
main:


(7 characters)

## Computational class

Fool can be proven to be Turing-complete as BitChanger can be compiled into it, with the omission of I/O, which is not required for Turing-completeness. First, reverse the program, and replace each square bracket with its opposite ([ → ], ] → [). The non‐looping functions are easy to define: < → <.<, } → >|*.*.>.*. Each bracketed block can be separated into its own function: [<code>] → <some unique name>:<.>|<some unique name>.<code>&*.<.*.*.>.

## Implementations

### Official interpreter

(The output form used is not part of the specification and is not required for any implementations.)

from __future__ import annotations

from sys import argv, stderr
from typing import Callable, Final
from collections.abc import Callable as CallableABC

class FoolFunc(CallableABC):
def __init__(self, func: Callable[[int], int], /) -> None:
self.func = func

def __add__(self, other: FoolFunc, /) -> FoolFunc:
return FoolFunc(lambda x: self.func(x) or other.func(x))

def __call__(self, value: int, /) -> int:
return self.func(value)

def __mul__(self, other: FoolFunc, /) -> FoolFunc:
return FoolFunc(lambda x: other.func(self.func(x)))

def __sub__(self, other: FoolFunc) -> FoolFunc:
return FoolFunc(lambda x: self.func(x) and other.func(x))

tape: Final[list[int]] = [0]
pointer_location: int = 0

def shift_left(value: int, /) -> int:
global pointer_location
if pointer_location == 0:
tape.insert(0, 0)
else:
pointer_location -= 1
return value

def shift_right(value: int, /) -> int:
global pointer_location
pointer_location += 1
if pointer_location == len(tape):
tape.append(0)
return value

def star(value: int, /) -> int:
result = value ^ tape[pointer_location]
tape[pointer_location] = result
return result

function_table: Final[dict[str, FoolFunc]] = {
"*": FoolFunc(star),
"<": FoolFunc(shift_left),
">": FoolFunc(shift_right),
}

if len(argv) < 2:
print(
"Pass the location of the script to run as a command-line argument.",
file=stderr,
)
elif len(argv) > 2:
print("Unknown command-line arguments passed.", file=stderr)
else:

try:
with open(argv[1]) as source:
for line_number, line_contents in tuple(enumerate(statements))[:-1]:
statements[line_number] = line_contents[:-1]

current_function_name: str
function_code: str
function_names: Final[list[str]] = []
num_colons: int
unclosed_parens: int
for line_number, statement in enumerate(statements, 1):
num_colons = statement.count(":")
if num_colons > 1:
raise SyntaxError(f"Multiple colons found on line {line_number}.")
elif num_colons == 0:
raise SyntaxError(f"Missing colon on line {line_number}.")
current_function_name, function_code = statement.split(":", 1)
if not frozenset(current_function_name).isdisjoint(frozenset("&().|")):
raise SyntaxError(f"Invalid function name on line {line_number}")
elif current_function_name in frozenset("*<>"):
raise SyntaxError(
f"Attempted to overwrite default function {current_function_name} on line {line_number}."
)
try:
raise SyntaxError(
f"Found multiple definitions for function {current_function_name}. (First 2 occurrences on lines {function_names.index(current_function_name)} & {line_number}.)"
)
except ValueError:
# Means function name not in list, this should be raised each time.
unclosed_parens = 0
for char in statement:
if char == "(":
unclosed_parens += 1
elif char == ")":
unclosed_parens -= 1
if unclosed_parens < 0:
raise SyntaxError(
f"Unmatched parenthesis on line {line_number}."
)
if unclosed_parens != 0:
raise SyntaxError(f"Unmatched parenthesis on line {line_number}.")
function_names.append(current_function_name)
function_table[current_function_name] = FoolFunc(lambda: None)
if "main" not in function_names:

function_definition_expression: str
is_function_name: bool
for line_number, statement in enumerate(statements, 1):
current_function_name, function_code = statement.split(":", 1)
function_definition_expression = ""
is_function_name = True
for token in reversed(
function_code.replace("&", ": - :")
.replace("(", "(:")
.replace(")", ":)")
.replace(".", ": * :")
.replace("|", ": + :")
.split(":")
):
if token == "(":
function_definition_expression += ")"
elif token == ")":
function_definition_expression += "("
else:
if is_function_name:
if token in function_table:
function_definition_expression += (
f"function_table[{token!r}]"
)
else:
raise SyntaxError(
f"Unrecognized function {token} on line {line_number}."
)
elif token in frozenset({" * ", " + ", " - "}):
function_definition_expression += token
else:
raise SyntaxError(f"Invalid syntax on line {line_number}")
is_function_name = not is_function_name
function_table[current_function_name].func = eval(
function_definition_expression
)
except OSError:
print("There was an issue accessing the file you requested.", file=stderr)
except SyntaxError as e:
print(e, file=stderr)
else:
main_return: Final[int] = function_table["main"](1)
print(f"...{''.join(map(str, tape))}... [{main_return}]")