typst 0.9.0.1 → 0.10
raw patch · 9 files changed
+112/−65 lines, 9 filesdep +optparse-applicativedep ~typst-symbols
Dependencies added: optparse-applicative
Dependency ranges changed: typst-symbols
Files
- CHANGELOG.md +20/−0
- app/Main.hs +36/−27
- src/Typst/Evaluate.hs +38/−14
- src/Typst/Module/Standard.hs +2/−14
- src/Typst/Parse.hs +1/−1
- src/Typst/Types.hs +4/−2
- src/Typst/Util.hs +6/−3
- test/Main.hs +1/−1
- typst.cabal +4/−3
CHANGELOG.md view
@@ -1,5 +1,25 @@ # Revision history for typst-hs +## 0.10++ * Add --input option to cli for passing key-value pairs to the+ evaluator (as can be done with `typst`).++ * Use optparse-applicative for CLI option parsing.++ * Add `inputs` parameter to `evalTypst`.++ * Remove `sysModule` from `Module.Standard` [API change].+ We now generate `sys` in `initialEvalState`, since only then+ do we know the inputs.++ * Properly locate package root for imports (#98).+ We need to go up the directory hierarchy til we find typst.toml.++ * Use released version of typst-symbols-0.2 with new typst-symbols API.++ * app/Main: remove unused options.+ ## 0.9.0.1 * Fix typo that caused close single quote to be parsed as double
app/Main.hs view
@@ -4,17 +4,19 @@ module Main where -import Control.Monad (foldM, when)+import Control.Monad (when) import qualified Data.ByteString as BS import Data.Maybe (fromMaybe)+import Data.Text (Text)+import qualified Data.Text as T import qualified Data.Text.Encoding as TE import qualified Data.Text.IO as TIO+import Options.Applicative import System.Directory (doesFileExist)-import System.Environment (getArgs, lookupEnv)+import System.Environment (lookupEnv) import System.Exit import System.IO (hPutStrLn, stderr) import System.Timeout (timeout)-import Text.Read (readMaybe) import Text.Show.Pretty (pPrint) import Typst (evaluateTypst, parseTypst) import Typst.Types (Val (..), repr, Operations(..))@@ -25,10 +27,9 @@ { optShowParse :: Bool, optShowEval :: Bool, optShowRepr :: Bool,- optShowLaTeX :: Bool,- optShowHtml :: Bool,- optStandalone :: Bool,- optTimeout :: Maybe (Maybe Int)+ optTimeout :: Maybe Int,+ optInputs :: [(Text, Text)],+ optFile :: Maybe FilePath } deriving (Show, Eq) @@ -37,22 +38,29 @@ hPutStrLn stderr msg exitWith (ExitFailure 1) -parseArgs :: [String] -> IO (Maybe FilePath, Opts)-parseArgs = foldM go (Nothing, Opts False False False False False False Nothing)+optsParser :: Parser Opts+optsParser =+ Opts+ <$> switch (long "parse" <> help "Show parse tree")+ <*> switch (long "eval" <> help "Show evaluated result")+ <*> switch (long "repr" <> help "Show repr output")+ <*> optional (option auto (long "timeout" <> metavar "MS" <> help "Timeout in milliseconds"))+ <*> many (option (eitherReader parseInput)+ (long "input" <> metavar "KEY=VALUE" <> help "Set an input key-value pair (repeatable)"))+ <*> optional (argument str (metavar "FILE" <> help "Input file (reads stdin if omitted)"))++parseInput :: String -> Either String (Text, Text)+parseInput s =+ case break (== '=') s of+ (_, []) -> Left "Expected KEY=VALUE format"+ (k, '=' : v) -> Right (T.pack k, T.pack (unquote v))+ _ -> Left "Expected KEY=VALUE format" where- go (f, opts) "--parse" = pure (f, opts {optShowParse = True})- go (f, opts) "--eval" = pure (f, opts {optShowEval = True})- go (f, opts) "--repr" = pure (f, opts {optShowRepr = True})- go (f, opts) "--latex" = pure (f, opts {optShowLaTeX = True})- go (f, opts) "--html" = pure (f, opts {optShowHtml = True})- go (f, opts) "--standalone" = pure (f, opts {optStandalone = True})- go (f, opts) "--timeout" = pure (f, opts {optTimeout = Just Nothing })- go (f, opts) x- | optTimeout opts == Just Nothing =- pure (f, opts {optTimeout = Just (readMaybe x) })- go _ ('-' : xs) = err $ "Unknown option -" ++ xs- go (Nothing, opts) f = pure (Just f, opts)- go _ _ = err $ "Only one file can be specified as input."+ unquote ('"' : rest)+ | not (null rest) && last rest == '"' = init rest+ unquote ('\'' : rest)+ | not (null rest) && last rest == '\'' = init rest+ unquote x = x operations :: Operations IO operations = Operations@@ -65,14 +73,15 @@ main :: IO () main = () <$ do- (mbfile, opts) <- getArgs >>= parseArgs+ opts <- execParser (info (optsParser <**> helper)+ (fullDesc <> progDesc "Parse and evaluate Typst documents"))+ let mbfile = optFile opts let showAll = case opts of- Opts False False False False False False _ -> True+ Opts False False False _ _ _ -> True _ -> False ( case optTimeout opts of Nothing -> fmap Just- Just Nothing -> timeout 1000- Just (Just ms) -> timeout (ms * 1000)+ Just ms -> timeout (ms * 1000) ) $ do bs <- maybe B.getContents B.readFile mbfile@@ -83,7 +92,7 @@ when (optShowParse opts || showAll) $ do when showAll $ putStrLn "--- parse tree ---" pPrint parseResult- result <- evaluateTypst operations "stdin" parseResult+ result <- evaluateTypst operations (optInputs opts) "stdin" parseResult case result of Left e -> err $ show e Right c -> do
src/Typst/Evaluate.hs view
@@ -46,6 +46,8 @@ import Typst.Util (makeFunction, nthArg) import qualified Toml as Toml import qualified Toml.Schema as Toml+import Paths_typst (version)+import Data.Version (showVersion) -- import Debug.Trace @@ -55,12 +57,14 @@ Monad m => -- | Dictionary of functions for IO operations Operations m ->+ -- | Parameters that would be set with --input in typst, for sys.inputs+ [(Text, Text)] -> -- | Path of parsed content FilePath -> -- | Markup produced by 'parseTypst' [Markup] -> m (Either ParseError Content)-evaluateTypst operations fp =+evaluateTypst operations inputs fp = runParserT (do contents <- mconcat <$> many pContent <* eof -- "All documents are automatically wrapped in a document element."@@ -70,27 +74,38 @@ case toList els of [e] -> pure e _ -> fail "element returned something other than a singleton")- initialEvalState { evalOperations = operations,- evalLocalDir = takeDirectory fp }+ (initialEvalState inputs) { evalOperations = operations,+ evalLocalDir = takeDirectory fp } fp -initialEvalState :: EvalState m-initialEvalState =+initialEvalState :: [(Text, Text)] -> EvalState m+initialEvalState inputs = emptyEvalState { evalIdentifiers = [(BlockScope, mempty)] , evalMathIdentifiers = [(BlockScope, mathModule <> symModule)]- , evalStandardIdentifiers = [(BlockScope, standardModule'')]+ , evalStandardIdentifiers = [(BlockScope, standardModule''')] , evalPackageRoot = "."+ , evalInputs = inputs } where standardModule' = M.insert "eval" evalFunction standardModule- standardModule'' = M.insert "std" (VModule "std" standardModule') standardModule'+ standardModule'' = M.insert+ "sys"+ (VModule "sys" $ M.fromList+ [ ("version", VVersion [0,14,0])+ , ("inputs", VDict $+ OM.fromList (map (\(k,v) -> (Identifier k, VString v))+ inputs'))+ ])+ standardModule'+ standardModule''' = M.insert "std" (VModule "std" standardModule') standardModule''+ inputs' = ("typst-hs-version", T.pack (showVersion version)) : inputs evalFunction = makeFunction $ do code :: Text <- nthArg 1 case parseTypst "eval" ("#{\n" <> code <> "\n}") of Left e -> fail $ "eval: " <> show e Right [Code _ expr] -> -- run in Either monad so we can't access file system- case runParserT (evalExpr expr) initialEvalState "eval" [] of+ case runParserT (evalExpr expr) (initialEvalState inputs) "eval" [] of Failure e -> fail $ "eval: " <> e Success (Left e) -> fail $ "eval: " <> show e Success (Right val) -> pure val@@ -940,7 +955,7 @@ findPackageEntryPoint modname = do let (namespace, rest) = break (=='/') (drop 1 $ T.unpack modname) let (name, rest') = break (==':') $ drop 1 rest- let version = drop 1 rest'+ let pkgversion = drop 1 rest' operations <- evalOperations <$> getState let getEnv var = do mbv <- lift $ lookupEnvVar operations var@@ -965,7 +980,7 @@ let localDir = dataDir </> "typst" let cacheDir = cacheDir' </> "typst" #endif- let subpath = "packages" </> namespace </> name </> version+ let subpath = "packages" </> namespace </> name </> pkgversion inLocal <- lift $ checkExistence operations (localDir </> subpath </> "typst.toml") tomlPath <- if inLocal@@ -993,11 +1008,19 @@ fp' <- findPackageEntryPoint modname operations <- evalOperations <$> getState txt <- lift $ TE.decodeUtf8 <$> loadBytes operations fp'+ let findPkgRoot "" = fail $ "Could not find package root for " <> fp'+ findPkgRoot dir' = do+ exists <- lift $ checkExistence operations+ (dir' </> "typst.toml")+ if exists+ then pure dir'+ else findPkgRoot (takeDirectory dir')+ pkgroot <- findPkgRoot (takeDirectory fp') pure ( takeFileName fp', Identifier (T.pack $ takeWhile (/= ':') . takeBaseName $ T.unpack modname),- Just (takeDirectory fp'),+ Just pkgroot, txt ) else do let fp = T.unpack modname@@ -1013,6 +1036,7 @@ operations <- evalOperations <$> getState currentLocalDir <- evalLocalDir <$> getState currentPackageRoot <- evalPackageRoot <$> getState+ inputs <- evalInputs <$> getState let (pkgroot , localdir) = case mbPackageRoot of Just r -> (r , takeDirectory fp)@@ -1027,9 +1051,9 @@ s <- getState pure (cs, s) )- initialEvalState{evalOperations = operations,- evalLocalDir = localdir,- evalPackageRoot = pkgroot }+ (initialEvalState inputs){evalOperations = operations,+ evalLocalDir = localdir,+ evalPackageRoot = pkgroot } fp ms case res of
src/Typst/Module/Standard.hs view
@@ -7,15 +7,12 @@ module Typst.Module.Standard ( standardModule, symModule,- sysModule, loadFileText, getPath, applyPureFunction ) where -import Paths_typst (version)-import Data.Version (showVersion) import Control.Applicative ((<|>)) import Control.Monad (mplus, unless) import Control.Monad.Reader (lift, ReaderT)@@ -36,10 +33,9 @@ import Text.Read (readMaybe) import qualified Text.XML as XML import qualified Toml-import Typst.Emoji (typstEmojis) import Typst.Module.Calc (calcModule) import Typst.Module.Math (mathModule)-import Typst.Symbols (typstSymbols)+import Typst.Symbols (typstSymbols, typstEmojis) import Typst.Types import Typst.Util import System.FilePath.Posix ((</>), normalise)@@ -52,9 +48,9 @@ M.fromList $ [ ("math", VModule "math" mathModule), ("sym", VModule "sym" symModule),- ("sys", VModule "sys" sysModule), ("emoji", VModule "emoji" emojiModule), ("calc", VModule "calc" calcModule)+ -- sys module is added in initialEvalState ] ++ types ++ colors@@ -68,14 +64,6 @@ ++ construct ++ time ++ dataLoading--sysModule :: M.Map Identifier Val-sysModule =- M.fromList [ ("version", VVersion [0,12,0])- , ("inputs", VDict (OM.fromList- [("typst-hs-version",- VString (T.pack (showVersion version)))]))- ] symModule :: M.Map Identifier Val symModule = M.map VSymbol $ makeSymbolMap typstSymbols
src/Typst/Parse.hs view
@@ -20,7 +20,7 @@ import Text.Parsec.Expr import Text.Read (readMaybe) import Typst.Syntax-import Typst.Shorthands (mathSymbolShorthands)+import Typst.Symbols (mathSymbolShorthands) -- import Debug.Trace
src/Typst/Types.hs view
@@ -624,7 +624,8 @@ evalFlowDirective :: FlowDirective, evalPackageRoot :: FilePath, evalLocalDir :: FilePath,- evalOperations :: Operations m+ evalOperations :: Operations m,+ evalInputs :: [(Text, Text)] } emptyEvalState :: EvalState m@@ -640,7 +641,8 @@ evalFlowDirective = FlowNormal, evalPackageRoot = mempty, evalLocalDir = mempty,- evalOperations = undefined+ evalOperations = undefined,+ evalInputs = [] } data Attempt a
src/Typst/Util.hs view
@@ -29,6 +29,7 @@ import qualified Data.Vector as V import Text.Parsec (getPosition) import Typst.Types+import Typst.Symbols (Sym(..)) data TypeSpec = One ValType@@ -148,11 +149,13 @@ allArgs :: Monad m => ReaderT Arguments (MP m) [Val] allArgs = asks positional -makeSymbolMap :: [(Text, Bool, Text)] -> M.Map Identifier Symbol+makeSymbolMap :: [Sym] -> M.Map Identifier Symbol makeSymbolMap = foldl' go mempty where- go :: M.Map Identifier Symbol -> (Text, Bool, Text) -> M.Map Identifier Symbol- go m (name, accent, v) =+ go :: M.Map Identifier Symbol -> Sym -> M.Map Identifier Symbol+ go m (Sym{ symName = name,+ symIsAccent = accent,+ symText = v }) = case T.split (== '.') name of [] -> m (k : ks) ->
test/Main.hs view
@@ -67,7 +67,7 @@ case parseResult of Left e -> pure $ fromText $ T.pack $ show e Right parsed -> do- evalResult <- evaluateTypst operations input (testParse <> parsed)+ evalResult <- evaluateTypst operations [] input (testParse <> parsed) let parseOutput = "--- parse tree ---\n" <> T.pack (ppShow parsed) <> "\n" case evalResult of Left e ->
typst.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: typst-version: 0.9.0.1+version: 0.10 synopsis: Parsing and evaluating typst syntax. description: A library for parsing and evaluating typst syntax. Typst (<https://typst.app>) is a document layout and@@ -62,7 +62,7 @@ -- other-extensions: build-depends: base >= 4.14 && < 5,- typst-symbols >= 0.1.9 && < 0.1.10,+ typst-symbols >= 0.2 && < 0.3, mtl, vector, parsec,@@ -112,7 +112,8 @@ bytestring, time, pretty-show,- ordered-containers+ ordered-containers,+ optparse-applicative else Buildable: False