module Parser where import Text.ParserCombinators.Parsec.Token import Text.ParserCombinators.Parsec import Text.ParserCombinators.Parsec.Language import Text.ParserCombinators.Parsec.Error import Editor import Offset -- | shortcut for a parser of chars with no state type ParseE = GenParser Char () -- | parse an integer number numero :: ParseE Integer numero = natural haskell parseFilename = manyTill anyChar ((many1 space >> return ()) <|> eof) parseExternalCommand = char '!' >> manyTill anyChar eof parseCommandName = parseFilename -- | parse an Offset parseOffset :: ParseE Offset parseOffset = let lastline = char '$' >> return LastLine absolute = numero >>= return . Absolute . fromInteger current = char '.' >> return Current previous1 = char '-' >> numero >>= return . Prev. fromInteger previous2 = many1 (char '-') >>= return . Prev . length next1 = (space <|> char '+') >> numero >>= return . Next . fromInteger next2 = many1 (char '+') >>= return . Next . length are c = char c >> manyTill anyChar ((char c >> return ()) <|> eof) renext = are '/' >>= return . ReNext lastrenext = string "//" >> return LastReNext reprev = are '?' >>= return . RePrev lastreprev = string "??" >> return LastRePrev markedas = string "'" >> lower >>= return . MarkedAs in choice (map try [lastline,absolute,current,previous1,previous2,next1,next2, lastrenext,renext,lastreprev,reprev,markedas]) -- | parse a Range parseRange :: ParseE Range parseRange = let couple = do l <- parseOffset char ',' r <- parseOffset return $ Range l r coma = char ',' >> return (Range (Absolute 1) LastLine) semicoma = char ';' >> return (Range Current LastLine) in choice (map try [coma,semicoma,couple]) -- | defaults Offset or Range for the commands defaultOR :: Command -> OffsetOrRange defaultOR Append = ORO Current defaultOR Insert = ORO Current defaultOR Change = ORO Current defaultOR Print = ORO Current defaultOR (SmallG _) = ORR (Range (Absolute 1) (Current)) defaultOR (BigG _) = ORR (Range (Absolute 1) (Current)) defaultOR Delete = ORO Current defaultOR NoCommand = ORN defaultOR (Edit _) = ORN defaultOR Write = ORN defaultOR (WriteNew _) = ORN defaultOR (SetFilename _) = ORN defaultOR GetFilename = ORN defaultOR (EditExternal s) = ORN defaultOR UndoChange = ORN defaultOR RedoChange = ORN -- | forces a failure for a command if a Range was parsed acceptOffsetOnly :: Command -> OffsetOrRange -> ParseE () acceptOffsetOnly c (ORR _) = pzero ("only offsets for function " ++ show c ++ ".") acceptOffsetOnly _ _ = return () -- | parse an OffsetOrRange parseOffsetOrRange :: ParseE OffsetOrRange parseOffsetOrRange = try (parseRange >>= return . ORR) <|> try (parseOffset >>= return .ORO) <|> return ORN -- | helper for skipping a filter rconst :: Command -> ParseE (OffsetOrRange -> ParseE Command) rconst = return . const . return -- | parse a function from OffsetOrRange to a parse Command parseCommand :: ParseE (OffsetOrRange -> ParseE Command) parseCommand = let append = char 'a' >> eof >> return (\r -> acceptOffsetOnly Append r >> return Append) insert = char 'i' >> eof >> return (\r -> acceptOffsetOnly Insert r >> return Insert) change = char 'c' >> eof >> rconst Change delete = char 'd' >> eof >> rconst Delete print = char 'p' >> eof >> rconst Print smallg = char 'g' >> char '/' >> many1 (noneOf "/") >>= \p -> char '/' >> eof >> rconst (SmallG p) bigg = char 'G' >> char '/' >> many1 (noneOf "/") >>= \p -> char '/' >> eof >> rconst (BigG p) nocomm = eof >> rconst NoCommand extedit = char 'e' >> many1 space >> parseExternalCommand >>= rconst . EditExternal edit = char 'e' >> many1 space >> parseFilename >>= rconst . Edit writen = char 'w' >> many1 space >> parseFilename >>= rconst . WriteNew write = char 'w' >> rconst Write setfn = char 'f' >> many1 space >> parseFilename >>= rconst . SetFilename getfn = char 'f' >> rconst GetFilename undo = char 'u' >> rconst UndoChange redo = char 'R' >> rconst RedoChange shelp = string "he" >> many1 space >> parseCommandName >>= rconst . HelpTopic help = string "he" >> rconst HelpList in choice (map try [append,insert,change,delete, print,smallg,bigg,extedit,edit ,writen,write,setfn,getfn,undo, shelp,help,redo]) <|> nocomm -- | parse a CompleteCommand made of an OffsetOrRange and a Command parser :: ParseE CompleteCommand parser = do r <- parseOffsetOrRange c <- parseCommand >>= ($ r) return $ CC c $ case r of ORN -> defaultOR c _ -> r -- | the parser from a String to either a String representing an error or a CompleteCommand parse :: String -> Either String CompleteCommand parse s = either (Left . show) Right $ Text.ParserCombinators.Parsec.parse parser "Command Parser" s