hslua-module-text 0.2.1 → 0.3.0
raw patch · 7 files changed
+230/−69 lines, 7 filesdep +tasty-luadep ~basedep ~hsluaPVP ok
version bump matches the API change (PVP)
Dependencies added: tasty-lua
Dependency ranges changed: base, hslua
API changes (from Hackage documentation)
+ Foreign.Lua.Module.Text: description :: Text
+ Foreign.Lua.Module.Text: documentedModule :: Module
+ Foreign.Lua.Module.Text: functions :: [(Text, HaskellFunction)]
+ Foreign.Lua.Module.Text: preloadModule :: String -> Lua ()
Files
- ChangeLog.md +8/−0
- LICENSE +1/−1
- hslua-module-text.cabal +9/−7
- src/Foreign/Lua/Module/Text.hs +125/−21
- test/hstext-test.lua +0/−29
- test/test-hslua-module-text.hs +9/−11
- test/test-text.lua +78/−0
ChangeLog.md view
@@ -1,5 +1,13 @@ # Revision history for hslua-module-text +## 0.3.0 -- 2020-08-15++- Use self-documenting module. This allows to include+ documentation with the module definition, and to auto-generate+ documentation from that. Requires hslua-1.2.0 or newer.++- Run CI tests with all GHC 8 versions, test stack builds.+ ## 0.2.1 -- 2019-05-04 - Require at least HsLua v1.0.3: that version has better support
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2017 Albert Krewinkel+Copyright (c) 2017-2020 Albert Krewinkel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
hslua-module-text.cabal view
@@ -1,5 +1,5 @@ name: hslua-module-text-version: 0.2.1+version: 0.3.0 synopsis: Lua module for text description: UTF-8 aware subset of Lua's `string` module. homepage: https://github.com/hslua/hslua-module-text@@ -7,17 +7,18 @@ license-file: LICENSE author: Albert Krewinkel maintainer: albert+hslua@zeitkraut.de-copyright: © 2017–2019 Albert Krewinkel+copyright: © 2017–2020 Albert Krewinkel category: Foreign build-type: Simple extra-source-files: ChangeLog.md- test/hstext-test.lua+ test/test-text.lua cabal-version: >=1.10-tested-with: GHC == 7.10.3- , GHC == 8.0.2+tested-with: GHC == 8.0.2 , GHC == 8.2.2 , GHC == 8.4.4 , GHC == 8.6.5+ , GHC == 8.8.3+ , GHC == 8.10.1 source-repository head type: git@@ -25,9 +26,9 @@ library exposed-modules: Foreign.Lua.Module.Text- build-depends: base >= 4.7 && < 5+ build-depends: base >= 4.8 && < 5 , bytestring >= 0.10.2 && < 0.11- , hslua >= 1.0.3 && < 1.2+ , hslua >= 1.2 && < 1.3 , text >= 1 && < 1.3 hs-source-dirs: src default-language: Haskell2010@@ -44,4 +45,5 @@ , hslua-module-text , tasty , tasty-hunit+ , tasty-lua >= 0.2 && < 0.3 , text
src/Foreign/Lua/Module/Text.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications #-} {-| Module : Foreign.Lua.Module.Text-Copyright : © 2017–2019 Albert Krewinkel+Copyright : © 2017–2020 Albert Krewinkel License : MIT Maintainer : Albert Krewinkel <tarleb+hslua@zeitkraut.de> Stability : alpha@@ -10,44 +11,147 @@ Provide a lua module containing a selection of useful Text functions. -} module Foreign.Lua.Module.Text- ( pushModule++ ( -- * Module+ pushModule+ , preloadModule+ , documentedModule+ , description+ , functions++ -- * Legacy , pushModuleText , preloadTextModule- )where+ ) where +import Prelude hiding (reverse) import Control.Applicative ((<$>)) import Data.ByteString (ByteString) import Data.Text (Text) import Data.Maybe (fromMaybe) import Foreign.Lua (NumResults, Lua, Peekable, Pushable, ToHaskellFunction)+import Foreign.Lua.Call+import Foreign.Lua.Module hiding (preloadModule, pushModule)+import Foreign.Lua.Peek (Peeker, peekIntegral, peekText)+import Foreign.Lua.Push (pushIntegral, pushText) import qualified Foreign.Lua as Lua import qualified Data.Text as T +import qualified Foreign.Lua.Module as Module+--+-- Module+--++-- | Textual description of the "text" module.+description :: Text+description =+ "UTF-8 aware text manipulation functions, implemented in Haskell."++documentedModule :: Module+documentedModule = Module+ { moduleName = "paths"+ , moduleFields = []+ , moduleDescription = description+ , moduleFunctions = functions+ }+ -- | Pushes the @text@ module to the Lua stack. pushModule :: Lua NumResults-pushModule = do- Lua.newtable- Lua.addfunction "lower" (return . T.toLower :: Text -> Lua Text)- Lua.addfunction "upper" (return . T.toUpper :: Text -> Lua Text)- Lua.addfunction "reverse" (return . T.reverse :: Text -> Lua Text)- Lua.addfunction "len" (return . fromIntegral . T.length :: Text -> Lua Lua.Integer)- Lua.addfunction "sub" sub- return 1+pushModule = 1 <$ Module.pushModule documentedModule --- | Legacy alias for '@pushModule@'. pushModuleText :: Lua NumResults-pushModuleText = pushModule+pushModuleText = 1 <$ Module.pushModule documentedModule +-- | Add the @text@ module under the given name to the table of+-- preloaded packages.+preloadModule :: String -> Lua ()+preloadModule name = Module.preloadModule $+ documentedModule { moduleName = T.pack name }+ -- | Add the text module under the given name to the table of preloaded -- packages. preloadTextModule :: String -> Lua ()-preloadTextModule = flip Lua.preloadhs pushModule+preloadTextModule = flip Lua.preloadhs pushModuleText +--+-- Functions+--++functions :: [(Text, HaskellFunction)]+functions =+ [ ("len", len)+ , ("lower", lower)+ , ("reverse", reverse)+ , ("sub", sub)+ , ("upper", upper)+ ]++-- | Wrapper for @'T.length'@.+len :: HaskellFunction+len = toHsFnPrecursor T.length+ <#> textParam "s"+ =#> intResult "length"+ #? "Determines the number of characters in a string."++-- | Wrapper for @'T.toLower'@.+lower :: HaskellFunction+lower = toHsFnPrecursor T.toLower+ <#> textParam "s"+ =#> textResult "Lowercase copy of `s`"+ #? "Convert a string to lower case"++-- | Wrapper for @'T.reverse'@.+reverse :: HaskellFunction+reverse = toHsFnPrecursor T.reverse+ <#> textParam "s"+ =#> textResult "Reversed `s`"+ #? "Reverses a string."+ -- | Returns a substring, using Lua's string indexing rules.-sub :: Text -> Lua.Integer -> Lua.Optional Lua.Integer -> Lua Text-sub s i j =- let i' = fromIntegral i- j' = fromIntegral . fromMaybe (-1) $ Lua.fromOptional j- fromStart = if i' >= 0 then i' - 1 else T.length s + i'- fromEnd = if j' < 0 then -j' - 1 else T.length s - j'- in return . T.dropEnd fromEnd . T.drop fromStart $ s+sub :: HaskellFunction+sub = toHsFnPrecursor substring+ <#> textParam "s"+ <#> textIndex "i" "substring start position"+ <#> textOptionalIndex "j" "substring end position"+ =#> textResult "text substring"+ #? "Returns a substring, using Lua's string indexing rules."+ where+ substring :: Text -> Int -> Maybe Int -> Text+ substring s i jopt =+ let j = fromMaybe (-1) jopt+ fromStart = if i >= 0 then i - 1 else T.length s + i+ fromEnd = if j < 0 then -j - 1 else T.length s - j+ in T.dropEnd fromEnd . T.drop fromStart $ s++-- | Wrapper for @'T.toUpper'@.+upper :: HaskellFunction+upper = toHsFnPrecursor T.toUpper+ <#> textParam "s"+ =#> textResult "Lowercase copy of `s`"+ #? "Convert a string to lower case"++--+-- Parameters+--++textParam :: Text -> Parameter Text+textParam name =+ parameter peekText "string" name "UTF-8 encoded string"++textIndex :: Text -> Text -> Parameter Int+textIndex = parameter (peekIntegral @Int) "integer"++textOptionalIndex :: Text -> Text -> Parameter (Maybe Int)+textOptionalIndex = optionalParameter (peekIntegral @Int) "integer"++--+-- Results+--++textResult :: Text -- ^ Description+ -> FunctionResults Text+textResult = functionResult pushText "string"++intResult :: Text -- ^ Description+ -> FunctionResults Int+intResult = functionResult (pushIntegral @Int) "integer"
− test/hstext-test.lua
@@ -1,29 +0,0 @@------ Tests for the hstext module----local hstext = require 'hstext'--assert(hstext.lower 'YELLING' == 'yelling')-assert(hstext.upper 'silence' == 'SILENCE')-assert(hstext.len 'five' == 4)---- Test UTF-8-assert(hstext.upper 'Lübeck' == 'LÜBECK')-assert(hstext.upper 'Spaß' == 'SPASS')--assert(hstext.len 'Straße' == 6)-assert(hstext.len 'Charité' == 7)-assert(hstext.len '☃' == 1)--assert(hstext.reverse 'être' == 'ertê')-assert(hstext.reverse 'être' == 'ertê')--local hw = 'Hello, World'-assert(string.sub(hw, 6) == hstext.sub(hw, 6))-assert(string.sub(hw, -1, -1) == hstext.sub(hw, -1, -1))-assert(string.sub(hw, -7, -2) == hstext.sub(hw, -7, -2))-assert(string.sub(hw, 7, -2) == hstext.sub(hw, 7, -2))-assert(string.sub(hw, 1, 5) == hstext.sub(hw, 1, 5))-assert(string.sub(hw, 5, 0) == hstext.sub(hw, 5, 0))-assert(string.sub(hw, 0, 2) == hstext.sub(hw, 0, 2))-assert(string.sub(hw, -19, 5) == hstext.sub(hw, -19, 5))
test/test-hslua-module-text.hs view
@@ -1,5 +1,5 @@ {--Copyright © 2017–2019 Albert Krewinkel+Copyright © 2017–2020 Albert Krewinkel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal@@ -21,16 +21,22 @@ -} {-# LANGUAGE OverloadedStrings #-} -import Control.Monad (void, when)+import Control.Monad (void) import Foreign.Lua (Lua) import Foreign.Lua.Module.Text (preloadTextModule, pushModuleText) import Test.Tasty (TestTree, defaultMain, testGroup) import Test.Tasty.HUnit (assertEqual, testCase)+import Test.Tasty.Lua (translateResultsFromFile) import qualified Foreign.Lua as Lua main :: IO ()-main = defaultMain $ testGroup "hslua-module-text" [tests]+main = do+ luaTest <- Lua.run $ do+ Lua.openlibs+ Lua.requirehs "text" (void pushModuleText)+ translateResultsFromFile "test/test-text.lua"+ defaultMain $ testGroup "hslua-module-text" [tests, luaTest] -- | HSpec tests tests :: TestTree@@ -50,14 +56,6 @@ preloadTextModule "hstext" assertEqual' "loading the module fails " Lua.OK =<< Lua.dostring "require 'hstext'"-- , testCase "Lua tests pass" . Lua.run $ do- Lua.openlibs- preloadTextModule "hstext"- assertEqual' "error while running lua tests" Lua.OK =<< do- st <- Lua.loadfile "test/hstext-test.lua"- when (st == Lua.OK) $ Lua.call 0 0- return st ] assertEqual' :: (Show a, Eq a) => String -> a -> a -> Lua ()
+ test/test-text.lua view
@@ -0,0 +1,78 @@+--+-- Tests for the text module+--+local text = require 'text'+local tasty = require 'tasty'++local group = tasty.test_group+local test = tasty.test_case+local assert = tasty.assert++return {+ group 'len' {+ test('ASCII', function ()+ tasty.assert.are_equal(text.len 'five!', 5)+ end),+ test('German sz', function ()+ tasty.assert.are_equal(text.len 'Straße', 6)+ end),+ test('string with small letter e accute', function ()+ tasty.assert.are_equal(text.len 'Charité', 7)+ end),+ test('Unicode snowman', function ()+ tasty.assert.are_equal(text.len '☃', 1)+ end)+ },++ group 'lower' {+ test('uppercase ASCII', function ()+ assert.are_equal(text.lower 'YELLING', 'yelling')+ end),+ test('lowercase ASCII', function ()+ assert.are_equal(text.lower 'talking', 'talking')+ end),+ test('capitalized word with umlaut', function ()+ assert.are_equal(text.lower 'Lübeck', 'lübeck')+ end),+ },++ group 'upper' {+ test('uppercase ASCII', function ()+ assert.are_equal(text.upper 'YELLING', 'YELLING')+ end),+ test('lowercase ASCII', function ()+ assert.are_equal(text.upper 'silence', 'SILENCE')+ end),+ test('capitalized word with umlaut', function ()+ assert.are_equal(text.upper 'Lübeck', 'LÜBECK')+ end),+ test('German ß becomes double S', function ()+ assert.are_equal(text.upper 'Spaß', 'SPASS')+ end),+ },++ group 'reverse' {+ test('être becomes ertê', function ()+ assert.are_equal(text.reverse 'être', 'ertê')+ end)+ },++ group 'sub' {+ test('behaves like string.sub for ASCII text', function ()+ local hw = 'Hello, World'+ assert.are_equal(text.sub(hw, 6), string.sub(hw, 6))+ assert.are_equal(text.sub(hw, -1, -1), string.sub(hw, -1, -1))+ assert.are_equal(text.sub(hw, -7, -2), string.sub(hw, -7, -2))+ assert.are_equal(text.sub(hw, 7, -2), string.sub(hw, 7, -2))+ assert.are_equal(text.sub(hw, 1, 5), string.sub(hw, 1, 5))+ assert.are_equal(text.sub(hw, 5, 0), string.sub(hw, 5, 0))+ assert.are_equal(text.sub(hw, 0, 2), string.sub(hw, 0, 2))+ assert.are_equal(text.sub(hw, -19, 5), string.sub(hw, -19, 5))+ end),+ test('respects UTF-8', function ()+ assert.are_equal(text.sub('Für dich', 5), 'dich')+ assert.are_equal(text.sub('☢ radioactive', 0, 1), '☢')+ assert.are_equal(text.sub('☢ radioactive', -11, -1), 'radioactive')+ end)+ }+}