1
Fork 0

Automatic formatting.

main
Joshua Potter 2022-01-12 06:51:56 -05:00
parent e67d2d8903
commit caac19270e
11 changed files with 377 additions and 327 deletions

13
.githooks/pre-commit Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
set -e
filesToFormat=$(
git --no-pager diff --name-status --no-color --cached | \
awk '$1 != "D" && $2 ~ /\.hs/ {print $NF}'
)
for path in $filesToFormat
do
ormolu --mode inplace $path
git add $path
done;

View File

@ -34,3 +34,11 @@ $ direnv allow
from the root directory, `nix develop` will be automatically invoked each time
a change is detected in `flake.nix` or you return to the directory.
## Formatting
Link in `.githooks` by running:
```bash
$ git config --local core.hooksPath .githooks/
```

View File

@ -25,6 +25,7 @@
# https://www.reddit.com/r/Nix/comments/jyczts/nixshell_locale_issue/
pkgs.glibcLocales
pkgs.haskellPackages.cabal-install
pkgs.haskellPackages.ormolu
pkgs.haskellPackages.tasty-discover
];
};

View File

@ -2,16 +2,15 @@
module Main where
import qualified Options.Applicative as O
import qualified Text.Megaparsec as M
import Data.Text (Text)
import Data.Text.IO (hGetContents)
import Options.Applicative ((<**>))
import qualified Options.Applicative as O
import Parser.Initial
import Parser.Utils
import System.Environment (getArgs)
import System.IO (IOMode (ReadMode), openFile)
import qualified Text.Megaparsec as M
-- ========================================
-- Arguments
@ -20,7 +19,8 @@ import System.IO (IOMode(ReadMode), openFile)
data Args = Args {argsFileName :: !FilePath, argsMethod :: !Text}
args :: O.Parser Args
args = Args
args =
Args
<$> O.strArgument
( O.metavar "FILENAME" <> O.help "The file we want to parse."
)
@ -30,7 +30,8 @@ args = Args
<> O.metavar "METHOD"
<> O.showDefault
<> O.value "naive"
<> O.help "The parse strategy we want to try. Should be one of 'naive', \
<> O.help
"The parse strategy we want to try. Should be one of 'naive', \
\'single', 'strict', or 'gadt'."
)
@ -56,7 +57,9 @@ run args = do
main :: IO ()
main = run =<< O.execParser opts
where
opts = O.info (args <**> O.helper)
opts =
O.info
(args <**> O.helper)
( O.fullDesc
<> O.progDesc "Different parsing strategies using initial encoding"
<> O.header "Initial encoding parsing"

View File

@ -6,30 +6,30 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Parser.Initial
( Expr(..)
, GExpr(..)
, Result(..)
, Wrapper(..)
, eval
, parseGadt
, parseNaive
, parseSingle
, parseStrict
, toResult
) where
import qualified Control.Monad.Combinators.Expr as E
import qualified Text.Megaparsec as M
( Expr (..),
GExpr (..),
Result (..),
Wrapper (..),
eval,
parseGadt,
parseNaive,
parseSingle,
parseStrict,
toResult,
)
where
import Control.Applicative ((<|>))
import Control.DeepSeq (NFData (..), deepseq)
import Control.Monad (join)
import qualified Control.Monad.Combinators.Expr as E
import Control.Monad.Except (MonadError, throwError)
import Data.Bifunctor (bimap, first)
import Data.Functor (void)
import Data.Maybe (isJust)
import Data.Text (Text, pack, unpack)
import Parser.Utils
import qualified Text.Megaparsec as M
-- ========================================
-- ADT
@ -85,9 +85,11 @@ toResult (EOr lhs rhs) = do
parseNaive :: Parser Result
parseNaive = expr >>= either (fail . unpack) pure . toResult
where
expr = E.makeExprParser term
[ [binary "+" EAdd, binary "-" ESub]
, [binary "&&" EAnd, binary "||" EOr]
expr =
E.makeExprParser
term
[ [binary "+" EAdd, binary "-" ESub],
[binary "&&" EAnd, binary "||" EOr]
]
binary name f = E.InfixL (f <$ symbol name)
@ -101,9 +103,11 @@ parseNaive = expr >>= either (fail . unpack) pure . toResult
parseSingle :: Parser Result
parseSingle = expr >>= either (fail . unpack) pure
where
expr = E.makeExprParser term
[ [binary "+" asInt EInt EAdd, binary "-" asInt EInt ESub]
, [binary "&&" asBool EBool EAnd, binary "||" asBool EBool EOr ]
expr =
E.makeExprParser
term
[ [binary "+" asInt EInt EAdd, binary "-" asInt EInt ESub],
[binary "&&" asBool EBool EAnd, binary "||" asBool EBool EOr]
]
binary name cast f bin = E.InfixL do
@ -135,13 +139,13 @@ parseStrict = term >>= expr
Just OpOr -> nest t asBool EBool EOr
_ -> pure t
nest
:: forall a
. Result
-> (Result -> Either Text a)
-> (a -> Expr)
-> (Expr -> Expr -> Expr)
-> Parser Result
nest ::
forall a.
Result ->
(Result -> Either Text a) ->
(a -> Expr) ->
(Expr -> Expr -> Expr) ->
Parser Result
nest t cast f bin = do
t' <- term
a <- either (fail . unpack) pure do
@ -152,8 +156,9 @@ parseStrict = term >>= expr
term = do
p <- M.option Nothing $ Just <$> symbol "("
if isJust p then (term >>= expr) <* symbol ")" else
RInt <$> integer <|> RBool <$> boolean
if isJust p
then (term >>= expr) <* symbol ")"
else RInt <$> integer <|> RBool <$> boolean
-- ========================================
-- GADTs
@ -201,14 +206,14 @@ parseGadt = term >>= expr
Just OpOr -> nest t asBool' GBool GOr
_ -> pure t
nest
:: forall b
. Show b
=> Wrapper
-> (forall a. GExpr a -> Either Text (GExpr b))
-> (b -> GExpr b)
-> (GExpr b -> GExpr b -> GExpr b)
-> Parser Wrapper
nest ::
forall b.
Show b =>
Wrapper ->
(forall a. GExpr a -> Either Text (GExpr b)) ->
(b -> GExpr b) ->
(GExpr b -> GExpr b -> GExpr b) ->
Parser Wrapper
nest (Wrapper t) cast f bin = do
Wrapper t' <- term
case (cast t, cast t') of
@ -220,5 +225,6 @@ parseGadt = term >>= expr
term = do
p <- M.option Nothing $ Just <$> symbol "("
if isJust p then (term >>= expr) <* symbol ")" else
Wrapper . GInt <$> integer <|> Wrapper . GBool <$> boolean
if isJust p
then (term >>= expr) <* symbol ")"
else Wrapper . GInt <$> integer <|> Wrapper . GBool <$> boolean

View File

@ -5,9 +5,8 @@
module Test.Parser.InitialTest
( spec_parser,
) where
import qualified Text.Megaparsec as M
)
where
import Data.Bifunctor (first)
import Data.Functor.Identity (Identity (..))
@ -15,6 +14,7 @@ import Data.Text (Text, pack)
import Parser.Initial
import Parser.Utils (Parser, allEqual, runParser)
import Test.Hspec (Expectation, Spec, describe, it, shouldBe)
import qualified Text.Megaparsec as M
-- ========================================
-- Utility
@ -30,11 +30,12 @@ convert (GOr lhs rhs) = EOr (convert lhs) (convert rhs)
runParsers :: Text -> [Either Text Result]
runParsers input =
[ runParser parseNaive
, runParser parseSingle
, runParser parseStrict
, runGadt
] <*> [input]
[ runParser parseNaive,
runParser parseSingle,
runParser parseStrict,
runGadt
]
<*> [input]
where
runGadt i = do
Wrapper res <- runParser parseGadt i
@ -84,7 +85,9 @@ spec_parser = do
shouldParse "false || false" (RBool False)
describe "invalid types" do
it "mismatch" do
shouldNotParse "true && 1"
shouldNotParse
"true && 1"
"1:10:\n |\n1 | true && 1\n | ^\nCould not cast boolean.\n"
shouldNotParse "1 + true"
shouldNotParse
"1 + true"
"1:9:\n |\n1 | 1 + true\n | ^\nCould not cast integer.\n"

View File

@ -3,20 +3,22 @@
module Main where
import Data.Eq.Type ((:=)(..), refl)
import Data.Eq.Type (refl, (:=) (..))
newtype F1 t b a = F1 {runF1 :: t := (a -> b)}
newtype F2 t a b = F2 {runF2 :: t := (a -> b)}
functionEquality
:: forall a1 a2 b1 b2
. a1 := a2
-> b1 := b2
-> (a1 -> b1) := (a2 -> b2)
functionEquality ::
forall a1 a2 b1 b2.
a1 := a2 ->
b1 := b2 ->
(a1 -> b1) := (a2 -> b2)
functionEquality
(Refl s1) -- s1 :: forall c. c a1 -> c a2
(Refl s2) -- s2 :: forall c. c b1 -> c b2
= runF2 -- (a1 -> b1) := (a2 -> b2)
=
runF2 -- (a1 -> b1) := (a2 -> b2)
. s2 -- F2 (a1 -> b1) a2 b2
. F2 -- F2 (a1 -> b1) a2 b1
. runF1 -- (a1 -> b1) := (a2 -> b1)

View File

@ -2,28 +2,28 @@
{-# LANGUAGE OverloadedStrings #-}
module Parser.Utils
( Op(..)
, Parser
, ParserT
, allEqual
, boolean
, integer
, lexeme
, ops
, parens
, runParser
, space
, symbol
) where
import qualified Text.Megaparsec as M
import qualified Text.Megaparsec.Char as MC
import qualified Text.Megaparsec.Char.Lexer as ML
( Op (..),
Parser,
ParserT,
allEqual,
boolean,
integer,
lexeme,
ops,
parens,
runParser,
space,
symbol,
)
where
import Control.Applicative ((<|>))
import Data.Functor (($>))
import Data.Text (Text, pack)
import Data.Void (Void)
import qualified Text.Megaparsec as M
import qualified Text.Megaparsec.Char as MC
import qualified Text.Megaparsec.Char.Lexer as ML
-- ========================================
-- Parsing
@ -66,11 +66,12 @@ instance Show Op where
show OpOr = "||"
ops :: forall m. ParserT m Op
ops = M.choice
[ symbol "+" $> OpAdd
, symbol "-" $> OpSub
, symbol "&&" $> OpAnd
, symbol "||" $> OpOr
ops =
M.choice
[ symbol "+" $> OpAdd,
symbol "-" $> OpSub,
symbol "&&" $> OpAnd,
symbol "||" $> OpOr
]
runParser :: forall a. Parser a -> Text -> Either Text a

View File

@ -3,11 +3,10 @@
module Main where
import qualified Options.Applicative as O
import Data.Text (Text)
import Data.Text.IO (hGetContents)
import Options.Applicative ((<**>))
import qualified Options.Applicative as O
import Parser.Final
import Parser.Utils
import System.Environment (getArgs)
@ -20,7 +19,8 @@ import System.IO (IOMode(ReadMode), openFile)
data Args = Args {argsFileName :: !FilePath, argsMethod :: !Text}
args :: O.Parser Args
args = Args
args =
Args
<$> O.strArgument
( O.metavar "FILENAME" <> O.help "The file we want to parse."
)
@ -30,7 +30,8 @@ args = Args
<> O.metavar "METHOD"
<> O.showDefault
<> O.value "single"
<> O.help "The parse strategy we want to try. Should be one of 'single' \
<> O.help
"The parse strategy we want to try. Should be one of 'single' \
\or 'strict'."
)
@ -59,7 +60,9 @@ run args = do
main :: IO ()
main = run =<< O.execParser opts
where
opts = O.info (args <**> O.helper)
opts =
O.info
(args <**> O.helper)
( O.fullDesc
<> O.progDesc "Different parsing strategies using initial encoding"
<> O.header "Initial encoding parsing"

View File

@ -11,32 +11,32 @@
{-# LANGUAGE ViewPatterns #-}
module Parser.Final
( Dynamic(..)
, Eval(..)
, SQ(..)
, Symantics(..)
, TQ(..)
, TextSymantics(..)
, Typeable(..)
, fromDyn
, parseSingle
, parseStrict
, toDyn
, runBoth'
) where
import qualified Control.Monad.Combinators.Expr as E
import qualified Data.Eq.Type as EQ
import qualified Text.Megaparsec as M
( Dynamic (..),
Eval (..),
SQ (..),
Symantics (..),
TQ (..),
TextSymantics (..),
Typeable (..),
fromDyn,
parseSingle,
parseStrict,
toDyn,
runBoth',
)
where
import Control.Applicative ((<|>))
import Control.DeepSeq (NFData (..), deepseq)
import Control.Monad.Combinators (sepBy)
import qualified Control.Monad.Combinators.Expr as E
import Data.Eq.Type ((:=))
import qualified Data.Eq.Type as EQ
import Data.Functor (void)
import Data.Maybe (isJust)
import Data.Text (Text, drop, dropEnd, pack, unpack)
import Parser.Utils
import qualified Text.Megaparsec as M
import Prelude hiding (drop)
-- ========================================
@ -141,14 +141,14 @@ fromDyn (Dynamic t e) = case t of
r' <- r
pure $ EQ.coerce (EQ.lift r') e
asDyn
:: forall repr a
. TextSymantics repr
=> IsDynamic a
=> (repr a -> repr a -> repr a)
-> Dynamic repr
-> Dynamic repr
-> Maybe (Dynamic repr)
asDyn ::
forall repr a.
TextSymantics repr =>
IsDynamic a =>
(repr a -> repr a -> repr a) ->
Dynamic repr ->
Dynamic repr ->
Maybe (Dynamic repr)
asDyn bin lhs rhs = do
lhs' <- fromDyn lhs
rhs' <- fromDyn rhs
@ -163,9 +163,11 @@ parseSingle = expr >>= either offsetFail pure
where
offsetFail (offset, msg) = M.setOffset offset >> fail msg
expr = E.makeExprParser term
[ [binary "+" eAdd, binary "-" eSub]
, [binary "&&" eAnd, binary "||" eOr]
expr =
E.makeExprParser
term
[ [binary "+" eAdd, binary "-" eSub],
[binary "&&" eAnd, binary "||" eOr]
]
binary name bin = E.InfixL do
@ -190,11 +192,11 @@ instance (NFData t) => NFData (Eval t) where
instance NFData (Dynamic Eval) where
rnf (Dynamic _ e) = e `seq` ()
parseStrict
:: forall repr
. NFData (Dynamic repr)
=> TextSymantics repr
=> Parser (Dynamic repr)
parseStrict ::
forall repr.
NFData (Dynamic repr) =>
TextSymantics repr =>
Parser (Dynamic repr)
parseStrict = term >>= expr
where
expr :: Dynamic repr -> Parser (Dynamic repr)
@ -207,13 +209,13 @@ parseStrict = term >>= expr
Just OpOr -> nest t eOr OpOr
_ -> pure t
nest
:: forall a
. IsDynamic a
=> Dynamic repr
-> (repr a -> repr a -> repr a)
-> Op
-> Parser (Dynamic repr)
nest ::
forall a.
IsDynamic a =>
Dynamic repr ->
(repr a -> repr a -> repr a) ->
Op ->
Parser (Dynamic repr)
nest t bin op = do
t' <- term
case asDyn bin t t' of
@ -223,8 +225,9 @@ parseStrict = term >>= expr
term :: Parser (Dynamic repr)
term = do
p <- M.option Nothing $ Just <$> symbol "("
if isJust p then (term >>= expr) <* symbol ")" else
toDyn <$> integer <|> toDyn <$> boolean
if isJust p
then (term >>= expr) <* symbol ")"
else toDyn <$> integer <|> toDyn <$> boolean
-- ========================================
-- Pretty print
@ -317,8 +320,10 @@ pPrint' d = case fromDyn @MSQ @Integer d of
data SCopy repr1 repr2 a = SCopy (repr1 a) (repr2 a)
instance (Symantics repr1, Symantics repr2)
=> Symantics (SCopy repr1 repr2) where
instance
(Symantics repr1, Symantics repr2) =>
Symantics (SCopy repr1 repr2)
where
eInt e = SCopy (eInt e) (eInt e)
eBool e = SCopy (eBool e) (eBool e)
eAdd (SCopy a1 a2) (SCopy b1 b2) = SCopy (eAdd a1 b1) (eAdd a2 b2)
@ -326,32 +331,34 @@ instance (Symantics repr1, Symantics repr2)
eAnd (SCopy a1 a2) (SCopy b1 b2) = SCopy (eAnd a1 b1) (eAnd a2 b2)
eOr (SCopy a1 a2) (SCopy b1 b2) = SCopy (eOr a1 b1) (eOr a2 b2)
instance (MulSymantics repr1, MulSymantics repr2)
=> MulSymantics (SCopy repr1 repr2) where
instance
(MulSymantics repr1, MulSymantics repr2) =>
MulSymantics (SCopy repr1 repr2)
where
eMul (SCopy a1 a2) (SCopy b1 b2) = SCopy (eMul a1 b1) (eMul a2 b2)
runEval'
:: forall repr
. Dynamic (SCopy Eval repr)
-> Maybe (Result, Dynamic repr)
runEval' ::
forall repr.
Dynamic (SCopy Eval repr) ->
Maybe (Result, Dynamic repr)
runEval' d = case fromDyn d :: Maybe (SCopy Eval repr Integer) of
Just (SCopy (Eval a) r) -> pure (RInt a, Dynamic pInt r)
Nothing -> case fromDyn d :: Maybe (SCopy Eval repr Bool) of
Just (SCopy (Eval a) r) -> pure (RBool a, Dynamic pBool r)
Nothing -> Nothing
runPPrint'
:: forall repr
. Dynamic (SCopy PPrint repr)
-> Maybe (Text, Dynamic repr)
runPPrint' ::
forall repr.
Dynamic (SCopy PPrint repr) ->
Maybe (Text, Dynamic repr)
runPPrint' d = case fromDyn d :: Maybe (SCopy PPrint repr Text) of
Just (SCopy (PPrint a) r) -> pure (a, Dynamic pText r)
Nothing -> Nothing
runBoth'
:: forall repr
. Dynamic (SCopy Eval (SCopy PPrint repr))
-> Maybe (Result, Text, Dynamic repr)
runBoth' ::
forall repr.
Dynamic (SCopy Eval (SCopy PPrint repr)) ->
Maybe (Result, Text, Dynamic repr)
runBoth' d = do
(r, d') <- runEval' d
(p, d'') <- runPPrint' d'

View File

@ -6,7 +6,8 @@
module Test.Parser.FinalTest
( spec_parser,
) where
)
where
import Data.Text (Text)
import Parser.Final
@ -78,7 +79,9 @@ spec_parser = do
shouldParse "false || false" (toDyn False)
describe "invalid types" do
it "mismatch" do
shouldNotParse "true && 1"
shouldNotParse
"true && 1"
"1:9:\n |\n1 | true && 1\n | ^\nInvalid operands for `&&`\n"
shouldNotParse "1 + true"
shouldNotParse
"1 + true"
"1:5:\n |\n1 | 1 + true\n | ^\nInvalid operands for `+`\n"