2
2
{-# LANGUAGE DeriveFunctor #-}
3
3
{-# LANGUAGE TemplateHaskell #-}
4
4
5
-
6
5
module Neorg.Parser where
7
6
8
- import Control.Applicative
7
+ import Control.Applicative ( Alternative ( many , (<|>) ))
9
8
import Control.Arrow (left )
10
- import Control.Monad
11
- import Control.Monad.Trans.Class
9
+ import Control.Monad ( guard , void )
10
+ import Control.Monad.Trans.Class ( MonadTrans ( lift ))
12
11
import Control.Monad.Trans.State
12
+ ( State ,
13
+ StateT (runStateT ),
14
+ evalStateT ,
15
+ get ,
16
+ gets ,
17
+ modify ,
18
+ put ,
19
+ )
13
20
import Data.Bool (bool )
14
21
import Data.Char (isLetter )
15
22
import Data.Foldable (foldl' )
16
- import Data.Functor
17
- import Data.Functor.Identity
18
- import Data.Maybe ( catMaybes )
23
+ import Data.Functor ( ($>) , (<&>) )
24
+ import Data.Functor.Identity ( Identity ( runIdentity ))
25
+ import Data.Maybe (catMaybes )
19
26
import qualified Data.Set as S
20
27
import Data.Text (Text , pack , unpack )
21
28
import qualified Data.Text as T
22
29
import Data.Time (dayOfWeek , defaultTimeLocale , parseTimeM )
23
30
import qualified Data.Vector as V
24
31
import Data.Vector.Generic.Mutable (clear )
25
- import Data.Void
32
+ import Data.Void ( Void )
26
33
import Debug.Trace
27
34
import Neorg.Document
28
35
import Neorg.Document.Tag (GenerateTagParser , parseTag )
29
36
import Neorg.ParsingUtils (embedParser )
30
- import Optics.Core
31
- import Optics.TH
37
+ import Optics.Core ( view , (%~) , (&) , (.~) , (<&>) , (?~) , (^.) )
38
+ import Optics.TH ( makeLenses )
32
39
import Text.Megaparsec (parseErrorPretty )
33
40
import qualified Text.Megaparsec as P
34
41
import qualified Text.Megaparsec.Char as P
35
42
import qualified Text.Megaparsec.Char.Lexer as P
36
- import Text.Megaparsec.Internal
43
+ import Text.Megaparsec.Internal ( ParsecT ( .. ))
37
44
38
45
data ParserState = ParserState
39
46
{ _parserHeadingLevel :: IndentationLevel ,
@@ -43,9 +50,9 @@ data ParserState = ParserState
43
50
44
51
data InlineState = InlineState { _modifierInline :: ModifierInline , _delimitedActive :: Bool } deriving (Show )
45
52
46
- data ModifierInline = NoModifier Inline | OpenModifier String Inline ModifierInline deriving (Show )
53
+ data ModifierInline = NoModifier Inline | OpenModifier Text Inline ModifierInline deriving (Show )
47
54
48
- hasModifier :: String -> ModifierInline -> Bool
55
+ hasModifier :: Text -> ModifierInline -> Bool
49
56
hasModifier c (NoModifier _) = False
50
57
hasModifier c1 (OpenModifier c2 i b) = c1 == c2 || hasModifier c1 b
51
58
@@ -72,12 +79,11 @@ parse fileName fileContent =
72
79
73
80
document :: GenerateTagParser tags => Parser (Document tags )
74
81
document = do
75
- blocks <- blocks
76
- pure $ Document blocks
82
+ Document <$> blocks
77
83
78
84
blocks :: GenerateTagParser tags => Parser (Blocks tags )
79
85
blocks = do
80
- blocks <- P. many $ singleBlock
86
+ blocks <- P. many singleBlock
81
87
pure $ V. fromList $ catMaybes blocks
82
88
83
89
singleBlock :: GenerateTagParser tags => Parser (Maybe (Block tags ))
@@ -261,12 +267,12 @@ runInline p = fmap canonalizeInline $ do
261
267
where
262
268
reduceModifierInline :: ModifierInline -> Inline
263
269
reduceModifierInline (NoModifier i) = i
264
- reduceModifierInline (OpenModifier c i b) = ConcatInline $ V. fromList [reduceModifierInline b, Text $ pack c, i]
270
+ reduceModifierInline (OpenModifier c i b) = ConcatInline $ V. fromList [reduceModifierInline b, Text c, i]
265
271
266
272
paragraph' :: forall p . (MonadFail p , P. MonadParsec Void Text p ) => StateT InlineState p () -> StateT InlineState p ()
267
273
paragraph' end' = do
268
274
(end >> pure () )
269
- <|> (lookChar >>= \ c -> openings c <|> word c)
275
+ <|> (lookChar >>= \ c -> attachedOpenings c <|> word c)
270
276
where
271
277
end = do
272
278
end' <|> P. eof
@@ -276,20 +282,41 @@ paragraph' end' = do
276
282
modifierInline %~ \ case
277
283
NoModifier i -> NoModifier $ i <> t
278
284
OpenModifier c i b -> OpenModifier c (i <> t) b
279
-
280
- openings :: Char -> StateT InlineState p ()
281
- openings = \ case
285
+ popStack :: Text -> (Inline -> Inline ) -> StateT InlineState p ()
286
+ popStack c f = do
287
+ s <- gets (view modifierInline)
288
+ new <- close s
289
+ modify $ modifierInline .~ new
290
+ where
291
+ close (NoModifier b) = fail " No closing"
292
+ close (OpenModifier cm i b) =
293
+ case b of
294
+ (OpenModifier cd id bd) -> if c == cm then pure (OpenModifier cd (id <> f i) bd) else close (OpenModifier cd (id <> Text cm <> i) bd)
295
+ (NoModifier id ) -> if c == cm then pure (NoModifier (id <> f i)) else fail " No closing"
296
+ pushStack :: Text -> StateT InlineState p ()
297
+ pushStack c = do
298
+ s <- gets (view modifierInline)
299
+ new <- case s of
300
+ NoModifier i -> pure $ OpenModifier c mempty (NoModifier i)
301
+ stack@ (OpenModifier cm i b) ->
302
+ if not $ hasModifier c stack
303
+ then pure $ OpenModifier c mempty stack
304
+ else fail " No open modifier"
305
+ modify $ modifierInline .~ new
306
+
307
+ attachedOpenings :: Char -> StateT InlineState p ()
308
+ attachedOpenings = \ case
282
309
' *' -> parseOpening " *"
283
310
' /' -> parseOpening " /"
284
311
' _' -> parseOpening " _"
285
312
' -' -> parseOpening " -"
286
- ' ^' -> parseOpening " ^"
287
313
' ,' -> parseOpening " ,"
288
314
' |' -> parseOpening " |"
315
+ ' ^' -> parseOpening " ^"
289
316
' `' -> parseTextModifier " `" Verbatim
290
317
' $' -> parseTextModifier " $" Math
291
318
-- '=' -> parseOpening TODO: Behavior unclear
292
- _ -> fail " No openings "
319
+ _ -> fail " No attachedOpenings "
293
320
where
294
321
parseTextModifier :: Text -> (Text -> Inline ) -> StateT InlineState p ()
295
322
parseTextModifier char f = P. try (go " " ) <|> word (T. head char)
@@ -299,59 +326,48 @@ paragraph' end' = do
299
326
P. string char
300
327
text <- P. takeWhileP (Just " Inline Text modifier" ) (\ c -> c /= T. head char && c /= ' \n ' )
301
328
let fullText = previousText <> text
302
- (P. string char >> appendInlineToStack (f fullText)) <|> (P. newline >> P. hspace >> P. newline >> fail " No Text modifier" ) <|> (P. newline >> P. hspace >> go fullText)
303
- pushStack c = do
304
- s <- gets (view modifierInline)
305
- new <- case s of
306
- NoModifier i -> pure $ OpenModifier c mempty (NoModifier i)
307
- stack@ (OpenModifier cm i b) ->
308
- if not $ hasModifier c stack
309
- then pure $ OpenModifier c mempty stack
310
- else fail " No open modifier"
311
- modify $ modifierInline .~ new
329
+ ( do
330
+ P. string char
331
+ followedBy
332
+ ( singleSpace <|> newline
333
+ <|> withNextChar
334
+ (guard . flip S. member (punctuationSymbols <> attachedModifierSymbols))
335
+ )
336
+ appendInlineToStack (f fullText)
337
+ modify (delimitedActive .~ False )
338
+ withNextChar $ \ c -> parWhitespace c <|> attachedClosings c <|> attachedOpenings c <|> word c
339
+ )
340
+ <|> (P. newline >> P. hspace >> P. newline >> fail " No Text modifier" )
341
+ <|> (P. newline >> P. hspace >> go fullText)
312
342
parseOpening c = do
313
343
P. try $ do
314
344
anyChar >> withNextChar (\ c -> guard $ isLetter c || S. member c specialSymbols)
315
345
pushStack c
316
346
modify (delimitedActive .~ False )
317
- withNextChar (\ c -> P. choice [parNewline c, openings c, word c])
318
-
319
- space :: Char -> StateT InlineState p ()
320
- space = \ case
321
- ' ' -> do
322
- P. hspace >> appendInlineToStack Space
323
- withNextChar $ \ c -> parNewline c <|> openings c <|> word c
324
- _ -> fail " No space"
347
+ withNextChar (\ c -> P. choice [attachedOpenings c, word c])
325
348
326
- closings :: Char -> StateT InlineState p ()
327
- closings = \ case
349
+ attachedClosings :: Char -> StateT InlineState p ()
350
+ attachedClosings = \ case
328
351
' *' -> parseClosing " *" Bold
329
352
' /' -> parseClosing " /" Italic
330
353
' _' -> parseClosing " _" Underline
331
354
' -' -> parseClosing " -" Strikethrough
332
355
' ^' -> parseClosing " ^" Superscript
333
356
' ,' -> parseClosing " ," Subscript
334
357
' |' -> parseClosing " |" Spoiler
335
- _ -> fail " No closings"
358
+ _ -> fail " No attached closings"
336
359
where
337
360
parseClosing c f = do
338
361
P. try $ do
339
- anyChar >> followedBy (singleSpace <|> newline <|> withNextChar punctuationOrModifier)
362
+ P. string c
363
+ >> followedBy
364
+ ( singleSpace <|> newline
365
+ <|> withNextChar
366
+ (guard . flip S. member (punctuationSymbols <> attachedModifierSymbols))
367
+ )
340
368
popStack c f
341
369
modify (delimitedActive .~ False )
342
- withNextChar $ \ c -> parNewline c <|> closings c <|> openings c <|> space c <|> punctuationOrModifier c
343
-
344
- popStack :: String -> (Inline -> Inline ) -> StateT InlineState p ()
345
- popStack c f = do
346
- s <- gets (view modifierInline)
347
- new <- close s
348
- modify $ modifierInline .~ new
349
- where
350
- close (NoModifier b) = fail " No closing"
351
- close (OpenModifier cm i b) =
352
- case b of
353
- (OpenModifier cd id bd) -> if c == cm then pure (OpenModifier cd (id <> f i) bd) else close (OpenModifier cd (id <> Text (pack cm) <> i) bd)
354
- (NoModifier id ) -> if c == cm then pure (NoModifier (id <> f i)) else fail " No closing"
370
+ withNextChar $ \ c -> parWhitespace c <|> attachedClosings c <|> attachedOpenings c <|> word c
355
371
356
372
word :: Char -> StateT InlineState p ()
357
373
word c = do
@@ -361,13 +377,13 @@ paragraph' end' = do
361
377
( do
362
378
p <- lift anyChar <&> pack . (: [] )
363
379
appendInlineToStack (Text p)
364
- withNextChar $ \ c -> space c <|> parNewline c <|> closings c <|> openings c <|> word c
380
+ withNextChar $ \ c -> parWhitespace c <|> attachedClosings c <|> attachedOpenings c <|> word c
365
381
)
366
382
else
367
383
( do
368
384
w <- P. takeWhile1P (Just " Word" ) (\ c -> c > ' ' && S. notMember c (punctuationSymbols <> attachedModifierSymbols))
369
385
appendInlineToStack (Text w)
370
- withNextChar $ \ c -> space c <|> parNewline c <|> closings c <|> word c
386
+ withNextChar $ \ c -> parWhitespace c <|> attachedClosings c <|> word c
371
387
)
372
388
punctuationSymbols = S. fromList " ?!:;,.<>()[]{}'\" /#%&$£€-*\\ ~"
373
389
attachedModifierSymbols = S. fromList " */_-^,|`$="
@@ -377,25 +393,32 @@ paragraph' end' = do
377
393
withNextChar :: (Char -> StateT InlineState p () ) -> StateT InlineState p ()
378
394
withNextChar f = end <|> (lookChar >>= f)
379
395
380
- parNewline :: Char -> StateT InlineState p ()
381
- parNewline = \ case
396
+ intersectingModifier :: Char -> StateT InlineState p ()
397
+ intersectingModifier = \ case
398
+ _ -> fail " No intersecting modifier"
399
+
400
+ parWhitespace :: Char -> StateT InlineState p ()
401
+ parWhitespace = \ case
402
+ ' ' -> do
403
+ appendInlineToStack Space
404
+ next
382
405
' ~' -> do
383
406
P. try (anyChar >> P. hspace >> P. newline)
384
407
next
385
408
' \n ' -> do
386
409
newline
387
410
modify (delimitedActive .~ True )
388
411
next
389
- _ -> fail " No newline"
412
+ _ -> fail " No newline or space "
390
413
where
391
414
next = do
392
415
P. hspace
393
- withNextChar (\ c -> openings c <|> word c)
416
+ withNextChar (\ c -> parWhitespace c <|> attachedOpenings c <|> word c)
394
417
punctuationOrModifier c = do
395
418
if S. member c (S. fromList " ?!:;,.<>()[]{}'\" /#%&$£€-*\\ ~" <> S. fromList " */_-^,|`$=" )
396
419
then do
397
420
modify (delimitedActive .~ False )
398
- anyChar >> withNextChar (\ c -> space c <|> parNewline c <|> closings c <|> openings c <|> word c)
421
+ anyChar >> withNextChar (\ c -> parWhitespace c <|> attachedClosings c <|> attachedOpenings c <|> word c)
399
422
else fail " "
400
423
401
424
many1 p = p >>= \ a -> (a : ) <$> many p
@@ -505,4 +528,8 @@ instance ParseTagContent "table" where
505
528
in P. try delimiter <|> inlines
506
529
cellParagraph = runInline $ do
507
530
modify $ delimitedActive .~ False
508
- paragraph' $ void (P. string " | " ) <|> (P. try $ P. string " |" >> P. lookAhead (void P. newline <|> P. eof)) <|> void (P. lookAhead P. newline)
531
+ paragraph' $
532
+ void (P. string " | " )
533
+ <|> P. try
534
+ (P. string " |" >> P. lookAhead (void P. newline <|> P. eof))
535
+ <|> void (P. lookAhead P. newline)
0 commit comments