Skip to content

Commit ffa0b2e

Browse files
committed
Improve CLI and add file running and formatting (fix axellang#47)
1 parent be19555 commit ffa0b2e

File tree

13 files changed

+178
-110
lines changed

13 files changed

+178
-110
lines changed

.hindent.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ extensions:
1717
- KindSignatures
1818
- LambdaCase
1919
- MultiParamTypeClasses
20+
- MultiWayIf
2021
- OverloadedStrings
2122
- PolyKinds
2223
- RankNTypes

.hlint.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- -XKindSignatures
2929
- -XLambdaCase
3030
- -XMultiParamTypeClasses
31+
- -XMultiWayIf
3132
- -XNoImplicitPrelude
3233
- -XOverloadedStrings
3334
- -XPolyKinds

app/Main.axel

Lines changed: 32 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,39 @@
11
(module Main)
2-
(importq Prelude
3-
GHCPrelude
4-
all)
5-
(importq Axel.Parse.AST
6-
AST
7-
all)
8-
(import Axel.Eff.App
9-
(AppEffs runApp))
10-
(import Axel.Eff.Console
11-
(putStrLn))
12-
(import Axel.Eff.Ghci
13-
(withGhci))
14-
(import Axel.Haskell.File
15-
(convertFileInPlace transpileFileInPlace))
16-
(import Axel.Haskell.Project
17-
(buildProject runProject))
18-
(import Axel.Haskell.Stack
19-
(axelStackageVersion))
20-
(importq Axel.Parse.AST
21-
AST
22-
all)
23-
(import Axel.Parse.Args
24-
((Command Convert
25-
File
26-
Project
27-
Version) commandParser))
2+
(importq Prelude GHCPrelude all)
3+
(importq Axel.Parse.AST AST all)
4+
(import Axel.Eff.App (AppEffs runApp))
5+
(import Axel.Eff.Console (putStrLn))
6+
(import Axel.Eff.Ghci (withGhci))
7+
(import Axel.Haskell.File (convertFileInPlace formatFileInPlace transpileFileInPlace))
8+
(import Axel.Haskell.Project (buildProject runProject))
9+
(import Axel.Haskell.Stack (axelStackageVersion))
10+
(importq Axel.Parse.AST AST all)
11+
(import Axel.Parse.Args ((Command FileCommand ProjectCommand Version) (FileCommand ConvertFile RunFile FormatFile) (ProjectCommand RunProject) commandParserInfo))
2812
(import Control.Monad (void))
2913
(importq Data.Map Map (empty))
30-
(import Options.Applicative
31-
(<**> execParser
32-
helper
33-
info
34-
progDesc))
14+
(import Options.Applicative (execParser))
3515
(importq Polysemy Sem all)
36-
(importq Polysemy.State
37-
Sem
38-
all)
16+
(importq Polysemy.State Sem all)
3917
(raw "import Prelude hiding (putStrLn)")
4018

41-
(def app
42-
((-> Command) (Sem.Sem AppEffs Unit))
43-
(((Convert filePath)) (void (convertFileInPlace filePath)))
44-
(((File filePath)) (void (Sem.evalState Map.empty
45-
(withGhci (transpileFileInPlace filePath)))))
46-
(((Project)) (>> buildProject runProject))
47-
(((Version)) (putStrLn (<> "Axel version "
48-
axelStackageVersion))))
19+
(def
20+
app
21+
((-> Command) (Sem.Sem AppEffs Unit))
22+
(((FileCommand fileCommand))
23+
(case fileCommand
24+
((ConvertFile filePath) (void (convertFileInPlace filePath)))
25+
((FormatFile filePath) (formatFileInPlace filePath))
26+
((RunFile filePath)
27+
(void (Sem.evalState Map.empty (withGhci (transpileFileInPlace filePath)))))))
28+
(((ProjectCommand projectCommand))
29+
(case projectCommand
30+
(RunProject (>> buildProject runProject))))
31+
(((Version)) (putStrLn (<> "Axel version " axelStackageVersion))))
4932

50-
(def main
51-
(IO Unit)
52-
(() (>>= (execParser (info (<**> commandParser helper)
53-
(progDesc "The command to run.")))
54-
(\ (modeCommand)
55-
(runApp (app modeCommand))))))
33+
(def
34+
main
35+
(IO Unit)
36+
(()
37+
(>>=
38+
(execParser commandParserInfo)
39+
(\ (modeCommand) (runApp (app modeCommand))))))

app/Main.hs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
module Main where
22
import qualified Prelude as GHCPrelude
33
import qualified Axel.Parse.AST as AST
4+
import qualified Prelude as GHCPrelude
5+
import qualified Axel.Parse.AST as AST
46
import Axel.Eff.App(AppEffs,runApp)
57
import Axel.Eff.Console(putStrLn)
68
import Axel.Eff.Ghci(withGhci)
7-
import Axel.Haskell.File(convertFileInPlace,transpileFileInPlace)
9+
import Axel.Haskell.File(convertFileInPlace,formatFileInPlace,transpileFileInPlace)
810
import Axel.Haskell.Project(buildProject,runProject)
911
import Axel.Haskell.Stack(axelStackageVersion)
1012
import qualified Axel.Parse.AST as AST
11-
import Axel.Parse.Args(Command(Convert,File,Project,Version),commandParser)
13+
import Axel.Parse.Args(Command(FileCommand,ProjectCommand,Version),FileCommand(ConvertFile,RunFile,FormatFile),ProjectCommand(RunProject),commandParserInfo)
1214
import Control.Monad(void)
1315
import qualified Data.Map as Map(empty)
14-
import Options.Applicative((<**>),execParser,helper,info,progDesc)
16+
import Options.Applicative(execParser)
1517
import qualified Polysemy as Sem
1618
import qualified Polysemy.State as Sem
1719
import Prelude hiding (putStrLn)
18-
app (Convert filePath) = (void (convertFileInPlace filePath))
19-
app (File filePath) = (void (Sem.evalState Map.empty (withGhci (transpileFileInPlace filePath))))
20-
app (Project ) = ((>>) buildProject runProject)
20+
app (FileCommand fileCommand) = (case fileCommand of {(ConvertFile filePath) -> (void (convertFileInPlace filePath));(FormatFile filePath) -> (formatFileInPlace filePath);(RunFile filePath) -> (void (Sem.evalState Map.empty (withGhci (transpileFileInPlace filePath))))})
21+
app (ProjectCommand projectCommand) = (case projectCommand of {RunProject -> ((>>) buildProject runProject)})
2122
app (Version ) = (putStrLn ((<>) "Axel version " axelStackageVersion))
2223
app :: (((->) Command) (Sem.Sem AppEffs ()))
23-
main = ((>>=) (execParser (info ((<**>) commandParser helper) (progDesc "The command to run."))) (\modeCommand -> (runApp (app modeCommand))))
24+
main = ((>>=) (execParser commandParserInfo) (\modeCommand -> (runApp (app modeCommand))))
2425
main :: (IO ())

axel.cabal

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ cabal-version: 1.12
44
--
55
-- see: https://github.com/sol/hpack
66
--
7-
-- hash: 3bd176f732bbb770b9959ea979b99b32f8723c2df6b2bfdaf208f8662379add4
7+
-- hash: 1a081e352686653c22acf2b4fe2222d390f6f14d7be24466e714125b3d4420f1
88

99
name: axel
1010
version: 0.0.11
@@ -98,7 +98,7 @@ library
9898
Paths_axel
9999
hs-source-dirs:
100100
src
101-
default-extensions: BangPatterns ConstraintKinds DataKinds DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances FunctionalDependencies GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses NoImplicitPrelude OverloadedStrings PolyKinds RankNTypes StandaloneDeriving TupleSections TypeApplications TypeOperators
101+
default-extensions: BangPatterns ConstraintKinds DataKinds DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances FunctionalDependencies GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NoImplicitPrelude OverloadedStrings PolyKinds RankNTypes StandaloneDeriving TupleSections TypeApplications TypeOperators
102102
ghc-options: -Weverything -Wno-all-missed-specialisations -Wno-implicit-prelude -Wno-missed-specialisations -Wno-missing-import-lists -Wno-missing-export-lists -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-safe -Wno-unsafe -optP-Wno-nonportable-include-path -fplugin=Polysemy.Plugin -O2 -flate-specialise -fspecialise-aggressively
103103
build-tool-depends:
104104
hpack:hpack
@@ -152,7 +152,7 @@ executable axel
152152
Paths_axel
153153
hs-source-dirs:
154154
app
155-
default-extensions: BangPatterns ConstraintKinds DataKinds DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances FunctionalDependencies GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses NoImplicitPrelude OverloadedStrings PolyKinds RankNTypes StandaloneDeriving TupleSections TypeApplications TypeOperators
155+
default-extensions: BangPatterns ConstraintKinds DataKinds DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances FunctionalDependencies GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NoImplicitPrelude OverloadedStrings PolyKinds RankNTypes StandaloneDeriving TupleSections TypeApplications TypeOperators
156156
ghc-options: -Weverything -Wno-all-missed-specialisations -Wno-implicit-prelude -Wno-missed-specialisations -Wno-missing-import-lists -Wno-missing-export-lists -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-safe -Wno-unsafe -optP-Wno-nonportable-include-path -fplugin=Polysemy.Plugin -O2 -flate-specialise -fspecialise-aggressively -threaded -rtsopts -with-rtsopts=-N
157157
build-tool-depends:
158158
hpack:hpack
@@ -230,7 +230,7 @@ test-suite axel-test
230230
Paths_axel
231231
hs-source-dirs:
232232
test
233-
default-extensions: BangPatterns ConstraintKinds DataKinds DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances FunctionalDependencies GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses NoImplicitPrelude OverloadedStrings PolyKinds RankNTypes StandaloneDeriving TupleSections TypeApplications TypeOperators
233+
default-extensions: BangPatterns ConstraintKinds DataKinds DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances FunctionalDependencies GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NoImplicitPrelude OverloadedStrings PolyKinds RankNTypes StandaloneDeriving TupleSections TypeApplications TypeOperators
234234
ghc-options: -Weverything -Wno-all-missed-specialisations -Wno-implicit-prelude -Wno-missed-specialisations -Wno-missing-import-lists -Wno-missing-export-lists -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-safe -Wno-unsafe -optP-Wno-nonportable-include-path -fplugin=Polysemy.Plugin -O2 -flate-specialise -fspecialise-aggressively -threaded -rtsopts -with-rtsopts=-N
235235
build-tool-depends:
236236
hpack:hpack

package.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ default-extensions:
9292
- KindSignatures
9393
- LambdaCase
9494
- MultiParamTypeClasses
95+
- MultiWayIf
9596
- NoImplicitPrelude
9697
- OverloadedStrings
9798
- PolyKinds # Required by `Polysemy.makeSem`

src/Axel/Haskell/File.hs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ import qualified Axel.Eff.Restartable as Effs (Restartable)
2424
import Axel.Haskell.Convert (convertFile)
2525
import Axel.Macros (handleFunctionApplication, processProgram)
2626
import Axel.Normalize (normalizeStatement, withExprCtxt)
27-
import Axel.Parse (parseSource)
27+
import Axel.Parse (parseMultiple, parseSource)
2828
import Axel.Parse.AST (Expression(Symbol))
29+
import Axel.Pretty (prettifyProgram)
2930
import qualified Axel.Sourcemap as SM
3031
( Expression
3132
, Output
@@ -146,3 +147,13 @@ transpileFileInPlace path = do
146147
let newPath = replaceExtension path "hs"
147148
unless alreadyCompiled $ transpileFile path newPath
148149
pure newPath
150+
151+
formatFileInPlace ::
152+
(Sem.Members '[ Effs.Console, Effs.FileSystem, Sem.Error Error] effs)
153+
=> FilePath
154+
-> Sem.Sem effs ()
155+
formatFileInPlace path = do
156+
contents <- FS.readFile path
157+
program <- parseMultiple (Just path) contents
158+
let prettifiedContents = prettifyProgram program
159+
FS.writeFile path prettifiedContents

src/Axel/Haskell/Project.hs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ import qualified Axel.Eff.Log as Effs (Log)
2020
import qualified Axel.Eff.Process as Effs (Process)
2121
import Axel.Eff.Resource (getResourcePath, newProjectTemplate)
2222
import qualified Axel.Eff.Resource as Effs (Resource)
23-
import Axel.Haskell.File (readModuleInfo, transpileFileInPlace)
23+
import Axel.Haskell.File
24+
( formatFileInPlace
25+
, readModuleInfo
26+
, transpileFileInPlace
27+
)
2428
import Axel.Haskell.Stack
2529
( addStackDependency
2630
, axelStackageId
@@ -30,8 +34,10 @@ import Axel.Haskell.Stack
3034
)
3135
import Axel.Sourcemap (ModuleInfo)
3236
import Axel.Utils.FilePath ((<.>), (</>))
37+
import Axel.Utils.Monad (concatMapM)
3338

3439
import Control.Lens (op)
40+
import Control.Monad (void)
3541

3642
import qualified Data.Text as T
3743

@@ -63,20 +69,21 @@ newProject projectName = do
6369
, FilePath "test" </> FilePath "Spec"
6470
]
6571

72+
getProjectAxelFiles ::
73+
(Sem.Member Effs.FileSystem effs) => Sem.Sem effs [FilePath]
74+
getProjectAxelFiles = do
75+
files <-
76+
concatMapM
77+
getDirectoryContentsRec
78+
[FilePath "app", FilePath "src", FilePath "test"]
79+
pure $ filter (\filePath -> ".axel" `T.isSuffixOf` op FilePath filePath) files
80+
6681
transpileProject ::
6782
(Sem.Members '[ Effs.Console, Sem.Error Error, Effs.FileSystem, Effs.Ghci, Effs.Log, Effs.Process, Effs.Resource] effs)
6883
=> Sem.Sem effs ModuleInfo
6984
transpileProject =
7085
Ghci.withGhci $ do
71-
files <-
72-
concat <$>
73-
mapM
74-
getDirectoryContentsRec
75-
[FilePath "app", FilePath "src", FilePath "test"]
76-
let axelFiles =
77-
filter
78-
(\filePath -> ".axel" `T.isSuffixOf` op FilePath filePath)
79-
files
86+
axelFiles <- getProjectAxelFiles
8087
initialModuleInfo <- readModuleInfo axelFiles
8188
(moduleInfo, _) <-
8289
Sem.runState initialModuleInfo $ mapM transpileFileInPlace axelFiles
@@ -94,3 +101,8 @@ runProject ::
94101
(Sem.Members '[ Effs.Console, Sem.Error Error, Effs.FileSystem, Effs.Process] effs)
95102
=> Sem.Sem effs ()
96103
runProject = getCurrentDirectory >>= runStackProject
104+
105+
formatProject ::
106+
(Sem.Members '[ Effs.Console, Effs.FileSystem, Sem.Error Error] effs)
107+
=> Sem.Sem effs ()
108+
formatProject = getProjectAxelFiles >>= void . traverse formatFileInPlace

src/Axel/Parse.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ parseReadMacro prefix wrapper = do
5959
ann SExpression (pure [Symbol Nothing (T.unpack wrapper), expr])
6060

6161
eol :: Parser ()
62-
eol = P.try (void P.newline) <|> P.eof
62+
eol = P.try (void P.eol) <|> P.eof
6363

6464
ignored :: Parser ()
6565
ignored = P.skipMany $ P.try comment <|> void P.spaceChar
@@ -171,8 +171,8 @@ parseMultiple maybeFilePath input =
171171
P.parse program (T.unpack $ op FilePath filePath) input
172172
where
173173
filePath = fromMaybe (FilePath "") maybeFilePath
174-
program = P.some (ignored *> expression <* ignored) <* eol
175-
throwErr = Sem.throw . ParseError filePath . showText
174+
program = P.some (ignored *> expression <* ignored) <* P.eof
175+
throwErr = Sem.throw . ParseError filePath . T.pack . P.errorBundlePretty
176176
expandQuasiquotes =
177177
map $
178178
bottomUpFmapSplicing

src/Axel/Parse/Args.axel

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,73 @@
22

33
(import Axel.Prelude all)
44

5-
(importq Data.Text T all)
5+
(import Control.Applicative (<**>))
6+
7+
(import Data.Foldable (asum))
68
(import Data.Semigroup (<>))
9+
(importq Data.Text T all)
10+
11+
(import Options.Applicative (Parser ParserInfo argument command fullDesc helper hsubparser info metavar progDesc str))
12+
13+
(data FileCommand
14+
(ConvertFile FilePath)
15+
(RunFile FilePath)
16+
(FormatFile FilePath))
717

8-
(import Options.Applicative (Parser argument command info metavar progDesc str subparser))
18+
(data ProjectCommand
19+
(RunProject))
920

1021
(data Command
11-
(Convert FilePath)
12-
(File FilePath)
13-
(Project)
14-
(Version))
15-
16-
(def commandParser (Parser Command)
17-
(() (subparser {projectCommand <> {fileCommand <> {convertCommand <> versionCommand}}})
18-
(= convertCommand ()
19-
(command "convert"
20-
(info {{Convert . {FilePath . T.pack}} <$> (argument str (metavar "FILE"))}
21-
(progDesc "(EXPERIMENTAL) Convert a Haskell file to Axel"))))
22-
(= fileCommand ()
23-
(command "file"
24-
(info {{File . {FilePath . T.pack}} <$> (argument str (metavar "FILE"))}
25-
(progDesc "Build and run a single file"))))
26-
(= projectCommand ()
27-
(command "project"
28-
(info (pure Project)
29-
(progDesc "Build and run the project"))))
30-
(= versionCommand ()
31-
(command "version"
32-
(info (pure Version)
33-
(progDesc "Display the version of the Axel compiler"))))))
22+
(FileCommand FileCommand)
23+
(ProjectCommand ProjectCommand)
24+
(Version))
25+
26+
(def experimental {String -> String}
27+
(() (<> "(EXPERIMENTAL) ")))
28+
29+
(def fileCommandParser (Parser FileCommand)
30+
(() (hsubparser (mconcat [convertFileCommand formatFileCommand runFileCommand]))
31+
(= convertFileCommand ()
32+
(command "convert"
33+
(filePathParser ConvertFile
34+
(experimental "Convert a Haskell file to Axel"))))
35+
(= runFileCommand ()
36+
(command "run"
37+
(filePathParser RunFile
38+
"Transpile and run an Axel file")))
39+
(= formatFileCommand ()
40+
(command "format"
41+
(filePathParser FormatFile
42+
"Format an Axel file")))
43+
(= filePathParser (ctor desc)
44+
(info {{ctor . {FilePath . T.pack}} <$> (argument str (metavar "FILE"))}
45+
(progDesc desc)))))
46+
47+
(def projectCommandParser (Parser ProjectCommand)
48+
(() (hsubparser (mconcat [runProjectCommand]))
49+
(= runProjectCommand ()
50+
(command "run"
51+
(info (pure RunProject)
52+
(progDesc "Build and run the project"))))))
53+
54+
(def commandParserInfo (ParserInfo Command)
55+
(() (let ((subparsers (asum [fileCommand projectCommand versionCommand])))
56+
(info {subparsers <**> helper} fullDesc))
57+
(= fileCommand ()
58+
(hsubparser {(command "file"
59+
(info {FileCommand <$> fileCommandParser}
60+
(progDesc "Run file-specific commands")))
61+
<>
62+
(metavar "file")}))
63+
(= projectCommand ()
64+
(hsubparser {(command "project"
65+
(info {ProjectCommand <$> projectCommandParser}
66+
(progDesc "Run project-wide commands")))
67+
<>
68+
(metavar "project")}))
69+
(= versionCommand ()
70+
(hsubparser {(command "version"
71+
(info (pure Version)
72+
(progDesc "Display the version of the Axel compiler")))
73+
<>
74+
(metavar "version")}))))

0 commit comments

Comments
 (0)