Hsed 0.2.1 → 0.2.2
raw patch · 6 files changed
+79/−95 lines, 6 filesdep +GotoT-transformersPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: GotoT-transformers
API changes (from Hackage documentation)
- Hsed.StreamEd: eval :: [SedCmd] -> SedState FlowControl
- Hsed.StreamEd: goto :: [SedCmd] -> Maybe Label -> [SedCmd]
- Hsed.StreamEd: loop :: [SedCmd] -> SedState ()
- Hsed.StreamEd: processStdin :: SedState ()
+ Hsed.StreamEd: jump :: [SedCmd] -> Maybe Label -> [SedCmd]
+ Hsed.StreamEd: nextLine :: SedEngine ()
+ Hsed.StreamEd: type SedEngine a = GotoT a (StateT Env IO) a
- Hsed.StreamEd: execCmds :: [SedCmd] -> SedState FlowControl
+ Hsed.StreamEd: execCmds :: [SedCmd] -> SedEngine ()
- Hsed.StreamEd: execute :: [FilePath] -> SedState ()
+ Hsed.StreamEd: execute :: [FilePath] -> SedEngine ()
- Hsed.StreamEd: processFiles :: [FilePath] -> SedState ()
+ Hsed.StreamEd: processFiles :: [FilePath] -> SedEngine ()
Files
- Hsed.cabal +2/−1
- src/Hsed/Ast.hs +1/−1
- src/Hsed/Sed.hs +4/−5
- src/Hsed/StreamEd.hs +67/−84
- tests/Append.ok +4/−3
- tests/Append.sed +1/−1
Hsed.cabal view
@@ -1,5 +1,5 @@ Name: Hsed-Version: 0.2.1+Version: 0.2.2 Description: A version of sed editor in Haskell based on POSIX "The Open Group Base Specifications Issue 7" IEEE Std 1003.1-2008. .@@ -48,6 +48,7 @@ data-accessor >= 0.2.1.4, data-accessor-template >= 0.2.1.5, data-accessor-transformers >= 0.2.1.2, + GotoT-transformers, parsec, bytestring, regex-compat,
src/Hsed/Ast.hs view
@@ -44,7 +44,7 @@ | Exchange -- ^ x - exchange pattern space with hold space | Transform Text Text -- ^ y - transform each char by position in Text to Text | Label Label -- ^ : - label a line in the scipt for transfering by b or t.- | Comment -- ^ # - ignore a line in the script except "#n" in the first line + | Comment -- ^ # - ignore a line in the script except \"#n\" in the first line | EmptyCmd -- ^ - ignore spaces deriving Show
src/Hsed/Sed.hs view
@@ -22,11 +22,10 @@ type SedScript = String --- | Execute the sed script and print the output to ByteString.------ Example. Suppose the ../tests/Transform.in file contains the line 'Hello world!'.--- The execScript ["../tests/Transform.in"] "y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/"--- will produce 'HELLO WORLD!' bytestring. +-- | Execute the sed script and print the output to ByteString. +-- Example. Suppose the ../tests/Transform.in file contains the line 'Hello world!'.+-- execScript [\"../tests/Transform.in\"] \"y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/\"+-- will produce 'HELLO WORLD!' bytestring. execScript :: [FilePath] -> SedScript -> IO B.ByteString execScript files script = do env <- runSed files script (initEnv {useMemSpace_ = True})
src/Hsed/StreamEd.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE DeriveDataTypeable #-}- -- | -- Module : StreamEd -- Copyright : (c) Vitaliy Rukavishnikov@@ -9,24 +7,27 @@ -- Stability : experimental -- Portability : non-portable ----- The Sed runtime engine. The execution sequence includes the parseArgs --- compile (parse) and execute Sed commands.+-- The Sed runtime engine module Hsed.StreamEd where import System.IO -import Control.Monad (unless, when)+import Control.Monad (unless, when, forM_, zipWithM) import qualified Control.Monad.State as S+import Control.Monad.Trans.Goto import Data.List (isPrefixOf) import Data.Char (isPrint) import Data.Maybe (fromMaybe) import qualified Data.ByteString.Char8 as B import Text.Printf (printf)+ import Hsed.Parsec (parseSed, sedCmds) import Hsed.Ast import Hsed.SedRegex import Hsed.SedState +type SedEngine a = GotoT a (S.StateT Env IO) a+ data Status = EOF | Cont deriving (Eq, Show) @@ -40,12 +41,12 @@ -- | Compile and execute the sed script runSed :: [FilePath] -> String -> Env -> IO Env-runSed fs sed env = - S.execStateT (do- when ("#n" `isPrefixOf` sed) $ - set defOutput False - compile sed - execute fs+runSed fs sed env = do+ S.execStateT (runGotoT $ do+ when ("#n" `isPrefixOf` sed) $ + S.lift $ set defOutput False + S.lift $ compile sed+ execute fs ) env -- | Parse the Sed commands@@ -57,49 +58,65 @@ return () -- | Execute the parsed Sed commands against input data-execute :: [FilePath] -> SedState ()+execute :: [FilePath] -> SedEngine () execute fs = do- if null fs then processStdin- else processFiles fs- fout <- get fileout- S.lift $ S.mapM_ hClose (map snd fout)---- | The standard input will be used if no file operands are specified-processStdin :: SedState ()-processStdin = do- e <- get exit- unless e $ do- set curFile (stdin, True)- a <- get ast- loop a+ processFiles fs+ fout <- S.lift $ get fileout+ S.liftIO $ S.mapM_ hClose (map snd fout) -- | Process the input text files-processFiles :: [FilePath] -> SedState ()+processFiles :: [FilePath] -> SedEngine () processFiles files = do- let len = length files- let fs = zipWith (\x y -> (x, y == len)) files [1..len]- S.forM_ fs $ \(file, lastFile) -> do- e <- get exit- unless e $ do- h <- S.lift $ openFile file ReadMode- set curFile (h, lastFile)- a <- get ast- loop a+ if null files then processFile stdin True+ else do+ let len = length files+ let fs = zipWith (\x y -> (x, y == len)) files [1..len]+ S.forM_ fs $ \(file, lastFile) -> do+ h <- S.liftIO $ openFile file ReadMode+ processFile h lastFile+ where+ processFile h lastFile = do+ S.lift $ set curFile (h, lastFile)+ nextLine --- | Cyclically append a line of input without newline into the pattern space-loop :: [SedCmd] -> SedState ()-loop cs = do- (res, str) <- line - case res of- EOF -> get curFile >>= \(h,_) -> S.lift $ hClose h >> return ()- _ -> do- set patternSpace str- set appendSpace []- sch <- eval cs- case sch of - Exit -> return ()- _ -> loop cs+-- | Process the next input line from the file+nextLine :: SedEngine ()+nextLine = do+ (res, str) <- S.lift line+ case res of+ EOF -> return ()+ Cont -> do+ S.lift $ set patternSpace str+ S.lift $ set appendSpace []+ cs <- S.lift $ get ast+ execCmds cs + nextLine +-- | Execute sed script+execCmds :: [SedCmd] -> SedEngine ()+execCmds cs = do+ forM_ cs $ \cmd -> do+ sch <- S.lift $ execCmd cmd+ case sch of+ Next -> return ()+ Break -> goto nextLine+ Continue -> goto (execCmds cs >> nextLine)+ Goto lbl -> (S.lift $ get ast) >>= \a -> goto (execCmds (jump a lbl) >> nextLine)+ Exit -> prnPat >> goto (return ())+ prnPat+ where prnPat = S.lift $ printPatSpace >> get appendSpace >>= \a -> mapM_ prnStr a++-- | Transfer control to the command marked with the label+jump :: [SedCmd] -> Maybe Label -> [SedCmd] +jump cmds = maybe [] (go cmds)+ where + go [] _ = []+ go (SedCmd _ fun:cs) str = case fun of+ Group cs' -> go cs' str+ Label x -> if x == str then cs+ else go cs str+ _ -> go cs str + -- | Read an input line line :: SedState (Status, B.ByteString) line = do@@ -111,42 +128,9 @@ modify curLine (+1) isLast <- if h == stdin then return False else S.lift (hIsEOF h) >>= \eof -> return eof- if isLast && b then get curLine >>= \l -> set lastLine l >> return (Cont, str)- else return (Cont, str)---- | Manage the flow control of the Sed commands-eval :: [SedCmd] -> SedState FlowControl-eval cs = do- sch <- execCmds cs- case sch of- Goto lbl -> get ast >>= \as -> eval (goto as lbl)- Exit -> printPatSpace >> get appendSpace >>= \a -> - mapM_ prnStr a >> set exit True >> return Exit- _ -> get appendSpace >>= \a -> mapM_ prnStr a >> return sch---- | Transfer control to the command marked with the label-goto :: [SedCmd] -> Maybe Label -> [SedCmd] -goto cmds = maybe [] (go cmds)- where - go [] _ = []- go (SedCmd _ fun:cs) str = case fun of- Group cs' -> go cs' str- Label x -> if x == str then cs- else go cs str- _ -> go cs str ---- | Execute Sed commands-execCmds :: [SedCmd] -> SedState FlowControl-execCmds [] = printPatSpace >> return Next-execCmds (x:xs) = do- sch <- execCmd x- if sch `elem` [Next] then execCmds xs- else if sch == Continue then do- cs <- get ast- execCmds cs- else return sch+ else return (Cont, str) -- | Execute the Sed function if the address is matched execCmd :: SedCmd -> SedState FlowControl@@ -155,7 +139,6 @@ if b then runCmd fun else return Next - -- | Check if the address interval is matched matchAddress :: Address -> SedState Bool matchAddress (Address addr1 addr2 invert) = @@ -240,7 +223,7 @@ -- the next input line append :: B.ByteString -> SedState FlowControl append txt = - modify appendSpace (++ [txt]) >> + modify appendSpace (++ [txt,B.pack "\n"]) >> return Next -- | 'b label' Transfer control to :label elsewhere in script
tests/Append.ok view
@@ -1,5 +1,6 @@ 789-~-123456-~+~~+123+456+~~ 123
tests/Append.sed view
@@ -1,3 +1,3 @@ a\-~\+~~\ 123