Compare commits
4 commits
1b20f4ef71
...
f9fd24261a
Author | SHA1 | Date | |
---|---|---|---|
f9fd24261a | |||
a94455f365 | |||
7705371b6b | |||
2fc0d46ee9 |
2 changed files with 37 additions and 32 deletions
|
@ -1,5 +1,4 @@
|
||||||
{-# LANGUAGE ImportQualifiedPost #-}
|
{-# LANGUAGE ImportQualifiedPost #-}
|
||||||
{-# LANGUAGE ViewPatterns #-}
|
|
||||||
{-# LANGUAGE DerivingStrategies #-}
|
{-# LANGUAGE DerivingStrategies #-}
|
||||||
{-# LANGUAGE LambdaCase #-}
|
{-# LANGUAGE LambdaCase #-}
|
||||||
module Language.Brainfuck (parse, ParseFailure, render) where
|
module Language.Brainfuck (parse, ParseFailure, render) where
|
||||||
|
@ -16,10 +15,11 @@ import Data.Vector.Mutable qualified as MutableVector
|
||||||
|
|
||||||
import Language.Brainfuck.Instruction ( Instruction(..) )
|
import Language.Brainfuck.Instruction ( Instruction(..) )
|
||||||
import qualified Data.List as List
|
import qualified Data.List as List
|
||||||
|
import Numeric.Natural (Natural)
|
||||||
|
|
||||||
data ParseFailure
|
data ParseFailure
|
||||||
= UnexpectedClosingBracket Int -- closing bracket position
|
= UnexpectedClosingBracket Natural -- closing bracket position
|
||||||
| UnmatchedOpenBracket Int -- opening bracket position
|
| UnmatchedOpenBracket Natural -- opening bracket position
|
||||||
deriving stock (Show, Eq)
|
deriving stock (Show, Eq)
|
||||||
|
|
||||||
-- | Convert a Text to a list of instructions, discard all comments.
|
-- | Convert a Text to a list of instructions, discard all comments.
|
||||||
|
@ -30,28 +30,31 @@ parse :: Text -> Either ParseFailure (Vector Instruction)
|
||||||
parse text = runST $ do
|
parse text = runST $ do
|
||||||
collectorVector <- MutableVector.new (Text.length text)
|
collectorVector <- MutableVector.new (Text.length text)
|
||||||
|
|
||||||
result <- go text collectorVector 0
|
result <- parseBlock text collectorVector 0
|
||||||
|
|
||||||
pure $ case result of
|
pure $ case result of
|
||||||
|
|
||||||
Right (instructions, rest) -> case rest of
|
Right (instructions, rest) -> case rest of
|
||||||
Text.Empty -> Right instructions
|
Text.Empty -> Right instructions
|
||||||
_ -> Left (UnexpectedClosingBracket $ Text.length text - Text.length rest)
|
_ -> Left (UnexpectedClosingBracket . fromIntegral $ Text.length text - Text.length rest)
|
||||||
|
|
||||||
Left failure -> Left failure
|
Left failure -> Left failure
|
||||||
|
|
||||||
where
|
where
|
||||||
|
|
||||||
go :: Text -> MVector s Instruction -> Int -> ST s (Either ParseFailure (Vector Instruction, Text))
|
-- | Parses the supplied text until the next closing bracket., the closing bracket will be returned in the rest.
|
||||||
go (Text.Empty ) instructions index = do
|
--
|
||||||
|
-- Assumes the vector has enough space to hold all the instructions. Assumes all elements until the Int index have already been initialized.
|
||||||
|
parseBlock :: Text -> MVector s Instruction -> Int -> ST s (Either ParseFailure (Vector Instruction, Text))
|
||||||
|
parseBlock Text.Empty instructions index = do
|
||||||
let populatedSlice = MutableVector.take index instructions
|
let populatedSlice = MutableVector.take index instructions
|
||||||
frozen <- Vector.force <$!> Vector.freeze populatedSlice
|
frozen <- Vector.force <$!> Vector.freeze populatedSlice
|
||||||
pure . Right $ (frozen, Text.empty)
|
pure . Right $ (frozen, Text.empty)
|
||||||
go t@(c Text.:< cs) instructions index = let
|
parseBlock t@(c Text.:< cs) instructions index = let
|
||||||
|
|
||||||
recognizeInstruction i cont = do
|
recognizeInstruction i cont = do
|
||||||
MutableVector.write instructions index i
|
MutableVector.write instructions index i
|
||||||
go cont instructions (succ index)
|
parseBlock cont instructions (succ index)
|
||||||
|
|
||||||
in case c of
|
in case c of
|
||||||
'+' -> recognizeInstruction Increment cs
|
'+' -> recognizeInstruction Increment cs
|
||||||
|
@ -66,12 +69,12 @@ parse text = runST $ do
|
||||||
pure $ Right (frozen, t)
|
pure $ Right (frozen, t)
|
||||||
'[' -> do
|
'[' -> do
|
||||||
innerVector <- MutableVector.new (Text.length cs)
|
innerVector <- MutableVector.new (Text.length cs)
|
||||||
innerResult <- go cs innerVector 0
|
innerResult <- parseBlock cs innerVector 0
|
||||||
case innerResult of
|
case innerResult of
|
||||||
Right (body, ']' Text.:< rest) -> recognizeInstruction (Loop body) rest
|
Right (body, ']' Text.:< rest) -> recognizeInstruction (Loop body) rest
|
||||||
Right _ -> pure $ Left (UnmatchedOpenBracket $ Text.length text - Text.length t)
|
Right _ -> pure $ Left (UnmatchedOpenBracket . fromIntegral $ Text.length text - Text.length t)
|
||||||
_ -> pure innerResult
|
_ -> pure innerResult
|
||||||
_ -> go cs instructions index
|
_ -> parseBlock cs instructions index
|
||||||
|
|
||||||
|
|
||||||
-- >>> parse $ Text.pack "<<>>,,..<,.>"
|
-- >>> parse $ Text.pack "<<>>,,..<,.>"
|
||||||
|
@ -100,6 +103,7 @@ render = Text.concat . List.map renderSingle . Vector.toList
|
||||||
|
|
||||||
-- >>> import Test.QuickCheck.Instances.Vector ()
|
-- >>> import Test.QuickCheck.Instances.Vector ()
|
||||||
-- >>> import Test.QuickCheck.Arbitrary (arbitrary)
|
-- >>> import Test.QuickCheck.Arbitrary (arbitrary)
|
||||||
|
-- >>> import Test.QuickCheck.Gen as Gen
|
||||||
-- >>> render <$> Gen.generate arbitrary
|
-- >>> render <$> Gen.generate arbitrary
|
||||||
-- "<,<>,<,."
|
-- "-<,.<<,>++.[]<+>.<.-,"
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
# snapshot: ./custom-snapshot.yaml
|
# snapshot: ./custom-snapshot.yaml
|
||||||
# snapshot: https://example.com/snapshots/2024-01-01.yaml
|
# snapshot: https://example.com/snapshots/2024-01-01.yaml
|
||||||
snapshot: nightly-2025-06-16
|
snapshot: nightly-2025-06-16
|
||||||
|
compiler: ghc-9.12.1
|
||||||
|
|
||||||
# User packages to be built.
|
# User packages to be built.
|
||||||
# Various formats can be used as shown in the example below.
|
# Various formats can be used as shown in the example below.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue