packages feed

morte 1.3.1 → 1.4.0

raw patch · 6 files changed

+225/−171 lines, 6 filesdep +EarleyPVP ok

version bump matches the API change (PVP)

Dependencies added: Earley

API changes (from Hackage documentation)

- Morte.Parser: instance Control.Monad.Trans.Error.Error Morte.Parser.ParseMessage
+ Morte.Core: instance Data.Binary.Class.Binary Morte.Core.X
+ Morte.Lexer: LocatedToken :: !Token -> {-# UNPACK #-} !Position -> LocatedToken
+ Morte.Lexer: [position] :: LocatedToken -> {-# UNPACK #-} !Position
+ Morte.Lexer: [token] :: LocatedToken -> !Token
+ Morte.Lexer: data LocatedToken
+ Morte.Lexer: instance GHC.Classes.Eq Morte.Lexer.Token
+ Morte.Lexer: instance GHC.Show.Show Morte.Lexer.LocatedToken
+ Morte.Parser: instance Control.Monad.Trans.Error.Error Morte.Parser.ParseError
- Morte.Lexer: lexExpr :: Text -> Producer Token (State Position) (Maybe Text)
+ Morte.Lexer: lexExpr :: Text -> Producer LocatedToken (State Position) (Maybe Text)
- Morte.Parser: Parsing :: Token -> ParseMessage
+ Morte.Parser: Parsing :: Token -> [Token] -> ParseMessage

Files

exec/Main.hs view
@@ -22,8 +22,8 @@         <>  header "morte - A bare-bones calculus of constructions"         <>  progDesc "Type-check and normalize a Morte program, reading the \                      \program from standard input, writing the program's type \-                     \to standard error, and writing the normalized program to\-                     \standard output"+                     \to standard error, and writing the normalized program to \+                     \standard output."         )     inText   <- Text.getContents     expr     <- throws (exprFromText inText)
morte.cabal view
@@ -1,7 +1,8 @@ Name: morte-Version: 1.3.1+Version: 1.4.0 Cabal-Version: >=1.8.0.2 Build-Type: Simple+Tested-With: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.2 License: BSD3 License-File: LICENSE Copyright: 2014 Gabriel Gonzalez@@ -39,6 +40,7 @@         binary             >= 0.7.0.0  && < 0.8  ,         containers         >= 0.5.0.0  && < 0.6  ,         deepseq            >= 1.3.0    && < 1.5  ,+        Earley             >= 0.10.1.0 && < 0.11 ,         http-client        >= 0.4.0    && < 0.5  ,         http-client-tls    >= 0.2.0    && < 0.3  ,         microlens          >= 0.2.0.0  && < 0.4  ,@@ -56,7 +58,7 @@         Morte.Lexer,         Morte.Parser,         Morte.Tutorial-    Build-Tools: alex, happy+    Build-Tools: alex     GHC-Options: -O2  Executable morte
src/Morte/Core.hs view
@@ -71,6 +71,7 @@ import Control.Applicative (Applicative(pure, (<*>)), (<$>)) import Control.DeepSeq (NFData(..)) import Control.Exception (Exception)+import Control.Monad (mzero) import Control.Monad.Trans.State (evalState) import qualified Control.Monad.Trans.State as State import Data.Binary (Binary(..), Get, Put)@@ -224,6 +225,10 @@  instance Buildable X where     build = absurd++instance Binary X where+    get = mzero+    put = absurd  -- | Syntax tree for expressions data Expr a
src/Morte/Lexer.x view
@@ -8,10 +8,11 @@      -- * Types     Token(..),-    Position(..)+    Position(..),+    LocatedToken(..)     ) where -import Control.Monad.Trans.State.Strict (State)+import Control.Monad.Trans.State.Strict (State, get) import Data.Bits (shiftR, (.&.)) import Data.Char (digitToInt) import Data.Text.Lazy (Text)@@ -20,7 +21,7 @@ import Filesystem.Path.CurrentOS (FilePath) import qualified Filesystem.Path.CurrentOS as Filesystem import Lens.Micro.Mtl ((.=), (+=))-import Pipes (Producer, lift, yield)+import Pipes (Producer, for, lift, yield) import Prelude hiding (FilePath)  }@@ -133,9 +134,13 @@     `lexExpr` keeps track of position and returns the remainder of the input if     lexing fails. -}-lexExpr :: Text -> Producer Token (State Position) (Maybe Text)-lexExpr text = go (AlexInput '\n' [] text)+lexExpr :: Text -> Producer LocatedToken (State Position) (Maybe Text)+lexExpr text = for (go (AlexInput '\n' [] text)) tag   where+    tag token = do+        pos <- lift get+        yield (LocatedToken token pos)+     go input = case alexScan input 0 of         AlexEOF                        -> return Nothing         AlexError (AlexInput _ _ text) -> return (Just text)@@ -147,6 +152,11 @@             lift (column += len)             go input' +data LocatedToken = LocatedToken+    { token    ::                !Token+    , position :: {-# UNPACK #-} !Position+    } deriving (Show)+ -- | Token type, used to communicate between the lexer and parser data Token     = OpenParen@@ -163,5 +173,5 @@     | File FilePath     | URL String     | EOF-    deriving (Show)+    deriving (Eq, Show) }
+ src/Morte/Parser.hs view
@@ -0,0 +1,198 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecursiveDo       #-}++module Morte.Parser (+    -- * Parser+    exprFromText,++    -- * Errors+    ParseError(..),+    ParseMessage(..)+    ) where++import Control.Applicative hiding (Const)+import Control.Exception (Exception)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Error (Error(..), throwError, runErrorT)+import Control.Monad.Trans.State.Strict (evalState, get)+import Data.Monoid+import Data.Text.Buildable (Buildable(..))+import Data.Text.Lazy (Text)+import Data.Text.Lazy.Builder (toLazyText)+import Data.Typeable (Typeable)+import Filesystem.Path.CurrentOS (FilePath)+import Morte.Core (Var(..), Const(..), Path(..), Expr(..))+import Morte.Lexer (LocatedToken(..), Position(..), Token)+import Prelude hiding (FilePath)+import Text.Earley++import qualified Morte.Lexer    as Lexer+import qualified Pipes.Prelude  as Pipes+import qualified Data.Text.Lazy as Text++match :: Token -> Prod r Token LocatedToken Token+match t = fmap Lexer.token (satisfy predicate) <?> t+  where+    predicate (LocatedToken t' _) = t == t'++label :: Prod r e LocatedToken Text+label = fmap unsafeFromLabel (satisfy isLabel)+  where+    isLabel (LocatedToken (Lexer.Label _) _) = True+    isLabel  _                               = False++    unsafeFromLabel (LocatedToken (Lexer.Label l) _) = l++number :: Prod r e LocatedToken Int+number = fmap unsafeFromNumber (satisfy isNumber)+  where+    isNumber (LocatedToken (Lexer.Number _) _) = True+    isNumber  _                                = False++    unsafeFromNumber (LocatedToken (Lexer.Number n) _) = n++file :: Prod r e LocatedToken FilePath+file = fmap unsafeFromFile (satisfy isFile)+  where+    isFile (LocatedToken (Lexer.File _) _) = True+    isFile  _                              = False++    unsafeFromFile (LocatedToken (Lexer.File n) _) = n++url :: Prod r e LocatedToken String+url = fmap unsafeFromURL (satisfy isURL)+  where+    isURL (LocatedToken (Lexer.URL _) _) = True+    isURL  _                             = False++    unsafeFromURL (LocatedToken (Lexer.URL n) _) = n++expr :: Grammar r (Prod r Token LocatedToken (Expr Path))+expr = mdo+    expr <- rule+        (   bexpr+        <|> (   Lam+            <$> (match Lexer.Lambda *> match Lexer.OpenParen *> label)+            <*> (match Lexer.Colon *> expr)+            <*> (match Lexer.CloseParen *> match Lexer.Arrow *> expr)+            )+        <|> (   Pi+            <$> (match Lexer.Pi *> match Lexer.OpenParen *> label)+            <*> (match Lexer.Colon *> expr)+            <*> (match Lexer.CloseParen *> match Lexer.Arrow *> expr)+            )+        <|> (   Pi "_"+            <$> bexpr+            <*> (match Lexer.Arrow *> expr)+            )+        )+    vexpr <- rule+        (   (   V+            <$> label+            <*> (match Lexer.At *> number)+            )+        <|> (   V+            <$> label+            <*> pure 0+            )+        )+    bexpr <- rule+        (   (   App+            <$> bexpr+            <*> aexpr+            )+        <|> aexpr+        )+    aexpr <- rule+        (   (   Var+            <$> vexpr+            )+        <|> (   match Lexer.Star *> pure (Const Star)+            )+        <|> (   match Lexer.Box  *> pure (Const Box)+            )+        <|> (   Embed+            <$> import_+            )+        <|> (   match Lexer.OpenParen *> expr <* match Lexer.CloseParen+            )+        )++    import_ <- rule+        (   (   File+            <$> file+            )+        <|> (   URL+            <$> url+            )+        )++    return expr++-- | The specific parsing error+data ParseMessage+    -- | Lexing failed, returning the remainder of the text+    = Lexing Text+    -- | Parsing failed, returning the invalid token and the expected tokens+    | Parsing Token [Token]+    deriving (Show)++-- | Structured type for parsing errors+data ParseError = ParseError+    { position     :: Position+    , parseMessage :: ParseMessage+    } deriving (Typeable)++instance Show ParseError where+    show = Text.unpack . toLazyText . build++instance Exception ParseError++instance Buildable ParseError where+    build (ParseError (Lexer.P l c) e) =+            "\n"+        <>  "Line:   " <> build l <> "\n"+        <>  "Column: " <> build c <> "\n"+        <>  "\n"+        <>  case e of+            Lexing r                                     ->+                    "Lexing: \"" <> build remainder <> dots <> "\"\n"+                <>  "\n"+                <>  "Error: Lexing failed\n"+              where+                remainder = Text.takeWhile (/= '\n') (Text.take 64 r)+                dots      = if Text.length r > 64 then "..." else mempty+            Parsing t ts ->+                    "Parsing : " <> build (show t ) <> "\n"+                <>  "Expected: " <> build (show ts) <> "\n"+                <>  "\n"+                <>  "Error: Parsing failed\n"++{- This is purely to satisfy the unnecessary `Error` constraint for `ErrorT`++    I will switch to `ExceptT` when the Haskell Platform incorporates+    `transformers-0.4.*`.+-}+instance Error ParseError where++-- | Parse an `Expr` from `Text` or return a `ParseError` if parsing fails+exprFromText :: Text -> Either ParseError (Expr Path)+exprFromText text = evalState (runErrorT m) (Lexer.P 1 0)+  where+    m = do+        (locatedTokens, mtxt) <- lift (Pipes.toListM' (Lexer.lexExpr text))+        case mtxt of+            Nothing  -> return ()+            Just txt -> do+                pos <- lift get+                throwError (ParseError pos (Lexing txt))+        let (parses, Report _ needed found) =+                fullParses (parser expr) locatedTokens+        case parses of+            parse:_ -> return parse+            []      -> do+                let LocatedToken t pos = case found of+                        lt:_ -> lt+                        _    -> LocatedToken Lexer.EOF (P 0 0)+                throwError (ParseError pos (Parsing t needed))
− src/Morte/Parser.y
@@ -1,161 +0,0 @@-{-{-# LANGUAGE OverloadedStrings  #-}-{-# LANGUAGE DeriveDataTypeable #-}---- | Parsing logic for the Morte language--module Morte.Parser (-    -- * Parser-    exprFromText,--    -- * Errors-    ParseError(..),-    ParseMessage(..)-    ) where--import Control.Exception (Exception)-import Control.Monad.Trans.Error (ErrorT, Error(..), throwError, runErrorT)-import Control.Monad.Trans.State.Strict (State, runState)-import Data.Functor.Identity (Identity, runIdentity)-import Data.Monoid (mempty, (<>))-import Data.Text.Buildable (Buildable(..))-import Data.Text.Lazy (Text)-import qualified Data.Text.Lazy as Text-import Data.Text.Lazy.Builder (toLazyText)-import Data.Typeable (Typeable)-import Lens.Micro (_1, _2)-import Lens.Micro.Mtl ((.=), use, zoom)-import Morte.Core (Var(..), Const(..), Path(..), Expr(..))-import qualified Morte.Lexer as Lexer-import Morte.Lexer (Token, Position)-import Pipes (Producer, hoist, lift, next)--}--%name parseExpr-%tokentype { Token }-%monad { Lex }-%lexer { lexer } { Lexer.EOF }-%error { parseError }--%token-    '('    { Lexer.OpenParen  }-    ')'    { Lexer.CloseParen }-    ':'    { Lexer.Colon      }-    '*'    { Lexer.Star       }-    '@'    { Lexer.At         }-    'BOX'  { Lexer.Box        }-    '->'   { Lexer.Arrow      }-    '\\'   { Lexer.Lambda     }-    '|~|'  { Lexer.Pi         }-    label  { Lexer.Label $$   }-    number { Lexer.Number $$  }-    file   { Lexer.File $$    }-    url    { Lexer.URL $$     }--%%--Expr :: { Expr Path }-    : BExpr                                   { $1           }-    | '\\'  '(' label ':' Expr ')' '->' Expr  { Lam $3 $5 $8 }-    | '|~|' '(' label ':' Expr ')' '->' Expr  { Pi  $3 $5 $8 }-    | BExpr '->' Expr                         { Pi "_" $1 $3 }--VExpr :: { Var }-    : label '@' number                        { V $1 $3      }-    | label                                   { V $1 0       }--BExpr :: { Expr Path }-    : BExpr AExpr                             { App $1 $2    }-    | AExpr                                   { $1           }--AExpr :: { Expr Path }-    : VExpr                                   { Var $1       }-    | '*'                                     { Const Star   }-    | 'BOX'                                   { Const Box    }-    | Import                                  { Embed $1     }-    | '(' Expr ')'                            { $2           }--Import :: { Path }-    : file                                    { File $1      }-    | url                                     { URL  $1      }--{--- | The specific parsing error-data ParseMessage-    -- | Lexing failed, returning the remainder of the text-    = Lexing Text-    -- | Parsing failed, returning the invalid token-    | Parsing Token-    deriving (Show)--{- This is purely to satisfy the unnecessary `Error` constraint for `ErrorT`--    I will switch to `ExceptT` when the Haskell Platform incorporates-    `transformers-0.4.*`.--}-instance Error ParseMessage where--type Status = (Position, Producer Token (State Position) (Maybe Text))--type Lex = ErrorT ParseMessage (State Status)---- To avoid an explicit @mmorph@ dependency-generalize :: Monad m => Identity a -> m a-generalize = return . runIdentity--lexer :: (Token -> Lex a) -> Lex a-lexer k = do-    x <- lift (do-        p <- use _2-        hoist generalize (zoom _1 (next p)) )-    case x of-        Left ml           -> case ml of-            Nothing -> k Lexer.EOF-            Just le -> throwError (Lexing le)-        Right (token, p') -> do-            lift (_2 .= p')-            k token--parseError :: Token -> Lex a-parseError token = throwError (Parsing token)---- | Parse an `Expr` from `Text` or return a `ParseError` if parsing fails-exprFromText :: Text -> Either ParseError (Expr Path)-exprFromText text = case runState (runErrorT parseExpr) initialStatus of-    (x, (position, _)) -> case x of-        Left  e    -> Left (ParseError position e)-        Right expr -> Right expr-  where-    initialStatus = (Lexer.P 1 0, Lexer.lexExpr text)---- | Structured type for parsing errors-data ParseError = ParseError-    { position     :: Position-    , parseMessage :: ParseMessage-    } deriving (Typeable)--instance Show ParseError where-    show = Text.unpack . toLazyText . build--instance Exception ParseError--instance Buildable ParseError where-    build (ParseError (Lexer.P l c) e) =-            "\n"-        <>  "Line:   " <> build l <> "\n"-        <>  "Column: " <> build c <> "\n"-        <>  "\n"-        <>  case e of-            Lexing r  ->-                    "Lexing: \"" <> build remainder <> dots <> "\"\n"-                <>  "\n"-                <>  "Error: Lexing failed\n"-              where-                remainder = Text.takeWhile (/= '\n') (Text.take 64 r)-                dots      = if Text.length r > 64 then "..." else mempty-            Parsing t ->-                    "Parsing: " <> build (show t) <> "\n"-                <>  "\n"-                <>  "Error: Parsing failed\n"-}