FerNANDo

ferNANDo is an esoteric programming language by User:Whtspc. It's based on NAND-logic and uses no other syntax-elements than variable names. When variables names are chosen carefully, programs may sound quite poetic.
At initialization every possible variable is set to 0. (Actually every time the program encounters an undefined variable, it creates this variable with the value 0.) The exception to this is ?, which acts as a PRNG bit.
The amount of words (variables) in a sentence determine what type of command will be executed. There are 4 commands.
=== The NAND command ===
Note: 2 variable NAND is new in version 0.5
 A B C
or
 A B
The NAND command is executed when there are 2 or 3 variables in a sentence. If there are 3 variables, it sets the value of the first variable to the NAND of the second and third; if there are 2, it sets the first variable to the NAND of itself and the second. In the above examples, it sets 'A' to 'B NAND C', and 'A' to 'A NAND B' respectively. Since the variables are set to 0 by default, A will be 0 NAND 0, which is 1.
=== The output command ===
 A B C D E F G H
The output command is executed when there are 8 variables in a sentence. The value of the binary digit represented by the values of the variables in the sentence together is displayed as an ascii character.
=== The input command ===
New in version 0.5
 R A B C D E F G H
The input command is executed when there are 9 variables in a sentence, and reads a single byte from STDIN. The first variable will be set to 1 if the operation was successful, or 0 otherwise (usually indicating EOF). The rest of the variables are set to bits of the byte read, most significant bit first. In the event of EOF, only the value of the first variable will be changed.
A sample program which reads the contents of STDIN, and echos them back to STDOUT:
 R A B C D E F G H
 R
 A B C D E F G H
 R A B C D E F G H
 R
=== The conditional loop command ===
 loop 
 loop loop loop
 loop
When there's only one variable in a sentence and this variable has value 1, program points back to the line right after the previous sentence that's exactly the same. If there isn't a correspondenting sentence back in the program, the command is ignored and program continues without any further action. The same happens when the value of the variable is 0.
So, walking through the example above:
*the word 'loop' is unknown, and undefined therefore. The variable 'loop' is created with value 0.
*Since the value is 0 we ignore the command in line 1.
*In the second line the variable 'loop' is set to 'loop NAND loop', or 0 NAND 0, which is 1. The variable 'loop' now has value 1.
*In the third line we have one variable with value 1, so we have to go back to the line after the previous sentence that's exactly the same. In this case line 1 is exactly the same, so the program points back to line 2.
*In line 2 'loop' is set to 'loop NAND loop', loop is 1 by now, 1 NAND 1 = 0, loop is set to 0.
*We encounter line 3 again, loop is now 0, so the command is ignored. Since there are no other lines left, program execution is stopped.
=== ? The PRNG Bit ===
New in version 0.5
? is a pre-defined variable, which is set to a function that returns a random bit each time it is accessed. ? may also be used in a write context, but afterwards, it will act as a normal variable.
The following prints out a random digit between 0 and 7:
 1 1 1
 0 0 1 1 0 ? ? ?
This functionality may be disabled by use of the --no-prng command line option, in which case ? will act as a normal variable, defaulting to value 0.
=== Other logic gates ===
To place the value of logic gate(A,B) into O:
NOT:
 O A A
AND:
 O A B
 O O O
OR:
 C A A
 D B B
 O C D
NOR:
 C A A
 D B B
 O C D
 O O O
XOR:
 C A A C A B
 D B B or D A C
 E A D E B C
 F C B O D E
 O E F
NXOR:
 C A A C A B
 D B B or D A C
 E A B E B C
 F C D O D E
 O E F O O O
A IMPLIES B:
 C B B
 O A C
===Arithmetic circuits===
HALF-ADDER
Single bit incrementer with carry.
Ci = Carry-in; Co = Carry-out; S = Bit
 Co Ci S
 t1 Ci Co
 t2 Co S
 S t1 t2
 Co Co Co
N-BIT INCREMENTER
Implemented by chaining N half-adders. The following is a 5-Bit incrementer (on bits a, b, c, d, e), which loops 26 times (until b, d, and e are all set), printing out the corresponding letter.
 1 1 1
 do
 a a a 
 Ca a a
 Cb Ca b
 t1 Ca Cb
 t2 Cb b
 b t1 t2
 Cb Cb Cb
 Cc Cb c
 t1 Cb Cc
 t2 Cc c
 c t1 t2
 Cc Cc Cc
 Cd Cc d
 t1 Cc Cd
 t2 Cd d
 d t1 t2
 Cd Cd Cd
 Ce Cd e
 t1 Cd Ce
 t2 Ce e
 e t1 t2
 Ce Ce Ce
 0 1 0 e d c b a
 t1 b d
 t1 t1 t1
 do t1 e
 do
