hwk (empty) → 0.2.0
raw patch · 7 files changed
+320/−0 lines, 7 filesdep +basedep +directorydep +extra
Dependencies added: base, directory, extra, filepath, hint, process, simple-cmd-args
Files
- ChangeLog.md +9/−0
- LICENSE +21/−0
- Main.hs +70/−0
- README.md +107/−0
- data/Hwk.hs +39/−0
- hwk.cabal +47/−0
- install.sh +27/−0
+ ChangeLog.md view
@@ -0,0 +1,9 @@+# Version history for hwk++## 0.2 (2020-10-10)+- first release by Jens Petersen+- uses hint library and Hwk configuration module++## 0.1.0.0 (2016-2017)+- original project by Lukas Martinelli+ https://github.com/lukasmartinelli/hwk
+ LICENSE view
@@ -0,0 +1,21 @@+The MIT License (MIT)++Copyright (c) 2016 Lukas Martinelli++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ Main.hs view
@@ -0,0 +1,70 @@+import qualified Data.List as L+import Language.Haskell.Interpreter+import SimpleCmdArgs+import System.Directory++import Paths_hwk (getDataDir, version)++main :: IO ()+main =+ simpleCmdArgs (Just version) "A Haskell awk/sed like tool"+ "Simple shell text processing with Haskell" $+ runExpr <$> switchWith 'a' "all" "All input as a single string" <*> strArg "FUNCTION" {-<*> many (strArg "FILE...")-}++runExpr :: Bool -> String -> {-[FilePath] ->-} IO ()+runExpr allinput stmt {-files-} = do+ --mapM_ checkFileExists files+ input <- getContents+ usercfg <- getXdgDirectory XdgConfig "hwk"+ datadir <- getDataDir+-- copyFile (datadir </> "hwk.hs") usercfg+ r <- runInterpreter (runHint [usercfg, datadir] input)+ case r of+ Left err -> putStrLn $ errorString err+ Right () -> return ()+ where+ runHint :: [FilePath] -> String -> Interpreter ()+ runHint cfgdirs input = do+ set [searchPath := cfgdirs]+ loadModules ["Hwk"]+ setHwkImports ["Prelude"]+ imports <- do+ haveModules <- typeChecks "userModules"+ if haveModules+ then interpret "userModules" infer+ else return ["Prelude", "Data.List"]+ setHwkImports imports+ -- -- Not sure which is better: typechecking manually or polymorph string+ -- etypchk <- typeChecksWithDetails stmt+ -- case etypchk of+ -- Left err -> error' err+ -- Right typ ->+ -- case typ of+ poly <- do+ havePoly <- typeChecks "polymorph"+ if havePoly then do+ s <- interpret "polymorph" infer+ return $ if null s then "" else s ++ " . "+ else return ""+ if allinput+ then do+ fn <- interpret (poly ++ stmt) (as :: String -> [String])+ liftIO $ mapM_ putStrLn (fn input)+ else do+ fn <- interpret (poly ++ stmt) (as :: [String] -> [String])+ liftIO $ mapM_ putStrLn (fn (lines input))++ -- FIXME use Set+ setHwkImports ms = setImports (L.nub (ms ++ ["Hwk"]))++ -- checkFileExists :: FilePath -> IO ()+ -- checkFileExists file = do+ -- unlessM (doesFileExist file) $+ -- error' $ "file not found: " ++ file++ errorString :: InterpreterError -> String+ errorString (WontCompile es) = L.intercalate "\n" (header : map unbox es)+ where+ header = "ERROR: Won't compile:"+ unbox (GhcError e) = e+ errorString e = show e
+ README.md view
@@ -0,0 +1,107 @@+# hwk ++hwk was originally written by Lukas Martinelli in 2016-2017:+see the [original README file](README.md.orig).++<img align="right" alt="hwk" src="hwk.png" />++**hwk** tries to demonstrate how a modern Haskell based stream manipulation tool could look like.+It is similar to tools like **awk** or **sed**.+`hwk` allows compact function sequences that operate on a list of strings. Because Haskell is lazy and has a powerful arsenal of functions, there is no need to invent another DSL and hopefully it encourages more people to think functionally.++## Example++Prepend a string to each line:+```bash+$ seq 1 3 | hwk 'map (++ ".txt")'+1.txt+2.txt+3.txt+```++Sum all negative numbers:+```bash+$ seq -100 100 | hwk 'sum . filter (< 0) . ints'+-5050+```+The ints function transforms a list of strings into a list of ints++Extract data from a file:+```bash+$ cat /etc/passwd | hwk 'take 3 . map (filter (/= "x") . take 3 . splitOn ":")'+root 0+bin 1+daemon 2+```+(a module defining `splitOn` from the extra or split library needs to be added to the Hwk.hs config file).++The argument passed to `hwk` must be a valid Haskell function: a function that takes a list of strings and returns a new list or a single value.++## Configuration+It uses a configuration module `Hwk` which provides the context for the hint evaluation of the supplied function.++It searches for `Hwk.hs` in `~/.config/hwk`, then the package's installed data directory.++The default configuration [Hwk module](data/Hwk.hs) just sets+the `Prelude`, `Data.List`, and `Data.Char` modules to be imported by default into the hint interpreter.++If you want to use other modules or define your own functions, you can copy the installed `Hwk.hs` or source `data/Hwk.hs` file to `~/.config/hwk/` to configure hwk.++## Install+Either use the `install.sh` script, or install by cabal-install or stack+as described below:++### Install script from source tree or git+Use `stack unpack hwk` or `git clone https://github.com/juhp/hwk`.++Then go to the source directory and run the `install.sh` script, which++- first runs `stack install`+- then moves the binary installed by `stack install` to `~/.local/bin/hwk-bin`, and sets up a wrapper script `~/.local/bin/hwk` which runs it.+- and also copies the Hwk.hs configuration module to `~/.config/hwk/Hwk.hs` (backing up any existing file).++You may wish to change the resolver in stack.yaml first, which is also use to determine the resolver used by the created `hwk` wrapper script.++### cabal+If you are on a Linux distro with a system installed ghc and Haskell libaries,+you can install with `cabal install` to make use of them.++If you install with a recent cabal the Hwk.hs config module probably lives somewhere like `~/.cabal/store/ghc-*/hwk-*/share/data/Hwk.hs`, or you can copy it from the source `data/Hwk.hs`.++### stack+Installing by stack is better if you do not have a system ghc+and/or global system Haskell libraries installed.++Alternatively to install by hand: run `stack install`,+and then run it with `stack exec hwk ...` using the same resolver,+To customize hwk after a stack install it is probably easier just to copy+the `data/Hwk.hs` source file.++## How does `hwk` work?++- `hwk` use the hint library to evaluate haskell functions on standard input.+- By default it splits the input to a list of lines: `[String] -> ToList a`+- Use `-a` or `--all` to apply a function to all the input: `String -> Tolist a`++## Supported return types++By default the following instances of the `ToList` class are defined:++- `String`+- `[String]`+- `[[String]]`+- `Int`+- `[Int]`++## Contribute++Open an issue or pull request at https://github.com/juhp/hwk+to report problems or make suggestions and contributions.++## Related/alternative projects++- https://github.com/gelisam/hawk+- https://github.com/bawolk/hsp+- https://code.google.com/p/pyp/+- https://en.wikipedia.org/wiki/AWK+- https://en.wikipedia.org/wiki/Sed
+ data/Hwk.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE FlexibleInstances #-}++module Hwk where++import Data.List (intercalate)++-- | modules to be imported into hint+-- these modules must be globally installed+userModules :: [String]+userModules = ["Prelude", "Data.List", "Data.Char"]++-- this string is used by hwk+-- | Determines the types of functions hwk can interpret+-- "toList" allows some simple polymorphism+-- use "id" or "" to allow only functions of type: [String] -> [String]+polymorph :: String+polymorph = "toList"++-- ToList allows handling functions of type: ToList a => [String] -> a+class ToList a where+ toList :: a -> [String]+instance ToList String where+ toList x = [x]+instance ToList [String] where+ toList = id+instance ToList [[String]] where+ toList = map (intercalate "\t")+instance ToList Int where+ toList x = [show x]+instance ToList [Int] where+ toList lst = map show lst++-- below here all user defined ------++int :: String -> Int+int str = read str :: Int++ints :: [String] -> [Int]+ints = map int
+ hwk.cabal view
@@ -0,0 +1,47 @@+name: hwk+version: 0.2.0+synopsis: A modern Haskell based AWK replacement+description: A simple Haskell-based replacement for awk/sed.+homepage: https://github.com/juhp/hwk+license: MIT+license-file: LICENSE+author: Lukas Martinelli+maintainer: Jens Petersen <juhpetersen@gmail.com>+copyright: 2016-2017 Lukas Martinelli,+ 2020 Jens Petersen+category: Development+build-type: Simple+data-files: data/Hwk.hs+extra-source-files: install.sh+extra-doc-files: README.md+ ChangeLog.md+cabal-version: 1.18++source-repository head+ type: git+ location: https://github.com/juhp/hwk.git++executable hwk+ main-is: Main.hs+ other-modules: Paths_hwk+ autogen-modules: Paths_hwk+ build-depends: base <5,+ directory,+ extra,+ filepath,+ hint,+ process,+ simple-cmd-args >= 0.1.2+ default-language: Haskell2010+ ghc-options: -Wall+ if impl(ghc >= 8.0)+ ghc-options: -Wcompat+ -Widentities+ -Wincomplete-uni-patterns+ -Wincomplete-record-updates+ -Wredundant-constraints+ if impl(ghc >= 8.2)+ ghc-options: -fhide-source-paths+ if impl(ghc >= 8.4)+ ghc-options: -Wmissing-export-lists+ -Wpartial-fields
+ install.sh view
@@ -0,0 +1,27 @@+#!/bin/sh++set -e++stack install++RESOLVER=$(grep -i "resolver:" stack.yaml | sed -e "s/.*: *//")++CFGDIR=$HOME/.config/hwk+mkdir -p $CFGDIR++if [ -f "$CFGDIR/Hwk.hs" ]; then+ CFGHASH=$(cd $CFGDIR; md5sum Hwk.hs)+fi+if [ "$CFGHASH" != "$(cd data; md5sum Hwk.hs)" ]; then+cp -p -v --backup=numbered data/Hwk.hs $CFGDIR/+fi++mv -f ~/.local/bin/hwk ~/.local/bin/hwk-bin++cat > ~/.local/bin/hwk <<EOF+#!/bin/sh++stack --resolver $RESOLVER exec ~/.local/bin/hwk-bin "\$@"+EOF++chmod u+x ~/.local/bin/hwk