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 -- | Parser for a full 'Program'.
28 --
29 -- >>> isRight $ parse program " +[->>+ +[<] ##garbage## ],.[-] can ignore garbage"
30 -- True
31 program :: Parser (Program Unoptimized)
32 program = Program <$> many1 operation
33
34 -- | Parser for an 'Op', ignoring unknown characters.
35 --
36 -- >>> parse operation " +///"
37 -- Right (Inc 1 0)
38 --
39 -- >>> parse operation "fooo [+>] baaar "
40 -- Right (Loop [Inc 1 0,Move 1])
41 operation :: Parser Op
42 operation = many garbage *> (simpleOp <|> loopOp) <* many garbage
43
44 -- | The characters allowed in a Brainfuck program except for the loop characters @[@ and @]@.
45 bfSimpleTokens :: String
46 bfSimpleTokens = "><+-.,"
47
48 -- | The characters allowed in a Brainfuck program.
49 bfTokens :: String
50 bfTokens = "[]" ++ bfSimpleTokens
51
52 -- | Parser for unknown characters
53 --
54 -- >>> parse garbage "this is @#! garbage"
55 -- Right 't'
56 --
57 -- >>> isLeft $ parse garbage "+"
58 -- True
59 garbage :: Parser Char
60 garbage = noneOf bfTokens
61
62 -- | Parser for simple operations (not loops).
63 --
64 -- >>> parse simpleOp ">"
65 -- Right (Move 1)
66 --
67 -- >>> parse simpleOp "."
68 -- Right (Out 1 0)
69 simpleOp :: Parser Op
70 simpleOp = build <$> oneOf bfSimpleTokens
71 where
72 build '>' = Move 1
73 build '<' = Move (-1)
74 build '+' = Inc 1 0
75 build '-' = Inc (-1) 0
76 build '.' = Out 1 0
77 build ',' = In 1 0
78 build _ = error "Unknown character"
79
80 -- | Parser for loops.
81 --
82 -- >>> parse loopOp "[+-]"
83 -- Right (Loop [Inc 1 0,Inc (-1) 0])
84 loopOp :: Parser Op
85 loopOp = Loop . instructions <$> between (char '[') (char ']') program
86
87 -- | Parse program stream. Returns an error or the parsed 'Program'
88 parseProgram :: Text -> Either ParseError (Program Unoptimized)
89 parseProgram = runP (program <* eof) () ""