Numble
Jump to navigation
Jump to search
Numble is an esoteric programming language, made by User:Cobl703, similar to Forte. It is Turing complete.
Commands
Each command in Numble consists of a single byte indicating which command it is, followed by its arguments. Integers are encoded using ZigZag encoding.
Starting Byte | Arguments | Action |
---|---|---|
00 |
Integer, Expression | Sets argument 0 to the value of argument 1. |
01 |
Integer | Sets argument 0 to a single byte from the input. If an EOF occurs, argument 0 is set to 256. |
02 |
Expression | Outputs a byte given by the value of argument 0 (mod 256). |
03 |
Expression | Creates a label named using the value of argument 0. If this label already exists, it is overwritten. |
04 |
00 , Expression, Expression |
Runs the next command only if the value of argument 1 is less than that of argument 2. |
04 |
01 , Expression, Expression |
Runs the next command only if the value of argument 1 is equal to that of argument 2. |
04 |
02 , Expression, Expression |
Runs the next command only if the value of argument 1 is less than or equal to that of argument 2. |
04 |
03 , Expression, Expression |
Runs the next command only if the value of argument 1 is not equal to that of argument 2. |
05 |
Expression | Goes to the label given by the value of argument 0. If such a label does not exist yet, the rest of the program is searched for a label that does (without running the code in between). If no such label is found, the program ends. |
Expressions
Expressions are similar to commands, with the difference being that they have a value instead of preforming an action. They have starting bytes and arguments like commands.
Starting Byte | Arguments | Output Value |
---|---|---|
00 |
Integer | Argument 0 |
01 |
Expression, Expression | The sum of the value of argument 0 and that of argument 1 |
02 |
Expression, Expression | The difference of the value of argument 0 and that of argument 1 |
03 |
Expression, Expression | The product of the value of argument 0 and that of argument 1 |
04 |
Expression, Expression | The floor quotient of the value of argument 0 and that of argument 1 |
Examples
Hello world
02 00 90 01 02 00 CA 01 02 00 D8 01 02 00 D8 01 02 00 DE 01 02 00 40 02 00 EE 01 02 00 DE 01 02 00 E4 01 02 00 D8 01 02 00 C8 01
Truth machine
01 00 03 00 62 02 00 00 05 00 00
Interpreter
import collections.abc import io import collections import sys import typing class NumbleProgram: file: typing.BinaryIO prgm_output: typing.BinaryIO prgm_input: typing.BinaryIO variables: dict[int, int] labels: dict[int, int] def __init__(self, file: typing.BinaryIO | str, prgm_output: typing.BinaryIO | str, prgm_input: typing.BinaryIO | str | None = None) -> None: if isinstance(file, str): self.file = open(file, "rb") else: self.file = file if isinstance(prgm_output, str): self.prgm_output = open(prgm_output, "ab") else: self.prgm_output = prgm_output if prgm_input == None: self.prgm_input = io.BytesIO() elif isinstance(prgm_input, str): self.prgm_input = open(prgm_input, "rb") else: self.prgm_input = prgm_input self.variables = {} self.labels = {} def read_zigzag(self) -> int: shift: int = 0 result: int = 0 while True: try: byte: int = self.file.read(1)[0] except IndexError: raise EOFError(f"Unexpected EOF while reading varint: [EOF]{result:0>{shift}b}") result |= (byte & 0x7f) << shift if not byte & 0x80: break shift += 7 if result & 1: return -result >> 1 return result >> 1 def get_var(self, name: int) -> int: result: int = name while True: try: result = self.variables[result] except KeyError: return result def eval_expression(self) -> int: operation_stack: list[int] = [] stack: list[int] = [] while True: try: operation: int = self.file.read(1)[0] except IndexError: raise EOFError("Unexpected EOF while reading expression") if operation: operation_stack.append(operation) else: stack.append(self.get_var(self.read_zigzag())) while True: if not operation_stack: return stack[-1] match operation_stack[-1]: case 1: # Addition if len(stack) < 2: break operation_stack.pop() second: int = stack.pop() first: int = stack.pop() stack.append(self.get_var(first + second)) case 2: # Subtraction if len(stack) < 1: break operation_stack.pop() second: int = stack.pop() first: int = stack.pop() stack.append(self.get_var(first - second)) case 3: # Multiplication if len(stack) < 2: break operation_stack.pop() second: int = stack.pop() first: int = stack.pop() stack.append(self.get_var(first * second)) case 4: # Division if len(stack) < 2: break operation_stack.pop() second: int = stack.pop() first: int = stack.pop() stack.append(self.get_var(first // second)) case _: raise SyntaxError(f"Unknown operation: {operation}") def run_command(self, command: int) -> None: match command: case 0: # Set name: int = self.read_zigzag() self.variables[name] = self.eval_expression() case 1: # Input name: int = self.read_zigzag() try: self.variables[name] = self.prgm_input.read(1)[0] except IndexError: self.variables[name] = 256 case 2: # Output self.prgm_output.write(bytes([self.eval_expression() % 256])) case 3: # Label label: int = self.eval_expression() self.labels[label] = self.file.tell() case 4: # If try: comparison: int = self.file.read(1)[0] except IndexError: raise EOFError("Unexpected EOF while reading command") match comparison: case 0: comparison_function: collections.abc.Callable[[int, int], bool] = int.__lt__ case 1: comparison_function: collections.abc.Callable[[int, int], bool] = int.__eq__ case 2: comparison_function: collections.abc.Callable[[int, int], bool] = int.__le__ case 3: comparison_function: collections.abc.Callable[[int, int], bool] = int.__ne__ case _: raise SyntaxError(f"Unknown comparison: {comparison}") exp1: int = self.eval_expression() exp2: int = self.eval_expression() if not comparison_function(exp1, exp2): self.skip_command() case 5: # Goto label: int = self.eval_expression() try: self.file.seek(self.labels[label]) except KeyError: while True: finished: None | bool = self.skip_command_unless_label(label) if finished: self.file.seek(self.labels[label]) return if finished == None: return case _: raise SyntaxError(f"Unknown command: {command}") def skip_command(self) -> None: try: command: int = self.file.read(1)[0] except IndexError: raise EOFError("Unexpected EOF while reading command") match command: case 0: # Set self.read_zigzag() self.eval_expression() case 1: # Input self.read_zigzag() case 2: # Output self.eval_expression() case 3: # Label self.eval_expression() case 4: # If try: self.file.read(1)[0] except IndexError: raise EOFError("Unexpected EOF while reading command") self.eval_expression() self.eval_expression() case 5: # Goto self.eval_expression() case _: raise SyntaxError(f"Unknown command: {command}") def skip_command_unless_label(self, label) -> None | bool: try: command: int = self.file.read(1)[0] except IndexError: return match command: case 0: # Set self.read_zigzag() self.eval_expression() case 1: # Input self.read_zigzag() case 2: # Output self.eval_expression() case 3: # Label clabel: int = self.eval_expression() if clabel == label: self.labels[label] = self.file.tell() return True case 4: # If try: self.file.read(1)[0] except IndexError: raise EOFError("Unexpected EOF while reading command") self.eval_expression() self.eval_expression() case 5: # Goto self.eval_expression() case _: raise SyntaxError(f"Unknown command: {command}") return False def run(self) -> None: while True: try: command: int = self.file.read(1)[0] except IndexError: break self.run_command(command) if __name__ == "__main__": try: file: str = sys.argv[1] except IndexError: file: str = input("Enter filename: ") np: NumbleProgram = NumbleProgram(file, sys.stdout.buffer, sys.stdin.buffer) np.run()