We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.
Fool/Python Interpreter
Jump to navigation
Jump to search
#!/usr/bin/env python
from collections.abc import Callable
from sys import argv, stderr
from typing import Final
class FoolFunc:
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:
statements = source.readlines()
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 number: 0)
if "main" not in function_names:
raise SyntaxError("main function not found.")
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}]")