packages feed

lexer-applicative (empty) → 1.0

raw patch · 6 files changed

+171/−0 lines, 6 filesdep +basedep +lexer-applicativedep +regex-applicativesetup-changed

Dependencies added: base, lexer-applicative, regex-applicative, srcloc, tasty, tasty-hunit

Files

+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2015 Roman Cheplyaka++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.
+ README.md view
@@ -0,0 +1,6 @@+Simple applicative lexer based on the article+[Lexical analysis with parser combinators][1]+and the [regex-applicative][2] library.++[1]: https://ro-che.info/articles/2015-01-02-lexical-analysis>+[2]: http://hackage.haskell.org/package/regex-applicative-0.3.1/docs/Text-Regex-Applicative.html
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ lexer-applicative.cabal view
@@ -0,0 +1,51 @@+-- Initial lexer-applicative.cabal generated by cabal init.  For further +-- documentation, see http://haskell.org/cabal/users-guide/++name:                lexer-applicative+version:             1.0+synopsis:            Simple lexer based on applicative regular expressions+description:         Simple lexer based on applicative regular expressions+homepage:            https://github.com/feuerbach/lexer-applicative+license:             MIT+license-file:        LICENSE+author:              Roman Cheplyaka <roma@ro-che.info>+maintainer:          Roman Cheplyaka <roma@ro-che.info>+-- copyright:           +category:            Language+build-type:          Simple+extra-source-files:+  README.md+cabal-version:       >=1.10++Source-repository head+  type:     git+  location: git://github.com/feuerbach/lexer-applicative.git++library+  exposed-modules:+    Language.Lexer.Applicative+  -- other-modules:       +  -- other-extensions:    +  build-depends:+    base >=4.5 && < 5,+    srcloc,+    regex-applicative >= 0.3.1+  hs-source-dirs:      src+  default-language:    Haskell2010++test-suite test+  default-language:+    Haskell2010+  type:+    exitcode-stdio-1.0+  hs-source-dirs:+    tests+  main-is:+    test.hs+  build-depends:+      base >= 4 && < 5+    , tasty >= 0.10+    , tasty-hunit >= 0.9+    , regex-applicative+    , lexer-applicative+    , srcloc
+ src/Language/Lexer/Applicative.hs view
@@ -0,0 +1,69 @@+{-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable #-}+-- | For an example, see+-- <https://ro-che.info/articles/2015-01-02-lexical-analysis>+module Language.Lexer.Applicative (tokens, LexicalError(..)) where++import Text.Regex.Applicative+import Data.Loc+import Data.List+import Data.Typeable (Typeable)+import Control.Exception++annotate+  :: String -- ^ source file name+  -> String -- ^ contents+  -> [(Char, Pos, Pos)] -- ^ the character, its position, and the previous position+annotate src s = snd $ mapAccumL f (startPos src, startPos src) s+  where+    f (pos, prev_pos) ch =+      let pos' = advancePos pos ch+      in pos' `seq` ((pos', pos), (ch, pos, prev_pos))++-- | The lexical error exception+data LexicalError = LexicalError !Pos+  deriving Typeable++instance Show LexicalError where+  show (LexicalError pos) = "Lexical error at " ++ displayPos pos+instance Exception LexicalError++-- | The lexer.+--+-- In case of a lexical error, throws the 'LexicalError' exception.+-- This may seem impure compared to using 'Either', but it allows to+-- consume the token list lazily.+tokens+  :: forall token.+     RE Char token -- ^ regular expression for tokens+  -> RE Char () -- ^ regular expression for whitespace and comments+  -> String -- ^ source file name (used in locations)+  -> String -- ^ source text+  -> [L token]+tokens pToken pJunk src = go . annotate src+  where+  go l = case l of+    [] -> []+    s@((_, pos1, _):_) ->+      case findLongestPrefix re s of+        Just (Just tok, rest) ->+          let+            pos2 =+              case rest of+                (_, _, p):_ -> p+                [] -> case last s of (_, p, _) -> p++          in L (Loc pos1 pos2) tok : go rest++        Just (Nothing, rest) -> go rest++        Nothing -> throw $ LexicalError pos1++  re :: RE (Char, Pos, Pos) (Maybe token)+  re = comap (\(c, _, _) -> c) $ (Just <$> pToken) <|> (Nothing <$ pJunk)++-- | Format a position+displayPos :: Pos -> String+displayPos (Pos src line col _) =+    src ++ (colon . shows line . colon . shows (col+1)) ""+  where+    colon = (':' :)
+ tests/test.hs view
@@ -0,0 +1,23 @@+import Test.Tasty+import Test.Tasty.HUnit++import Language.Lexer.Applicative+import Text.Regex.Applicative+import Text.Regex.Applicative.Common+import Data.Char+import Data.Loc (L(..), Loc(..), Pos(..))++whitespace = () <$ some (psym isSpace)++unloc (L l a) = (a, l)++main = defaultMain $ testGroup "Tests"+  [ testCase "Empty string" $+      tokens (empty :: RE Char Int) empty "-" "" @=? []+  , testCase "Space- and newline-separated numbers" $+      unloc <$> tokens decimal whitespace "-" "1\n 23  456" @=?+      [ (1,  Loc (Pos "-" 1 0 0) (Pos "-" 1 0 0))+      , (23, Loc (Pos "-" 2 1 3) (Pos "-" 2 2 4))+      , (456,Loc (Pos "-" 2 5 7) (Pos "-" 2 7 9))+      ]+  ]