Skip to content

Instantly share code, notes, and snippets.

@ScottFreeCode
Created July 25, 2023 03:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ScottFreeCode/2b1389adae55c35a60b6a89561125fd5 to your computer and use it in GitHub Desktop.
Save ScottFreeCode/2b1389adae55c35a60b6a89561125fd5 to your computer and use it in GitHub Desktop.
Haskell+Nix+BNFC, Monorepo
dist/
dist-*/
result
result-*
Example
cabal.project.local
Setup
*~
*#*

Fixup (work around gist limitations)

mkdir -p app/src lib/src ; git mv app{_slash_,/}example.cabal ; git mv app{_slash_src_slash_,/src/}Main.hs ; git mv lib{_slash_,/}lib.cabal ; git mv lib{_slash_,/}Setup.hs ; git mv lib{_slash_src_slash_,/src/}Example.cf ; git mv lib{_slash_src_slash_,/src/}lex-fixes.patch

Commands

Clean full build

nix-build

Dev mode

nix-shell

Then you can build the library and then the app separately via cd lib && cabal v1-install && cd ../app && cabal v1-build – with or without --nix=True (or $HOME/.cabal/config containing nix: True).

cabal-version: 3.0
name: example
version: 0.1.0.0
build-type: Simple
executable example
main-is: Main.hs
build-depends:
base
, text
, array
, example-lib
hs-source-dirs: src
default-language: Haskell2010
-- Program to test parser, automatically generated by BNF Converter.
module Main where
import Prelude
( ($)
, Either(..)
, Int, (>)
, String, (++), unlines
, Show, show
, IO, (>>), (>>=), mapM_, putStrLn
, FilePath
)
import Data.Text.IO ( getContents, readFile )
import qualified Data.Text
import System.Environment ( getArgs )
import System.Exit ( exitFailure, exitSuccess )
import Control.Monad ( when )
import Example.Abs ()
import Example.Lex ( Token )
import Example.Par ( pExample, myLexer )
import Example.Print ( Print, printTree )
type Err = Either String
type ParseFun a = [Token] -> Err a
type Verbosity = Int
putStrV :: Verbosity -> String -> IO ()
putStrV v s = when (v > 1) $ putStrLn s
runFile :: (Print a, Show a) => Verbosity -> ParseFun a -> FilePath -> IO ()
runFile v p f = putStrLn f >> readFile f >>= run v p
run :: (Print a, Show a) => Verbosity -> ParseFun a -> Data.Text.Text -> IO ()
run v p s =
case p ts of
Left err -> do
putStrLn "\nParse Failed...\n"
putStrV v "Tokens:"
putStrV v $ show ts
putStrLn err
exitFailure
Right tree -> do
putStrLn "\nParse Successful!"
showTree v tree
exitSuccess
where
ts = myLexer s
showTree :: (Show a, Print a) => Int -> a -> IO ()
showTree v tree = do
putStrV v $ "\n[Abstract Syntax]\n\n" ++ show tree
putStrV v $ "\n[Linearized tree]\n\n" ++ printTree tree
usage :: IO ()
usage = do
putStrLn $ unlines
[ "usage: Call with one of the following argument combinations:"
, " --help Display this help message."
, " (no arguments) Parse stdin verbosely."
, " (files) Parse content of files verbosely."
, " -s (files) Silent mode. Parse content of files silently."
]
exitFailure
main :: IO ()
main = do
args <- getArgs
case args of
["--help"] -> usage
[] -> getContents >>= run 2 pExample
"-s":fs -> mapM_ (runFile 0 pExample) fs
fs -> mapM_ (runFile 2 pExample) fs
packages: */*.cabal
{ pkgs ? import <nixpkgs> {} }:
let
gitignore = pkgs.nix-gitignore.gitignoreSourcePure [ ./.gitignore ];
lib = pkgs.haskellPackages.callCabal2nix "" (gitignore ./lib) {};
app = gitignore ./app;
in { lib = lib; app = pkgs.haskellPackages.callCabal2nix "" app { example-lib = lib; }; }
cabal-version: 3.0
name: example-lib
version: 0.1.0.0
build-type: Custom
custom-setup
setup-depends:
Cabal
, base
, process
--, BNFC
library
exposed-modules:
Example.Par
, Example.Lex
, Example.Abs
, Example.Print
autogen-modules:
Example.Par
Example.Lex
Example.Abs
Example.Print
build-depends:
base
, text
, array
hs-source-dirs: src
default-language: Haskell2010
build-tool-depends:
BNFC:bnfc,
-- ^ needed for Nix even though it should probably be supplied through custom-setup:setup-depends
alex:alex,
happy:happy
import Distribution.Simple (defaultMainWithHooks, simpleUserHooks, confHook, cleanHook)
import System.Process (callProcess)
main = defaultMainWithHooks simpleUserHooks
{ confHook = interceptConf
, cleanHook = interceptClean
}
interceptConf (pkg, info) flags = do
callProcess "bnfc" ["-o", "src", "-d", "--text-token", "--functor", "src/Example.cf" ]
callProcess "rm" [ "src/Example/" <> file <> ".hs" | file <- ["Skel", "ErrM", "Test"] ]
-- Do any needed patching here.
--callProcess "patch" [ "src/Example/Lex.x", "src/lex-fixes.patch" ]
confHook simpleUserHooks (pkg, info) flags
interceptClean pkg () hooks flags = do
callProcess "rm" [ "-r", "src/Example/" ]
cleanHook simpleUserHooks pkg () hooks flags
--- top level
entrypoints Example;
Example . Example ::= Ident;
diff --git a/app/Example/Lex.x~ b/app/Example/Lex.x
index d17d052..2d37f97 100644
--- a/app/Example/Lex.x~
+++ b/app/Example/Lex.x
@@ -35,21 +35,21 @@ $u = [. \n] -- universal: any character
-- Whitespace (skipped)
$white+ ;
-- Symbols
@rsyms
{ tok (eitherResIdent TV) }
-- token Name
\' ([$u # [\' \\]] | \\ [\' \\ f n r t]) * \'
- { tok (eitherResIdent T_Name) }
+ { tok (T_Name . unescapeInitTail) }
-- Keywords and Ident
$l $i*
{ tok (eitherResIdent TV) }
-- String
\" ([$u # [\" \\ \n]] | (\\ (\" | \\ | \' | n | t | r | f)))* \"
{ tok (TL . unescapeInitTail) }
-- Integer
@@ -179,20 +179,21 @@ resWords =
unescapeInitTail :: Data.Text.Text -> Data.Text.Text
unescapeInitTail = Data.Text.pack . unesc . tail . Data.Text.unpack
where
unesc s = case s of
'\\':c:cs | elem c ['\"', '\\', '\''] -> c : unesc cs
'\\':'n':cs -> '\n' : unesc cs
'\\':'t':cs -> '\t' : unesc cs
'\\':'r':cs -> '\r' : unesc cs
'\\':'f':cs -> '\f' : unesc cs
'"':[] -> []
+ '\'':[] -> []
c:cs -> c : unesc cs
_ -> []
-------------------------------------------------------------------
-- Alex wrapper code.
-- A modified "posn" wrapper.
-------------------------------------------------------------------
data Posn = Pn !Int !Int !Int
deriving (Eq, Show, Ord)
{ pkgs ? import <nixpkgs> {} }:
pkgs.haskellPackages.shellFor {
packages = p: builtins.attrValues (import ./. { inherit pkgs; });
buildInputs = [
pkgs.cabal-install
];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment