brainf
This commit is contained in:
commit
ab2f2c6045
8 changed files with 246 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
||||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
result
|
||||||
|
dist-newstyle
|
||||||
|
.direnv
|
||||||
9
README.md
Normal file
9
README.md
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Brainf
|
||||||
|
|
||||||
|
Just a basic bf interpretation in Haskell for fun.
|
||||||
|
|
||||||
|
```
|
||||||
|
nix build
|
||||||
|
|
||||||
|
./result/bin/brainf hello.bf
|
||||||
|
```
|
||||||
19
brainf.cabal
Normal file
19
brainf.cabal
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
cabal-version: 3.0
|
||||||
|
name: bf
|
||||||
|
version: 0.1.0.0
|
||||||
|
license: NONE
|
||||||
|
author: Jack
|
||||||
|
maintainer: jack@jingus.dev
|
||||||
|
build-type: Simple
|
||||||
|
|
||||||
|
common warnings
|
||||||
|
ghc-options: -Wall
|
||||||
|
|
||||||
|
executable brainf
|
||||||
|
import: warnings
|
||||||
|
main-is: Main.hs
|
||||||
|
build-depends:
|
||||||
|
base,
|
||||||
|
vector
|
||||||
|
hs-source-dirs: src
|
||||||
|
default-language: Haskell2010
|
||||||
77
flake.lock
generated
Normal file
77
flake.lock
generated
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1775087534,
|
||||||
|
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"haskell-flake": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1776367004,
|
||||||
|
"narHash": "sha256-P2E65OCyIe2EjIG30vWF0HseHxZb4oujGdFhQInQ9c8=",
|
||||||
|
"owner": "srid",
|
||||||
|
"repo": "haskell-flake",
|
||||||
|
"rev": "dd6bbc834f5942f3255d9f9d7b06dbf14b84f05c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "srid",
|
||||||
|
"repo": "haskell-flake",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1776329215,
|
||||||
|
"narHash": "sha256-a8BYi3mzoJ/AcJP8UldOx8emoPRLeWqALZWu4ZvjPXw=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "b86751bc4085f48661017fa226dee99fab6c651b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1774748309,
|
||||||
|
"narHash": "sha256-+U7gF3qxzwD5TZuANzZPeJTZRHS29OFQgkQ2kiTJBIQ=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"rev": "333c4e0545a6da976206c74db8773a1645b5870a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"haskell-flake": "haskell-flake",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
58
flake.nix
Normal file
58
flake.nix
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||||
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
|
haskell-flake.url = "github:srid/haskell-flake";
|
||||||
|
};
|
||||||
|
outputs = inputs@{ self, nixpkgs, flake-parts, ... }:
|
||||||
|
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||||
|
systems = nixpkgs.lib.systems.flakeExposed;
|
||||||
|
imports = [ inputs.haskell-flake.flakeModule ];
|
||||||
|
|
||||||
|
perSystem = { self', pkgs, ... }: {
|
||||||
|
|
||||||
|
# Typically, you just want a single project named "default". But
|
||||||
|
# multiple projects are also possible, each using different GHC version.
|
||||||
|
haskellProjects.default = {
|
||||||
|
# The base package set representing a specific GHC version.
|
||||||
|
# By default, this is pkgs.haskellPackages.
|
||||||
|
# You may also create your own. See https://haskell.nixos.asia/package-set
|
||||||
|
# basePackages = pkgs.haskellPackages;
|
||||||
|
|
||||||
|
# Extra package information. See https://haskell.nixos.asia/dependency
|
||||||
|
#
|
||||||
|
# Note that local packages are automatically included in `packages`
|
||||||
|
# (defined by `defaults.packages` option).
|
||||||
|
#
|
||||||
|
packages = {
|
||||||
|
# aeson.source = "1.5.0.0"; # Override aeson to a custom version from Hackage
|
||||||
|
# shower.source = inputs.shower; # Override shower to a custom source path
|
||||||
|
};
|
||||||
|
settings = {
|
||||||
|
# aeson = {
|
||||||
|
# check = false;
|
||||||
|
# };
|
||||||
|
# relude = {
|
||||||
|
# haddock = false;
|
||||||
|
# broken = false;
|
||||||
|
# };
|
||||||
|
};
|
||||||
|
|
||||||
|
devShell = {
|
||||||
|
# Enabled by default
|
||||||
|
# enable = true;
|
||||||
|
|
||||||
|
# Programs you want to make available in the shell.
|
||||||
|
# Default programs can be disabled by setting to 'null'
|
||||||
|
# tools = hp: { fourmolu = hp.fourmolu; ghcid = null; };
|
||||||
|
|
||||||
|
# Check that haskell-language-server works
|
||||||
|
# hlsCheck.enable = true; # Requires sandbox to be disabled
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# haskell-flake doesn't set the default package, but you can do it here.
|
||||||
|
packages.default = self'.packages.brainf;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
1
hello.bf
Normal file
1
hello.bf
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
|
||||||
78
src/Main.hs
Normal file
78
src/Main.hs
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
module Main where
|
||||||
|
|
||||||
|
import Data.Bifunctor (Bifunctor (first))
|
||||||
|
import Data.Char (chr, ord)
|
||||||
|
import qualified Data.Vector.Unboxed.Mutable as UM
|
||||||
|
import Data.Word (Word8)
|
||||||
|
import System.Environment (getArgs)
|
||||||
|
import System.IO (IOMode (ReadMode), hGetContents, openFile)
|
||||||
|
|
||||||
|
data Instruction
|
||||||
|
= IncPointer
|
||||||
|
| DecPointer
|
||||||
|
| IncByte
|
||||||
|
| DecByte
|
||||||
|
| OutByte
|
||||||
|
| AccByte
|
||||||
|
| Loop [Instruction]
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
parseBf' :: String -> ([Instruction], String)
|
||||||
|
parseBf' ('>' : rest) = first (IncPointer :) (parseBf' rest)
|
||||||
|
parseBf' ('<' : rest) = first (DecPointer :) (parseBf' rest)
|
||||||
|
parseBf' ('+' : rest) = first (IncByte :) (parseBf' rest)
|
||||||
|
parseBf' ('-' : rest) = first (DecByte :) (parseBf' rest)
|
||||||
|
parseBf' ('.' : rest) = first (OutByte :) (parseBf' rest)
|
||||||
|
parseBf' (',' : rest) = first (AccByte :) (parseBf' rest)
|
||||||
|
parseBf' ('[' : rest) =
|
||||||
|
let (parsed, next) = parseBf' rest
|
||||||
|
in first (Loop (parsed) :) (parseBf' next)
|
||||||
|
parseBf' (']' : rest) = ([], rest)
|
||||||
|
parseBf' (_ : rest) = parseBf' rest
|
||||||
|
parseBf' [] = ([], "")
|
||||||
|
|
||||||
|
parseBf :: String -> [Instruction]
|
||||||
|
parseBf = fst . parseBf'
|
||||||
|
|
||||||
|
interpret :: [Instruction] -> UM.IOVector Word8 -> Int -> IO Int
|
||||||
|
interpret [] _ pointer = return pointer
|
||||||
|
interpret (IncPointer : rest) tape pointer = interpret rest tape (pointer + 1)
|
||||||
|
interpret (DecPointer : rest) tape pointer = interpret rest tape (pointer - 1)
|
||||||
|
interpret (IncByte : rest) tape pointer = do
|
||||||
|
UM.modify tape (+ 1) pointer
|
||||||
|
interpret rest tape pointer
|
||||||
|
interpret (DecByte : rest) tape pointer = do
|
||||||
|
UM.modify tape (subtract 1) pointer
|
||||||
|
interpret rest tape pointer
|
||||||
|
interpret (OutByte : rest) tape pointer = do
|
||||||
|
UM.read tape pointer >>= putChar . chr . fromIntegral
|
||||||
|
interpret rest tape pointer
|
||||||
|
interpret (AccByte : rest) tape pointer = do
|
||||||
|
getChar >>= (return . fromIntegral . ord) >>= UM.write tape pointer
|
||||||
|
interpret rest tape pointer
|
||||||
|
interpret (Loop instructions : rest) tape pointer = do
|
||||||
|
pointer' <- looper pointer
|
||||||
|
interpret rest tape pointer'
|
||||||
|
where
|
||||||
|
looper :: Int -> IO Int
|
||||||
|
looper pointer' = do
|
||||||
|
currentByte <- UM.read tape pointer'
|
||||||
|
case currentByte of
|
||||||
|
0 -> return pointer'
|
||||||
|
_ -> do
|
||||||
|
pointer'' <- interpret instructions tape pointer'
|
||||||
|
looper pointer''
|
||||||
|
|
||||||
|
run :: String -> IO ()
|
||||||
|
run input = do
|
||||||
|
initialTape <- UM.replicate 30000 0
|
||||||
|
_ <- interpret (parseBf input) initialTape 0
|
||||||
|
return ()
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = do
|
||||||
|
args <- getArgs
|
||||||
|
|
||||||
|
let path = args !! 0
|
||||||
|
|
||||||
|
openFile path ReadMode >>= hGetContents >>= run
|
||||||
Loading…
Add table
Add a link
Reference in a new issue