Befunge93 (empty) → 0.1
raw patch · 12 files changed
+1232/−0 lines, 12 filesdep +arraydep +basedep +mtlsetup-changedbinary-added
Dependencies added: array, base, mtl, random
Files
- Befunge.hs +512/−0
- Befunge93.cabal +64/−0
- LICENSE +30/−0
- README +23/−0
- Setup.hs +3/−0
- Tests.hs +61/−0
- tests/mycology/license.txt +24/−0
- tests/mycology/mycology.b98 binary
- tests/mycology/readme.txt +498/−0
- tests/phlamethrower/README +3/−0
- tests/phlamethrower/compat.bf +8/−0
- tests/phlamethrower/divtest.bf +6/−0
+ Befunge.hs view
@@ -0,0 +1,512 @@+module Main + where++--- Copyright Brandon Simmons, 2010+--- http://coder.bsimmons.name/blog/ ++-- TODO:+ -- performance improvements+ -- consider switch over to IO Array or Vector?+ -- catch errors in exec block+ -- constrain grid to max proportions of array,+ -- - expanding up to 80x25 when we have a 'p'+ -- command outside the grid.+ -- - Warn when we try to expand past 80x25, but+ -- do expand.+++-- helpers:+import System.Environment+import Data.Char+import Control.Arrow (first,second,(&&&),(|||))++-- we need to be able to modify our array efficiently+-- on 'p' calls. Using an IO Array would be easy here. For+-- now we use basic unboxed immutable array. +import Data.Array.Unboxed++-- we do a lot of work with IO in the State monad using StateT:+ --- using 'mtl' library+import Control.Monad.State.Strict+ --- using 'transformers' library: SLOWER THAN 'MTL'+--import Control.Monad.Trans.State.Strict+--import Control.Monad+--import Control.Monad.IO.Class++-- we need to be able to do randomness if we see '?':+import System.Random++-- error catching:+import System.IO+import System.IO.Error++--------------------------------------------------------------------------------++-- position of the "program counter":+type Position = (Int,Int)++-- directions as contintuations:+type Direction = Position -> Position++-- the source code is mapped onto a 2d array as Int ASCII values:+type Code = UArray Position Int++-- The stack stores integers, per the Funge-93 spec. When in "stringmode"+-- we push the ASCII value of the characters we read: +type Stack = [ Int ]++-- the state of our befunge program's execution. This is passed around+-- and modified as IO actions are performed:+data ProgramState = + ES { -- state of the code and stack:+ code :: Code, + stack :: Stack,+ -- state of program flow:+ position :: Position, + direction :: Direction,+ haltBit :: Bool,+ -- random generator:+ randGen :: StdGen, + -- should we announce messages and warnings?:+ verbose :: Bool+ } ++-- the default initial state of a befunge computation:+defaultState = ES { + -- these are defaults:+ haltBit = False,+ position = (0,0), + direction = right,+ stack = [],+ randGen = mkStdGen 31337, + verbose = True, + code = array ((0,0),(0,0)) [] }++++-- we are using the State monad for our plumbing, but we need to be in +-- the IO monad, so we use the State monad transformer! This is our+-- read-evaluate-print loop:+type REPL a = StateT ProgramState IO a++-- our command line paramaters:+data Opts = Opts { quiet :: Bool,+ allow_oversize :: Bool,+ seed :: Maybe Int,+ srcFile :: FilePath }++-- by default we assume input is a correct funge-93 program:+defaultOpts = Opts False False Nothing ""+ +usage = "\n\tusage: " +++ "befunge-93 [--quiet] [--allow-oversize] [--seed INT] source.bf\n"+++--------------------------------------------------------------------------------+ --PROGRAM INITIALIZATION--+--------------------------------------------------------------------------------++main = do + -- parse command line options, then build our initial state for our befunge+ -- computation, catching any IO errors in the process:+ --state_i <- try $ processOpts . parseArgs =<< getArgs+ args <- getArgs+ opts <- either parsingErrors return (parseArgs args)+ state_i <- try $ processOpts opts+ + -- it seems most programs expect input to be line-buffered: + hSetBuffering stdin LineBuffering++ -- if there are errors, then print them and exit...+ either processingErrors+ -- else supply our initial state to the State/IO magic monadic+ -- machinery and begin executing our befunge program:+ (evalStateT evalLoop) state_i+++--------------------------------------------------------------------------------+ --EVALUATION LOOPS--+--------------------------------------------------------------------------------++evalLoop :: REPL ()+evalLoop = do+ -- extract the command at our position & execute it:+ getCmd >>= execute+ -- halt if @ command issued, else move and recurse:+ halting <- gets haltBit+ unless halting (move >> evalLoop) + ++-- we push characters' ASCII values onto the stack until we hit " again:+stringModeLoop :: REPL ()+stringModeLoop = do + c <- getCmd+ unless (c == '"') $ do+ push (ord c)+ move+ stringModeLoop+ +-- this is the code that does all the work: a function which takes a program+-- state and does IO operations in the State/IO monads:+execute :: Char -> REPL ()+execute c = + case c of+ -- stack operations: --+ '+' -> do (a,b) <- pop2+ push (a+b)++ '-' -> do (a,b) <- pop2+ push (b-a)+ + '*' -> do (a,b) <- pop2+ push (a*b)+ + -- according to the befunge-98 spec, befunge-93 should ask the user+ -- what the result of the division by zero should be. so elegant:+ '/' -> do (a,b) <-pop2+ safeZero b quot a >>= push + + '%' -> do (a,b) <- pop2+ safeZero b rem a >>= push + + '!' -> do i <- pop+ if i == 0 + then push 1+ else push 0++ '`' -> do (a,b) <- pop2 + if b>a+ then push 1+ else push 0++ '$' -> do pop + return ()+ + -- Pop value and output as an integer. funge-98 spec calls for+ -- integer to be followed by a space, so we'll do that too:+ '.' -> do i <- pop+ liftIO $ putStr $ show i ++" "+ + -- Pop value and output as ASCII character+ ',' -> do i <- pop+ liftIO $ putChar $ chr i++ '\\' -> do (a,b) <- pop2+ push a+ push b+ + ':' -> do a <- pop+ replicateM_ 2 (push a)+ + -- program flow commands: --+ ' ' -> return () ++ '>' -> setDirection right+ + '<' -> setDirection left + + '^' -> setDirection up+ + 'v' -> setDirection down+ + '?' -> getRandomDirection >>= setDirection + + -- hop over the next cell:+ '#' -> move+ + '@' -> halt+ + -- stack and program flow: --+ '"' -> move >> stringModeLoop+ + + '_' -> do a <- pop+ if a == 0+ then setDirection right+ else setDirection left+ + '|' -> do a <- pop + if a == 0+ then setDirection down+ else setDirection up+ + -- IO operations: --+ 'p' -> do (y,x) <- pop2+ pop >>= putCell (x,y) + + 'g' -> do (y,x) <- pop2 + getCell (x,y) >>= push++ '&' -> liftIO askInt >>= push+ + '~' -> liftIO askChar >>= push++ x -> if isDigit x + then push (digitToInt x)+ -- halt and complain if invalid command:+ else do loc <- gets position+ halt+ maybeSay$ "unknown command '" ++ show x ++ + "' in code path at " ++ show loc +++ ". Maybe you want a funge-98 interpreter?"+ ++--------------------------------------------------------------------------------+ -- LOOP HELPERS -- +--------------------------------------------------------------------------------++ -- -- -- SPECIAL ARITHMETIC -- -- --++safeZero :: Int -> (Int -> Int -> Int) -> Int -> REPL Int+safeZero b func a =+ if a /= 0+ then return (b `func` a)+ else do liftIO $ putStr + "\ndiv by zero; enter desired Int result: "+ liftIO askInt+ ++ -- -- -- STACK HELPERS -- -- --++-- when empty stack, we are supposed to pop 0:+pop :: REPL Int+pop = do + st <- gets stack + if null st + then return 0 + else do modify $ \s-> s{stack = tail st} + return (head st) ++pop2 :: REPL (Int,Int)+pop2 = do a <- pop+ b <- pop+ return (a,b)++push :: Int -> REPL ()+push c = modify $ \s-> s{stack = c : stack s}+ +++ -- -- -- MISC STATE HELPERS -- -- --++-- the command character at our position:+getCmd :: REPL Char +getCmd = do p <- gets position + gets (chr . (! p) . code) +++getCell :: Position -> REPL Int+getCell xy = do+ xy' <- wrap xy+ unless (xy == xy') (maybeSay "warning: getCell out of bounds. wrapping.")+ gets ((! xy') . code)+++-- modify our array by changing its value at position xy:+putCell :: Position -> Int -> REPL ()+putCell xy c = do+ xy' <- wrap xy+ unless (xy == xy') (maybeSay "warning: putCell out of bounds. wrapping.")+ a <- gets code+ modify $ \s-> s{code = a//[(xy',c)] }+++-- update state with new seed, returning a random direction:+getRandomDirection :: REPL Direction+getRandomDirection = do+ (i,g) <- gets (randomR (0,3) . randGen)+ modify $ \s-> s{randGen = g}+ return ([up,down,left,right] !! i)+++ -- -- -- PROGRAM FLOW HELPERS -- -- --++halt :: REPL ()+halt = modify $ \s-> s{ haltBit = True }+++setDirection :: Direction -> REPL ()+setDirection d = modify $ \s-> s{direction = d}+ ++move :: REPL ()+move = do+ -- we need to know when we're out of bounds, so we can wrap:+ (pos,mv) <- gets (position &&& direction)+ pos' <- wrapSimple (mv pos)+ modify $ \s-> s{position = pos'}+++-- if we exceed bounds, we wrap like a torus:+wrap :: Position -> REPL Position+wrap (pX,pY) = do+ (bX,bY) <- gets (snd . bounds . code)+ let x = pX `mod` (bX+1)+ y = pY `mod` (bY+1)+ return (x,y)++-- DIFFICULT TO TELL IF THIS IS SIGNIFICANTLY FASTER THAN `wrap`:+-- a more efficient wrapping function, for the program counter. Because+-- the PC moves only one space at a time, we can use this in the 'move'+-- function:+wrapSimple :: Position -> REPL Position+wrapSimple (pX,pY) = do+ (bX,bY) <- gets (snd . bounds . code)+ let x | pX < 0 = bX+ | pX > bX = 0+ | otherwise = pX+ y | pY < 0 = bY+ | pY > bY = 0+ | otherwise = pY+ return (x,y)++++-- we use continuations for the direction, because we never need to inspect+-- our direction, thus a function is appropriate:+down, up, left, right :: Direction+down = second (+1)+right = first (+1)+left = first (subtract 1)+up = second (subtract 1)++++ -- -- -- IO HELPERS -- -- --++askInt :: IO Int+askInt = do n <- getLine + if isInt n + then return (read n)+ else putStr "NOT AN INT. Try again: " >> askInt+ ++askChar :: IO Int+askChar = getChar >>= return . ord +++isInt :: String -> Bool+isInt = all isDigit++ -- -- -- ERROR AND DEBUGGING HELPERS -- -- --++maybeSay :: String -> REPL ()+maybeSay m = do+ v <- gets verbose+ when v (liftIO $ putStrLn m )++++--------------------------------------------------------------------------------+ -- MAIN IO HELPERS --+--------------------------------------------------------------------------------+++ -- -- -- CLI ARGUMENTS PROCESSING -- -- -- ++-- convert user-supplied options in our Opt container into+-- an initial state, in the IO monad:+processOpts :: Opts -> IO ProgramState+processOpts os = do+ let loud = not $ quiet os -- print warnings+ bigOk = allow_oversize os -- okay if grid is oversize+ + -- build code grid from source file; we return the code array+ -- and a Bool set to True if the input file was larger than the+ -- standard 80x25:+ hdl <- openFile (srcFile os) ReadMode+ -- This should be compatible with UTF-8, and any symbols outside+ -- the allowed Funge-93 charset will raise an error in `execute`:+ hSetEncoding hdl latin1 + src <- hGetContents hdl+ let (wasBig,arr) = buildGrid bigOk src+ + -- initialize the RNG either from user-supplied seed, or+ -- with the system generator:+ rng <- maybe getStdGen (return . mkStdGen) (seed os)++ when (loud && wasBig) $ putStrLn $+ if bigOk+ then "WARNING: using code area larger than 80x25."+ else "WARNING: source was truncated to 80x25; " ++ + "use --allow-oversize for source code of " ++ + "arbitrary dimensions." + + return$ defaultState{ code = arr,+ randGen = rng,+ verbose = loud }+++-- we catch only errors of parsing the command line arguments here.+-- btw this frankly seems easier than System.Console.GetOpts:+parseArgs :: [String] -> Either IOError Opts+parseArgs [] = Left$ userError$ "SOURCE FILE REQUIRED:"++ usage+parseArgs as = parse as defaultOpts+ where -- the source code file:+ parse [src] opts = Right$ opts{srcFile = src}+ + -- simple optional flags:+ parse ("--quiet":as) os = parse as os{quiet = True}+ parse ("--allow-oversize":as) os = parse as os{allow_oversize = True}+ + -- options with args:+ parse ("--seed":s:as) os = + if isInt s+ then parse as os{seed = Just$ read s}+ else Left$ userError$ "SEED MUST BE AN INTEGER:"++usage+ + -- parse errors:+ parse _ _ = Left$ userError $ "ERROR PARSING OPTIONS"++usage++++ -- -- -- INPUT PROCESSING -- -- --+++-- returns the array of source code, along with a Bool denoting whether+-- the input source file exceeded 80x25 in any of it's dimensions:+buildGrid :: Bool -> String -> (Bool,Code)+buildGrid allowing_oversize str =+ let ls = lines $ fixLineBreaks str+ -- our array must be at least 80x25, but we will expand it if+ -- the source seems to exceed those dimensions anywhere:+ sxN = maximum $ 79 : map (subtract 1 . length) ls+ syN = max 24 (length ls - 1)+ (xN,yN) = if allowing_oversize+ then (sxN, syN)+ else (79,24)+ -- we pad the array with spaces:+ pad = repeat ' '+ ls' = take (yN+1) (ls ++ repeat pad)+ -- array is indexed from (0,0) in the upper left corner:+ coords = [ (x,y) | y <- [0..yN], x <- [0..xN] ]+ cells = map ord $ concatMap (take (xN+1) . (++pad)) ls'+ -- was the source input bigger than 80x25?:+ big = sxN > 79 || syN > 24+ arr = array ((0,0),(xN,yN)) (zip coords cells)+ in (big, arr)++++--------------------------------------------------------------------------------+ -- ERROR AND FILE HANDLING -- +--------------------------------------------------------------------------------++-- convert all three line break types to newlines. This isn't required by+-- the spec, but is useful:+fixLineBreaks :: String -> String+fixLineBreaks [] = []+fixLineBreaks ('\r':'\n':xs) = '\n' : fixLineBreaks xs --win+fixLineBreaks ('\r':xs) = '\n' : fixLineBreaks xs --old mac+fixLineBreaks (x:xs) = x : fixLineBreaks xs++-- minimal error handling. An error here seems to mean File Encoding+-- issues:+processingErrors :: IOError -> IO ()+processingErrors e = do+ print e+ putStrLn "* Please report this bug to me so I can fix it *"++-- raise the error retuned by the parsing function: parseArgs+parsingErrors :: IOError -> IO a+parsingErrors = ioError
+ Befunge93.cabal view
@@ -0,0 +1,64 @@+-- Befunge-93.cabal auto-generated by cabal init. For additional+-- options, see+-- http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html#pkg-descr.+-- The name of the package.+Name: Befunge93++-- The package version. See the Haskell package versioning policy+-- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for+-- standards guiding when and how versions should be incremented.+Version: 0.1++-- A short (one-line) description of the package.+Synopsis: An interpreter for the Befunge-93 Programming Language++-- A longer description of the package.+Description: An interpreter for the '93 spec of the esoteric programming language Befunge++-- URL for the project homepage or repository.+Homepage: http://coder.bsimmons.name/blog/2010/05/befunge-93-interpreter-on-hackage++-- The license under which the package is released.+License: BSD3++-- The file containing the license text.+License-file: LICENSE++-- The package author(s).+Author: Brandon Simmons++-- An email address to which users can send suggestions, bug reports,+-- and patches.+Maintainer: brandon.m.simmons@gmail.com++-- A copyright notice.+-- Copyright: ++-- Stability of the pakcage (experimental, provisional, stable...)+Stability: Experimental++Category: Compilers/Interpreters++Build-type: Simple++-- Extra files to be distributed with the package, such as examples or+-- a README.+Extra-source-files: README, Tests.hs, tests/phlamethrower/divtest.bf, tests/phlamethrower/compat.bf, tests/phlamethrower/README, tests/mycology/mycology.b98, tests/mycology/readme.txt, tests/mycology/license.txt++-- Constraint on the version of Cabal needed to build this package.+Cabal-version: >=1.2+++Executable Befunge93+ -- .hs or .lhs file containing the Main module.+ Main-is: Befunge.hs + + -- Packages needed in order to build this package.+ Build-depends: base >= 2 && < 5, mtl < 2, array < 4, random < 2+ + -- Modules not exported by this package.+ -- Other-modules: + + -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.+ -- Build-tools: +
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Brandon Simmons 2010++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Brandon Simmons nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README view
@@ -0,0 +1,23 @@+A Befunge Interpreter by jberryman+--------------------------------++The interpreter works on only Befunge-93 files, not the newer '98 version+of the language. You can run the interpreter with:++ $> Befunge93 [--quiet] [--allow-oversize] [--seed INT] source.bf++Explanation of options:++ --quiet + Don't print any interpreter warnings or messages++ --allow-oversize+ Stretch the code area to fit the dimensions of the+ file. A strict funge-93 interpreter should truncate+ the file to 80x25 characters.++ --seed+ Provide your own random seed++Please send me any bug reports, suggestions, etc. + brandon.m.simmons@gmail.com
+ Setup.hs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+import Distribution.Simple+main = defaultMain
+ Tests.hs view
@@ -0,0 +1,61 @@+module Main+ where++import System.Process+import System.IO+import System.Exit+import Control.Monad++main = do + (_,testH1,_,_) <- runInteractiveCommand $+ "runhaskell Befunge.hs tests/mycology/mycology.b98"+ test1 <- hGetContents testH1+ + (_,testH2,_,_) <- runInteractiveCommand $+ "runhaskell Befunge.hs tests/phlamethrower/compat.bf"+ test2 <- hGetContents testH2+ + (_,testH3,_,_) <- runInteractiveCommand $+ "runhaskell Befunge.hs tests/phlamethrower/divtest.bf"+ test3 <- hGetContents testH3+ + -- test output:+ putStr test1+ when (test1 /= goodTest1) exitFailure+ putStr test2+ when (test2 /= goodTest2) exitFailure+ putStr test3+ when (test3 /= goodTest3) exitFailure++goodTest1 = unlines $+ ["WARNING: source was truncated to 80x25; use --allow-oversize for source code of arbitrary dimensions.",+ "0 1 2 3 4 5 6 7 ",+ "GOOD: , works",+ "GOOD: : duplicates",+ "GOOD: empty stack pops zero",+ "GOOD: 2-2 = 0",+ "GOOD: | works",+ "GOOD: 0! = 1",+ "GOOD: 7! = 0",+ "GOOD: 8*0 = 0",+ "GOOD: # < jumps into <",+ "GOOD: \\ swaps",+ "GOOD: 01` = 0",+ "GOOD: 10` = 1",+ "GOOD: 900pg gets 9",+ "GOOD: p modifies space",+ "GOOD: wraparound works",+ "UNDEF: edge # skips column 80",+ "GOOD: Funge-93 spaces",+ "The Befunge-93 version of the Mycology test suite is done.",+ "Quitting..."]++goodTest2 = unlines $+ ["warning: getCell out of bounds. wrapping.",+ "Get/put wraps",+ "Cells are >8 bit",+ "Edge jumps work",+ "Negative remainders work",+ "@ in stringmode works"]++goodTest3 = ""
+ tests/mycology/license.txt view
@@ -0,0 +1,24 @@+Copyright (c) 2006-2008, Matti Niemenmaa +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the project nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE.
+ tests/mycology/mycology.b98 view
binary file changed (absent → 120198 bytes)
+ tests/mycology/readme.txt view
@@ -0,0 +1,498 @@+This is the Mycology Befunge-98 test suite, by Matti "Deewiant" Niemenmaa. + +To contact me, E-mail me. The address for Mycology-related things is +matti.niemenmaa+mycology at the domain iki.fi. + +Mycology is licensed under the so-called 3-clause BSD license. See license.txt +for the text of the license. + +Contents of this readme: + Changelog + Quick summary + More detailed info + Fingerprints + Notes on particular messages + "# across left edge" + "101-{}" + "4k #" and "2k ;;;5" + "2k6" + +Mycology changelog: +------------------- + + 2009-04-04 - Made the u test not abort if the storage offset isn't (0,0). + - Bugfix: u outputs the correct error message if it fails with + a positive count. + 2009-03-31 - Bugfix: some w were misaligned in the TOYS test. + 2009-03-29 - Bugfix: 1y bits testing was really broken, really fixed it + now. + 2009-03-28 - Update: FILE's R really should reflect at EOF. + - Update: removed the 'G to an infinite loop' test from STRN, + it makes sense that it does indeed loop forever. + - Bugfix: it was always claimed that I/O was buffered. + - Bugfix: 1y being greater than 15 was complained about: + should have been 31. + 2008-11-15 - ) with a negative count wasn't actually tested, ( was used + both times. + 2008-10-17 - If o doesn't work, it is reported that i in binary mode will + not be tested. + 2008-09-21 - Fixed a misalignment in the fingerprint loading code. + 2008-09-16 - MycoTRDS now expects ticks to start from zero, thus the + expected value of G is one lower. + 2008-09-15 - Fixed a misalignment in the u test with a negative argument. + 2008-09-14 - Made the wraparound with non-cardinal delta test catch a + common case. + 2008-09-13 - Bugfix: test for k with negative argument was expecting + incorrect k behaviour. + - Bugfix: "GOOD: SGML spaces" was never output, who knows for + how long that's been disabled. + - Bugfix: IMAP check for non-ASCII now says it works when it + works. + - Update: IMAP check for non-ASCII is now GOOD when it works + and BAD otherwise, per the latest spec. + - Update: IMAP now checks mappings outside range 0-255. + - Update: INDV now expects the logical order, reporting BAD + otherwise. + 2008-09-12 - Reduce stacking in HRTI test. + - Corrected typo in a TOYS error message. + 2008-09-10 - Made the check for wraparound with non-cardinal delta a bit + stricter (instead of a delta of (12,0) it uses (13,2)). + 2008-09-06 - Fixed a misspelled error message in mycouser.b98 for + Befunge-93 interpreters. + - Fixed a bug in 2k6 testing that led to an infinite loop. + 2008-08-30 - Fixed the case where SCKE is included in SOCK. + 2008-08-28 - SOCK and SCKE fixed: much code still assumed that A + overwrites the original socket, and thus wrong sockets were + being given to K and P. + 2008-08-20 - Bugfix: results for the ;; concurrency test were off by one. + - Test new A and O instructions in SUBR. + 2008-08-19 - Bugfix: results for the concurrency tests 5kz and "a b" + were incorrect. + 2008-08-14 - New fingerprint: DATE. + 2008-08-13 - I had managed to get the way y should work as a pick + instruction completely wrong. Thanks to Johannes Laire for + noticing this and notifying me. + 2008-08-11 - Removed PNTR (the same as INDV), it wasn't meant to exist + any more. + 2008-08-09 - The new addition to the FILE fingerprint, D, is now tested. + - Using it, created .tmp files can now be removed from within + Mycology. + 2008-07-26 - Thanks to Arvid Norlander, Chris Pressey, and Mike Riley, + none of k is UNDEF any longer, and some tests were changed + to reflect the intended behaviour. + - Expanded the null byte test. + - Bugfix: in SOCK, the original socket should /not/ be + destroyed: flipped a GOOD and BAD. + 2008-07-19 - Now testing whether null bytes are handled correctly. + 2008-05-02 - Bugfix: mycouser.b98 had a forgotten r in place of a (. + 2008-03-30 - Bugfix: J test in SUBR was misaligned. + 2008-03-29 - Bugfix: D failing in TOYS had no error message. + - Bugfex: L and R in TOYS had incorrect error messages. + 2008-03-15 - Bugfix: time output for hours <= 10 was incorrect. + 2008-03-13 - Bugfix: a missing ; caused an incorrect error message. + 2008-03-11 - i and o are now UNDEF if unavailable. + - PERL is now tested with "5-1" instead of the palindromic + "2+2". Thanks to Alex Smith for the input. + 2008-02-02 - 1k # now considered UNDEF. + 2008-01-09 - More typos or incorrect messages. + 2007-12-02 - Corrected some typos. + 2007-09-22 - Minor bugfixes. + 2007-09-20 - Public release. + 2007-07-26 - Creation of mycoterm.b98 and mycotrds.b98. + 2007-06-17 - Creation of mycouser.b98. + 2007-01-06 - Creation of mycorand.bf. + 2006-12-31 - Creation of sanity.bf and mycology.b98. + +Quick summary of how to test your Befunge interpreter: +------------- + + - If, at any point, you get messages beginning with "BAD:", correct the + issues before moving on to the next step. + - If your interpreter needs any command line switches for + standard-conforming mode, use them. + - Run sanity.bf and make sure it outputs "0 1 2 3 4 5 6 7 8 9 ". + - Run mycology.b98, make sure it outputs "0 1 2 3 4 5 6 7 " and that there + are no lines beginning with "BAD:" anywhere in the output. + - If your interpreter is Befunge-93 only, run mycorand.bf and examine the + results. + - Run mycouser.b98: for complete testing, run it a few times and try giving + it both valid and invalid input. + - If your interpreter should support one or both of the NCRS and TERM + fingerprints, run mycoterm.b98. + - If your interpreter should support the TRDS fingerprint, run mycotrds.b98. + +More detailed info: +------------------- + +Files with the .bf extension are valid Befunge-93 source code, while *.b98 are +intended for Befunge-98 interpreters. mycology.b98 and mycouser.b98 are +exceptions: they work in both standards - if the standards are implemented +correctly. + +Note for Befunge-93: mycology.b98 is much bigger than the 80x25 allowed in +Befunge-93. If your interpreter bails out on a file bigger than the maximum +allowed, you can simply take the 80x25 square starting at the top left corner +of mycology.b98 into a separate file and use that for testing. + +In order to test the absolute basics of the interpreter, feed it the file +sanity.bf. This tests that the IP (instruction pointer) begins at the correct +point in Funge-Space and moves in the correct direction. In addition, it makes +sure the following instructions work: + +Decimal ASCII Instruction + + 32 Space + 35 # Trampoline + 46 . Output Decimal + 48 0 Push Zero + 49 1 Push One + 50 2 Push Two + 51 3 Push Three + 52 4 Push Four + 53 5 Push Five + 54 6 Push Six + 55 7 Push Seven + 56 8 Push Eight + 57 9 Push Niner + 64 @ Stop + +The above are the absolute minimum which the interpreter must support. In +addition, it should reflect upon encountering an instruction it does not +recognize. + +sanity.bf will, if the interpreter supports all of the above, output the string +"0 1 2 3 4 5 6 7 8 9 ". If it doesn't, anything might happen: sanity.bf does +_not_ fail safe. + +Hereafter the actual testing in mycology.b98 can be conducted. The initial +behaviour of the program is to output a code, using the Output Decimal +instruction, after having successfully tested a certain instruction. These +codes are as follows: + +Code Decimal ASCII Instruction Notes + + 0 62 > Go East + 1 118 v Go South + 2 60 < Go West + 3 94 ^ Go North + 4 36 $ Pop Explicitly testing whether popping an + empty stack works as it should is + done separately, later. + + 5 34 " Toggle Stringmode If no reflection on the instruction + occurs, but the string's contents are + interpreted as instructions, a second + "4 " is output before exiting. + + 6 95 _ East-West If If the comparison is done incorrectly + (i.e. the wrong direction is taken) + an additional "5 " is output. + + 7 43 + Add + +For example, if the interpreter emits only "0 1 ", the Go East and Go South +instructions were correctly interpreted, but a reflection occurred upon meeting +Go West. A conforming interpreter should output every code in the listing once. + +These tests are very simple, due to the basic nature of the instructions +involved. All that is tested is whether a reflection occurs or not. If +mycology.b98 claims that an instruction which appears to work perfectly is +failing, make sure that the above do what they should. + +Having tested the above, mycology.b98 tests the Output Character instruction +and hence reverts to plain English output. If there is no output after code 7, +the Output Character instruction does not function as it should. + +The output format changes to lines beginning with "BAD:" or "GOOD:", followed +by a description of what the interpreter does wrong or correctly, respectively. +Some, but not all, "BAD" lines are followed by a Stop instruction - these tend +to be features which are deemed useful enough that they are used later in the +program. (Or, possibly, features which may be used later, but aren't: it's hard +to modify Befunge source code after it's first written, so there may be some +cases where a Stop isn't necessary but is there anyway.) In some cases, a Stop +was added to simplify the code: the Befunge-93 area is particularly snarly, +since space is at a premium. + +Some lines begin with "UNDEF:". This means that the specification is either +ambiguous or completely ignorant of an issue, and so different possibilities +are acceptable. It is possible that some such undefined cases may result in +"BAD:" if the interpreter does something completely unexpected, but there is no +"GOOD:" equivalent, only "UNDEF:". + +Some comment lines not beginning with "BAD:", "GOOD:", or "UNDEF:" are also +emitted occasionally, in order to clarify what is going on. + +Other notes on mycology.b98: + + Befunge-93 detection relies on the interpreter using only the first 80 + characters of lines, since Befunge-93 has a Funge-Space of 80x25 cells. + + The checks are generally very simple. For instance, if the Subtract or + Multiply instructions empty the stack, they will be considered to work + properly: only a few checks are done, and they all check for zero. + + Instructions are assumed to work if they pass one test (for the more + complicated instructions, more tests are needed, but every behaviour of the + instruction is still tested only once). If, for instance, an instruction + works the first 41 times and randomly fails every third time after that, + Mycology will probably not detect it, but will silently fail or, in the + worst case, pass. + + Instructions are assumed to be at least somewhat sane: e.g. | should either + reflect or pop a cell and cause the IP to start moving north or south. Not + east or west, or to teleport to a random location in Funge- Space, or push + 72 ampersands onto the stack. It is the tester's responsibility, not + Mycology's, to make sure that the interpreter doesn't go crazy and perform + malicious acts. You use Mycology at your own risk. + +See the end of this file for notes on particular messages. + +The following instructions are _not_ tested by mycology.b98 (those preceded by +an asterisk are tested if the interpreter is detected as supporting +Befunge-98): + +Decimal ASCII Instruction + + 37 % *Remainder + 38 & Input Decimal + 47 / *Divide + 61 = Execute + 63 ? *Go Away + 126 ~ Input Character + +The division and input instructions are tested in mycouser.b98 because they all +require user intervention (the division instructions only when dividing by +zero, but I felt it would be better to not split the testing of an instruction +into two files). Their correct behaviour is also very difficult to verify +without a knowledgeable user. + +Go Away is tested separately in mycorand.bf because it takes too much space to +fit in the Befunge-93 area of mycology.b98. If the Befunge-98 instruction Input +File works, mycorand.bf is loaded using it and Go Away is thus tested in +mycology.b98. + +Execute is completely untested, because to get a reliable result would require +testing various commands and noting their behaviours on different platforms. An +educated guess regarding the user's platform is in order, and overall it would +be too complicated. It is simplest to test this manually, rather than to try to +cater for all cases in a suite such as Mycology. + +Implementation notes regarding mycorand.bf: + + Beware! If Go Away is unimplemented and thus reflects, an infinite loop + is entered! + + The testing is very simple: it is only made sure that Go Away causes the + instruction pointer to go at least once in every cardinal direction, + though it is always encountered from the same direction. + + The number of tries it took to successfully go in every direction is + output, so an interpreter's implementer can make sure the number + fluctuates somewhat. The order in which the directions were generated is + also output, so that it can be verified that the order isn't always the + same. + +Make sure that the interpreter successfully passes the Befunge-93 area of +mycology.b98 before loading mycorand.bf or mycouser.bf! + +Regarding fingerprints +---------------------- + +mycology.b98 tests every fingerprint that I am aware of, apart from FNGR, SGNL, +and WIND. It is completely up to the interpreter's writer(s) whether any should +be supported: a completely specification-conforming interpreter does not need +to support any fingerprint at all, as long as the ( "Load Semantics" and ) +"Unload Semantics" instructions perform correctly. + +FNGR is not tested because its specifications contradict the Befunge-98 +specifications. It contains operations for performing on a single fingerprint +stack, but the specifications for Befunge-98 state that there should be a stack +of semantics for each instruction in the range [A, Z]. RC/Funge-98, the (only, +as far as I know) interpreter implementing FNGR, fails some of Mycology's tests +due to this. + +SGNL is not tested simply because it is platform-specific. There is no +technical obstacle to it, only my own convictions regarding platform-specific +code. If anybody wishes to write code to test it, feel free to send it to me, +it may be worthy of addition to Mycology. + +WIND is not tested because I do not wish to support it in my interpreter, and +thus I didn't feel like writing tests for it. RC/Funge-98 is the only +interpreter supporting it, and if I had discovered any bugs in it I would have +had to delve into unfamiliar code to make it even possible to test the whole +thing. I decided it was too much work and left it out. Once again, the addition +of WIND to Mycology is perfectly fine, but I won't be the one to write the +code. + +The list of fingerprints which are tested: + + Official Cat's Eye Technologies fingerprints: + + "HRTI" 0x48525449 High-Resolution Timer Interface + "MODE" 0x4d4f4445 Funge-98 Standard Modes + "MODU" 0x4d4f4455 Modulo Arithmetic Extension + "NULL" 0x4e554c4c Funge-98 Null Fingerprint + "ORTH" 0x4f525448 Orthogonal Easement Library + "PERL" 0x5045524c Generic Interface to the Perl Language + "REFC" 0x52454643 Referenced Cells Extension + "ROMA" 0x524f4d41 Funge-98 Roman Numerals + "TOYS" 0x544f5953 Funge-98 Standard Toys + "TURT" 0x54555254 Simple Turtle Graphics Library + + RC/Funge-98 fingerprints: + + In all cases, the documentation is considered the primary source of how + an instruction should behave. Precise semantics have been inferred from + the RC/Funge-98 source code where not properly documented. + + For all fingerprints involving vectors, RC/Funge-98 doesn't, for some + reason, use the IP's storage offset. Thus, the tests assume the same + behaviour. + + "BASE" 0x42415345 I/O for numbers in other bases + "CPLI" 0x43504c49 Complex Integer extension + "DIRF" 0x44495246 Directory functions extension + "EVAR" 0x45564152 Environment variables extension + "FILE" 0x46494c45 File I/O functions + "FIXP" 0x46495850 Some useful math functions + "FPDP" 0x46504450 Double precision floating point + "FPSP" 0x46505350 Single precision floating point + "FRTH" 0x46525448 Some common forth [sic] commands + "IIPC" 0x49495043 Inter IP [sic] communicaiton [sic] extension + "IMAP" 0x494d4150 Instruction remap extension + "INDV" 0x494e4456 Pointer functions + "PNTR" 0x504e5452 (an alias of "INDV") + "SOCK" 0x534f434b tcp/ip [sic] socket extension + "STRN" 0x5354524e String functions + "SUBR" 0x53554252 Subroutine extension + "TERM" 0x5445524d Terminal control functions + "TIME" 0x54494d45 Time and Date functions + "TRDS" 0x54524453 IP travel in time and space + + Jesse van Herk's extensions to RC/Funge-98: + + "JSTR" 0x4a535452 + "NCRS" 0x4e435253 Ncurses [sic] extension + + GLFunge98 fingerprints: + + "SCKE" 0x53434b45 + +Notes on particular messages output by mycology.b98 +--------------------------------------------------- + +"UNDEF: # across left edge..." +.............................. + +Here, the line and file cases are considered separately. This is because some +interpreters consider the Funge-Space as a rectangle: see below, using 0 to +mark empty cells which are outside Funge-Space. + +> v000 +v > > +@00000000 + +Even though the file doesn't contain the three spaces at the end of the first +line, or any of the spaces at the end of the third line, the program's +representation of Funge-Space does, because Funge-Space is padded out to the +width of the longest line in the file. + +Because jumping across the edge of Funge-Space isn't mentioned in the +specification, one cannot be sure as to what should happen. If it is considered +that Funge-Space is infinitely surrounded by spaces, jumping across the edge of +space shouldn't skip over anything. On the other hand, # jumps over "the next +Funge-Space cell in [the instruction pointer's] path", which might not include +this void. + +However, it may be that an existing space cell which is not part of this void +is skipped over. Thus, both jumping over the edge of the physical edge of the +file, with only the void in between, and jumping over the edge of a line which +is shorter, but may contain the spaces as the 0s in the above example, are +tested. Most interpreters have different behaviour for the two. + +"BAD: 101-{} doesn't leave stack top as 0 and next as 1" +........................................................ + +This is something which may be tricky to get right. Let's examine what happens. +On each following line, the instruction comes first, followed by the stack +stack, with the contents of each stack in square brackets, starting at the +bottom. + +1 [1] +0 [1, 0] +1 [1, 0, 1] +- [1, -1] + +This part is trivial. What happens next, however, varies. + +One incorrect possibility: +{ [1, 0, 0], [] +} [1] + +Here, { pushes abs(-1) zeroes onto the SOSS before a new stack is pushed. Since +there was no SOSS at that time, the zero pushed doesn't appear. + +This is the behaviour of the Flaming Bofine Befunge Interpreter version +2003.0326, amended with the 2003.0722 and 2003.0726 patches. + +Another: +{ [1, 0, 0], [0] +} [1] + +It seems that here, abs(-1) zeroes are being pushed on the TOSS instead of the +SOSS. + +This is the behaviour of the RC/Funge-98 interpreter, version 1.07, as well as +of the RC/Funge-98 interpreter modified by Jesse van Herk, version 1.05. + +What should happen: +{ [1, 0, 0, 0], [] +} [1, 0] + +This is the behaviour of the Conforming Concurrent Befunge-98 Interpreter, +version 1.00. + +Let's see what the spec has to say about the subject: + + "The { 'Begin Block' instruction pops a cell it calls n, then pushes a new + stack on the top of the stack stack, transfers n elements from the SOSS to + the TOSS, then pushes the storage offset as a vector onto the SOSS..." + + "If n is negative, |n| zeroes are pushed onto the SOSS." + +In other words, { should: + + Pop the -1 from the stack. [1] + Push a new stack on the stack stack. [1], [] + Since -1 < 0, push |-1| = 1 zero onto the SOSS. [1, 0], [] + Push the storage offset onto the SOSS. [1, 0, 0, 0], [] + +"BAD: 4k #..." and "BAD: 2k ;;;5..." +.................................... + +In Funge-98, spaces and semicolons are ethereal. The "next instruction" +mentioned in the spec refers specifically to the next instruction the +interpreter would execute if the k would not be there. + +Hence, k always executes its operand at the k, but reaches past all spaces and +semicolons to find the operand. Hence 2k ;;;5 should execute the 5 twice at the +k. (See the next section for the reason why it should be executed a third time +afterward.) + +"BAD: 2k6..." +............. + +The specification does not say that the operand should be skipped over after +execution. The only special case is when the amount of times to execute is +zero. + +This means that 2k6 should indeed first push 2 sixes at the k, and then a third +when encountering the 6 itself. + +This also means that there is no way to execute an instruction only once: 1k6 +results in two sixes. (Another IP may certainly modify the 6 immediately after +the k is executed, but that's a somewhat unlikely case and not exactly a good +way to handle this limitation.) + +The spec is somewhat unclear on the entirety of k, but both of the above issues +have been confirmed with Chris Pressey.
+ tests/phlamethrower/README view
@@ -0,0 +1,3 @@+Test files in this directory gratefully stolen from:++ http://www.phlamethrower.co.uk/befunge/#files
+ tests/phlamethrower/compat.bf view
@@ -0,0 +1,8 @@+1-0g:"Z"-#v_$91+"sparw tup/teG">:#,_$ v Z + >:" "-#v_$91+"ecaps snruter teg BOO">:#,_$v +v >0" snruter teg BOO">:#,_$.91+, > +>8:+:*11p11g#v_91+"tib 8 dengis"01-11p11g!#v_"nu">" era slleC">:#,_v +vv >91+"tib 8>" > ^ >91+"krow " # + > >"spmuj egdE">:#,_ 91+"krow "04-3%1+#v_ >"sredniamer evitag"v +>"ton od "^ >"ton od "^ +"eN">:#,_ 91+"skrow edomgnirts ni @">:#,_@ >
+ tests/phlamethrower/divtest.bf view
@@ -0,0 +1,6 @@+0 91+ -01p>0 91+ -11p>01g11g:#v_ v + + +>!#@_01p ^ >/02p01g11g%12p02g11g*12g+:22p01g-#v_ v +^- +19 :+1g10$< v,+19.g22.g21.g20.g11.g10< +11g1+: 91+ -!#^_11p ^ > >