Funge-98
Funge-98 is the successor to, and a generalization of, Befunge. It supports 1- and 3- dimensional funges, and includes many new commands.
Paradigm(s) | imperative |
---|---|
Designed by | Chris Pressey |
Appeared in | Category:1998 |
Memory system | stack-based |
Dimensions | two-dimensional |
Computational class | Turing complete (Befunge-98) |
Reference implementation | |
Dialects | Befunge-93, Befunge-98 |
File extension(s) | .b98,.tf,.uf |
History
It started with a typo in 1993. Chatting at 4am, Curtis Coleman typed the word "before" as "befunge". Chris Pressey immediately knew that the typo needed to be made more famous and started pondering on an esoteric programming language (before esoteric was actually the common name for the field) deserving of the name. Some few weeks later, Befunge was born; an attempt to create a language that could be interpreted but would defy compilation. Get and Put commands allowed the program space to be modified at runtime and the instruction pointer is free to travel in multiple directions. Befunge was one of the first 2D programming languages. Chris didn't know at the time of invention, but Ward Cunningham, famous for conceptualizing the first Wiki (wiki wiki), had developed a 2 dimensional programming system, called Biota, a few years prior in 1991 as an experiment in cellular automata theory. Just after the early Befunge code was being written, Orthagonal was being independently developed by Jeff Epler (announced in September 1994) as another early multi dimensional programming system.
Befunge was revisited by Chris through the 1990s and specifications for an extended version went through a few iterations. Befunge-97 was not quite right, but Befunge-98 seemed to hit the sweet spot of esoteric yet practical. Funge-98 was born. Unefunge (1D programming), Befunge (2D programming - the most common case), Trefunge (3D programming - many Funge-98 implementations support 3D) and a spec that allowed for Nefunge higher dimensions. Since the release of the Funge-98 specification, original Befunge is now sometimes referred to as Befunge-93.
The first implementation of Befunge-98 was FBBI, the Flaming Bovine Befunge-98 Interpreter, written by Chris Pressey of Cat's Eye Technologies. It does not claim to be a reference implementation (as the language is defined by the Funge-98 Specification rather than any implementation) and was buggy and unmaintained for many years (FBBI version 1.0 was not released until 2011). Since the Funge-98 Specification's inception, many other implementations of the Funge-98 languages (Unefunge-98, Befunge-98, Trefunge-98) have been written by many people.
Interest in Befunge has been fairly steady over the last 20+ years, and quite a few Befunge-93 implementations are available. Befunge enjoys a respectable number of solutions to programming tasks on Rosetta Code, the language comparison site. https://rosettacode.org/wiki/Category:Befunge
In 2006 Matti Niemenmaa started working on a Befunge-98 interpreter (CCBI) in the D programming language and developed a test suite that he called Mycology, to verify compliance with the Funge-98 spec. This test suite has been used for Funge-98 compliance validation for most of the Funge implementations ever since. While working with Mycology and CCBI, Matti worked with Arvid Norlander, the developer of cfunge and efunge, to iron out some of the kinks in the edge cases that are part and parcel of esoteric programming.
The existence of Mycology has helped create a very robust Funge-98 implementation environment, and practitioners of the fungal arts get to enjoy an arguably higher level of compatibility than is common in esoteric programming circles. Especially given that Funge-98 and the Fingerprint extensions can be somewhat complicated. Implementations have been developed in C, D, Erlang, ECMAScript, Clojure, to name a few of the available environments, and they are all closely compatible when executing complex Funge code.
OS Integration
Concurrency
There is a "t" command that splits an IP and allows concurrent operations using an IP list. Each IP in the list has a separate Position, Delta and Offset. Command sequences are defined by a tick concept, each IP in the list executing a tick for each step in a sequential circular loop (which is always in the same order until "threads" are created via the Split "t" command or end via the Stop "@" command.
When a Split occurs, the original IP continues along its delta and the new IP reflects.
"@" removes an IP from the list when stopping, further entries shuffled up to keep the same order. When there are no remaining IPs, a Funge program will end. The "q" quit command ends a Funge program regardless of how many active IPs are in the list.
Stack Stack
Along with the normal stack, Funge-98 defines Block Start/Block End "{" "}" operations that can manage a stack of stacks. TOSS is the Top of stack stack and SOSS becomes a Second to top of stack stack. The "u" under command provides a facility for moving SOS stack items up to the TOS stack. The block operators allow Funge routines to work on a stack without disturbing existing contents, and provides a method of digging down into a stack by copying parts of the TOSS to a SOSS (which effectively becomes TOSS as soon as the block command completes) and then manipulating items with judicious use of Under.
FungeSpace
Schematics
Cats eye, RC Funge
Instructions
Funge-98 includes and expands on the original Befunge instruction set.
ASCII | Instruction | Before | After | Other Effects |
---|---|---|---|---|
space
|
Space | not normally executed; k iterator ignores multiples (unless 0) | ||
!
|
Logical Not | a | Not a | Zero becomes one, non zero becomes zero |
"
|
Toggle Stringmode | Pushes each value between quotes to stack | ||
#
|
Trampoline | pos <- pos + delta (IP skips over one command position) | ||
$
|
Pop | a | ||
%
|
Remainder | a b | a%b | Pop b, pop a, push remainder of dividing a by b (or push zero if b is zero). Negative arguments produce implementation-defined results. |
&
|
Input Integer | a | Signed range determined by cell size; reflect on EOF | |
'
|
Fetch Character/98 | c | pos <- pos + delta (character is not executed) | |
(
|
Load Semantics/98 | en..e1 n | f 1 | overloads A-Z, given fingerprint; reflect on load error |
)
|
Unload Semantics/98 | en..e1 n | unloads A-Z overloaded by given fingerprint, may reflect | |
*
|
Multiply | a b | ab | Signed range determined by cell size |
+
|
Add | a b | a+b | Signed range determined by cell size |
,
|
Output Character | c | ||
-
|
Subtract | a b | a-b | Pop b, pop a, push a-b. Signed range determined by cell size |
.
|
Output Integer | a | ||
/
|
Divide | a b | a/b | Pop b, pop a, push a/b (or push zero if b is zero). |
0
|
Push Zero | 0 | ||
1
|
Push One | 1 | ||
2
|
Push Two | 2 | ||
3
|
Push Three | 3 | ||
4
|
Push Four | 4 | ||
5
|
Push Five | 5 | ||
6
|
Push Six | 6 | ||
7
|
Push Seven | 7 | ||
8
|
Push Eight | 8 | ||
9
|
Push Nine | 9 | ||
:
|
Duplicate | v | v v | |
;
|
Jump Over/98 | nothing executed until next ; | ||
<
|
Go West | delta <- Une(-1), Be(-1,0), Tre(0,-1,0) | ||
=
|
Execute/98/f | 0gnirts | r | result-code = system-execute(string) |
>
|
Go East | delta <- Une(1), Be(1,0), Tre(0,1,0) | ||
?
|
Go Away | delta <- random direction, (1/-1), (-1,0/1,0/0,1/0,-1), (-1,0,0/.../-1,-1,-1) | ||
@
|
Stop | halt current IP, if last concurrent thread then end program | ||
A-Z
|
Fingerprint-Defined/98 | Overloadable. If not overloaded, act as 'r'. | ||
[
|
Turn Left/98/2D | delta <- rot(-90, delta) | ||
\
|
Swap | a b | b a | Pop b, pop a, push b, push a |
]
|
Turn Right/98/2D | delta <- rot(90, delta) | ||
^
|
Go North/2D | delta <- Be(0,-1), Tre(0,-1,0). Reflects in Unefunge | ||
_
|
East-West If/2D | n | Act as '>' if n is zero, otherwise '<'. | |
`
|
Greater | a b | a>b | Pop b, pop a. If a is greater than b, push 1; otherwise push 0. |
a
|
Push Ten/98 | 10 | ||
b
|
Push Eleven/98 | 11 | ||
c
|
Push Twelve/98 | 12 | ||
d
|
Push Thirteen/98 | 13 | ||
e
|
Push Fourteen/98 | 14 | ||
f
|
Push Fifteen/98 | 15 | ||
g
|
Get | x [y [z]] | n | Push contents of (x) Une, (x,y) Be, (x,y,z) Tre. Push 0 when out of bounds |
h
|
Go High/98/3D | delta <- (0,0,-1). Reflects in Unefunge and Befunge | ||
i
|
Input File/98/f | x [y [z]] f 0gnirts | x [y [z]] x' [y' [z']] | inputs file given string name. Low bit set in flag f for binary mode |
j
|
Jump Forward/98 | s | pos <- pos + delta * s | |
k
|
Iterate/98 | n | execute next instruction now, 0 or n+1 times (k does not skip IP unless 0) | |
l
|
Go Low/98/3D | delta <- (0,0,1). Reflects in Unefunge and Befunge | ||
m
|
High-Low If/98/3D | b | delta <- if not zero, go high (0,0,-1) else (0,0,1) | |
n
|
Clear Stack/98 | en..e1 | Top of stack stack for current thread is emptied | |
o
|
Output File/98/f | x [y [z]] x' [y' [z']] f 0gnirts | outputs Funge space between start/end to named file, low bit set in f for binary | |
p
|
Put | n x [y [z]] | n | Pop vector, then pop n. Store n at that location in Funge space. |
q
|
Quit/98 | r | immediate exit, returncode = r | |
r
|
Reflect/98 | delta <- delta * -1 | ||
s
|
Store Character/98 | c | store-funge-space(position+delta,v). | |
t
|
Split/98/c | Split IP. Position, delta, stacks copied, child reflects. | ||
u
|
Stack Under Stack/98 | n | (en..e1) | Pushes extra zeros if SOSS contains less than n items. |
v
|
Go South | delta <- (1) Une, (0, 1) Be, (0, 1, 0) Tre | ||
w
|
Compare/98/2D | a b | if (a>b) ']' elsif (a<b) '[' else 'z' | |
x
|
Absolute Delta/98 | x [y [z]] | delta <- (x,[y,[z]]) | |
y
|
Get SysInfo/98 | c | en(..e1) | Retrieve item n for n>0 else all items |
z
|
No Operation/98 | |||
{
|
Begin Block/98 | en..e1 n | (en..e1) | offset <- pos + delta, etc |
|
|
North-South If/2D | n | Act as 'v' if n is zero, otherwise '^'. In Unefunge, act as 'r'. | |
}
|
End Block/98 | en..e1 n | (en..e1) | offset <- SOSS Va, etc |
~
|
Input Character | c | reflect on EOF |
Fingerprints
The Funge-98 specification allows for extensions to the Funge instruction set via loadable Fingerprints. Capital A through Z instructions can be overloaded using the "(" Load Semantics/98 op-code and unloaded using ")". The specification allows arbitrary mixing of the redefinitions.
Some well known fingerprints
The FBBI (Flaming Bovine Befunge-98 Interpreter) supports some initial extensions that are defined along with the Funge-98 specification.
The Rc/Funge-98 and cfunge interpreters supports a wide range of overload semantics, many designed by the Rc/Funge-98 project.
gnirts form and character count is usually used to feed the Load Semantics op-code, "TOYS" being "SYOT"4. The "(" command reflects on load failure, or leaves two items on the stack on success. This pair can be used to ")" unload the fingerprint. The pair is an ID and a count of 1 that matches the fingerprint loader/unloader needs. This pair is often discarded, and the gnirts form used to feed the unload operation.
rcfunge supports a -ux run option that opens an X11 window for on screen Turtle graphics display.
Example of fingerprint loading
v ;Overlapping fingerprint; ;Load BOOL and N takes on a Not semantic; >0a"detroppus ton LOOB">:#,_$@ >0a"detcelfer N :DAB">:#,_$@ >"LOOB"4#^(7#^N7." toN"3k," si"2k,.a, v v ;Load TOYS and now N is Negate; < >0a"detroppus ton SYOT">:#,_$@ >0a"detcelfer N :DAB">:#,_$@ >"SYOT"4#^(7#^N7." detageN"7k," si"2k,.a,v v ;Load NULL and N now reflects; < >0a"detroppus ton LLUN">:#,_$@ >0a"LLUN edisni detcelfer N :DOOG">:#,_$ v >"LLUN"4#^(:.\:.\#^N0"tcelfer t'ndid O :DAB">:#,_$@ ;Fingerprints still on stack; v ;Unload NULL and restore N as Negate.; < >0"detcelfer daolnu LLUN :DAB">:#,_$@ >#^) v v < >0a"detcelfer N :DAB">:#,_$@ >7#^N7." detageN"7k," si"2k,.a, v v ;Unload TOYS and restore N as Not; < >0"detcelfer daolnu SYOT :DAB">:#,_$@ >#^) v v < >0"detcelfer N :DAB">:#,_$@ >7#^N7." toN"3k," si"2k,.a,0a"gnixim tnirpregnif">:#,_$@
Sample run:
prompt$ cfunge fingerprint.b98 7 Not is -8 7 Negated is -7 1 1314212940 GOOD: N reflected inside NULL 7 Negated is -7 7 Not is -8 fingerprint mixing
Examples
Hello World
<v"Hello World!" >:v ^,_@
Display y sysinfo
Works with Befunge and Trefunge, but not Unefunge. Requires BOOL fingerprint support.
v >'t,v >'i,v >'o,v >'=,v >"LOOB"4#v($$0y0" stroppus">:#,_$:1A| >:2A| >:4A| >:8A| >$a,v >0a"LOOB on">:#,_1y.@ > ^ > ^ > ^ > ^ v ,a.,k7"version ",a.,k9"handprint ",a,kd"bytes per cell".< >" cexe"4k,:3-#v_"llehs emas"9k, v >:2-#v_"llehs emos"9k, v >:1-#v_"metsys"5k, v >"elbaliavanu"ak,v v ,a,,ke"path separator ",a$< >" )snoisnemid( ezis rotcev"55*1-k,.a, v v,a.k-1y7,kb"IP location ",a.,k7"team id ",a.,k5"IP id "< >" atled"5k,7y1-k.a," tesffo"6k,7y1-k.a, v v,a.k-1y7,k7"largest ",a.k-1y7,k6"origin "< >" raey"4k,:82*82**82*82***/a9+aa**+. v v./**28*28A*-1**28*28**28*28:,k5"month "< >" yad"3k,82*82**1-A. v v./***28*28**28*28:,k4"hour "< >" etunim"6k,:82*82**82*82**1-*A82*82**/.vv < v ,a.A-1**28*28,k6"second "< >'",>:#,_$'",' ,^ v v-1.\_$a," enil-dnammoc"ck,>:| > #v^# < >" skcats"6k,:.> :^ >$:| >'",>:#,_$'",' ,^ >a," vne"3k,>:| > ^ >$:#^_a,@ ;display Funge-98 y sysinfo; BOOL fingerprint required;
Sample outputs (environment space display elided):
cfunge (sandbox)
prompt$ cfunge -S sysinfo.b98 'a b c' a b c supports t 8 bytes per cell handprint 1128682830 version 90 exec unavailable path separator / vector size (dimensions) 2 IP id 0 team id 0 IP location 1 14 delta 0 1 offset 0 0 origin 0 0 largest 23 77 year 2017 month 7 day 31 hour 20 minute 22 second 49 stacks 1 0 command-line "sysinfo.b98" "a b c" "a" "b" "c" env "COLORTERM=truecolor" "LC_TIME=en_CA.utf8" ... "SHELL=/bin/bash"
rcfunge (in Trefunge mode)
prompt$ rcfunge -3 sysinfo.b98 'a b c' a b c supports tio= 4 bytes per cell handprint 1380143957 version 20300 exec same shell path separator / vector size (dimensions) 3 IP id 0 team id 0 IP location 0 5 14 delta 0 0 1 offset 0 0 0 origin 0 0 0 largest 0 23 77 year 2017 month 7 day 31 hour 16 minute 35 second 1 stacks 1 0 command-line "sysinfo.b98" "a b c" "a" "b" "c" env "COLORTERM=truecolor" ... "MANPATH=/usr/local/share/man:" "XDG_VTNR=1" VM sysinfo.b98 ended Instructions Executed: 35148 in 35174 cycles Execution time : 0.04 seconds Instructions per second: 799654.19 Exiting with return code = 0
Rock Scissors Paper, on 3
v > v >a"3 no"4k,1.0"1 peels"#^=$>2.0"1 peels"#v=$v v < < v,k3"rock"?"srossics"7k,v " r e p a p " 4 k , @ v < >'y-#^_a,0by-8-0ay-x > >0"2 peels"#v=$>a," ?niaga yalp"ack,>~:a-| > ^ ^ $ < Rock Scissors Paper, on 3
Plays a game of Rock Scissors Paper with delay count (if system exec "=" is supported, if not, computer player displays choice immediately). Any response to play again? other than 'y' will terminate game. Any buffered newlines are eaten.
Implementations
The Funge-98 specification was written by Chris Pressey of Cat's Eye Technologies. Chris invented the original Befunge back in 1993.
Incomplete list
Name | Author(s) | Source | License | Commentary |
---|---|---|---|---|
FBBI, Flaming Bovine Befunge-98 Interpreter | Chris Pressey, inventor of Befunge | C | BSD license | First implementation of Befunge-98. Not a reference implementation, but probably mistakenly used as a reference on at least one occasion. |
CCBI, Conforming Concurrent Befunge-98 Interpreter | Matti Niemenmaa (Deewiant) | D 1.0 | BSD (with Tango) | Deewiant is also the author of the Mycology Funge-98 verification suite. |
cfunge | Arvid Norlander, (VorpalBlade) | C | GPL v3 | A complete Funge-98 interpreter, many fingerprints supported. |
rcfunge | Michael Riley, Susan now listed as maintainer | C | Free for non commercial use | Rc/Funge-98 is the source of many of the known Funge fingerprints. |
jsfunge | Pietu1998 | ECMAScript, HTML, CSS | Unknown | In browser Funge-98 interpreter. |
efunge | Arvid Norlander, (VorpalBlade) | Erlang | GPL v3 | An Erlang based interpreter by the author of cfunge. |
pyfunge | Kang Seonghoon | Python | permissive MIT/X11 | Documentation hosted at https://pythonhosted.org/PyFunge/ |
Clostridium | Tom Parker | Clojure | AGPLv3 | Development article. |
See also
External resources
- Funge-98 entry at Cat's Eye Technologies
- Funge-98 specification
- vsync's Funge stuff.
- Mycology and CCBI A complete Befunge-98 test suite based on the specification, and an interpreter which passes all the tests.
- Closidrium - Author retrospective of creating a Befunge-98 Interpreter in Clojure.
- BefungeSharp A Funge-98 IDE being written in C#. Designed to have a strong editor and a complaint interpreter. Currently in development and well supported by its author.
- Fungus (from the Wayback Machine; retrieved on 22 March 2007) - a nice Befunge-98 IDE for Win32. Warning: its interperter is not fully standards compliant.
- mooz' Befunge page (from the Wayback Machine; retrieved on 25 September 2006) - contains a Javascript interpreter and several interesting Befunge programs.
- BeQunge A cross-platform Funge-98 interpreter, code editor, and debugger. Works in any number of dimensions.
- J^4: Befunge Jeffrey Lee's Befunge site, features plenty of interesting programs.
- cfunge - Small fast Befunge-98 interpreter in C, standard compliant. Updated from SourceForge
- efunge - Erlang implementation of Befunge-98, standard compliant. Has an experimental branch with a fingerprint for asynchronous threading.
- Rc/Funge-98 - rcfunge, implementation in C, diagnostics, tutorials and documentation. Very complete; Une, Be, Tre, including the Time Travel fingerprint.
- Archive of the above hosted on Github
- Sponge - a compiler (in Common Lisp) from a tiny subset of Scheme to Befunge 98.
- PyFunge - A Befunge-93/Funge-98 interpreter in Python. Its goal is a fully functional, compliant and optimizing implementation of Funge-98.
- Fungi - A standards compliant Funge-98 interpreter and debugger written in Haskell.
- Multilang - A shell supporting multiple languages, including Befunge-98.
- Fungewars - A programming game in Funge-98.