The language
LogiMuxi is, as its name suggests, a programming language based on multiplexers.
Built-in gates
M(A,B,C)
(Multiplexer): If A
is 0
, returns is B
, otherwise returns C
.
R()
(Random): Returns either 0
or 1
uniformly randomly.
I()
(Input): Reads a bit from STDIN and returns it. Terminates program execution on EOF.
O(A)
(Output): Appends bit A
to STDOUT and returns it.
Literals
0
, 1
are literals. Literals are expressions, and can be used as values.
Gate calling
G(<arg1>,<arg2>,...,<argx>)
calls gate G
with the provided arguments in order. Gate calling is an expression.
Conditional loop
G
<cmd1>
<cmd2>
<...>
<cmdx>
Evaluates G
. If G
returns 1
, the indented commands are executed, and this process repeats again. If G
returns 0
, the loop is skipped. If there are no indented commands under G
, the loop is empty, and, therefore, if G
is 0
then nothing happens, while if G
is 1
we enter an infinite loop with no way out.
Value assignment
X=G
Assigns X
to the result of G
. After that, the identifier of the variable can be used as an expression, and it will evaluate to the current value of the variable.
Gate definition
G(<arg1>,<arg2>,...,<argx>)
<cmd1>
<cmd2>
<...>
<cmdx>
Defines gate G
to take arguments <arg1>
up to <argx>
(actual argument identifiers are specified by the programmer) and return the result of H
. The identifiers of the arguments are localized, as well as variables assigned inside the gate. This means that, if I assign variable X
to value A
outside of G
and then there's an X=B
command in the definition of G
, calling G
will not assign X
to B
. However, inside the gate's scope, X
will take the new value B
. Assigning the arguments themselves to new values is allowed. Example:
X=0
G(A,B)
X=1
O(X)
:M(R(),A,B)
O(X)
This will output the bits 1
and 0
in order. For reference, G
chooses randomly between A
and B
in this example.
Gate definitons may also be nested, in which case they will be localized too.
Returning happens by prepending a :
(colon) to a value (e.g. to return value A
, use command :A
). This will exit the gate and return the value to the right of it. You can't leave the part to the right of :
empty. A gate that doesn't return is invalid.
Additional notes
- Commands are separated by line separators.
- An identifier has to meet these criteria:
- The first character has to be in
ABCDEFGHIJKLMNOPQRSTUVWXYZ_
- From the second character onward, the identifier must only be composed of characters in
ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_
.
- It can't be the identifier of any of the built-in gates.
- A variable and a gate may use the same identifier. They can be separated by the way they are invoked. Of course, this is bad practice, but this language is already pretty esoteric. ;-)
- Use of an undefined identifier is invalid.
- A gate is always to be called with parentheses, even if it doesn't take arguments. This includes the built-in gates.
- A gate may only return one output, and it must return one.
- Nested loops or gates are represented with the appropriate number of spaces used as indentation.
- Useless indentation is prohibited.
- Lines may be empty, but their indentation is significant. Empty lines do nothing.
- Calling gates with the wrong number of arguments is invalid.
- If the number of bits sent to STDOUT isn't a multiple of 8, the bits are post-padded with
0
s (e.g. if STDOUT is 00010000 11100
, it will be converted to 00010000 11100000
before actual printing).
- If 8 bits have been sent to STDOUT, they will be converted to a character and output immediately.
- Any syntax not defined above should be considered undefined.
Reference gates
You may skip this section.
NOT:
NOT(A)
:M(A,1,0)
AND:
AND(A,B)
:M(A,0,B)
OR:
OR(A,B)
:M(A,B,1)
XOR:
XOR(A,B)
:M(A,B,M(B,1,0))
NAND:
NAND(A,B)
:M(A,1,M(B,1,0))
NOR:
NOR(A,B)
:M(A,M(B,1,0),0)
XNOR:
XNOR(A,B)
:M(A,M(B,1,0),B)
Simplification tips
You may skip this section.
M(A,0,1)
→ A
M(M(A,1,0),B,C)
→ M(A,C,B)
M(0,B,C)
→ B
M(1,B,C)
→ C
Sample programs
You may skip this section.
Infinite loop, no output:
1
Cat:
1
O(I())
Hello, World!:
O(0)
O(1)
O(0)
O(0)
O(1)
O(0)
O(0)
O(0)
O(0)
O(1)
O(1)
O(0)
O(0)
O(1)
O(0)
O(1)
O(0)
O(1)
O(1)
O(0)
O(1)
O(1)
O(0)
O(0)
O(0)
O(1)
O(1)
O(0)
O(1)
O(1)
O(0)
O(0)
O(0)
O(1)
O(1)
O(0)
O(1)
O(1)
O(1)
O(1)
O(0)
O(0)
O(1)
O(0)
O(1)
O(1)
O(0)
O(0)
O(0)
O(0)
O(1)
O(0)
O(0)
O(0)
O(0)
O(0)
O(0)
O(1)
O(0)
O(1)
O(0)
O(1)
O(1)
O(1)
O(0)
O(1)
O(1)
O(0)
O(1)
O(1)
O(1)
O(1)
O(0)
O(1)
O(1)
O(1)
O(0)
O(0)
O(1)
O(0)
O(0)
O(1)
O(1)
O(0)
O(1)
O(1)
O(0)
O(0)
O(0)
O(1)
O(1)
O(0)
O(0)
O(1)
O(0)
O(0)
O(0)
O(0)
O(1)
O(0)
O(0)
O(0)
O(0)
O(1)
1-bit half adder:
A=I()
B=I()
O(M(A,0,B))
O(M(A,B,M(B,1,0)))
Challenge rules
- You may assume you'll not receive an invalid program, or a program with input that will make it do invalid actions, so you don't need to check it for validity.
- The program can be either separated by line separators, or given as a list of lines. Acceptable line separators are
\n
, \r\n
and \r
(\n
denotes character 0x0A, \r
denotes character 0x0D).
- Input can be taken in any reasonable form explicitly separated from the program, not necessarily from STDIN. Also, it can either be the actual input, or its bits (e.g. you can take
0100000001000001
instead of @A
). It's guaranteed to be finite for this challenge.
- Output can be provided in any reasonable form, either as bits or as text. Also, you don't actually need to output while the program is executing, unlike what the specification above says.
- You may use any four distinct identifiers for the built-in gates
M
, R
, I
and O
. Identifier rules will apply to your chosen identifiers in this case.
- You may use
[]
instead of ()
, and/or tabs instead of spaces in indentation. You must be consistent with these choices.