1 {-# LANGUAGE ConstraintKinds #-}
2 {-# OPTIONS_GHC -fno-warn-orphans #-}
6 import Test.Tasty.HUnit
8 import Control.Applicative (Applicative(..))
9 import qualified Control.Applicative as Alt
11 import qualified Data.Char as Char
12 import Data.Semigroup ((<>))
13 import Data.String (IsString(..))
14 import qualified Data.Text as Text
15 import Prelude hiding (any, (^), exp)
16 import qualified Text.Megaparsec as P
18 import Language.Symantic.Grammar
21 type ParsecC e s = (P.Token s ~ Char, P.Stream s, P.ErrorComponent e)
22 instance ParsecC e s => IsString (P.ParsecT e s m [Char]) where
24 instance ParsecC e s => Gram_Rule (P.ParsecT e s m) where
25 rule = P.label . Text.unpack
26 instance ParsecC e s => Gram_Terminal (P.ParsecT e s m) where
31 unicat cat = P.satisfy $ (`elem` cats) . Char.generalCategory
32 where cats = unicode_categories cat
33 range (l, h) = P.satisfy $ \c -> l <= c && c <= h
34 but (Terminal f) (Terminal p) = Terminal $ P.notFollowedBy (P.try p) *> f
35 instance ParsecC e s => Alter (P.ParsecT e s m) where
39 instance ParsecC e s => Try (P.ParsecT e s m) where
41 instance ParsecC e s => Gram_RegR (P.ParsecT e s m) where
42 Terminal f .*> Reg x = Reg $ f <*> x
43 instance ParsecC e s => Gram_RegL (P.ParsecT e s m) where
44 Reg f <*. Terminal x = Reg $ f <*> x
45 instance ParsecC e s => App (P.ParsecT e s m)
46 instance ParsecC e s => Alt (P.ParsecT e s m)
47 instance ParsecC e s => Gram_CF (P.ParsecT e s m) where
48 CF f <& Reg p = CF $ P.lookAhead f <*> p
49 Reg f &> CF p = CF $ P.lookAhead f <*> p
50 CF f `minus` Reg p = CF $ P.notFollowedBy (P.try p) *> f
51 instance ParsecC e s => Gram_Meta P.SourcePos (P.ParsecT e s m) where
55 instance ParsecC e s => Gram_Lexer (P.ParsecT e s m)
57 elide :: Text.Text -> String
58 elide s | Text.length s > 42 = take 42 (Text.unpack s) <> ['…']
59 elide s = Text.unpack s
62 tests = testGroup "Grammar"
63 [ testGroup "Terminal" $
65 testCase (elide exp) $
66 runEBNF (unTerminal (void inp)) @?= exp
68 [ string "" ==> "\"\""
69 , string "abé\"to" ==> "\"abé\", U+34, \"to\""
70 , string "\"" ==> "U+34"
71 , range ('a', 'z') ==> "\"a\"…\"z\""
72 , unicat Unicat_Letter ==> "Unicat_Letter"
73 , unicat (Unicat Char.LowercaseLetter) ==> "Unicat LowercaseLetter"
77 testCase (elide exp) $
78 runEBNF (unReg (void inp)) @?= exp
80 [ (<>) <$> string "0" .*> someR (char '1') ==> "\"0\", {\"1\"}-"
81 , (<>) <$> someL (char '1') <*. string "0" ==> "{\"1\"}-, \"0\""
85 testCase (elide exp) $
86 runEBNF (unCF (void inp)) @?= exp
88 [ (<>) <$> string "0" <*> string "1" ==> "\"0\", \"1\""
89 , (<>) <$> string "0" <* string "X" <*> string "1" ==> "\"0\", \"X\", \"1\""
90 , (<>) <$> (string "0" <+> string "1") <*> string "2" ==> "(\"0\" | \"1\"), \"2\""
91 , (<>) <$> string "0" <*> (string "1" <+> string "2") ==> "\"0\", (\"1\" | \"2\")"
92 , string "0" <+> string "1" <+> string "2" ==> "\"0\" | \"1\" | \"2\""
93 , choice [string "0", string "1", string "2"] ==> "\"0\" | \"1\" | \"2\""
95 [ (<>) <$> string "0" <*> string "1"
96 , string "2" <+> string "3"
98 ] <*> string "5" ==> "(\"0\", \"1\" | \"2\" | \"3\" | \"4\"), \"5\""
99 , concat <$> many (string "0") ==> "{\"0\"}"
100 , () <$ char 'a' <* char 'b' <* char 'c' ==> "\"a\", \"b\", \"c\""
101 ,let g0 = (<>) <$> string "0" .*> someR (char '1') in
102 (<>) <$> string "0" <& g0 ==> "\"0\" & \"0\", {\"1\"}-"
103 ,let g0 = (<>) <$> string "0" .*> someR (char '1') in
104 let g1 = (<>) <$> someL (char '1') <*. string "0" in
105 string "0" `minus` g0 `minus` g1 ==>
106 "\"0\" - \"0\", {\"1\"}- - {\"1\"}-, \"0\""
108 <$> many (string "0" <+> string "1")
109 <*> some (string "2") ==> "{\"0\" | \"1\"}, {\"2\"}-"
116 testGroup "Language.Symantic"