1{-# LANGUAGE ScopedTypeVariables #-}
2{-# LANGUAGE BangPatterns #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE NoMonomorphismRestriction #-}
5{-# LANGUAGE NamedFieldPuns #-}
6{-# OPTIONS_GHC -funbox-strict-fields -O #-}
7module Data.Terminfo.Parse ( module Data.Terminfo.Parse
8 , Text.ParserCombinators.Parsec.ParseError
12import Control.Applicative ( Applicative(..), pure, (<*>) )
13import Control.Monad ( liftM )
14import Control.Monad.Trans
15import Control.Parallel.Strategies
17import Data.Array.Unboxed
22import Foreign.Marshal.Array
25import Text.ParserCombinators.Parsec
27type CapBytes = ( Ptr Word8, CSize )
29data CapExpression = CapExpression
31 , cap_bytes :: !CapBytes
32 , source_string :: !String
33 , param_count :: !Word
34 , param_ops :: !ParamOps
37instance NFData CapExpression where
38 rnf (CapExpression ops !_bytes !_str !_c !_p_ops)
45 Bytes !Int !CSize !Int
47 -- This stores a 0-based index to the parameter. However the operation that implies this op is
49 | PushParam !Word | PushValue !Word
50 -- The conditional parts are the sequence of (%t expression, %e expression) pairs.
51 -- The %e expression may be NOP
53 { conditional_expr :: !CapOps
54 , conditional_parts :: ![(CapOps, CapOps)]
56 | BitwiseOr | BitwiseXOr | BitwiseAnd
57 | ArithPlus | ArithMinus
58 | CompareEq | CompareLt | CompareGt
61instance NFData CapOp where
62 rnf ( Bytes offset count next_offset ) = rnf offset
63 rnf (PushParam !_pn) = ()
64 rnf (PushValue !_v) = ()
65 rnf (Conditional c_expr c_parts) = rnf c_expr >| rnf c_parts
68type ParamOps = [ParamOp]
73parse_cap_expression :: ( Applicative m
77 -> m ( Either ParseError CapExpression )
78parse_cap_expression cap_string =
79 let v = runParser cap_expression_parser
84 Left e -> return $ Left e
85 Right build_results -> pure Right <*> construct_cap_expression cap_string build_results
87construct_cap_expression cap_string build_results = do
88 byte_array <- liftIO $ newArray (map ( toEnum . fromEnum ) cap_string )
89 let expr = CapExpression
90 { cap_ops = out_cap_ops build_results
91 -- The cap bytes are the lower 8 bits of the input string's characters.
92 -- \todo Verify the input string actually contains an 8bit byte per character.
93 , cap_bytes = ( byte_array, toEnum $! length cap_string )
94 , source_string = cap_string
95 , param_count = out_param_count build_results
96 , param_ops = out_param_ops build_results
98 return (demanding expr (rnf expr))
100type CapParser a = GenParser Char BuildState a
102cap_expression_parser :: CapParser BuildResults
103cap_expression_parser = do
104 rs <- many $ param_escape_parser <|> bytes_op_parser
107param_escape_parser :: CapParser BuildResults
108param_escape_parser = do
111 literal_percent_parser <|> param_op_parser
113literal_percent_parser :: CapParser BuildResults
114literal_percent_parser = do
116 start_offset <- getState >>= return . next_offset
118 return $ BuildResults 0 [Bytes start_offset 1 1] []
120param_op_parser :: CapParser BuildResults
122 = increment_op_parser
126 <|> conditional_op_parser
127 <|> bitwise_op_parser
129 <|> literal_int_op_parser
130 <|> compare_op_parser
131 <|> char_const_parser
133increment_op_parser :: CapParser BuildResults
134increment_op_parser = do
137 return $ BuildResults 0 [] [ IncFirstTwo ]
139push_op_parser :: CapParser BuildResults
142 param_n <- digit >>= return . (\d -> read [d])
144 return $ BuildResults param_n [ PushParam $ param_n - 1 ] []
146dec_out_parser :: CapParser BuildResults
150 return $ BuildResults 0 [ DecOut ] []
152char_out_parser :: CapParser BuildResults
156 return $ BuildResults 0 [ CharOut ] []
158conditional_op_parser :: CapParser BuildResults
159conditional_op_parser = do
162 cond_part <- many_expr conditional_true_parser
165 true_part <- many_expr $ choice [ try $ lookAhead conditional_end_parser
166 , conditional_false_parser
168 false_part <- many_expr $ choice [ try $ lookAhead conditional_end_parser
169 , conditional_true_parser
171 return ( true_part, false_part )
173 conditional_end_parser
175 let true_parts = map fst parts
176 false_parts = map snd parts
177 BuildResults n cond cond_param_ops = cond_part
179 let n' = maximum $ n : map out_param_count true_parts
180 n'' = maximum $ n' : map out_param_count false_parts
182 let true_ops = map out_cap_ops true_parts
183 false_ops = map out_cap_ops false_parts
184 cond_parts = zip true_ops false_ops
186 let true_param_ops = mconcat $ map out_param_ops true_parts
187 false_param_ops = mconcat $ map out_param_ops false_parts
188 p_ops = mconcat [cond_param_ops, true_param_ops, false_param_ops]
190 return $ BuildResults n'' [ Conditional cond cond_parts ] p_ops
193 many_p !p !end = choice
194 [ try end >> return []
199 many_expr end = liftM mconcat $ many_p ( param_escape_parser <|> bytes_op_parser ) end
201conditional_true_parser :: CapParser ()
202conditional_true_parser = do
206conditional_false_parser :: CapParser ()
207conditional_false_parser = do
211conditional_end_parser :: CapParser ()
212conditional_end_parser = do
216bitwise_op_parser :: CapParser BuildResults
219 <|> bitwise_and_parser
220 <|> bitwise_xor_parser
222bitwise_or_parser :: CapParser BuildResults
223bitwise_or_parser = do
226 return $ BuildResults 0 [ BitwiseOr ] [ ]
228bitwise_and_parser :: CapParser BuildResults
229bitwise_and_parser = do
232 return $ BuildResults 0 [ BitwiseAnd ] [ ]
234bitwise_xor_parser :: CapParser BuildResults
235bitwise_xor_parser = do
238 return $ BuildResults 0 [ BitwiseXOr ] [ ]
240arith_op_parser :: CapParser BuildResults
248 return $ BuildResults 0 [ ArithPlus ] [ ]
252 return $ BuildResults 0 [ ArithMinus ] [ ]
254literal_int_op_parser :: CapParser BuildResults
255literal_int_op_parser = do
259 inc_offset $ toEnum $ length n_str
260 let n :: Word = read n_str
263 return $ BuildResults 0 [ PushValue n ] [ ]
265compare_op_parser :: CapParser BuildResults
274 return $ BuildResults 0 [ CompareEq ] [ ]
278 return $ BuildResults 0 [ CompareLt ] [ ]
282 return $ BuildResults 0 [ CompareGt ] [ ]
284bytes_op_parser :: CapParser BuildResults
286 bytes <- many1 $ satisfy (/= '%')
287 start_offset <- getState >>= return . next_offset
288 let !c = length bytes
290 let s' = s { next_offset = start_offset + c }
292 return $ BuildResults 0 [Bytes start_offset ( toEnum c ) c ] []
294char_const_parser :: CapParser BuildResults
295char_const_parser = do
297 char_value <- liftM (toEnum . fromEnum) anyChar
300 return $ BuildResults 0 [ PushValue char_value ] [ ]
302data BuildState = BuildState
306inc_offset :: Int -> CapParser ()
309 let s' = s { next_offset = next_offset s + n }
312initial_build_state :: BuildState
313initial_build_state = BuildState 0
315data BuildResults = BuildResults
316 { out_param_count :: !Word
317 , out_cap_ops :: !CapOps
318 , out_param_ops :: !ParamOps
321instance Monoid BuildResults where
322 mempty = BuildResults 0 [] []
325 { out_param_count = (out_param_count v0) `max` (out_param_count v1)
326 , out_cap_ops = (out_cap_ops v0) `mappend` (out_cap_ops v1)
327 , out_param_ops = (out_param_ops v0) `mappend` (out_param_ops v1)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list