1 {-# LANGUAGE OverloadedLists #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE RankNTypes #-}
4 {-# LANGUAGE StandaloneDeriving #-}
6 module Language.Pronunciation where
8 import Control.Applicative (Alternative (..))
9 import Control.Monad.Combinators qualified as P
10 import Control.Monad.Trans.Class qualified as MT
11 import Control.Monad.Trans.State qualified as MT
12 import Data.List qualified as List
13 import Data.List.Zipper qualified as LZ
14 import Data.Map.Strict qualified as Map
15 import Data.Set qualified as Set
16 import Data.Text qualified as Text
17 import Data.Text.Short qualified as TextShort
18 import Data.Traversable (traverse)
20 import Paths_worksheets qualified as Self
21 import System.FilePath.Posix ((</>))
22 import System.FilePath.Posix qualified as File
23 import Text.Blaze.Html5.Attributes qualified as HA
24 import Text.Megaparsec qualified as P
25 import Worksheets.Utils.Char qualified as Char
26 import Worksheets.Utils.HTML (className, classes, styles, (!))
27 import Worksheets.Utils.HTML qualified as HTML
28 import Worksheets.Utils.IPA qualified as IPA
29 import Worksheets.Utils.Paper qualified as Paper
30 import Worksheets.Utils.Prelude
32 data Pronunciation = Pronunciation
33 { pronunciationIPABroad :: [IPA.Syllable []]
34 , pronunciationText :: Text
36 deriving (Eq, Ord, Show)
37 instance Semigroup Pronunciation where
40 { pronunciationIPABroad = pronunciationIPABroad x <> pronunciationIPABroad y
42 [pronunciationText x, pronunciationText y]
44 & Text.intercalate "."
46 instance IsList Pronunciation where
47 type Item Pronunciation = IPA.Syllable []
48 toList = pronunciationIPABroad
50 -- fromList :: HasCallStack => [Item Pronunciation] -> Pronunciation
53 { pronunciationIPABroad = ipa
54 , pronunciationText = ipa & foldMap (IPA.toIPA >>> maybe "" IPA.unIPA)
59 & mapButLast (IPA.WithSuprasegmentalFeature IPA.Break)
61 instance IsString Pronunciation where
64 { pronunciationIPABroad = ipa
65 , pronunciationText = ipa & foldMap (IPA.toIPA_ >>> IPA.unIPA)
71 & IPA.parseSyllables @[]
75 data PronunciationKey = PronunciationKey
76 { pronunciationKeyText :: Text
77 , pronunciationKeyPron :: Pronunciation
82 newtype Pronunciations = Pronunciations
83 { unPronunciations :: [(RuleLexemes, Pronunciation)]
86 deriving newtype (Show)
87 deriving newtype (Semigroup)
88 deriving newtype (Monoid)
89 joinPronunciations :: Pronunciations -> Pronunciations
90 joinPronunciations (Pronunciations ps) =
94 { pronunciationIPABroad = ipa
95 , pronunciationText = ipa & foldMap (IPA.toIPA >>> maybe "" IPA.unIPA)
100 ipa :: [IPA.Syllable []]
104 ( \(inp, Pronunciation{pronunciationIPABroad}) (suffix, l, acc) ->
105 case pronunciationIPABroad of
106 [] -> (inp <> suffix, IPA.Syllable [], acc)
107 [syl@(IPA.Syllable [])] -> (inp <> suffix, syl, acc)
108 [syl] -> (inp <> suffix, IPA.Syllable [], (syl <> l) : acc)
109 [sylL, sylR] -> (inp <> suffix, sylL, glueSyllableToTheRight sylR acc)
110 _ -> errorShow pronunciationIPABroad
118 , glueSyllableToTheRight l acc
119 & mapButLast (IPA.setSuprasegmentalFeatures [IPA.Break])
122 glueSyllableToTheRight ::
126 glueSyllableToTheRight x y =
130 yL : yR -> x <> yL : yR
131 instance IsList Pronunciations where
132 type Item Pronunciations = (RuleLexemes, Pronunciation)
133 toList = unPronunciations
134 fromList l = Pronunciations{unPronunciations = l & fromList}
137 instance IsString Pronunciations where
139 "" -> Pronunciations "" [IPA.Syllable [IPA.Zero]]
140 s -> Pronunciations (s & Text.pack) $
142 & IPA.parseSyllables @[]
143 & either errorShow id
145 data ExampleLiteral = ExampleLiteral
146 { exampleLiteralText :: ShortText
147 , exampleLiteralTags :: Set LiteralTag
148 , exampleLiteralMeaning :: ShortText
150 deriving (Eq, Ord, Show)
151 instance IsString ExampleLiteral where
154 { exampleLiteralText = s & fromString
155 , exampleLiteralTags = Set.empty
156 , exampleLiteralMeaning = ""
159 = LiteralTagOccurence
162 deriving (Eq, Ord, Show)
163 exampleLiteralsText :: [ExampleLiteral] -> ShortText
164 exampleLiteralsText ls = ls <&> exampleLiteralText & mconcat
168 { exampleLiteralText = "-"
169 , exampleLiteralTags = [LiteralTagMeta] & Set.fromList
170 , exampleLiteralMeaning = ""
172 occurence lit = lit{exampleLiteralTags = lit & exampleLiteralTags & Set.insert LiteralTagOccurence}
173 silent lit = lit{exampleLiteralTags = lit & exampleLiteralTags & Set.insert LiteralTagSilent}
175 type SyllableText = ShortText
176 type SyllableBroad = IPA.Syllable []
177 type SyllablesTable = Map SyllableText (Map SyllableText [([ExampleLiteral], SyllableBroad)])
183 deriving (Eq, Ord, Show)
185 = VariantDefinition Text
187 deriving (Eq, Ord, Show)
189 { -- , ruleStress :: Bool
190 -- , ruleDefinition :: Maybe Text
192 -- [ "e" := ["ɛ"] , "x" := ["g","z"] , "er" := ["ɛʁ"] , "cice" := ["sis"] ]
193 -- [ "exercice" := ["ɛg.zɛʁ.sis"]
195 rulePron :: Pronunciations
196 , ruleExamples :: Map InputLexemes Pronunciation
198 deriving (Eq, Ord, Show)
202 { rulePron = Pronunciations{unPronunciations = []}
203 , ruleExamples = mempty
209 RuleLexemes -> RuleLexemes
210 word = begining >>> ending
211 begining = after [LexemeBorder]
212 ending = before [LexemeBorder]
213 before ls r = RuleLexemes (unRuleLexemes r <> ls)
214 after ls r = RuleLexemes (ls <> unRuleLexemes r)
215 meaning r d = RuleLexemes (unRuleLexemes r <> [LexemeMeaning d])
217 type Table = Map RuleLexemes Rule
218 examples :: Table -> Map InputLexemes Pronunciation
221 | v <- tbl & Map.elems
223 & Map.unionsWith (\new old -> if new == old then new else errorShow (new, old))
226 { pronInput :: [Lexeme]
233 , sylDependsOnBefore :: Bool
234 , sylDependsOnAfter :: Bool
235 , sylDependsOnMeaning :: Bool
236 , sylSound :: Text -- [IPA.Syllable []]
242 addIndexes :: [[Either Char Pron]] -> [[Syl]]
246 go idx (prons : next) = List.reverse prons' : go idx' next
255 { sylText = Text.singleton c
256 , sylDependsOnAfter = False
257 , sylDependsOnBefore = False
258 , sylDependsOnMeaning = False
266 Right Pron{pronRule = Rule{rulePron = Pronunciations{unPronunciations = ps}}} ->
269 ( \(j, js) (t, Pronunciation{..}) ->
270 let sylText = t & unRuleLexemes & lexemesChars & Text.pack
271 in case pronunciationIPABroad of
273 | not (Text.null pronunciationText) ->
277 , sylSound = pronunciationText
278 , sylDependsOnBefore = t & unRuleLexemes & sylDependsOnBefore
279 , sylDependsOnAfter = t & unRuleLexemes & sylDependsOnAfter
280 , sylDependsOnMeaning = t & unRuleLexemes & sylDependsOnMeaning
288 | Text.null pronunciationText ->
294 , sylDependsOnBefore = t & unRuleLexemes & sylDependsOnBefore
295 , sylDependsOnAfter = t & unRuleLexemes & sylDependsOnAfter
296 , sylDependsOnMeaning = t & unRuleLexemes & sylDependsOnMeaning
303 j0@Syl{sylText = j0t} : jss -> j0{sylText = j0t <> sylText} : jss
306 | (syls & all IPA.isSilent) && Text.null pronunciationText ->
310 , sylDependsOnBefore = t & unRuleLexemes & sylDependsOnBefore
311 , sylDependsOnAfter = t & unRuleLexemes & sylDependsOnAfter
312 , sylDependsOnMeaning = t & unRuleLexemes & sylDependsOnMeaning
324 , sylDependsOnBefore = t & unRuleLexemes & sylDependsOnBefore
325 , sylDependsOnAfter = t & unRuleLexemes & sylDependsOnAfter
326 , sylDependsOnMeaning = t & unRuleLexemes & sylDependsOnMeaning
327 , sylSound = pronunciationText
338 , sylDependsOnBefore = t & unRuleLexemes & sylDependsOnBefore
339 , sylDependsOnAfter = t & unRuleLexemes & sylDependsOnAfter
340 , sylDependsOnMeaning = t & unRuleLexemes & sylDependsOnMeaning
341 , sylSound = pronunciationText
348 syls -> errorShow syls
353 List.reverse >>> \case
354 LexemeBorder : _ -> True
355 LexemeSilent : _ -> True
356 LexemeConsonant : _ -> True
357 LexemeDoubleConsonant : _ -> True
358 LexemeVowel : _ -> True
359 LexemeSemiVowel : _ -> True
363 LexemeBorder : _ -> True
364 LexemeSilent : _ -> True
365 LexemeConsonant : _ -> True
366 LexemeDoubleConsonant : _ -> True
367 LexemeVowel : _ -> True
368 LexemeSemiVowel : _ -> True
370 sylDependsOnMeaning =
371 List.reverse >>> \case
372 LexemeMeaning{} : _ -> True
377 withCapital :: [(RuleLexemes, Rule)] -> [(RuleLexemes, Rule)]
379 foldMap \(RuleLexemes pat, rul) ->
380 [ (RuleLexemes pat, rul)
382 ( RuleLexemes (withCapitalLexemes pat)
384 { rulePron = rul & rulePron & withCapitalPronunciations
390 >>> withCapitalLexemes
397 withCapitalPronunciations (Pronunciations []) = Pronunciations []
398 withCapitalPronunciations (Pronunciations ((t, p) : ps)) =
399 Pronunciations ((RuleLexemes $ withCapitalLexemes $ unRuleLexemes t, p) : ps)
400 withCapitalLexemes (LexemeChar x : xs) = LexemeChar (Char.toUpper x) : xs
401 withCapitalLexemes (x : xs) = x : withCapitalLexemes xs
402 withCapitalLexemes [] = []
404 lexemesChars :: [Lexeme] -> [Char]
415 (P.ParseErrorBundle Text ())
416 (P.ParseErrorBundle [Lexeme] ())
422 & either (Left . Left) \lexs ->
425 & either (Left . Right) Right
427 runParser :: Table -> [Lexeme] -> Either (P.ParseErrorBundle [Lexeme] ()) [Either Char Pron]
428 runParser tbl inp = inp & P.runParser (parser tbl) "input"
435 (P.ParseErrorBundle Text ())
436 (P.ParseErrorBundle [Lexeme] ())
439 parseLiterals rules inp =
442 ( \ExampleLiteral{..} ->
447 [ LexemeMeaning exampleLiteralMeaning
448 | exampleLiteralMeaning & TextShort.null & not
452 & either (Left . Left) \lexs ->
456 & either (Left . Right) Right
458 parser :: Table -> P.Parsec () [Lexeme] [Either Char Pron]
460 res <- P.many $ (Just . Right) <$> parseRules <|> parseChar
462 return $ res & catMaybes
464 -- Match one of the rules, trying longuest first
465 parseRules :: P.Parsec () [Lexeme] Pron
469 | r <- tbl & Map.toDescList
475 rulePat & unRuleLexemes <&> \case
476 LexemeChar c -> LexemeChar (c & Char.toUpper)
480 | (rulePat, curRule) <- tbl & Map.toDescList
483 parseRule (rulePat, curRule@Rule{..}) =
486 pat = rulePat & unRuleLexemes
487 patSep = (`List.elem` list [LexemeVowel, LexemeSemiVowel, LexemeConsonant, LexemeSilent])
488 -- (patEnd, patBegin) = pat & List.reverse & List.span patSep
489 patBegin = pat & List.dropWhileEnd patSep
490 patEnd = pat & List.reverse & List.takeWhile patSep & List.reverse
491 -- parse without the ending Lexeme{Vowel,SemiVowel,Consonant}
493 inpAfterBegin <- P.getInput
494 unless (List.null patEnd) do
495 inpWithAhead <- parseAhead
496 -- traceShowM ("inpWithAhead"::Text, inpWithAhead)
497 P.setInput inpWithAhead
498 P.chunk patEnd & void
499 -- insert the Lexeme{Vowel,SemiVowel,Consonant} from the output of the current rule
508 >>> pronunciationIPABroad
511 >>> maybe [] (IPA.syllableToSegments >>> List.reverse >>> lexemeHeadSound)
513 P.setInput $ lastSound <> inpAfterBegin
514 return Pron{pronInput = pat, pronRule = curRule}
515 parseChar :: P.Parsec () [Lexeme] (Maybe (Either Char Pron))
517 P.anySingle <&> \case
518 LexemeChar c -> Just $ Left c
520 parseAhead :: P.Parsec () [Lexeme] [Lexeme]
522 nextStep <- P.observing $ Right <$> parseRules <|> Left <$> P.anySingle
523 -- traceShowM ("nextStep"::Text, nextStep & either (\err -> Left ()) Right)
525 Right (Right Pron{pronInput, pronRule}) -> do
534 >>> pronunciationIPABroad
536 >>> maybe [] (IPA.syllableToSegments >>> lexemeHeadSound)
539 return $ x <> pronInput <> inp
540 Right (Left lex) -> do
541 parseAhead <&> (lex :)
543 lexemeHeadSound :: [_] -> [Lexeme]
545 headMaybe >>> fmap IPA.dropSegmentalFeatures >>> \case
546 Just IPA.Zero{} -> [LexemeSilent]
547 Just IPA.Vowel{} -> [LexemeVowel]
548 Just (IPA.Consonant consonant) -> do
550 IPA.Pulmonic _phonation _place IPA.Approximant -> [LexemeSemiVowel]
551 IPA.Ejective _place IPA.Approximant -> [LexemeSemiVowel]
552 _ -> [LexemeConsonant]
555 runLexer :: Text -> Either (P.ParseErrorBundle Text ()) [Lexeme]
556 runLexer inp = inp & P.runParser lexer "input"
558 exampleLiteralsLexemes :: [ExampleLiteral] -> [Lexeme]
559 exampleLiteralsLexemes ls =
560 ls & foldMap \ExampleLiteral{..} ->
561 unRuleLexemes (fromString (TextShort.unpack exampleLiteralText))
562 <> [ LexemeMeaning exampleLiteralMeaning
565 lexer :: P.Parsec () Text [Lexeme]
570 [ P.takeWhile1P Nothing Char.isSpace >>= \cs ->
571 return [LexemeChar c | c <- cs & Text.unpack]
573 cs <- P.takeWhile1P Nothing Char.isLetter
574 mean <- (<|> return []) $ P.try do
576 m <- P.takeWhile1P Nothing (/= '}')
578 return [LexemeMeaning (TextShort.fromText m)]
581 : [LexemeChar c | c <- cs & Text.unpack]
584 , P.takeWhile1P Nothing Char.isNumber >>= \cs ->
585 return (LexemeBorder : ([LexemeChar c | c <- cs & Text.unpack] <> [LexemeBorder]))
586 , P.satisfy Char.isSymbol >>= \c ->
587 return [LexemeChar c]
588 , P.satisfy Char.isSeparator >>= \c ->
589 return [LexemeChar c]
590 , P.satisfy Char.isMark >>= \c ->
591 return [LexemeChar c]
592 , P.satisfy Char.isPunctuation >>= \c ->
593 return [LexemeChar c]
598 words :: [Either Char Pron] -> [[Either Char Pron]]
600 words prons = word0 : words next
602 (word0, rest) = prons & List.span (isSep >>> not)
603 (_sep, next) = rest & List.span isSep
605 Left c | c & Char.isSpace -> True
610 , inputPhonetic :: [IPA.Syllable []]
611 , inputMeaning :: Maybe ShortText
614 patterns :: Map PatKey PatNode
616 [ PatKeyNext (PatContextChar 'a') :=
617 [ PatKeyNext PatContextLexicalBorder :=
620 , PatKeyNext (PatContextChar 't') :=
621 [ PatKeyNext (PatContextChar 'h') :=
622 [ PatKeyNext (PatContextChar 'e') :=
623 [ PatKeyNext (PatContextLexicalCategory Char.Space) :=
624 PatEnd ["the" := "zi"]
631 { stateInput :: LZ.Zipper Inp
632 , stateBuffer :: [PatKey]
633 , statePats :: Map PatKey PatNode
634 , statePatReset :: Bool
637 parse :: Map PatKey PatNode -> Text -> [Inp]
638 parse initPats input =
641 { stateInput = input & Text.unpack & fmap charToInp & LZ.fromList
643 , statePats = initPats
644 , statePatReset = True
649 charToInp :: Char -> Inp
652 { inpPats = [PatKeyNext (PatContextChar c)]
653 , inpPronunciations = []
655 loop :: State -> State
658 | (key, val) <- st & statePats & Map.toList
662 & fromMaybe (loop st{statePats = initPats})
663 look :: (PatKey, PatNode) -> State -> Maybe State
664 look kv@(key, val) st =
666 PatKeyPrev patPrev -> errorShow ("prev" :: Text)
667 PatKeyNext patNext ->
669 PatContextLexicalBorder
670 | stateInput st & LZ.endp -> match kv st
672 -- | Just inpNext <- stateInput st & LZ.safeCursor ->
673 -- inpNext & inpPats &
677 match kv@(key, val) st =
682 { statePats = initPats
688 { inpPats = stateBuffer st & (key :) & List.reverse
689 , inpPronunciations = pron
696 , stateBuffer = key : stateBuffer st
700 case statePats st & Map.lookup k of
702 Just (PatNode pats) ->
703 loop st {statePats = pats, stateBuffer = k : stateBuffer st}
705 loop st { statePats = initPats
707 , stateInput = stateInput st
709 { inpPats = stateBuffer st & List.reverse
710 , inpPronunciations = end
717 parse :: PatNode -> Text -> [Inp]
718 parse initPats input =
719 let inpZip = input & Text.unpack & fmap charToInp & LZ.fromList in
720 runInp [] initPats inpZip & LZ.toList
722 charToInp :: Char -> Inp
724 { inpPats = [PatKeyNext (PatContextChar c)]
725 , inpPronunciations = []
727 runInp :: [PatKey] -> PatNode -> LZ.Zipper Inp -> LZ.Zipper Inp
729 traceShow ("runInp"::Text, ("oks"::Text) := oks, ("cur"::Text) := LZ.safeCursor inp) $
735 { inpPats = oks & List.reverse
736 , inpPronunciations = end
741 -- the pattern may go on
742 case inp & LZ.safeCursor of
745 & runPat [] oks [PatKeyNext PatContextLexicalBorder] pats
748 & runPat [] oks (inpPats cur & List.sort) pats
750 runPat :: [PatKey] -> [PatKey] -> [PatKey] -> Map PatKey PatNode -> LZ.Zipper Inp -> LZ.Zipper Inp
751 runPat kos oks todos pats inp =
752 traceShow ( "runPat"::Text
753 , ("kos"::Text) := kos
754 , ("oks"::Text) := oks
755 , ("todos"::Text) := todos
756 , ("cur"::Text) :=LZ.safeCursor inp
759 [] | LZ.endp inp -> inp
760 & runInp kos (PatEnd [])
761 & runInp oks (PatEnd [])
763 -- nothing left to advance the pattern
764 --traceShow ("runPat/[]"::Text) $
766 & (if null kos then id else runInp kos (PatEnd []))
767 & runInp oks (PatNode pats)
769 case pats & Map.lookup k of
772 --traceShow ("runPat/End"::Text) $
775 & (if null kos then id else runInp kos (PatEnd []))
776 & runInp (k:oks) (PatEnd end)
777 -- the pattern advances
778 Just (PatNode nextPats) ->
779 --traceShow ("runPat/Node"::Text) $
780 inp & runPat kos (k:oks) ks nextPats
781 -- the pattern does not advance
783 inp & runPat (k:kos) oks ks pats
787 = PatContextChar Char
788 | PatContextLexicalCategory Char.GeneralCategory
789 | PatContextLexicalBorder
790 | PatContextPhoneticVowel
791 | PatContextPhoneticSemiVowel
792 | PatContextPhoneticConsonant
793 deriving (Eq, Ord, Show)
796 { inpPats :: [PatKey]
797 , inpPronunciations :: Pronunciations
802 = PatKeyPrev PatContext
803 | PatKeyNext PatContext
804 deriving (Eq, Ord, Show)
806 = PatNode (Map PatKey PatNode)
807 | PatEnd Pronunciations
809 instance IsList PatNode where
810 type Item PatNode = (PatKey, PatNode)
811 fromList = PatNode . Map.fromListWith (errorShow)
819 | LexemeDoubleConsonant
821 | LexemeMeaning ShortText
822 | -- | `LexemeChar` is last to have priority when using `Map.toDescList`
824 deriving (Eq, Ord, Show)
829 -- deriving (Eq, Ord, Show)
832 newtype Lexemes = Lexemes { unLexemes :: [Lexeme] }
833 deriving (Eq, Ord, Show)
834 instance P.Stream Lexemes where
835 type Token Lexemes = Lexeme
836 type Tokens Lexemes = Lexemes
837 tokensToChunk _px = Lexemes
838 chunkToTokens _px = unLexemes
839 chunkLength _px = unLexemes >>> List.length
840 chunkEmpty _px = unLexemes >>> List.null
841 take1_ = unLexemes >>> P.take1_ >>> coerce
842 takeN_ n = unLexemes >>> P.takeN_ n >>> coerce
843 takeWhile_ p = unLexemes >>> P.takeWhile_ p >>> coerce
845 instance IsString Lexemes where
852 ((`appEndo` []) >>> Lexemes)
855 newtype RuleLexemes = RuleLexemes {unRuleLexemes :: [Lexeme]}
856 deriving (Eq, Ord, Show)
857 instance HasTypeDefault RuleLexemes where
858 typeDefault = RuleLexemes typeDefault
859 instance Semigroup RuleLexemes where
860 RuleLexemes x <> RuleLexemes y = RuleLexemes (x <> y)
861 instance Monoid RuleLexemes where
862 mempty = RuleLexemes mempty
863 instance IsList RuleLexemes where
864 type Item RuleLexemes = Lexeme
865 fromList = RuleLexemes
866 toList = unRuleLexemes
867 instance IsString RuleLexemes where
874 ( List.dropWhileEnd (== LexemeBorder)
875 >>> List.dropWhile (== LexemeBorder)
879 newtype InputLexemes = InputLexemes {unInputLexemes :: [Lexeme]}
880 deriving (Eq, Ord, Show)
881 instance HasTypeDefault InputLexemes where
882 typeDefault = InputLexemes typeDefault
883 instance Semigroup InputLexemes where
884 InputLexemes x <> InputLexemes y = InputLexemes (x <> y)
885 instance Monoid InputLexemes where
886 mempty = InputLexemes mempty
887 instance IsList InputLexemes where
888 type Item InputLexemes = Lexeme
889 fromList = InputLexemes
890 toList = unInputLexemes
891 instance IsString InputLexemes where
896 & either errorShow InputLexemes
898 instance P.ShowErrorComponent () where
899 showErrorComponent = show
900 errorComponentLen _ = 2
901 instance P.VisualStream [Lexeme] where
903 tokensLength _s xs = xs <&> (show >>> List.length) & sum
904 instance P.TraversableStream [Lexeme] where
905 reachOffset off pos = (Nothing, pos{P.pstateOffset = P.pstateOffset pos + off})
910 | LexemeTagPunctuation
914 | LexemeTagDefinition
916 deriving (Eq, Ord, Show)
917 deriving instance Ord (IPA.Syllable [])
918 deriving instance Ord IPA.SuprasegmentalFeature
919 deriving instance Ord IPA.SegmentalFeature
920 deriving instance Ord IPA.Sibilance
921 deriving instance Ord IPA.Manner
922 deriving instance Ord IPA.Phonation
923 deriving instance Ord IPA.Roundedness
924 deriving instance Ord IPA.Height
925 deriving instance Ord IPA.Vowel
926 deriving instance Ord IPA.Consonant
927 deriving instance Ord IPA.Segment
930 tableToMatch :: Table -> [Lexeme] -> [Pronunciations]
931 tableToMatch tbl = loop
933 loop prevBorder = \case
936 | (trans, transMach) <- chunk & chunkMachine & machineAlts & Map.toList
937 , let matchingLength = transMatchingLength input trans
938 , 0 < matchingLength || not (isTransConsume trans)
939 , let (inputRead, inputRest) = input & Text.splitAt matchingLength
941 & Map.fromListWith (\new old -> old)
946 tableHtml :: Table -> IO HTML.Html
948 dataPath <- Self.getDataDir <&> File.normalise
949 let title :: String = "LexerDict"
950 let pageOrientation = Paper.PageOrientationPortrait
951 let pageSize = Paper.PageSizeA4
952 let partLangue = LangueFrançais
956 HTML.title $ title & HTML.toHtml
958 ( [ "styles/Paper.css"
959 , "styles/French/Lexer.css"
960 , "styles/Rosetta/Reading.css"
966 ! HA.rel "stylesheet"
967 ! HA.type_ "text/css"
968 ! HA.href (dataPath </> cssFile & HTML.toValue)
969 HTML.styleCSS $ HTML.cssPrintPage pageOrientation pageSize
970 -- HTML.styleCSS $ pageDifficulties & difficultyCSS
972 ! classes ["A4", "french-lexer"]
975 let rulesChunks = tbl & Map.toList & chunksOf 50
976 forM_ rulesChunks \rules ->
984 forM_ (rules & List.zip [1 :: Int ..]) \(ruleIndex, (rulePat, Rule{..})) -> do
989 , if even ruleIndex then "even" else "odd"
998 , "lang-" <> className partLangue
1002 -- "grid-template-columns" :=
1003 -- (0.5 & cm & HTML.toCSS)
1004 -- & List.replicate lexerDictMaxKeyLength
1008 forM_ (["model"] :: [String]) \rowKind -> do
1009 forM_ (rulePat & unRuleLexemes) \ruleChar -> do
1010 -- let uniScript = cellToken & tokenMeta & snd & fromMaybe (UnicodeBlockLatin UnicodeBlockLatin_Basic)
1017 -- , "script-" <> className uniScript
1024 [ "dict-pronunciation"
1027 -- HTML.span ! classes ["arrow"] $ "→"
1030 { unPronunciations =
1031 all (snd >>> pronunciationIPABroad >>> all IPA.isSilent) -> True
1033 Pronunciations{unPronunciations = is} ->
1035 & foldMap (snd >>> pronunciationIPABroad >>> foldMap (IPA.toIPA_ >>> IPA.transcribe IPA.Phonemic))
1042 -- HTML.span ! classes ["arrow"] $ "→"
1043 forM_ (ruleExamples & Map.toList) \(_inp, Pronunciation{..}) -> do
1048 case pronunciationIPABroad of
1049 [] -> pronunciationText & HTML.toHtml
1050 _ -> pronunciationIPABroad & foldMap (IPA.toIPA_ >>> IPA.transcribe IPA.Phonemic) & HTML.toHtml