Compare commits
	
		
			7 Commits
		
	
	
		
			b0451300a5
			...
			7d7f628175
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7d7f628175 | |||
| 4ffefe1f9e | |||
| 34c28813fe | |||
| 8b31df9193 | |||
| a37643b5cd | |||
| 61355405d5 | |||
| d698ebce72 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,4 +1,6 @@ | |||||||
| .stack-work/ | .stack-work/ | ||||||
|  | dist-newstyle | ||||||
| .vscode/ | .vscode/ | ||||||
| *.swp | *.swp | ||||||
| result | result | ||||||
|  | bench.html | ||||||
|  | |||||||
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								README.md
									
									
									
									
									
								
							| @ -6,12 +6,6 @@ | |||||||
| $ nix build | $ nix build | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## Running |  | ||||||
| 
 |  | ||||||
| ``` |  | ||||||
| $ nix run |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ## Building the Docker image | ## Building the Docker image | ||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
| @ -26,9 +20,7 @@ Includes Stack, GHC, `haskell-language-server`, `gen-hie` etc. | |||||||
| $ nix develop | $ nix develop | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## Running Stack and GHC | ## TODO | ||||||
| 
 | 
 | ||||||
| ``` | - [x] Benchmark, profile and optimize search | ||||||
| $ nix run .#stack | - [ ] Trim down the docker image | ||||||
| $ nix run .#ghc |  | ||||||
| ``` |  | ||||||
|  | |||||||
							
								
								
									
										93
									
								
								app/Main.hs
									
									
									
									
									
								
							
							
						
						
									
										93
									
								
								app/Main.hs
									
									
									
									
									
								
							| @ -3,6 +3,7 @@ | |||||||
| {-# LANGUAGE DuplicateRecordFields #-} | {-# LANGUAGE DuplicateRecordFields #-} | ||||||
| {-# LANGUAGE FlexibleInstances #-} | {-# LANGUAGE FlexibleInstances #-} | ||||||
| {-# LANGUAGE MultiParamTypeClasses #-} | {-# LANGUAGE MultiParamTypeClasses #-} | ||||||
|  | {-# LANGUAGE NumericUnderscores #-} | ||||||
| {-# LANGUAGE OverloadedRecordDot #-} | {-# LANGUAGE OverloadedRecordDot #-} | ||||||
| {-# LANGUAGE OverloadedStrings #-} | {-# LANGUAGE OverloadedStrings #-} | ||||||
| {-# LANGUAGE RankNTypes #-} | {-# LANGUAGE RankNTypes #-} | ||||||
| @ -15,8 +16,7 @@ module Main (main) where | |||||||
| import Data.Char (chr) | import Data.Char (chr) | ||||||
| import Data.FileEmbed (embedFile) | import Data.FileEmbed (embedFile) | ||||||
| import Data.Foldable (for_) | import Data.Foldable (for_) | ||||||
| import Data.List (intercalate) | import Data.Maybe (fromMaybe) | ||||||
| import Data.Maybe (fromMaybe, maybeToList) |  | ||||||
| import Data.Text (Text) | import Data.Text (Text) | ||||||
| import Data.Word (Word8) | import Data.Word (Word8) | ||||||
| import Network.HTTP.Media ((//), (/:)) | import Network.HTTP.Media ((//), (/:)) | ||||||
| @ -48,11 +48,10 @@ import qualified Data.Text.Encoding as Encoding | |||||||
| import qualified Network.Wai.Handler.Warp as Warp | import qualified Network.Wai.Handler.Warp as Warp | ||||||
| import qualified Text.Blaze.Html5 as H | import qualified Text.Blaze.Html5 as H | ||||||
| import qualified Text.Blaze.Html5.Attributes as A | import qualified Text.Blaze.Html5.Attributes as A | ||||||
| import qualified Unicode.Char.General.Blocks as UnicodeBlocks |  | ||||||
| import qualified Unicode.Char.General.Names as UnicodeNames |  | ||||||
| 
 | 
 | ||||||
| import qualified UToy.Decode as Decode | import qualified UToy.Decode as Decode | ||||||
| import qualified UToy.Table as Table | import qualified UToy.Table as Table | ||||||
|  | import qualified UToy.Names as Names | ||||||
| import qualified UToy.Parsers as Parsers | import qualified UToy.Parsers as Parsers | ||||||
| 
 | 
 | ||||||
| main :: IO () | main :: IO () | ||||||
| @ -66,13 +65,14 @@ type API = | |||||||
|   :<|> "utf8" :> Capture "bytes" Text :> Get '[PlainText, HTML] Utf8Model |   :<|> "utf8" :> Capture "bytes" Text :> Get '[PlainText, HTML] Utf8Model | ||||||
|   :<|> "codepoints" :> Capture "codepoints" Text :> Get '[PlainText, HTML] CodepointsModel |   :<|> "codepoints" :> Capture "codepoints" Text :> Get '[PlainText, HTML] CodepointsModel | ||||||
|   :<|> "text" :> Capture "text" Text :> Get '[PlainText, HTML] TextModel |   :<|> "text" :> Capture "text" Text :> Get '[PlainText, HTML] TextModel | ||||||
|  |   :<|> "search" :> Capture "search" Text :> Get '[PlainText, HTML] SearchModel | ||||||
| 
 | 
 | ||||||
| server :: Server API | server :: Server API | ||||||
| server = | server = | ||||||
|   rootR :<|> utf8R :<|> codepointsR :<|> textR |   rootR :<|> utf8R :<|> codepointsR :<|> textR :<|> searchR | ||||||
|   where |   where | ||||||
|     rootR host = do |     rootR host' = do | ||||||
|       pure $ RootModel $ fromMaybe "" host |       pure $ RootModel $ fromMaybe "" host' | ||||||
| 
 | 
 | ||||||
|     utf8R bytesP = do |     utf8R bytesP = do | ||||||
|       bytes <- Parsers.parseHexBytes bytesP `orThrow` const err400 |       bytes <- Parsers.parseHexBytes bytesP `orThrow` const err400 | ||||||
| @ -85,6 +85,9 @@ server = | |||||||
|     textR textP = do |     textR textP = do | ||||||
|       pure $ TextModel textP |       pure $ TextModel textP | ||||||
| 
 | 
 | ||||||
|  |     searchR searchP = do | ||||||
|  |       pure $ mkSearchModel searchP | ||||||
|  | 
 | ||||||
| -- / | -- / | ||||||
| 
 | 
 | ||||||
| newtype RootModel = RootModel | newtype RootModel = RootModel | ||||||
| @ -96,6 +99,7 @@ examples = | |||||||
|   [ "/text/✅🤔" |   [ "/text/✅🤔" | ||||||
|   , "/codepoints/x2705+x1F914" |   , "/codepoints/x2705+x1F914" | ||||||
|   , "/utf8/e2.9c.85.f0.9f.a4.94" |   , "/utf8/e2.9c.85.f0.9f.a4.94" | ||||||
|  |   , "/search/asterisk" | ||||||
|   ] |   ] | ||||||
| 
 | 
 | ||||||
| instance MimeRender PlainText RootModel where | instance MimeRender PlainText RootModel where | ||||||
| @ -139,8 +143,8 @@ instance MimeRender PlainText Utf8Model where | |||||||
|             Right c -> |             Right c -> | ||||||
|               [ Text.pack [c] |               [ Text.pack [c] | ||||||
|               , Text.pack $ printf "U+%04X" c |               , Text.pack $ printf "U+%04X" c | ||||||
|               , Text.pack $ intercalate ", " $ allNames c |               , Text.intercalate ", " $ Names.allNames c | ||||||
|               , Text.pack $ fromMaybe "" $ blockName c |               , fromMaybe "" $ Names.blockName c | ||||||
|               ] |               ] | ||||||
|           ) |           ) | ||||||
|         ] |         ] | ||||||
| @ -158,8 +162,8 @@ instance MimeRender HTML Utf8Model where | |||||||
|           Right c -> do |           Right c -> do | ||||||
|             H.td $ H.input ! A.class_ "charbox" ! A.value (H.toValue [c]) |             H.td $ H.input ! A.class_ "charbox" ! A.value (H.toValue [c]) | ||||||
|             H.td $ H.code $ printfHtml "U+%04X" c |             H.td $ H.code $ printfHtml "U+%04X" c | ||||||
|             H.td $ H.code $ H.toHtml $ intercalate ", " $ allNames c |             H.td $ H.code $ H.toHtml $ Text.intercalate ", " $ Names.allNames c | ||||||
|             H.td $ H.code $ H.toHtml $ fromMaybe "" $ blockName c |             H.td $ H.code $ H.toHtml $ fromMaybe "" $ Names.blockName c | ||||||
| 
 | 
 | ||||||
| -- /codepoints/<codepoints> | -- /codepoints/<codepoints> | ||||||
| 
 | 
 | ||||||
| @ -173,14 +177,14 @@ mkCodepointsModel = | |||||||
|   -- Limit number of returned codepoints. Otherwise it's |   -- Limit number of returned codepoints. Otherwise it's | ||||||
|   -- too easy to provoke massive response bodies with requests like |   -- too easy to provoke massive response bodies with requests like | ||||||
|   -- /codepoints/0-99999999 |   -- /codepoints/0-99999999 | ||||||
|   . take 100000 |   . take 100_000 | ||||||
|   . map go |   . map go | ||||||
|   . concatMap (uncurry enumFromTo) |   . concatMap (uncurry enumFromTo) | ||||||
|   where |   where | ||||||
|     go codepoint = (codepoint, toChar codepoint) |     go codepoint = (codepoint, toChar codepoint) | ||||||
| 
 | 
 | ||||||
|     toChar codepoint |     toChar codepoint | ||||||
|       | codepoint > 0x10FFFF = Left "Would be too big (maximum: U+10FFFF)" |       | codepoint > 0x10_FFFF = Left "Would be too big (maximum: U+10FFFF)" | ||||||
|       | isSurrogate codepoint = Left "Is a surrogate" |       | isSurrogate codepoint = Left "Is a surrogate" | ||||||
|       | otherwise = Right $ chr $ fromIntegral codepoint |       | otherwise = Right $ chr $ fromIntegral codepoint | ||||||
| 
 | 
 | ||||||
| @ -196,8 +200,8 @@ instance MimeRender PlainText CodepointsModel where | |||||||
|         Right c -> |         Right c -> | ||||||
|           [ Text.pack [c] |           [ Text.pack [c] | ||||||
|           , Text.pack $ printf "U+%04X" c |           , Text.pack $ printf "U+%04X" c | ||||||
|           , Text.pack $ intercalate ", " $ allNames c |           , Text.intercalate ", " $ Names.allNames c | ||||||
|           , Text.pack $ fromMaybe "" $ blockName c |           , fromMaybe "" $ Names.blockName c | ||||||
|           ] |           ] | ||||||
|       ) |       ) | ||||||
|     | (codepoint, eiC) <- model.codepoints |     | (codepoint, eiC) <- model.codepoints | ||||||
| @ -214,8 +218,8 @@ instance MimeRender HTML CodepointsModel where | |||||||
|           Right c -> do |           Right c -> do | ||||||
|             H.td $ H.input ! A.class_ "charbox" ! A.value (H.toValue [c]) |             H.td $ H.input ! A.class_ "charbox" ! A.value (H.toValue [c]) | ||||||
|             H.td $ H.code $ printfHtml "U+%04X" c |             H.td $ H.code $ printfHtml "U+%04X" c | ||||||
|             H.td $ H.code $ H.toHtml $ intercalate ", " $ allNames c |             H.td $ H.code $ H.toHtml $ Text.intercalate ", " $ Names.allNames c | ||||||
|             H.td $ H.code $ H.toHtml $ fromMaybe "" $ blockName c |             H.td $ H.code $ H.toHtml $ fromMaybe "" $ Names.blockName c | ||||||
| 
 | 
 | ||||||
| -- /text/<text> | -- /text/<text> | ||||||
| 
 | 
 | ||||||
| @ -224,28 +228,50 @@ newtype TextModel = TextModel | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| instance MimeRender HTML TextModel where | instance MimeRender HTML TextModel where | ||||||
|   mimeRender _ model = renderHtml $ documentWithBody $ do |   mimeRender _ model = charTableHtml $ Text.unpack model.text | ||||||
|     H.table $ for_ (Text.unpack model.text) $ \c -> do | instance MimeRender PlainText TextModel where | ||||||
|  |   mimeRender _ model = charTableText $ Text.unpack model.text | ||||||
|  | 
 | ||||||
|  | -- /search/<search> | ||||||
|  | 
 | ||||||
|  | newtype SearchModel = SearchModel | ||||||
|  |   { results :: [Char] | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | mkSearchModel :: Text -> SearchModel | ||||||
|  | mkSearchModel search = SearchModel $ take 100_000 $ Names.searchCaseInsensitive search | ||||||
|  | 
 | ||||||
|  | instance MimeRender HTML SearchModel where | ||||||
|  |   mimeRender _ model = charTableHtml model.results | ||||||
|  | 
 | ||||||
|  | instance MimeRender PlainText SearchModel where | ||||||
|  |   mimeRender _ model = charTableText model.results | ||||||
|  | 
 | ||||||
|  | -- Utilities | ||||||
|  | 
 | ||||||
|  | charTableHtml :: [Char] -> BL.ByteString | ||||||
|  | charTableHtml chars = | ||||||
|  |   renderHtml $ documentWithBody $ do | ||||||
|  |     H.table $ for_ chars $ \c -> do | ||||||
|       H.tr $ do |       H.tr $ do | ||||||
|         H.td $ H.input ! A.class_ "charbox" ! A.value (H.toValue [c]) |         H.td $ H.input ! A.class_ "charbox" ! A.value (H.toValue [c]) | ||||||
|         H.td $ H.code $ printfHtml "U+%04X" c |         H.td $ H.code $ printfHtml "U+%04X" c | ||||||
|         H.td $ H.code $ H.toHtml $ intercalate ", " $ allNames c |         H.td $ H.code $ H.toHtml $ Text.intercalate ", " $ Names.allNames c | ||||||
|         H.td $ H.code $ H.toHtml $ fromMaybe "" $ blockName c |         H.td $ H.code $ H.toHtml $ fromMaybe "" $ Names.blockName c | ||||||
| 
 | 
 | ||||||
| instance MimeRender PlainText TextModel where | 
 | ||||||
|   mimeRender _ model = renderText $ Table.render "  " | charTableText :: [Char] -> BL.ByteString | ||||||
|  | charTableText chars = | ||||||
|  |   renderText $ Table.render "  " | ||||||
|     [ map Table.cl |     [ map Table.cl | ||||||
|       [ Text.pack [c] |       [ Text.pack [c] | ||||||
|       , Text.pack $ printf "U+%04X" c |       , Text.pack $ printf "U+%04X" c | ||||||
|       , Text.pack $ intercalate ", " $ allNames c |       , Text.intercalate ", " $ Names.allNames c | ||||||
|       , Text.pack $ fromMaybe "" $ blockName c |       , fromMaybe "" $ Names.blockName c | ||||||
|       ] |       ] | ||||||
|     | c <- Text.unpack model.text |     | c <- chars | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| -- Utilities |  | ||||||
| 
 |  | ||||||
| renderText :: Text -> BL.ByteString | renderText :: Text -> BL.ByteString | ||||||
| renderText = BL.fromStrict . Encoding.encodeUtf8 | renderText = BL.fromStrict . Encoding.encodeUtf8 | ||||||
| 
 | 
 | ||||||
| @ -255,15 +281,6 @@ showByteHex = printf "      %02X" | |||||||
| showByteBin :: Word8 -> String | showByteBin :: Word8 -> String | ||||||
| showByteBin = printf "%08b" | showByteBin = printf "%08b" | ||||||
| 
 | 
 | ||||||
| -- | Retrieve name and aliases (suffixed with @*@) of a 'Char'. |  | ||||||
| allNames :: Char -> [String] |  | ||||||
| allNames c = |  | ||||||
|   maybeToList (UnicodeNames.name c) |  | ||||||
|   ++ map (++ "*") (UnicodeNames.nameAliases c) |  | ||||||
| 
 |  | ||||||
| blockName :: Char -> Maybe String |  | ||||||
| blockName c = UnicodeBlocks.blockName . UnicodeBlocks.blockDefinition <$> UnicodeBlocks.block c |  | ||||||
| 
 |  | ||||||
| orThrow :: Either a b -> (a -> ServerError) -> Handler b | orThrow :: Either a b -> (a -> ServerError) -> Handler b | ||||||
| orThrow (Left err) f = throwError $ f err | orThrow (Left err) f = throwError $ f err | ||||||
| orThrow (Right val) _ = pure val | orThrow (Right val) _ = pure val | ||||||
|  | |||||||
							
								
								
									
										69
									
								
								bench/Main.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								bench/Main.hs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | |||||||
|  | {-# LANGUAGE OverloadedStrings #-} | ||||||
|  | 
 | ||||||
|  | module Main where | ||||||
|  | 
 | ||||||
|  | import Criterion.Main (bench, bgroup, defaultMain, whnf) | ||||||
|  | import Data.Char (ord) | ||||||
|  | import Data.Maybe (maybeToList) | ||||||
|  | import Data.Text (Text) | ||||||
|  | 
 | ||||||
|  | import qualified Data.Text as Text | ||||||
|  | import qualified Data.Vector as Vector | ||||||
|  | import qualified Unicode.Char.General.Names as UnicodeNames | ||||||
|  | 
 | ||||||
|  | main = defaultMain | ||||||
|  |   [ bench "naiveSearchCI" $ whnf naiveSearchCI "latin" | ||||||
|  |   , bench "naiveSearchCS" $ whnf naiveSearchCS "LATIN" | ||||||
|  |   , bench "memoSearchCI" $ whnf memoSearchCI "latin" | ||||||
|  |   , bench "memoSearchCS" $ whnf memoSearchCS "LATIN" | ||||||
|  |   ] | ||||||
|  | 
 | ||||||
|  | naiveSearchCI :: Text -> [Char] | ||||||
|  | naiveSearchCI search = | ||||||
|  |   filter go [minBound..maxBound] | ||||||
|  |   where | ||||||
|  |     go c = any matches $ allNames c | ||||||
|  |     matches t = Text.toLower search `Text.isInfixOf` Text.toLower (Text.pack t) | ||||||
|  | 
 | ||||||
|  | naiveSearchCS :: Text -> [Char] | ||||||
|  | naiveSearchCS search = | ||||||
|  |   filter go [minBound..maxBound] | ||||||
|  |   where | ||||||
|  |     go c = any matches $ allNames c | ||||||
|  |     matches t = search `Text.isInfixOf` Text.pack t | ||||||
|  | 
 | ||||||
|  | memoSearchCS :: Text -> [Char] | ||||||
|  | memoSearchCS search = | ||||||
|  |   filter go [minBound..maxBound] | ||||||
|  |   where | ||||||
|  |     go c = any matches $ allNamesText c | ||||||
|  |     matches t = search `Text.isInfixOf` t | ||||||
|  | 
 | ||||||
|  | memoSearchCI :: Text -> [Char] | ||||||
|  | memoSearchCI search = | ||||||
|  |   filter go [minBound..maxBound] | ||||||
|  |   where | ||||||
|  |     go c = any matches $ allNamesTextLower c | ||||||
|  |     matches t = Text.toLower search `Text.isInfixOf` t | ||||||
|  | 
 | ||||||
|  | -- | Retrieve name and aliases (suffixed with @*@) of a 'Char'. | ||||||
|  | allNames :: Char -> [String] | ||||||
|  | allNames c = | ||||||
|  |   maybeToList (UnicodeNames.name c) | ||||||
|  |   ++ map (++ "*") (UnicodeNames.nameAliases c) | ||||||
|  | 
 | ||||||
|  | allNamesText :: Char -> [Text] | ||||||
|  | allNamesText c = Vector.unsafeIndex textNames $ ord c | ||||||
|  | 
 | ||||||
|  | textNames :: Vector.Vector [Text] | ||||||
|  | textNames = Vector.fromList $ map go [minBound..maxBound] | ||||||
|  |   where | ||||||
|  |     go c = map Text.pack $ allNames c | ||||||
|  | 
 | ||||||
|  | allNamesTextLower :: Char -> [Text] | ||||||
|  | allNamesTextLower c = Vector.unsafeIndex textNamesLower $ ord c | ||||||
|  | 
 | ||||||
|  | textNamesLower :: Vector.Vector [Text] | ||||||
|  | textNamesLower = Vector.fromList $ map go [minBound..maxBound] | ||||||
|  |   where | ||||||
|  |     go c = map (Text.toLower . Text.pack) $ allNames c | ||||||
							
								
								
									
										8
									
								
								flake.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								flake.lock
									
									
									
										generated
									
									
									
								
							| @ -2,16 +2,16 @@ | |||||||
|   "nodes": { |   "nodes": { | ||||||
|     "nixpkgs": { |     "nixpkgs": { | ||||||
|       "locked": { |       "locked": { | ||||||
|         "lastModified": 1703588687, |         "lastModified": 1740243248, | ||||||
|         "narHash": "sha256-yj/AFxJjW/aE0lmHz1wlTk3jScZqVjQQEeBOnhyroRc=", |         "narHash": "sha256-GJ/fV5AE+4i38/eXmAgBZs4lKYXfScpxPYviUV8ujlk=", | ||||||
|         "owner": "nixos", |         "owner": "nixos", | ||||||
|         "repo": "nixpkgs", |         "repo": "nixpkgs", | ||||||
|         "rev": "ad9ca03be8aaf8d6e458102e7d77370b7fe71ccf", |         "rev": "a6c4bfe37c51686c042b4bdb4e650a9414e96680", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       }, |       }, | ||||||
|       "original": { |       "original": { | ||||||
|         "owner": "nixos", |         "owner": "nixos", | ||||||
|         "ref": "release-23.11", |         "ref": "release-24.11", | ||||||
|         "repo": "nixpkgs", |         "repo": "nixpkgs", | ||||||
|         "type": "github" |         "type": "github" | ||||||
|       } |       } | ||||||
|  | |||||||
							
								
								
									
										92
									
								
								flake.nix
									
									
									
									
									
								
							
							
						
						
									
										92
									
								
								flake.nix
									
									
									
									
									
								
							| @ -1,89 +1,45 @@ | |||||||
| { | { | ||||||
|   description = "Unicode toy"; |   description = "Unicode toy"; | ||||||
| 
 | 
 | ||||||
|   inputs.nixpkgs.url = "github:nixos/nixpkgs/release-23.11"; |   inputs.nixpkgs.url = "github:nixos/nixpkgs/release-24.11"; | ||||||
| 
 | 
 | ||||||
|   outputs = { self, nixpkgs }: |   outputs = { self, nixpkgs }: | ||||||
|     let |     let | ||||||
|       settings = import ./nix/settings.nix; |  | ||||||
|       haskellDeps = import ./nix/haskell-deps.nix; |  | ||||||
| 
 |  | ||||||
|       pkgs = nixpkgs.legacyPackages.x86_64-linux; |       pkgs = nixpkgs.legacyPackages.x86_64-linux; | ||||||
|       haskellPackages = pkgs.haskell.packages."${settings.ghc}"; |  | ||||||
| 
 | 
 | ||||||
|       ghc = haskellPackages.ghcWithPackages haskellDeps; |       utoy = pkgs.haskellPackages.developPackage { | ||||||
| 
 |         root = ./.; | ||||||
|       # Wrap stack to disable its slow Nix integration. |         overrides = self: super: { | ||||||
|       # Instead, make it use the GHC defined above. |           unicode-data = super.unicode-data_0_6_0; | ||||||
|       stack = pkgs.stdenv.mkDerivation { |           unicode-data-names = pkgs.haskell.lib.markUnbroken super.unicode-data-names; | ||||||
|         name = "stack"; |         }; | ||||||
| 
 |  | ||||||
|         # The build is simply a call to makeWrapper, so we don't have to |  | ||||||
|         # do any of the typical build steps. |  | ||||||
|         phases = [ "installPhase" ]; |  | ||||||
| 
 |  | ||||||
|         nativeBuildInputs = [ pkgs.makeWrapper ]; |  | ||||||
|         # makeBinaryWrapper creates a stack executable for us that uses |  | ||||||
|         # the GHC defined in this file. |  | ||||||
|         installPhase = '' |  | ||||||
|           makeWrapper ${pkgs.stack}/bin/stack $out/bin/stack \ |  | ||||||
|             --prefix PATH : ${ghc}/bin \ |  | ||||||
|             --add-flags '--no-nix --system-ghc --no-install-ghc' |  | ||||||
|         ''; |  | ||||||
|       }; |       }; | ||||||
| 
 |  | ||||||
|       utoy = pkgs.haskell.lib.justStaticExecutables (haskellPackages.callPackage |  | ||||||
|         ({ mkDerivation }: |  | ||||||
|         mkDerivation { |  | ||||||
|           # Keep this in sync with package.yaml |  | ||||||
|           version = "0.6.1"; |  | ||||||
|           pname = "utoy"; |  | ||||||
|           license = pkgs.lib.licenses.mit; |  | ||||||
|           src = |  | ||||||
|             # We only need these files for building: |  | ||||||
|             let |  | ||||||
|               whitelist = [ |  | ||||||
|                 ./LICENSE |  | ||||||
|                 ./utoy.cabal |  | ||||||
|                 ./Setup.hs |  | ||||||
|                 ./app |  | ||||||
|                 ./src |  | ||||||
|                 ./static |  | ||||||
|                 ./test |  | ||||||
|               ]; |  | ||||||
|             in |  | ||||||
|               pkgs.lib.sources.cleanSourceWith { |  | ||||||
|                 src = ./.; |  | ||||||
|                 filter = path: _type: pkgs.lib.any (prefix: pkgs.lib.hasPrefix (toString prefix) path) whitelist; |  | ||||||
|               }; |  | ||||||
|           libraryHaskellDepends = haskellDeps haskellPackages; |  | ||||||
|         }) {}); |  | ||||||
|     in { |     in { | ||||||
|       packages.x86_64-linux = { |       packages.x86_64-linux = rec { | ||||||
|         inherit ghc; |  | ||||||
|         inherit stack; |  | ||||||
| 
 |  | ||||||
|         docker = |         docker = | ||||||
|           pkgs.dockerTools.buildImage { |           pkgs.dockerTools.buildImage { | ||||||
|             name = "git.pbrinkmeier.de/paul/utoy"; |             name = "git.pbrinkmeier.de/paul/utoy"; | ||||||
|             tag = utoy.version; |             tag = utoy.version; | ||||||
|             config.Cmd = [ "${utoy}/bin/utoy" ]; |             config.Cmd = [ "${pkgs.haskell.lib.justStaticExecutables utoy}/bin/utoy" ]; | ||||||
|           }; |           }; | ||||||
| 
 | 
 | ||||||
|         default = utoy; |         default = utoy; | ||||||
|       }; |       }; | ||||||
| 
 | 
 | ||||||
|       devShells.x86_64-linux.default = pkgs.mkShell { |       devShells.x86_64-linux.default = | ||||||
|         packages = [ |         (pkgs.haskellPackages.developPackage { | ||||||
|           stack |           root = ./.; | ||||||
|           ghc |           overrides = self: super: { | ||||||
| 
 |             unicode-data = super.unicode-data_0_6_0; | ||||||
|           haskellPackages.haskell-language-server |             unicode-data-names = pkgs.haskell.lib.markUnbroken super.unicode-data-names; | ||||||
|           haskellPackages.implicit-hie |           }; | ||||||
|         ]; |           modifier = drv: | ||||||
|         shellHook = '' |             pkgs.haskell.lib.addBuildTools drv [ | ||||||
|           PS1+="(utoy) "; |               pkgs.cabal-install | ||||||
|         ''; |               pkgs.haskellPackages.implicit-hie | ||||||
|       }; |               pkgs.haskell-language-server | ||||||
|  |             ]; | ||||||
|  |           cabal2nixOptions = "--benchmark"; | ||||||
|  |         }).env; | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								hie.yaml
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								hie.yaml
									
									
									
									
									
								
							| @ -1,10 +1,10 @@ | |||||||
| cradle: | cradle: | ||||||
|   stack: |   cabal: | ||||||
|     - path: "./src" |     - path: "src" | ||||||
|       component: "utoy:lib" |       component: "lib:utoy" | ||||||
| 
 | 
 | ||||||
|     - path: "./app/Main.hs" |     - path: "app/Main.hs" | ||||||
|       component: "utoy:exe:utoy" |       component: "utoy:exe:utoy" | ||||||
| 
 | 
 | ||||||
|     - path: "./test" |     - path: "bench/Main.hs" | ||||||
|       component: "utoy:test:utoy-test" |       component: "utoy:bench:utoy-bench" | ||||||
|  | |||||||
| @ -1,13 +0,0 @@ | |||||||
| haskellPackages: with haskellPackages; [ |  | ||||||
|   attoparsec |  | ||||||
|   blaze-html |  | ||||||
|   bytestring |  | ||||||
|   file-embed |  | ||||||
|   http-media |  | ||||||
|   servant-server |  | ||||||
|   text |  | ||||||
|   unicode-data |  | ||||||
|   unicode-data-names |  | ||||||
|   wai |  | ||||||
|   warp |  | ||||||
| ] |  | ||||||
| @ -1,5 +0,0 @@ | |||||||
| { |  | ||||||
|   # GHC version to use with Nix. |  | ||||||
|   # Should match the one in stack.yaml. |  | ||||||
|   ghc = "ghc944"; |  | ||||||
| } |  | ||||||
							
								
								
									
										70
									
								
								package.yaml
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								package.yaml
									
									
									
									
									
								
							| @ -1,70 +0,0 @@ | |||||||
| # Adapted from new-template.hsfiles |  | ||||||
| 
 |  | ||||||
| name:                utoy |  | ||||||
| # Keep this in sync with the version in flake.nix. |  | ||||||
| version:             0.6.1 |  | ||||||
| git:                 "https://git.pbrinkmeier.de/paul/utoy" |  | ||||||
| license:             MIT |  | ||||||
| author:              "Paul Brinkmeier" |  | ||||||
| maintainer:          "hallo@pbrinkmeier.de" |  | ||||||
| copyright:           "2023 Paul Brinkmeier" |  | ||||||
| 
 |  | ||||||
| extra-source-files: |  | ||||||
| - README.md |  | ||||||
| - static/utoy.css |  | ||||||
| 
 |  | ||||||
| dependencies: |  | ||||||
| - base >= 4.7 && < 5 |  | ||||||
| - attoparsec |  | ||||||
| - text |  | ||||||
| 
 |  | ||||||
| ghc-options: |  | ||||||
| - -Wall |  | ||||||
| - -Wcompat |  | ||||||
| - -Widentities |  | ||||||
| - -Wincomplete-record-updates |  | ||||||
| - -Wincomplete-uni-patterns |  | ||||||
| - -Wmissing-export-lists |  | ||||||
| - -Wmissing-home-modules |  | ||||||
| - -Wpartial-fields |  | ||||||
| - -Wredundant-constraints |  | ||||||
| 
 |  | ||||||
| library: |  | ||||||
|   source-dirs: src |  | ||||||
| 
 |  | ||||||
| executables: |  | ||||||
|   utoy: |  | ||||||
|     main:                Main.hs |  | ||||||
|     source-dirs:         app |  | ||||||
|     ghc-options: |  | ||||||
|     - -threaded |  | ||||||
|     - -rtsopts |  | ||||||
|     - -with-rtsopts=-N |  | ||||||
|     dependencies: |  | ||||||
|     - utoy |  | ||||||
|     - blaze-html |  | ||||||
|     - bytestring |  | ||||||
|     - file-embed |  | ||||||
|     - http-media |  | ||||||
|     - servant-server |  | ||||||
|     - text |  | ||||||
|     - unicode-data |  | ||||||
|     - unicode-data-names |  | ||||||
|     - wai |  | ||||||
|     - warp |  | ||||||
|     # Fix "Multiple files use the same module name", see |  | ||||||
|     # https://stackoverflow.com/questions/67519851/multiple-files-use-the-same-module-name |  | ||||||
|     when: |  | ||||||
|     - condition: false |  | ||||||
|       other-modules: Paths_utoy |  | ||||||
| 
 |  | ||||||
| tests: |  | ||||||
|   utoy-test: |  | ||||||
|     main:                Spec.hs |  | ||||||
|     source-dirs:         test |  | ||||||
|     ghc-options: |  | ||||||
|     - -threaded |  | ||||||
|     - -rtsopts |  | ||||||
|     - -with-rtsopts=-N |  | ||||||
|     dependencies: |  | ||||||
|     - utoy |  | ||||||
							
								
								
									
										31
									
								
								src/UToy/Names.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/UToy/Names.hs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | |||||||
|  | module UToy.Names (allNames, blockName, searchCaseInsensitive) where | ||||||
|  | 
 | ||||||
|  | import Data.Char (ord) | ||||||
|  | import Data.Maybe (maybeToList) | ||||||
|  | import Data.Text (Text) | ||||||
|  | import Data.Vector (Vector) | ||||||
|  | 
 | ||||||
|  | import qualified Data.Text as Text | ||||||
|  | import qualified Data.Vector as Vector | ||||||
|  | import qualified Unicode.Char.General.Blocks as UnicodeBlocks | ||||||
|  | import qualified Unicode.Char.General.Names as UnicodeNames | ||||||
|  | 
 | ||||||
|  | -- | Retrieve name and aliases (suffixed with @*@) of a 'Char'. | ||||||
|  | allNames :: Char -> [Text] | ||||||
|  | allNames c = map Text.pack $ | ||||||
|  |   maybeToList (UnicodeNames.name c) | ||||||
|  |   ++ map (++ "*") (UnicodeNames.nameAliases c) | ||||||
|  | 
 | ||||||
|  | blockName :: Char -> Maybe Text | ||||||
|  | blockName c = Text.pack . UnicodeBlocks.blockName . UnicodeBlocks.blockDefinition <$> UnicodeBlocks.block c | ||||||
|  | 
 | ||||||
|  | searchCaseInsensitive :: Text -> [Char] | ||||||
|  | searchCaseInsensitive search = filter go [minBound..maxBound] | ||||||
|  |   where | ||||||
|  |     go c = any matches $ Vector.unsafeIndex lowerNames (ord c - ord minBound) | ||||||
|  |     matches t = Text.toLower search `Text.isInfixOf` t | ||||||
|  | 
 | ||||||
|  | lowerNames :: Vector [Text] | ||||||
|  | lowerNames = Vector.fromList $ map go [minBound..maxBound] | ||||||
|  |   where | ||||||
|  |     go = map Text.toLower . allNames | ||||||
| @ -1,5 +0,0 @@ | |||||||
| # You can get a working environment using nix develop. |  | ||||||
| # Keep this in sync with nix/settings.nix |  | ||||||
| resolver: ghc-9.4.4 |  | ||||||
| packages: |  | ||||||
| - . |  | ||||||
| @ -1,2 +0,0 @@ | |||||||
| main :: IO () |  | ||||||
| main = putStrLn "Test suite not yet implemented" |  | ||||||
							
								
								
									
										28
									
								
								utoy.cabal
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								utoy.cabal
									
									
									
									
									
								
							| @ -1,11 +1,7 @@ | |||||||
| cabal-version: 1.12 | cabal-version: 1.12 | ||||||
| 
 | 
 | ||||||
| -- This file has been generated from package.yaml by hpack version 0.35.2. |  | ||||||
| -- |  | ||||||
| -- see: https://github.com/sol/hpack |  | ||||||
| 
 |  | ||||||
| name:           utoy | name:           utoy | ||||||
| version:        0.6.1 | version:        0.6.2 | ||||||
| author:         Paul Brinkmeier | author:         Paul Brinkmeier | ||||||
| maintainer:     hallo@pbrinkmeier.de | maintainer:     hallo@pbrinkmeier.de | ||||||
| copyright:      2023 Paul Brinkmeier | copyright:      2023 Paul Brinkmeier | ||||||
| @ -24,6 +20,7 @@ library | |||||||
|   exposed-modules: |   exposed-modules: | ||||||
|       UToy.Decode |       UToy.Decode | ||||||
|       UToy.Parsers |       UToy.Parsers | ||||||
|  |       UToy.Names | ||||||
|       UToy.Table |       UToy.Table | ||||||
|   other-modules: |   other-modules: | ||||||
|       Paths_utoy |       Paths_utoy | ||||||
| @ -34,6 +31,9 @@ library | |||||||
|       attoparsec |       attoparsec | ||||||
|     , base >=4.7 && <5 |     , base >=4.7 && <5 | ||||||
|     , text |     , text | ||||||
|  |     , unicode-data | ||||||
|  |     , unicode-data-names | ||||||
|  |     , vector | ||||||
|   default-language: Haskell2010 |   default-language: Haskell2010 | ||||||
| 
 | 
 | ||||||
| executable utoy | executable utoy | ||||||
| @ -50,24 +50,20 @@ executable utoy | |||||||
|     , http-media |     , http-media | ||||||
|     , servant-server |     , servant-server | ||||||
|     , text |     , text | ||||||
|     , unicode-data |  | ||||||
|     , unicode-data-names |  | ||||||
|     , utoy |     , utoy | ||||||
|     , wai |     , wai | ||||||
|     , warp |     , warp | ||||||
|   default-language: Haskell2010 |   default-language: Haskell2010 | ||||||
| 
 | 
 | ||||||
| test-suite utoy-test | benchmark utoy-bench | ||||||
|   type: exitcode-stdio-1.0 |   type: exitcode-stdio-1.0 | ||||||
|   main-is: Spec.hs |   main-is: Main.hs | ||||||
|   other-modules: |  | ||||||
|       Paths_utoy |  | ||||||
|   hs-source-dirs: |   hs-source-dirs: | ||||||
|       test |     bench | ||||||
|   ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N |  | ||||||
|   build-depends: |   build-depends: | ||||||
|       attoparsec |       base >=4.7 && <5 | ||||||
|     , base >=4.7 && <5 |     , criterion | ||||||
|     , text |     , text | ||||||
|     , utoy |     , unicode-data-names | ||||||
|  |     , vector | ||||||
|   default-language: Haskell2010 |   default-language: Haskell2010 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user