DECREMENTER
A Half-Adder can also be used as a decrementer with a little modification. Once again, these can be chained together to create N-Bit decrementers.
Ci = Carry-in; Co = Carry-out; S = Bit
 t1 Ci S
 t2 Ci t1
 t3 t1 S
 S t2 t3
 Co Ci S
 Co Co Co
==Example programs==
===Hello world===
The following program displays 'Hello, world!'
 ave ave ave
 terra ave terra terra ave terra terra terra
 terra ave ave terra terra ave terra ave
 terra ave ave terra ave ave terra terra
 terra ave ave terra ave ave terra terra
 terra ave ave terra ave ave ave ave
 terra terra ave terra ave ave terra terra
 terra terra ave terra terra terra terra terra
 terra ave ave ave terra ave ave ave
 terra ave ave terra ave ave ave ave
 terra ave ave ave terra terra ave terra
 terra ave ave terra ave ave terra terra
 terra ave ave terra terra ave terra terra
 terra terra ave terra terra terra terra ave
===bounded rule30 automaton===
It's very small, but it's there.
 x x x
 h h h
 x
 z z x z z z a a
 z z x z z z b b
 z z x z z z c c
 z z x z z z d d
 z z x z z z e e 
 z z x z z z f f
 z z x z z z g g
 z z x z z z h h
 z z z z x z x z
 i a a
 j b b
 o i j
 i null null
 j o o
 k null j
 l i o
 aa k l
 i b b
 j c c
 o i j
 i a a
 j o o
 k a j
 l i o
 bb k l
 i c c
 j d d
 o i j
 i b b
 j o o
 k b j
 l i o
 cc k l
 i d d
 j e e
 o i j
 i c c
 j o o
 k c j
 l i o
 dd k l
 i e e
 j f f
 o i j
 i d d
 j o o
 k d j
 l i o
 ee k l
 i f f
 j g g
 o i j
 i e e
 j o o
 k e j
 l i o
 ff k l
 i g g
 j h h
 o i j
 i f f
 j o o
 k f j
 l i o
 gg k l
 i h h
 j null null
 o i j
 i g g
 j o o
 k g j
 l i o
 hh k l
 a aa aa
 a a a
 b bb bb
 b b b
 c cc cc
 c c c
 d dd dd
 d d d
 e ee ee
 e e e
 f ff ff
 f f f
 g gg gg
 g g g
 h hh hh
 h h h
 x
===Rock, Paper, Scissors===
The following plays a game of Rock, Paper, Scissors against the user. Input is accepted as a single character from STDIN: r ⇒ rock, p ⇒ paper, s ⇒ scissors. It then displays the computer's move, chosen randomly, as well as the result from the computer's perspective - Win!, Lose or Draw. The script may be run interactively, by typing your move followed by Enter.
 not sure, but right now i'm guessing you're_not_paper you're_scissors
 you're_paper not you're_not_paper
 you're_not_scissors not you're_scissors
 you're_rock you're_not_paper you're_not_scissors
 you're_rock you're_rock
 
 o_shi-
 i'm_not_paper right ?
 i'm_scissors right ?
 i'm_paper not i'm_not_paper
 i'm_not_scissors not i'm_scissors
 o_shi- i'm_paper i'm_scissors
 o_shi- o_shi-
 o_shi-
 i'm_rock i'm_not_paper i'm_not_scissors
 i'm_rock i'm_rock
 
 print right now but only if i'm_not_paper i'm_scissors
 print a newline here, not more, not less
 
 i_win_if i'm_scissors you're_paper
 or_if i'm_rock you're_scissors
 or_even_if i'm_paper you're_rock
 
 i_win i_win_if or_if
 i_win i_win
 i_win or_even_if
 
 i_lose_if i'm_paper you're_scissors
 or_if i'm_scissors you're_rock
 or_even_if i'm_rock you're_paper
 
 i_lose i_lose_if or_if
 i_lose i_lose
 i_lose or_even_if
 
 i_don't_win not i_win
 i_don't_lose not i_lose
 we_tie i_don't_win i_don't_lose
 we_tie we_tie
 we_don't_tie not we_tie
 
 print now if i_win i_lose not i_win i_win
 print but not we_tie we_don't_tie i_lose i_don't_win we_don't_tie
 print right now i_lose i_win i_win we_don't_tie i_don't_win
 print i_don't_win but we_tie or i_don't_win we_tie now
