never executed always true always false
1 {-|
2 Description : Brainfuck parsing
3 Copyright : (c) Sebastian Galkin, 2018
4 License : GPL-3
5
6 Parsing 'Text' into 'Program' 'Unparsed'
7 -}
8 module HBF.Parser
9 ( module HBF.Parser
10 -- * Reexport from "Text.Parsec"
11 , Text.Parsec.ParseError
12 ) where
13
14 import Control.Applicative ((<|>))
15 import Data.Text.Lazy (Text)
16 import Text.Parsec (ParseError, between, eof, many, many1,
17 runP)
18 import Text.Parsec.Char (char, noneOf, oneOf)
19 import Text.Parsec.Text.Lazy (Parser)
20
21 import HBF.Types
22
23 -- $setup
24 -- >>> :set -XOverloadedStrings
25 -- >>> import Data.Either
26 -- >>> let parse :: Parser a -> Text -> Either ParseError a; parse p text = runP p () "" text
27
28
29 -- | Parser for a full 'Program'.
30 --
31 -- >>> isRight $ parse program " +[->>+ +[<] ##garbage## ],.[-] can ignore garbage"
32 -- True
33 program :: Parser (Program Unoptimized)
34 program = Program <$> many1 operation
35
36 -- | Parser for an 'Op', ignoring unknown characters.
37 --
38 -- >>> parse operation " +///"
39 -- Right (Inc 1 0)
40 --
41 -- >>> parse operation "fooo [+>] baaar "
42 -- Right (Loop [Inc 1 0,Move 1])
43 operation :: Parser Op
44 operation = many garbage *> (simpleOp <|> loopOp) <* many garbage
45
46 -- | The characters allowed in a Brainfuck program except for the loop characters @[@ and @]@.
47 bfSimpleTokens :: String
48 bfSimpleTokens = "><+-.,"
49
50 -- | The characters allowed in a Brainfuck program.
51 bfTokens :: String
52 bfTokens = "[]" ++ bfSimpleTokens
53
54 -- | Parser for unknown characters
55 --
56 -- >>> parse garbage "this is @#! garbage"
57 -- Right 't'
58 --
59 -- >>> isLeft $ parse garbage "+"
60 -- True
61 garbage :: Parser Char
62 garbage = noneOf bfTokens
63
64 -- | Parser for simple operations (not loops).
65 --
66 -- >>> parse simpleOp ">"
67 -- Right (Move 1)
68 --
69 -- >>> parse simpleOp "."
70 -- Right (Out 1 0)
71 simpleOp :: Parser Op
72 simpleOp = build <$> oneOf bfSimpleTokens
73 where
74 build '>' = Move 1
75 build '<' = Move (-1)
76 build '+' = Inc 1 0
77 build '-' = Inc (-1) 0
78 build '.' = Out 1 0
79 build ',' = In 1 0
80 build _ = error "Unknown character"
81
82 -- | Parser for loops.
83 --
84 -- >>> parse loopOp "[+-]"
85 -- Right (Loop [Inc 1 0,Inc (-1) 0])
86 loopOp :: Parser Op
87 loopOp = Loop . instructions <$> between (char '[') (char ']') program
88
89 -- | Parse program stream. Returns an error or the parsed 'Program'
90 parseProgram :: Text -> Either ParseError (Program Unoptimized)
91 parseProgram = runP (program <* eof) () ""