summaryrefslogtreecommitdiff
path: root/src/Graphics/WaveFront/Parse/Common.hs
diff options
context:
space:
mode:
authorJoe Crayne <joe@jerkface.net>2019-06-10 23:03:04 -0400
committerJoe Crayne <joe@jerkface.net>2019-06-10 23:03:04 -0400
commit38b7bcf654e5e804a13518b060ebdba59bf232bb (patch)
tree2fa3c4ccf3496750f0ce388a9ea0998fdd93bf69 /src/Graphics/WaveFront/Parse/Common.hs
Initial commit.
Diffstat (limited to 'src/Graphics/WaveFront/Parse/Common.hs')
-rw-r--r--src/Graphics/WaveFront/Parse/Common.hs166
1 files changed, 166 insertions, 0 deletions
diff --git a/src/Graphics/WaveFront/Parse/Common.hs b/src/Graphics/WaveFront/Parse/Common.hs
new file mode 100644
index 0000000..bfeb2d8
--- /dev/null
+++ b/src/Graphics/WaveFront/Parse/Common.hs
@@ -0,0 +1,166 @@
1-- |
2-- Module : Graphics.WaveFront.Parse.Common
3-- Description :
4-- Copyright : (c) Jonatan H Sundqvist, October 2 2016
5-- License : MIT
6-- Maintainer : Jonatan H Sundqvist
7-- Stability : experimental|stable
8-- Portability : POSIX (not sure)
9
10-- TODO | - Fully polymorphic (even in the string and list types) (?)
11-- -
12
13-- SPEC | -
14-- -
15
16
17
18--------------------------------------------------------------------------------------------------------------------------------------------
19-- GHC Extensions
20--------------------------------------------------------------------------------------------------------------------------------------------
21{-# LANGUAGE OverloadedStrings #-}
22
23
24
25--------------------------------------------------------------------------------------------------------------------------------------------
26-- Section
27--------------------------------------------------------------------------------------------------------------------------------------------
28module Graphics.WaveFront.Parse.Common where
29
30
31
32--------------------------------------------------------------------------------------------------------------------------------------------
33-- We'll need these
34--------------------------------------------------------------------------------------------------------------------------------------------
35import Data.Text (Text, pack)
36import qualified Data.Attoparsec.Text as Atto
37
38import Control.Applicative (pure, liftA2, (<$>), (<*>), (<*), (*>), (<|>))
39import Linear (V2(..), V3(..))
40
41import Graphics.WaveFront.Types
42
43
44
45--------------------------------------------------------------------------------------------------------------------------------------------
46-- Functions (pure)
47--------------------------------------------------------------------------------------------------------------------------------------------
48
49-- Jon's little helpers --------------------------------------------------------------------------------------------------------------------
50
51-- | Consumes all input, including any leading or trailing comments and whitespace
52-- TODO | - Rename (?)
53wholeFile :: Atto.Parser a -> Atto.Parser a
54wholeFile p = cutToTheChase *> p <* cutToTheChase <* Atto.endOfInput
55
56
57-- | Skips any leading comments, line breaks and empty lines
58-- TODO | - Rename (?)
59-- - Skip whitespace
60cutToTheChase :: Atto.Parser ()
61cutToTheChase = Atto.skipMany ((comment *> pure ()) <|> (Atto.satisfy isLinearSpace *> pure ()) <|> Atto.endOfLine)
62
63
64-- | OBJ rows may be separated by one or more lines of comments and whitespace, or empty lines.
65-- TODO | - Make sure this is right
66lineSeparator :: Atto.Parser ()
67lineSeparator = Atto.skipMany1 $ ignore space *> ignore comment *> Atto.endOfLine
68
69
70-- | Parses a comment (from the '#' to end of the line), possibly preceded by whitespace
71-- TODO | - Break out the whitespace part (?)
72comment :: Atto.Parser Text
73comment = Atto.skipSpace *> Atto.char '#' *> Atto.takeTill (\c -> (c == '\r') || (c == '\n')) -- TODO: Is the newline consumed (?)
74
75
76-- | Tries the given parser, falls back to 'Nothing' if it fails
77-- TODO | - Use 'try' to enforce backtracking (?)
78optional :: Atto.Parser a -> Atto.Parser (Maybe a)
79optional p = Atto.option Nothing (Just <$> p)
80
81
82-- | Like Atto.skipMany, except it skips one match at the most
83ignore :: Atto.Parser a -> Atto.Parser ()
84ignore p = optional p *> pure ()
85
86
87-- |
88atleast :: Int -> Atto.Parser a -> Atto.Parser [a]
89atleast n p = liftA2 (++) (Atto.count n p) (Atto.many' p)
90
91
92-- | Skips atleast one white space character (not including newlines and carriage returns)
93space :: Atto.Parser ()
94space = Atto.skipMany1 (Atto.satisfy isLinearSpace)
95
96
97-- | Predicate for linear space (eg. whitespace besides newlines)
98-- TODO | - Unicode awareness (cf. Data.Char.isSpace)
99-- - Come up with a better name (?)
100isLinearSpace :: Char -> Bool
101isLinearSpace c = (c == ' ') || (c == '\t')
102
103
104-- | One or more letters (cf. 'Atto.letter' for details)
105word :: Atto.Parser Text
106word = pack <$> Atto.many1 Atto.letter
107
108
109-- | Used for texture, material, object and group names (and maybe others that I have yet to think of)
110-- TODO | - Use Unicode groups, make more robust (?)
111name :: Atto.Parser Text
112name = pack <$> Atto.many1 (Atto.satisfy $ \c -> (c /= ' ') && (c /= '\t') && (c /= '\r') && (c /= '\n'))
113
114
115-- | Parses the strings "off" (False) and "on" (True)
116toggle :: Atto.Parser Bool
117toggle = (Atto.string "off" *> pure False) <|> (Atto.string "on" *> pure True)
118
119
120-- | Wraps a parser in a '(' and a ')', with no whitespace in between
121parenthesised :: Atto.Parser a -> Atto.Parser a
122parenthesised p = Atto.char '(' *> p <* Atto.char ')'
123
124
125-- TODO | - Allow scientific notation (?)
126
127-- |
128coord :: Fractional f => Atto.Parser f
129coord = space *> (parenthesised Atto.rational <|> Atto.rational)
130
131
132-- | A single colour channel
133-- TODO | - Clamp to [0,1] (cf. partial from monadplus) (?)
134-- - Can channels be parenthesised (?)
135channel :: Fractional f => Atto.Parser f
136channel = space *> (parenthesised Atto.rational <|> Atto.rational)
137
138
139-- | A colour with three or four channels (RGB[A])
140colour :: Fractional f => Atto.Parser (Colour f)
141colour = Colour <$> channel <*> channel <*> channel <*> Atto.option 1 channel
142
143
144-- | A point in 3D space
145point3D :: Fractional f => Atto.Parser (V3 f)
146point3D = V3 <$> coord <*> coord <*> coord
147
148
149-- | A point in 2D space
150point2D :: Fractional f => Atto.Parser (V2 f)
151point2D = V2 <$> coord <*> coord
152
153
154-- |
155clamp :: Ord n => n -> n -> n -> Atto.Parser n
156clamp lower upper n
157 | between lower upper n = pure n
158 | otherwise = fail "Number not in range"
159 where
160 between lw up n = (lower <= n) && (n <= upper)
161 -- between 0 <. n <. 5
162
163-- |
164-- TODO | - Clean up and generalise
165clamped :: Integral i => i -> i -> Atto.Parser i
166clamped lower upper = Atto.decimal >>= clamp lower upper \ No newline at end of file