==Haskell implementation==
Note: This implemenation V0.2 is deprecated, the current version is V0.5
import qualified Data.Map as M
import System.Environment (getArgs)
type Prog = [[String]]
data LabelData = L (M.Map String LabelData) Prog
value v vs = M.findWithDefault False v vs
x `nand` y = not (x && y)
runCmds :: M.Map String Bool -> M.Map String LabelData -> [[String]] -> String
runCmds vars labels prog@([label]:rest)
 = case (value label vars, M.lookup label labels) of
 (True, Just (L labels' prog'))
 -> runCmds vars labels' prog'
 _ -> runCmds vars labels' rest
 where labels' = M.insert label (L labels' rest) labels
runCmds vars labels ([v1,v2,v3]:rest)
 = runCmds (M.insert v1 (value v2 vars `nand` value v3 vars) vars)
 labels rest
runCmds vars labels (vs@[_,_,_,_,_,_,_,_]:rest)
 = c : runCmds vars labels rest
 where
 c = toEnum $ foldl (\n b -> n*2 + fromEnum (value b vars)) 0 vs
runCmds _ _ [] = ""
runCmds _ _ (snt:_) = error $ "Undefined sentence: " ++ unwords snt
interpret = runCmds M.empty M.empty . map words . lines
main = do
 args <- getArgs
 case args of
 [] -> interact interpret
 [fileName] -> putStr . interpret =<< readFile fileName
 _ -> error "Too many arguments, only one supported"
==Python implementation==
# FerNANDo Interpreter
# Runs fernando programs, used command line style.
# Author: Orange
# Editors: Stop h time, Oerjan, Primo
# Version 0.5
# You're free to use this code as you wish
class varfunc(object):
 def __init__(self, func): self._func = func
 __and__ = lambda self, other: self._func() & other
 __rand__ = lambda self, other: other & self._func()
 __add__ = lambda self, other: self._func() + other
 __radd__ = lambda self, other: other + self._func()
 __nonzero__ = lambda self: bool(self._func())
def FerNANDo(program, no_prng=False):
 Run a FerNANDo program 
 #Split the program into a sequence of lines
 p = program.split('\n')
 #Split each line into variables
 p = map(lambda a: a.split(), p)
 pc = 0 #program counter
 plen = len(p) #program length
 var = {} #variable look-up table
 if not no_prng:
 var['?'] = varfunc(lambda: random.randint(0, 1)) #PRNG
 while pc < plen:
 #Current line
 line = p[pc]
 #See how many variables on a line
 #If 1, 2, 3, 8, or 9, execute a command
 #Check in order of likelihood
 count = len(line)
 if count == 3 or count == 2: #NAND
 #bitwise & instead of 'and' is necessary
 #to prevent varfuncs from evaluating twice
 var[line[0]] = not (var.get(line[-2], 0) & var.get(line[-1], 0))
 elif count == 8: #Output
 ascii = 0
 for item in line:
 #Double the value (<<1) and add the next bit
 ascii += ascii + var.get(item, 0)
 sys.stdout.write(chr(ascii))
 elif count == 1: #Jump
 #Var is set and has occurred previously
 if bool(var.get(line[0], 0)) and (line in p[:pc]):
 #Find the previous occurrence
 pc -= p[:pc][::-1].index(line) + 1
 elif count == 9: #Read
 char = sys.stdin.read(1)
 if char:
 var[line[0]] = 1
 ascii = ord(char)
 shift = 7
 for item in line[1:]:
 var[item] = (ascii >> shift) & 1
 shift -= 1
 else:
 var[line[0]] = 0
 elif count==0: #Empty
 pass
 else:
 sys.stderr.write('Error: Undefined sentence on line %d: '%(pc+1) + ' '.join(line))
 sys.exit(1)
 pc += 1
import sys, random, argparse
try:
 parser = argparse.ArgumentParser(description='Run a FerNANDo program')
 parser.add_argument('progname', metavar='progtorun.nand', type=str,
 help='the filename of the FerNANDo program to run')
 parser.add_argument('--no-prng', dest='no_prng', action='store_true',
 help="disable the PRNG bit variable '?' (leave it unset)")
 args = parser.parse_args()
 
 f = open(args.progname)
 prog = f.read()
 f.close()
 FerNANDo(prog, args.no_prng)
except IOError:
 sys.stderr.write("Error: Can't open file '%s'"%args.progname)
 sys.exit(1)
== Other programs on FerNANDo ==
There are a few solutions at Anarchy Golf.
Enormous 7MB quine by kikx is described on this page and following two pages.
== See also ==
* Stringle, a language with a similar way of selecting between different commands by word count like ferNANDo.
Category:Languages
Category:Implemented
Category:Unknown computational class
Category:2009