Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Lets start with question 2 because it is easier to answer. Your approach is correct: as you parse things, you remove those characters from the input string, and return a tuple containing the result of the parse, and the remaining string. However, thereis no reason to write all this from scratch (except perhaps as an academic exercise) - there are plenty of parsers which will take care of this issue for you. The one I will use is <a href="http://hackage.haskell.org/package/parsec-3.0.0" rel="nofollow"><code>Parsec</code></a>. If you are new to monadic parsing you should first read <a href="http://book.realworldhaskell.org/read/using-parsec.html" rel="nofollow">the section on <code>Parsec</code> in RWH.</a></p> <p>As for question 1, if you use <code>ByteString</code> instead of <code>String</code>, then parsing single bytes is easy since single bytes are the atomic elements of <code>ByteString</code>s! </p> <p>There is also the issue of the <code>Char</code>/<code>ByteString</code> interface. With <code>Parsec</code>, this is a non-issue since you can treat a <code>ByteString</code> as a sequence of <code>Byte</code> or <code>Char</code> - we will see this later.</p> <p>I decided to just write the full parser - this is a very simple language so with all the primitives defined for you in the <code>Parsec</code> library, it is very easy and very concise. </p> <p>The file header:</p> <pre><code>import Text.Parsec.Combinator import Text.Parsec.Char import Text.Parsec.ByteString import Text.Parsec import Text.Parsec.Pos import Data.ByteString (ByteString, pack) import qualified Data.ByteString.Char8 as C8 import Control.Monad (replicateM) import Data.Monoid </code></pre> <p>First, we write the 'primitive' parsers - that is, parsing bytes, parsing textual numbers, and parsing whitespace (which the PPM format uses as a seperator):</p> <pre><code>parseIntegral :: (Read a, Integral a) =&gt; Parser a parseIntegral = fmap read (many1 digit) </code></pre> <p><code>digit</code> parses a single digit - you'll notice that many function names explain what the parser does - and <code>many1</code> will apply the given parser 1 or more times. Then we read the resulting string to return an actual number (as opposed to a string). In this case, the input <code>ByteString</code> is being treated as text.</p> <pre><code>parseByte :: Integral a =&gt; Parser a parseByte = fmap (fromIntegral . fromEnum) $ tokenPrim show (\pos tok _ -&gt; updatePosChar pos tok) Just </code></pre> <p>For this parser, we parse a single <code>Char</code> - which is really just a byte. It is just returned as a <code>Char</code>. We could safely make the return type <code>Parser Word8</code> because the universe of values that can be returned is <code>[0..255]</code></p> <pre><code>whitespace1 :: Parser () whitespace1 = many1 (oneOf "\n ") &gt;&gt; return () </code></pre> <p><code>oneOf</code> takes a list of <code>Char</code> and parses any one of the characters in the order given - again, the <code>ByteString</code> is being treated as <code>Text</code>. </p> <p>Now we can write the parser for the header. </p> <pre><code>parseHeader :: Parser Header parseHeader = do f &lt;- choice $ map try $ [string "P3" &gt;&gt; return TextualBitmap ,string "P6" &gt;&gt; return BinaryBitmap] w &lt;- whitespace1 &gt;&gt; parseIntegral h &lt;- whitespace1 &gt;&gt; parseIntegral d &lt;- whitespace1 &gt;&gt; parseIntegral return $ Header f w h d </code></pre> <p>A few notes. <code>choice</code> takes a list of parsers and tries them in order. <code>try p</code> takes the parser p, and 'remembers' the state before <code>p</code> starts parsing. If p succeeds, then <code>try p == p</code>. If p fails, then the state before p started is restored and you pretend you never tried <code>p</code>. This is necessary due to how <code>choice</code> behaves.</p> <p>For the pixels, we have two choices as of now:</p> <pre><code>parseTextual :: Header -&gt; Parser [Pixel] parseTextual h = do xs &lt;- replicateM (3 * width h * height h) (whitespace1 &gt;&gt; parseIntegral) return $ map (\[a,b,c] -&gt; Pixel a b c) $ chunksOf 3 xs </code></pre> <p>We could use <code>many1 (whitespace 1 &gt;&gt; parseIntegral)</code> - but this wouldn't enforce the fact that we know what the length should be. Then, converting the list of numbers to a list of pixels is trivial.</p> <p>For binary data:</p> <pre><code>parseBinary :: Header -&gt; Parser [Pixel] parseBinary h = do whitespace1 xs &lt;- replicateM (3 * width h * height h) parseByte return $ map (\[a,b,c] -&gt; Pixel a b c) $ chunksOf 3 xs </code></pre> <p>Note how the two are almost identical. You could probably generalize this function (it would be especially useful if you decided to parse the other types of pixel data - monochrome and greyscale).</p> <p>Now to bring it all together:</p> <pre><code>parsePPM :: Parser PPM parsePPM = do h &lt;- parseHeader fmap (PPM h) $ case format h of TextualBitmap -&gt; parseTextual h BinaryBitmap -&gt; parseBinary h </code></pre> <p>This should be self-explanatory. Parse the header, then parse the body based on the format. Here are some examples to try it on. They are the ones from the specification page.</p> <pre><code>example0 :: ByteString example0 = C8.pack $ unlines ["P3" , "4 4" , "15" , " 0 0 0 0 0 0 0 0 0 15 0 15" , " 0 0 0 0 15 7 0 0 0 0 0 0" , " 0 0 0 0 0 0 0 15 7 0 0 0" , "15 0 15 0 0 0 0 0 0 0 0 0" ] example1 :: ByteString example1 = C8.pack ("P6 4 4 15 ") &lt;&gt; pack [0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 15, 0, 0, 0, 0, 15, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 7, 0, 0, 0, 15, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] </code></pre> <p>Several notes: this doesn't handle comments, which are part of the spec. The error messages are not very useful; you can use the <a href="http://hackage.haskell.org/package/parsec-3.0.0/docs/Text-Parsec-Prim.html#v%3a-60--63--62-" rel="nofollow"><code>&lt;?&gt;</code></a> function to create your own error messages. The spec also indicates 'The lines should not be longer than 70 characters.' - this is also not enforced.</p> <p>edit:</p> <p>Just because you see do-notation, doesn't necessarily mean that you are working with impure code. Some monads (like this parser) are still pure - they are just used for convenience. For example, you can write your parser with the type <code>parser :: String -&gt; (a, String)</code>, or, what we have done here, is we use a new type: <code>data Parser a = Parser (String -&gt; (a, String))</code> and have <code>parser :: Parser a</code>; we then write a monad instance for <code>Parser</code> to get the useful do-notation. To be clear, <code>Parsec</code> supports monadic parsing, but our parser is not monadic - or rather, uses the <code>Identity</code> monad, which is just <code>newtype Identity a = Identity { runIdentity :: a }</code>, and is only necessary because if we used <code>type Identity a = a</code> we would have 'overlapping instances' errors everywhere, which is not good.</p> <pre><code>&gt;:i Parser type Parser = Parsec ByteString () -- Defined in `Text.Parsec.ByteString' &gt;:i Parsec type Parsec s u = ParsecT s u Data.Functor.Identity.Identity -- Defined in `Text.Parsec.Prim' </code></pre> <p>So then, the type of <code>Parser</code> is really <code>ParsecT ByteString () Identity</code>. That is, the parser input is <code>ByteString</code>, the user state is <code>()</code> - which just means we aren't using the user state, and the monad in which we are parsing is <code>Identity</code>. <code>ParsecT</code> is itself just a newtype of:</p> <pre><code>forall b. State s u -&gt; (a -&gt; State s u -&gt; ParseError -&gt; m b) -&gt; (ParseError -&gt; m b) -&gt; (a -&gt; State s u -&gt; ParseError -&gt; m b) -&gt; (ParseError -&gt; m b) -&gt; m b </code></pre> <p>All those functions in the middle are just used to pretty-print errors. If you are parsing 10's of thousands of characters and an error occurs, you won't be able to just look at it and see where that happened - but <code>Parsec</code> will tell you the line and column. If we specialize all the types to our <code>Parser</code>, and pretend that <code>Identity</code> is just <code>type Identity a = a</code>, then all the monads disappear and you can see that the parser is not impure. As you can see, <code>Parsec</code> is a lot more powerful than is required for this problem - I just used it due to familiarity, but if you were willing to write your own primitive functions like <code>many</code> and <code>digit</code>, then you could get away with using <code>newtype Parser a = Parser (ByteString -&gt; (a, ByteString))</code>.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload