feat(haskell): CLI rewrite
This commit is contained in:
588
src/Main.hs
588
src/Main.hs
@@ -1,280 +1,374 @@
|
||||
module Main where
|
||||
|
||||
import ContentStore (initContentStore, loadEnvironment, loadTerm, resolveExportTarget)
|
||||
import ContentStore (initContentStoreWithPath, loadEnvironment, loadTerm, resolveExportTarget)
|
||||
import System.Exit (die)
|
||||
import Server (runServer)
|
||||
import Eval (evalTricu, mainResult, result)
|
||||
import FileEval
|
||||
import Parser (parseTricu)
|
||||
import REPL
|
||||
import Research
|
||||
import Wire
|
||||
import Server (runServerWithPath)
|
||||
import Eval (evalTricu, evalTricuWithStore, mainResult, result)
|
||||
import FileEval (evaluateFileWithContext, evaluateFileWithStore, compileFile)
|
||||
import Parser (parseTricu)
|
||||
import REPL (repl)
|
||||
import Research (T, EvaluatedForm(..), Env, formatT, exportDag)
|
||||
import Wire (exportNamedBundle, defaultExportNames, importBundle)
|
||||
|
||||
import Control.Monad (foldM)
|
||||
import Data.Text (Text, unpack)
|
||||
import qualified Data.Text as T
|
||||
import Data.Version (showVersion)
|
||||
import Paths_tricu (version)
|
||||
import System.Console.CmdArgs
|
||||
import System.Environment (lookupEnv)
|
||||
import System.IO (hPutStrLn, stderr)
|
||||
import Text.Megaparsec ()
|
||||
import Control.Monad (foldM, unless, when)
|
||||
import Data.Text (unpack, pack)
|
||||
import qualified Data.Text as T
|
||||
import Data.Version (showVersion)
|
||||
import Paths_tricu (version)
|
||||
import Options.Applicative
|
||||
|
||||
import qualified Data.ByteString.Lazy as BL
|
||||
import Database.SQLite.Simple (close)
|
||||
import Database.SQLite.Simple (Connection, close)
|
||||
|
||||
import qualified Data.Map as Map
|
||||
import System.Environment (lookupEnv)
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- CLI argument types
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
data TricuArgs
|
||||
= Repl
|
||||
| Evaluate { file :: [FilePath], form :: EvaluatedForm, outFile :: FilePath }
|
||||
| TDecode { file :: [FilePath] }
|
||||
| Compile { inputFile :: FilePath, outFile :: FilePath, names :: [String] }
|
||||
| Export { hash :: String, exportNameOpt :: String, outFile :: FilePath, names :: [String] }
|
||||
| Import { inFile :: FilePath }
|
||||
| Serve { host :: String, port :: Int }
|
||||
| ExportDag { target :: String, outFile :: FilePath }
|
||||
deriving (Show, Data, Typeable)
|
||||
| Eval
|
||||
{ evalFiles :: [FilePath]
|
||||
, evalFormat :: EvaluatedForm
|
||||
, evalOutput :: FilePath
|
||||
, evalDb :: Maybe FilePath
|
||||
}
|
||||
| ArboricxCompile
|
||||
{ compileInput :: FilePath
|
||||
, compileOutput :: FilePath
|
||||
, compileNames :: [String]
|
||||
, compileDb :: Maybe FilePath
|
||||
}
|
||||
| ArboricxImport
|
||||
{ importFile :: FilePath
|
||||
, importDb :: Maybe FilePath
|
||||
}
|
||||
| ArboricxExport
|
||||
{ exportTargets :: [String]
|
||||
, exportOutput :: FilePath
|
||||
, exportNames :: [String]
|
||||
, exportDb :: Maybe FilePath
|
||||
, dag :: Bool
|
||||
}
|
||||
| ArboricxServe
|
||||
{ serveHost :: String
|
||||
, servePort :: Int
|
||||
, serveDb :: Maybe FilePath
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
replMode :: TricuArgs
|
||||
replMode = Repl
|
||||
&= help "Start interactive REPL"
|
||||
&= auto
|
||||
&= name "repl"
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- optparse-applicative parsers
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
evaluateMode :: TricuArgs
|
||||
evaluateMode = Evaluate
|
||||
{ file = def &= help "Input file path(s) for evaluation.\n \
|
||||
\ Defaults to stdin."
|
||||
&= name "f" &= typ "FILE"
|
||||
, form = TreeCalculus &= typ "FORM"
|
||||
&= help "Optional output form: (tree|fsl|ast|ternary|ascii|decode).\n \
|
||||
\ Defaults to tricu-compatible `t` tree form."
|
||||
&= name "t"
|
||||
, outFile = def &= help "Optional output file path. Defaults to stdout."
|
||||
&= name "o" &= typ "FILE"
|
||||
}
|
||||
&= help "Evaluate tricu and return the result of the final expression."
|
||||
&= explicit
|
||||
&= name "eval"
|
||||
readEvaluatedForm :: ReadM EvaluatedForm
|
||||
readEvaluatedForm = eitherReader $ \s -> case s of
|
||||
"tree" -> Right Tree
|
||||
"fsl" -> Right FSL
|
||||
"ast" -> Right AST
|
||||
"ternary" -> Right Ternary
|
||||
"ascii" -> Right Ascii
|
||||
"decode" -> Right Decode
|
||||
_ -> Left $ "Unknown format: " ++ s ++ ". Expected: tree, fsl, ast, ternary, ascii, decode"
|
||||
|
||||
decodeMode :: TricuArgs
|
||||
decodeMode = TDecode
|
||||
{ file = def
|
||||
&= help "Optional input file path to attempt decoding.\n \
|
||||
\ Defaults to stdin."
|
||||
&= name "f" &= typ "FILE"
|
||||
}
|
||||
&= help "Decode a Tree Calculus value into a string representation."
|
||||
&= explicit
|
||||
&= name "decode"
|
||||
evalParser :: Parser TricuArgs
|
||||
evalParser = Eval
|
||||
<$> many (argument str (metavar "FILE..."))
|
||||
<*> option readEvaluatedForm
|
||||
( long "format"
|
||||
<> short 'f'
|
||||
<> metavar "FORM"
|
||||
<> value Tree
|
||||
<> help "Output format: tree, fsl, ast, ternary, ascii, decode"
|
||||
)
|
||||
<*> option str
|
||||
( long "output"
|
||||
<> short 'o'
|
||||
<> metavar "FILE"
|
||||
<> value ""
|
||||
<> help "Write output to file instead of stdout"
|
||||
)
|
||||
<*> optional (option str
|
||||
( long "db"
|
||||
<> short 'd'
|
||||
<> metavar "PATH"
|
||||
<> help "Content store database path"
|
||||
))
|
||||
|
||||
exportMode :: TricuArgs
|
||||
exportMode = Export
|
||||
{ hash = def &= help "Hash or stored term name(s) to export (comma-separated)."
|
||||
&= name "h" &= typ "HASH_OR_NAME"
|
||||
, exportNameOpt = def &= help "Export name (legacy; use -n NAME for full control)."
|
||||
&= name "n" &= typ "NAME"
|
||||
, outFile = def &= help "Output file path for the bundle." &= name "o" &= typ "FILE"
|
||||
, names = def &= help "Export name(s) for the bundle manifest (comma-separated or repeated -n)."
|
||||
&= typ "NAME"
|
||||
}
|
||||
&= help "Export a Merkle bundle from the content store."
|
||||
&= explicit
|
||||
&= name "export"
|
||||
compileParser :: Parser TricuArgs
|
||||
compileParser = ArboricxCompile
|
||||
<$> option str
|
||||
( long "file"
|
||||
<> short 'f'
|
||||
<> metavar "FILE"
|
||||
<> value ""
|
||||
<> help "Input .tri source file"
|
||||
)
|
||||
<*> option str
|
||||
( long "output"
|
||||
<> short 'o'
|
||||
<> metavar "FILE"
|
||||
<> value ""
|
||||
<> help "Output bundle file path (required)"
|
||||
)
|
||||
<*> many (option str
|
||||
( long "name"
|
||||
<> short 'n'
|
||||
<> metavar "NAME"
|
||||
<> help "Definition name(s) to export as bundle roots (repeatable)"
|
||||
))
|
||||
<*> optional (option str
|
||||
( long "db"
|
||||
<> short 'd'
|
||||
<> metavar "PATH"
|
||||
<> help "Content store database path"
|
||||
))
|
||||
|
||||
importMode :: TricuArgs
|
||||
importMode = Import
|
||||
{ inFile = def &= help "Path to the bundle file to import."
|
||||
&= name "f" &= typ "FILE"
|
||||
}
|
||||
&= help "Import a Merkle bundle into the content store."
|
||||
&= explicit
|
||||
&= name "import"
|
||||
importParser :: Parser TricuArgs
|
||||
importParser = ArboricxImport
|
||||
<$> option str
|
||||
( long "file"
|
||||
<> short 'f'
|
||||
<> metavar "FILE"
|
||||
<> value ""
|
||||
<> help "Bundle file to import"
|
||||
)
|
||||
<*> optional (option str
|
||||
( long "db"
|
||||
<> short 'd'
|
||||
<> metavar "PATH"
|
||||
<> help "Content store database path"
|
||||
))
|
||||
|
||||
compileMode :: TricuArgs
|
||||
compileMode = Compile
|
||||
{ inputFile = def &= help "Path to the tricu source file (.tri) to compile."
|
||||
&= name "f" &= typ "FILE"
|
||||
, outFile = def &= help "Output bundle file path (.tri.bundle)."
|
||||
&= name "o" &= typ "FILE"
|
||||
, names = def &= help "Definition name(s) to export as bundle roots (comma-separated or repeated -x). Defaults to 'main'."
|
||||
&= name "x" &= typ "NAME"
|
||||
}
|
||||
&= help "Compile a tricu source file into a standalone Arboricx portable bundle."
|
||||
&= explicit
|
||||
&= name "compile"
|
||||
exportParser :: Parser TricuArgs
|
||||
exportParser = ArboricxExport
|
||||
<$> many (option str
|
||||
( long "target"
|
||||
<> short 't'
|
||||
<> metavar "TARGET"
|
||||
<> help "Target hash or name (repeatable)"
|
||||
))
|
||||
<*> option str
|
||||
( long "output"
|
||||
<> short 'o'
|
||||
<> metavar "FILE"
|
||||
<> value ""
|
||||
<> help "Output file path (required for bundle export)"
|
||||
)
|
||||
<*> many (option str
|
||||
( long "name"
|
||||
<> short 'n'
|
||||
<> metavar "NAME"
|
||||
<> help "Export name(s) for the bundle manifest (repeatable)"
|
||||
))
|
||||
<*> optional (option str
|
||||
( long "db"
|
||||
<> short 'd'
|
||||
<> metavar "PATH"
|
||||
<> help "Content store database path"
|
||||
))
|
||||
<*> switch
|
||||
( long "dag"
|
||||
<> help "Export as a topologically-sorted DAG node table instead of a bundle"
|
||||
)
|
||||
|
||||
serveMode :: TricuArgs
|
||||
serveMode = Serve
|
||||
{ host = "127.0.0.1" &= help "Host to bind the server to." &= name "h" &= typ "HOST"
|
||||
, port = 8787 &= help "HTTP port to listen on." &= name "p" &= typ "PORT"
|
||||
}
|
||||
&= help "Start a read-only HTTP server for exporting Arboricx bundles."
|
||||
&= explicit
|
||||
&= name "server"
|
||||
serveParser :: Parser TricuArgs
|
||||
serveParser = ArboricxServe
|
||||
<$> option str
|
||||
( long "host"
|
||||
<> metavar "HOST"
|
||||
<> value "127.0.0.1"
|
||||
<> help "Host to bind the server to"
|
||||
)
|
||||
<*> option auto
|
||||
( long "port"
|
||||
<> short 'p'
|
||||
<> metavar "PORT"
|
||||
<> value 8787
|
||||
<> help "HTTP port to listen on"
|
||||
)
|
||||
<*> optional (option str
|
||||
( long "db"
|
||||
<> short 'd'
|
||||
<> metavar "PATH"
|
||||
<> help "Content store database path"
|
||||
))
|
||||
|
||||
exportDagMode :: TricuArgs
|
||||
exportDagMode = ExportDag
|
||||
{ target = def &= help "Stored term name or hash to export as a DAG node table."
|
||||
&= name "t" &= typ "NAME_OR_HASH"
|
||||
, outFile = def &= help "Optional output file path. Defaults to stdout."
|
||||
&= name "o" &= typ "FILE"
|
||||
}
|
||||
&= help "Export a term's Merkle DAG as a topologically-sorted node table for host embedding."
|
||||
&= explicit
|
||||
&= name "export-dag"
|
||||
versionStr :: String
|
||||
versionStr = "tricu " ++ showVersion version
|
||||
|
||||
tricuParser :: Parser TricuArgs
|
||||
tricuParser = (subparser topCommands <|> pure Repl)
|
||||
<**> infoOption versionStr (long "version" <> help "Show version")
|
||||
where
|
||||
topCommands = mconcat
|
||||
[ command "eval" (info (evalParser <**> helper)
|
||||
(progDesc "Evaluate tricu source and print the result of the final expression"))
|
||||
, command "arboricx" (info (arboricxParser <**> helper)
|
||||
(progDesc "Arboricx bundle operations"))
|
||||
]
|
||||
|
||||
arboricxParser :: Parser TricuArgs
|
||||
arboricxParser = subparser $ mconcat
|
||||
[ command "compile" (info (compileParser <**> helper)
|
||||
(progDesc "Compile a .tri file into a standalone Arboricx bundle"))
|
||||
, command "import" (info (importParser <**> helper)
|
||||
(progDesc "Import an Arboricx bundle into the content store"))
|
||||
, command "export" (info (exportParser <**> helper)
|
||||
(progDesc "Export one or more terms from the content store"))
|
||||
, command "serve" (info (serveParser <**> helper)
|
||||
(progDesc "Start a read-only HTTP server for Arboricx bundles"))
|
||||
]
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Entry point
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
let versionStr = "tricu Evaluator and REPL " ++ showVersion version
|
||||
cmdArgsParsed <- cmdArgs $ modes [replMode, evaluateMode, decodeMode, compileMode, exportMode, importMode, serveMode, exportDagMode]
|
||||
&= help "tricu: Exploring Tree Calculus"
|
||||
&= program "tricu"
|
||||
&= summary versionStr
|
||||
&= versionArg [explicit, name "version", summary versionStr]
|
||||
case cmdArgsParsed of
|
||||
Repl -> do
|
||||
putStrLn "Welcome to the tricu REPL"
|
||||
putStrLn "You may exit with `CTRL+D` or the `!exit` command."
|
||||
repl
|
||||
Evaluate { file = filePaths, form = outputForm, outFile = evalOutFile } -> do
|
||||
maybeDbPath <- lookupEnv "TRICU_DB_PATH"
|
||||
evalResult <- case filePaths of
|
||||
[] -> do
|
||||
initialEnv <- case maybeDbPath of
|
||||
Just _ -> do
|
||||
conn <- initContentStore
|
||||
env <- loadEnvironment conn
|
||||
close conn
|
||||
return env
|
||||
Nothing -> return Map.empty
|
||||
input <- getContents
|
||||
pure $ runTricuTEnv initialEnv input
|
||||
filePaths@(_:_) -> do
|
||||
initialEnv <- case maybeDbPath of
|
||||
Just _ -> do
|
||||
conn <- initContentStore
|
||||
env <- loadEnvironment conn
|
||||
close conn
|
||||
return env
|
||||
Nothing -> return Map.empty
|
||||
finalEnv <- foldM evaluateFileWithContext initialEnv filePaths
|
||||
pure $ mainResult finalEnv
|
||||
let fRes = formatT outputForm evalResult
|
||||
if null evalOutFile
|
||||
then putStr fRes
|
||||
else writeFile evalOutFile fRes
|
||||
TDecode { file = filePaths } -> do
|
||||
value <- case filePaths of
|
||||
[] -> getContents
|
||||
(filePath:_) -> readFile filePath
|
||||
putStrLn $ decodeResult $ result $ evalTricu Map.empty $ parseTricu value
|
||||
Export { hash = hashStr, exportNameOpt = legacyName, names = namesArg, outFile = outFilePath } -> do
|
||||
conn <- initContentStore
|
||||
let hashList = T.split (== ',') (T.pack hashStr)
|
||||
hashes <- mapM (\h -> do
|
||||
(resolvedHash, _) <- resolveExportTarget conn (T.unpack h)
|
||||
return resolvedHash) hashList
|
||||
-- Merge legacy -n and new -n (names); names wins when non-empty
|
||||
let allNames = if null namesArg
|
||||
then if null legacyName then [] else [legacyName]
|
||||
else namesArg
|
||||
let expNames = if null allNames
|
||||
then defaultExportNames (length hashes)
|
||||
else map T.pack allNames
|
||||
let exports = zip expNames hashes
|
||||
bundleData <- exportNamedBundle conn exports
|
||||
BL.writeFile outFilePath (BL.fromStrict bundleData)
|
||||
putStrLn $ "Exported bundle with " ++ show (length exports) ++ " export(s) to " ++ outFilePath
|
||||
close conn
|
||||
Import { inFile = importFile } -> do
|
||||
conn <- initContentStore
|
||||
bundleData <- BL.readFile importFile
|
||||
roots <- importBundle conn (BL.toStrict bundleData)
|
||||
putStrLn $ "Imported " ++ show (length roots) ++ " root(s):"
|
||||
mapM_ (\r -> putStrLn $ " " ++ unpack r) roots
|
||||
close conn
|
||||
Compile { inputFile = compileInputFile, outFile = compileOutFile, names = namesArg } ->
|
||||
let exportNames = if null namesArg then [] else map T.pack namesArg
|
||||
in compileFile compileInputFile compileOutFile exportNames
|
||||
Serve { host = hostStr, port = portNum } -> do
|
||||
putStrLn $ "Starting Arboricx bundle server on " ++ hostStr ++ ":" ++ show portNum
|
||||
putStrLn $ " GET /bundle/hash/:hash -- primary endpoint"
|
||||
putStrLn $ " GET /bundle/name/:name -- convenience endpoint"
|
||||
putStrLn $ " Content-Type: application/vnd.arboricx.bundle"
|
||||
runServer hostStr portNum
|
||||
ExportDag { target = targetName, outFile = dagOutFile } -> do
|
||||
conn <- initContentStore
|
||||
maybeTerm <- loadTerm conn targetName
|
||||
close conn
|
||||
args <- execParser $ info (tricuParser <**> helper)
|
||||
( fullDesc
|
||||
<> progDesc "Exploring Tree Calculus"
|
||||
<> header versionStr
|
||||
)
|
||||
case args of
|
||||
Repl -> runRepl
|
||||
Eval {} -> runEval args
|
||||
ArboricxCompile {} -> runCompile args
|
||||
ArboricxImport {} -> runImport args
|
||||
ArboricxExport {} -> runExport args
|
||||
ArboricxServe {} -> runServe args
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Command runners
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
runRepl :: IO ()
|
||||
runRepl = do
|
||||
putStrLn "Welcome to the tricu REPL"
|
||||
putStrLn "You may exit with `CTRL+D` or the `!exit` command."
|
||||
repl
|
||||
|
||||
runEval :: TricuArgs -> IO ()
|
||||
runEval opts = do
|
||||
let files = evalFiles opts
|
||||
form = evalFormat opts
|
||||
out = evalOutput opts
|
||||
mconn <- case evalDb opts of
|
||||
Just dbPath -> Just <$> initContentStoreWithPath (Just dbPath)
|
||||
Nothing -> do
|
||||
mDbPath <- lookupEnv "TRICU_DB_PATH"
|
||||
case mDbPath of
|
||||
Just _ -> Just <$> initContentStoreWithPath Nothing
|
||||
Nothing -> return Nothing
|
||||
resultT <- case files of
|
||||
[] -> do
|
||||
input <- getContents
|
||||
env <- evalTricuWithStore mconn Map.empty (parseTricu input)
|
||||
return $ result env
|
||||
_ -> do
|
||||
finalEnv <- foldM (evaluateFileWithStore mconn) Map.empty files
|
||||
return $ mainResult finalEnv
|
||||
case mconn of
|
||||
Just conn -> close conn
|
||||
Nothing -> return ()
|
||||
writeOutput out (formatT form resultT)
|
||||
|
||||
runCompile :: TricuArgs -> IO ()
|
||||
runCompile opts = do
|
||||
let input = compileInput opts
|
||||
out = compileOutput opts
|
||||
names = compileNames opts
|
||||
when (null out) $ die "tricu arboricx compile: --output is required"
|
||||
when (null input) $ die "tricu arboricx compile: input file is required"
|
||||
let nameTexts = if null names then [] else map T.pack names
|
||||
compileFile input out nameTexts
|
||||
|
||||
runImport :: TricuArgs -> IO ()
|
||||
runImport opts = do
|
||||
let file = importFile opts
|
||||
when (null file) $ die "tricu arboricx import: input file is required"
|
||||
withContentStore (importDb opts) $ \conn -> do
|
||||
bundleData <- BL.readFile file
|
||||
roots <- importBundle conn (BL.toStrict bundleData)
|
||||
putStrLn $ "Imported " ++ show (length roots) ++ " root(s):"
|
||||
mapM_ (\r -> putStrLn $ " " ++ unpack r) roots
|
||||
|
||||
runExport :: TricuArgs -> IO ()
|
||||
runExport opts =
|
||||
if dag opts
|
||||
then runExportDag opts
|
||||
else runExportBundle opts
|
||||
|
||||
runExportBundle :: TricuArgs -> IO ()
|
||||
runExportBundle opts = do
|
||||
let targets = exportTargets opts
|
||||
out = exportOutput opts
|
||||
names = exportNames opts
|
||||
when (null out) $ die "tricu arboricx export: --output is required"
|
||||
when (null targets) $ die "tricu arboricx export: at least one --target is required"
|
||||
withContentStore (exportDb opts) $ \conn -> do
|
||||
hashes <- mapM (\t -> do
|
||||
(h, _) <- resolveExportTarget conn t
|
||||
return h) targets
|
||||
let expNames = if null names
|
||||
then defaultExportNames (length hashes)
|
||||
else map T.pack names
|
||||
when (length expNames /= length hashes) $
|
||||
die "tricu arboricx export: number of --name values must match number of TARGETs"
|
||||
let exports = zip expNames hashes
|
||||
bundleData <- exportNamedBundle conn exports
|
||||
BL.writeFile out (BL.fromStrict bundleData)
|
||||
putStrLn $ "Exported bundle with " ++ show (length exports) ++ " export(s) to " ++ out
|
||||
|
||||
runExportDag :: TricuArgs -> IO ()
|
||||
runExportDag opts = do
|
||||
let targets = exportTargets opts
|
||||
out = exportOutput opts
|
||||
case targets of
|
||||
[target] -> withContentStore (exportDb opts) $ \conn -> do
|
||||
maybeTerm <- loadTerm conn target
|
||||
case maybeTerm of
|
||||
Nothing -> die $ "Term not found: " ++ targetName
|
||||
Nothing -> die $ "Term not found: " ++ target
|
||||
Just term -> do
|
||||
let (rootIdx, nodes) = exportDag term
|
||||
output = unlines $ show rootIdx : map (\(tag, refs) -> unwords (tag : map show refs)) nodes
|
||||
if null dagOutFile
|
||||
then putStr output
|
||||
else writeFile dagOutFile output
|
||||
let (rootIdx, nodes) = Research.exportDag term
|
||||
output = unlines $
|
||||
show rootIdx :
|
||||
map (\(tag, refs) -> unwords (tag : map show refs)) nodes
|
||||
writeOutput out output
|
||||
[] -> die "tricu arboricx export --dag: exactly one --target is required"
|
||||
_ -> die "tricu arboricx export --dag: exactly one --target is required"
|
||||
|
||||
runTricu :: String -> String
|
||||
runTricu = formatT TreeCalculus . runTricuT
|
||||
runServe :: TricuArgs -> IO ()
|
||||
runServe opts = do
|
||||
let hostStr = serveHost opts
|
||||
portNum = servePort opts
|
||||
putStrLn $ "Starting Arboricx bundle server on " ++ hostStr ++ ":" ++ show portNum
|
||||
putStrLn $ " GET /bundle/hash/:hash -- primary endpoint"
|
||||
putStrLn $ " GET /bundle/name/:name -- convenience endpoint"
|
||||
putStrLn $ " Content-Type: application/vnd.arboricx.bundle"
|
||||
runServerWithPath (serveDb opts) hostStr portNum
|
||||
|
||||
runTricuT :: String -> T
|
||||
runTricuT input =
|
||||
let asts = parseTricu input
|
||||
finalEnv = evalTricu Map.empty asts
|
||||
in result finalEnv
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Helpers
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
runTricuEnv :: Env -> String -> String
|
||||
runTricuEnv env = formatT TreeCalculus . runTricuTEnv env
|
||||
withContentStore :: Maybe FilePath -> (Connection -> IO a) -> IO a
|
||||
withContentStore mPath act = do
|
||||
conn <- initContentStoreWithPath mPath
|
||||
result <- act conn
|
||||
close conn
|
||||
return result
|
||||
|
||||
writeOutput :: FilePath -> String -> IO ()
|
||||
writeOutput path content
|
||||
| null path = putStr content
|
||||
| otherwise = writeFile path content
|
||||
|
||||
runTricuTEnv :: Env -> String -> T
|
||||
runTricuTEnv env input =
|
||||
let asts = parseTricu input
|
||||
finalEnv = evalTricu env asts
|
||||
in result finalEnv
|
||||
|
||||
runTricuWithEnvT :: String -> (Env, T)
|
||||
runTricuWithEnvT input =
|
||||
let asts = parseTricu input
|
||||
finalEnv = evalTricu Map.empty asts
|
||||
in (finalEnv, result finalEnv)
|
||||
|
||||
runTricuWithEnv :: String -> (Env, String)
|
||||
runTricuWithEnv input =
|
||||
let asts = parseTricu input
|
||||
finalEnv = evalTricu Map.empty asts
|
||||
res = result finalEnv
|
||||
in (finalEnv, formatT TreeCalculus res)
|
||||
|
||||
runTricuEnvWithEnvT :: Env -> String -> (Env, T)
|
||||
runTricuEnvWithEnvT env input =
|
||||
let asts = parseTricu input
|
||||
finalEnv = evalTricu env asts
|
||||
in (finalEnv, result finalEnv)
|
||||
|
||||
runTricuEnvWithEnv :: Env -> String -> (Env, String)
|
||||
runTricuEnvWithEnv env input =
|
||||
let asts = parseTricu input
|
||||
finalEnv = evalTricu env asts
|
||||
res = result finalEnv
|
||||
in (finalEnv, formatT TreeCalculus res)
|
||||
|
||||
chooseExportName :: String -> String -> [Text] -> IO Text
|
||||
chooseExportName explicitName input storedNames
|
||||
| not (null explicitName) = return $ T.pack explicitName
|
||||
| Just firstName <- firstNonEmpty storedNames = return firstName
|
||||
| otherwise = do
|
||||
hPutStrLn stderr $
|
||||
"No stored name found for export target " ++ input ++ "; using export name 'root'. "
|
||||
++ "Use export -n NAME to preserve a semantic name."
|
||||
return "root"
|
||||
|
||||
firstNonEmpty :: [Text] -> Maybe Text
|
||||
firstNonEmpty = go
|
||||
where
|
||||
go [] = Nothing
|
||||
go (x:xs)
|
||||
| T.null x = go xs
|
||||
| otherwise = Just x
|
||||
|
||||
Reference in New Issue
Block a user