diff options
author | Joe Crayne <joe@jerkface.net> | 2019-06-10 23:03:04 -0400 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2019-06-10 23:03:04 -0400 |
commit | 38b7bcf654e5e804a13518b060ebdba59bf232bb (patch) | |
tree | 2fa3c4ccf3496750f0ce388a9ea0998fdd93bf69 /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.hs | 166 |
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 | -------------------------------------------------------------------------------------------------------------------------------------------- | ||
28 | module Graphics.WaveFront.Parse.Common where | ||
29 | |||
30 | |||
31 | |||
32 | -------------------------------------------------------------------------------------------------------------------------------------------- | ||
33 | -- We'll need these | ||
34 | -------------------------------------------------------------------------------------------------------------------------------------------- | ||
35 | import Data.Text (Text, pack) | ||
36 | import qualified Data.Attoparsec.Text as Atto | ||
37 | |||
38 | import Control.Applicative (pure, liftA2, (<$>), (<*>), (<*), (*>), (<|>)) | ||
39 | import Linear (V2(..), V3(..)) | ||
40 | |||
41 | import 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 (?) | ||
53 | wholeFile :: Atto.Parser a -> Atto.Parser a | ||
54 | wholeFile p = cutToTheChase *> p <* cutToTheChase <* Atto.endOfInput | ||
55 | |||
56 | |||
57 | -- | Skips any leading comments, line breaks and empty lines | ||
58 | -- TODO | - Rename (?) | ||
59 | -- - Skip whitespace | ||
60 | cutToTheChase :: Atto.Parser () | ||
61 | cutToTheChase = 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 | ||
66 | lineSeparator :: Atto.Parser () | ||
67 | lineSeparator = 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 (?) | ||
72 | comment :: Atto.Parser Text | ||
73 | comment = 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 (?) | ||
78 | optional :: Atto.Parser a -> Atto.Parser (Maybe a) | ||
79 | optional p = Atto.option Nothing (Just <$> p) | ||
80 | |||
81 | |||
82 | -- | Like Atto.skipMany, except it skips one match at the most | ||
83 | ignore :: Atto.Parser a -> Atto.Parser () | ||
84 | ignore p = optional p *> pure () | ||
85 | |||
86 | |||
87 | -- | | ||
88 | atleast :: Int -> Atto.Parser a -> Atto.Parser [a] | ||
89 | atleast 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) | ||
93 | space :: Atto.Parser () | ||
94 | space = 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 (?) | ||
100 | isLinearSpace :: Char -> Bool | ||
101 | isLinearSpace c = (c == ' ') || (c == '\t') | ||
102 | |||
103 | |||
104 | -- | One or more letters (cf. 'Atto.letter' for details) | ||
105 | word :: Atto.Parser Text | ||
106 | word = 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 (?) | ||
111 | name :: Atto.Parser Text | ||
112 | name = pack <$> Atto.many1 (Atto.satisfy $ \c -> (c /= ' ') && (c /= '\t') && (c /= '\r') && (c /= '\n')) | ||
113 | |||
114 | |||
115 | -- | Parses the strings "off" (False) and "on" (True) | ||
116 | toggle :: Atto.Parser Bool | ||
117 | toggle = (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 | ||
121 | parenthesised :: Atto.Parser a -> Atto.Parser a | ||
122 | parenthesised p = Atto.char '(' *> p <* Atto.char ')' | ||
123 | |||
124 | |||
125 | -- TODO | - Allow scientific notation (?) | ||
126 | |||
127 | -- | | ||
128 | coord :: Fractional f => Atto.Parser f | ||
129 | coord = 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 (?) | ||
135 | channel :: Fractional f => Atto.Parser f | ||
136 | channel = space *> (parenthesised Atto.rational <|> Atto.rational) | ||
137 | |||
138 | |||
139 | -- | A colour with three or four channels (RGB[A]) | ||
140 | colour :: Fractional f => Atto.Parser (Colour f) | ||
141 | colour = Colour <$> channel <*> channel <*> channel <*> Atto.option 1 channel | ||
142 | |||
143 | |||
144 | -- | A point in 3D space | ||
145 | point3D :: Fractional f => Atto.Parser (V3 f) | ||
146 | point3D = V3 <$> coord <*> coord <*> coord | ||
147 | |||
148 | |||
149 | -- | A point in 2D space | ||
150 | point2D :: Fractional f => Atto.Parser (V2 f) | ||
151 | point2D = V2 <$> coord <*> coord | ||
152 | |||
153 | |||
154 | -- | | ||
155 | clamp :: Ord n => n -> n -> n -> Atto.Parser n | ||
156 | clamp 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 | ||
165 | clamped :: Integral i => i -> i -> Atto.Parser i | ||
166 | clamped lower upper = Atto.decimal >>= clamp lower upper \ No newline at end of file | ||