pandoc-lua-engine-0.2.0.1/0000755000000000000000000000000007346545000013327 5ustar0000000000000000pandoc-lua-engine-0.2.0.1/COPYING.md0000644000000000000000000004274607346545000014776 0ustar0000000000000000### GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. ### Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. ### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION **0.** This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. **1.** You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. **2.** You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: **a)** You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. **b)** You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. **c)** If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. **3.** You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: **a)** Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, **b)** Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, **c)** Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. **4.** You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. **5.** You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. **6.** Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. **7.** If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. **8.** If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. **9.** The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. **10.** If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. **NO WARRANTY** **11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. **12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ### END OF TERMS AND CONDITIONS ### How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands \`show w' and \`show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than \`show w' and \`show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the [GNU Lesser General Public License](https://www.gnu.org/licenses/lgpl.html) instead of this License.pandoc-lua-engine-0.2.0.1/README.md0000644000000000000000000000023007346545000014601 0ustar0000000000000000# pandoc-lua-engine This package provides a Lua pandoc scripting engine based. It allows to write filters, custom readers, and custom writers in Lua. pandoc-lua-engine-0.2.0.1/pandoc-lua-engine.cabal0000644000000000000000000001502007346545000017577 0ustar0000000000000000cabal-version: 2.4 name: pandoc-lua-engine version: 0.2.0.1 build-type: Simple license: GPL-2.0-or-later license-file: COPYING.md copyright: © 2006-2022 John MacFarlane, 2017-2022 Albert Krewinkel author: John MacFarlane, Albert Krewinkel maintainer: Albert Krewinkel bug-reports: https://github.com/jgm/pandoc/issues homepage: https://pandoc.org category: Text tested-with: GHC == 8.6.5 , GHC == 8.8.4 , GHC == 8.10.7 , GHC == 9.0.2 , GHC == 9.2.5 , GHC == 9.4.4 synopsis: Lua engine to power custom pandoc conversions description: This package provides a pandoc scripting engine based on Lua. extra-source-files: README.md , test/bytestring.bin , test/bytestring.lua , test/bytestring-reader.lua , test/extensions.lua , test/lua/*.lua , test/lua/module/*.lua , test/lua/module/partial.test , test/lua/module/sample.svg , test/lua/module/tiny.epub , test/sample.lua , test/tables.custom , test/tables.native , test/testsuite.native , test/writer.custom , test/writer-template.lua , test/writer-template.out.txt source-repository head type: git location: https://github.com/jgm/pandoc.git subdir: pandoc-lua-engine common common-options default-language: Haskell2010 build-depends: base >= 4.12 && < 5 ghc-options: -Wall -fno-warn-unused-do-bind -Wincomplete-record-updates -Wnoncanonical-monad-instances -Wcpp-undef -Wincomplete-uni-patterns -Widentities -Wpartial-fields -Wmissing-export-lists -Wmissing-signatures -fhide-source-paths if impl(ghc >= 8.10) ghc-options: -Wunused-packages if impl(ghc >= 9.0) ghc-options: -Winvalid-haddock library import: common-options hs-source-dirs: src exposed-modules: Text.Pandoc.Lua other-modules: Text.Pandoc.Lua.Custom , Text.Pandoc.Lua.Filter , Text.Pandoc.Lua.Global , Text.Pandoc.Lua.Init , Text.Pandoc.Lua.Marshal.Chunks , Text.Pandoc.Lua.Marshal.CommonState , Text.Pandoc.Lua.Marshal.Context , Text.Pandoc.Lua.Marshal.Format , Text.Pandoc.Lua.Marshal.PandocError , Text.Pandoc.Lua.Marshal.ReaderOptions , Text.Pandoc.Lua.Marshal.Reference , Text.Pandoc.Lua.Marshal.Sources , Text.Pandoc.Lua.Marshal.Template , Text.Pandoc.Lua.Marshal.WriterOptions , Text.Pandoc.Lua.Module.CLI , Text.Pandoc.Lua.Module.Format , Text.Pandoc.Lua.Module.JSON , Text.Pandoc.Lua.Module.MediaBag , Text.Pandoc.Lua.Module.Pandoc , Text.Pandoc.Lua.Module.Scaffolding , Text.Pandoc.Lua.Module.Structure , Text.Pandoc.Lua.Module.System , Text.Pandoc.Lua.Module.Template , Text.Pandoc.Lua.Module.Text , Text.Pandoc.Lua.Module.Types , Text.Pandoc.Lua.Module.Utils , Text.Pandoc.Lua.Orphans , Text.Pandoc.Lua.PandocLua , Text.Pandoc.Lua.Writer.Classic , Text.Pandoc.Lua.Writer.Scaffolding build-depends: SHA >= 1.6 && < 1.7 , aeson , bytestring >= 0.9 && < 0.12 , citeproc >= 0.8 && < 0.9 , containers >= 0.6.0.1 && < 0.7 , data-default >= 0.4 && < 0.8 , doclayout >= 0.4 && < 0.5 , doctemplates >= 0.11 && < 0.12 , exceptions >= 0.8 && < 0.11 , hslua >= 2.3 && < 2.4 , hslua-module-doclayout>= 1.1 && < 1.2 , hslua-module-path >= 1.1 && < 1.2 , hslua-module-system >= 1.1 && < 1.2 , hslua-module-text >= 1.1 && < 1.2 , hslua-module-version >= 1.1 && < 1.2 , hslua-module-zip >= 1.1 && < 1.2 , hslua-repl >= 0.1.1 && < 0.2 , lpeg >= 1.0.4 && < 1.1 , mtl >= 2.2 && < 2.4 , pandoc >= 3.1.2 && < 3.2 , pandoc-lua-marshal >= 0.2.2 && < 0.3 , pandoc-types >= 1.22 && < 1.24 , parsec >= 3.1 && < 3.2 , text >= 1.1.1 && < 2.1 test-suite test-pandoc-lua-engine import: common-options type: exitcode-stdio-1.0 main-is: test-pandoc-lua-engine.hs hs-source-dirs: test build-depends: pandoc-lua-engine , bytestring , directory , data-default , exceptions >= 0.8 && < 0.11 , filepath , hslua >= 2.3 && < 2.4 , pandoc , pandoc-types >= 1.22 && < 1.24 , tasty , tasty-golden , tasty-hunit , tasty-lua >= 1.1 && < 1.2 , text >= 1.1.1 && < 2.1 other-modules: Tests.Lua , Tests.Lua.Module , Tests.Lua.Reader , Tests.Lua.Writer pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/0000755000000000000000000000000007346545000016246 5ustar0000000000000000pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua.hs0000644000000000000000000000255607346545000017333 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Lua Copyright : Copyright © 2017-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Running pandoc Lua filters. -} module Text.Pandoc.Lua ( -- * High-level functions applyFilter , loadCustom -- * Low-level functions , Global(..) , setGlobals , runLua , runLuaNoEnv -- * Engine , getEngine ) where import Control.Monad.IO.Class (MonadIO (liftIO)) import HsLua.Core (getglobal, openlibs, run, top, tostring) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Filter (applyFilter) import Text.Pandoc.Lua.Global (Global (..), setGlobals) import Text.Pandoc.Lua.Init (runLua, runLuaNoEnv) import Text.Pandoc.Lua.Custom (loadCustom) import Text.Pandoc.Lua.Orphans () import Text.Pandoc.Scripting (ScriptingEngine (..)) import qualified Text.Pandoc.UTF8 as UTF8 -- | Constructs the Lua scripting engine. getEngine :: MonadIO m => m ScriptingEngine getEngine = do versionName <- liftIO . run @PandocError $ do openlibs getglobal "_VERSION" tostring top pure $ ScriptingEngine { engineName = maybe "Lua (unknown version)" UTF8.toText versionName , engineApplyFilter = applyFilter , engineLoadCustom = loadCustom } pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/0000755000000000000000000000000007346545000016767 5ustar0000000000000000pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Custom.hs0000644000000000000000000001374007346545000020602 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Lua.Custom Copyright : © 2021-2023 Albert Krewinkel, John MacFarlane License : GPL-2.0-or-later Maintainer : Albert Krewinkel Supports custom parsers written in Lua which produce a Pandoc AST. -} module Text.Pandoc.Lua.Custom ( loadCustom ) where import Control.Exception import Control.Monad ((<=<), (<$!>)) import Control.Monad.IO.Class (MonadIO) import Data.Maybe (fromMaybe) import HsLua as Lua hiding (Operation (Div)) import Text.Pandoc.Class (PandocMonad, findFileWithDataFallback) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Global (Global (..), setGlobals) import Text.Pandoc.Lua.Init (runLuaWith) import Text.Pandoc.Lua.Marshal.Format (peekExtensionsConfig) import Text.Pandoc.Lua.Marshal.Pandoc (peekPandoc) import Text.Pandoc.Lua.Marshal.WriterOptions (pushWriterOptions) import Text.Pandoc.Readers (Reader (..)) import Text.Pandoc.Sources (ToSources(..)) import Text.Pandoc.Scripting (CustomComponents (..)) import Text.Pandoc.Writers (Writer (..)) import qualified Text.Pandoc.Lua.Writer.Classic as Classic -- | Convert custom markup to Pandoc. loadCustom :: (PandocMonad m, MonadIO m) => FilePath -> m (CustomComponents m) loadCustom luaFile = do luaState <- liftIO newGCManagedState luaFile' <- fromMaybe luaFile <$> findFileWithDataFallback "custom" luaFile either throw pure <=< runLuaWith luaState $ do let globals = [ PANDOC_SCRIPT_FILE luaFile' ] setGlobals globals dofileTrace (Just luaFile') >>= \case OK -> pure () _ -> throwErrorAsException mextsConf <- rawgetglobal "Extensions" >>= \case TypeNil -> pure Nothing TypeFunction -> Just <$!> do callTrace 0 1 forcePeek $ peekExtensionsConfig top `lastly` pop 1 _ -> Just <$!> do forcePeek $ peekExtensionsConfig top `lastly` pop 1 mtemplate <- rawgetglobal "Template" >>= \case TypeNil -> pure Nothing TypeFunction -> Just <$!> do callTrace 0 1 forcePeek $ peekText top `lastly` pop 1 _ -> Just <$!> do forcePeek $ peekText top `lastly` pop 1 mreader <- rawgetglobal "Reader" >>= \case TypeNil -> do pop 1 rawgetglobal "ByteStringReader" >>= \case TypeNil -> pure Nothing _ -> do setfield registryindex readerField pure . Just $ byteStringReader luaState _ -> do setfield registryindex readerField pure . Just $ textReader luaState mwriter <- rawgetglobal "Writer" >>= \case TypeNil -> rawgetglobal "ByteStringWriter" >>= \case TypeNil -> do -- Neither `Writer` nor `BinaryWriter` are defined. Check for -- "Doc"; if present, use the file as a classic writer. docType <- rawgetglobal "Doc" pop 3 -- remove nils/value of "Writer", "ByteStringWriter", "Doc" pure $ if docType /= TypeFunction then Nothing else Just . TextWriter $ \opts doc -> liftIO $ withGCManagedState luaState $ Classic.runCustom @PandocError opts doc _ -> Just <$!> do -- Binary writer. Writer function is on top of the stack. setfield registryindex writerField pure $ ByteStringWriter $ \opts doc -> -- Call writer with document and writer options as arguments. liftIO $ withGCManagedState luaState $ do getfield registryindex writerField push doc pushWriterOptions opts callTrace 2 1 forcePeek @PandocError $ peekLazyByteString top _ -> Just <$!> do -- New-type text writer. Writer function is on top of the stack. setfield registryindex writerField pure $ TextWriter $ \opts doc -> liftIO $ withGCManagedState luaState $ do getfield registryindex writerField push doc pushWriterOptions opts callTrace 2 1 forcePeek @PandocError $ peekText top pure $ CustomComponents { customReader = mreader , customWriter = mwriter , customTemplate = mtemplate , customExtensions = mextsConf } -- | "Raw", non-metatable lookup of a key in the global table. -- -- Most classic writers contain code that throws an error if a global -- is not present. This would break our check for the existence of a -- "Writer" function. We resort to raw access for that reason, but -- could also catch the error instead. -- -- TODO: This function ensures the proper behavior of legacy custom -- writers. It should be replaced with 'getglobal' in the future. rawgetglobal :: LuaError e => Name -> LuaE e Lua.Type rawgetglobal x = do pushglobaltable pushName x rawget (nth 2) <* remove (nth 2) -- remove global table -- | Name under which the reader function is stored in the registry. readerField :: Name readerField = "Pandoc Reader function" -- | Name under which the writer function is stored in the registry. writerField :: Name writerField = "Pandoc Writer function" -- | Runs a Lua action in a continueable environment. inLua :: MonadIO m => GCManagedState -> LuaE PandocError a -> m a inLua st = liftIO . withGCManagedState @PandocError st -- | Returns the ByteStringReader function byteStringReader :: MonadIO m => GCManagedState -> Reader m byteStringReader st = ByteStringReader $ \ropts input -> inLua st $ do getfield registryindex readerField push input push ropts pcallTrace 2 1 >>= \case OK -> forcePeek $ peekPandoc top _ -> throwErrorAsException -- | Returns the TextReader function textReader :: MonadIO m => GCManagedState -> Reader m textReader st = TextReader $ \ropts srcs -> inLua st $ do let input = toSources srcs getfield registryindex readerField push input push ropts pcallTrace 2 1 >>= \case OK -> forcePeek $ peekPandoc top _ -> throwErrorAsException pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Filter.hs0000644000000000000000000000545507346545000020561 0ustar0000000000000000{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE IncoherentInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Lua.Filter Copyright : © 2012-2023 John MacFarlane, © 2017-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Types and functions for running Lua filters. -} module Text.Pandoc.Lua.Filter ( applyFilter ) where import Control.Monad ((>=>), (<$!>)) import HsLua as Lua import Text.Pandoc.Definition import Text.Pandoc.Filter (Environment (..)) import Text.Pandoc.Lua.Marshal.AST import Text.Pandoc.Lua.Marshal.Filter import Text.Pandoc.Lua.Global (Global (..), setGlobals) import Text.Pandoc.Lua.Init (runLua) import Text.Pandoc.Lua.PandocLua () import Control.Exception (throw) import qualified Data.Text as T import Text.Pandoc.Class (PandocMonad) import Control.Monad.Trans (MonadIO) import Text.Pandoc.Error (PandocError (PandocFilterError, PandocLuaError)) -- | Transform document using the filter defined in the given file. runFilterFile :: FilePath -> Pandoc -> LuaE PandocError Pandoc runFilterFile filterPath doc = do oldtop <- gettop stat <- dofileTrace (Just filterPath) if stat /= Lua.OK then throwErrorAsException else do newtop <- gettop -- Use the returned filters, or the implicitly defined global -- filter if nothing was returned. luaFilters <- forcePeek $ if newtop - oldtop >= 1 then peekList peekFilter top else (:[]) <$!> (liftLua pushglobaltable *> peekFilter top) settop oldtop runAll luaFilters doc runAll :: [Filter] -> Pandoc -> LuaE PandocError Pandoc runAll = foldr ((>=>) . applyFully) return -- | Run the Lua filter in @filterPath@ for a transformation to the -- target format (first element in args). Pandoc uses Lua init files to -- setup the Lua interpreter. applyFilter :: (PandocMonad m, MonadIO m) => Environment -> [String] -> FilePath -> Pandoc -> m Pandoc applyFilter fenv args fp doc = do let globals = [ FORMAT $ case args of x:_ -> T.pack x _ -> "" , PANDOC_READER_OPTIONS (envReaderOptions fenv) , PANDOC_WRITER_OPTIONS (envWriterOptions fenv) , PANDOC_SCRIPT_FILE fp ] runLua >=> forceResult fp $ do setGlobals globals runFilterFile fp doc forceResult :: (PandocMonad m, MonadIO m) => FilePath -> Either PandocError Pandoc -> m Pandoc forceResult fp eitherResult = case eitherResult of Right x -> return x Left err -> throw . PandocFilterError (T.pack fp) $ case err of PandocLuaError msg -> msg _ -> T.pack $ show err pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Global.hs0000644000000000000000000000446307346545000020532 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua Copyright : Copyright © 2017-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Pandoc's Lua globals. -} module Text.Pandoc.Lua.Global ( Global (..) , setGlobals ) where import HsLua as Lua import HsLua.Module.Version (pushVersion) import Text.Pandoc.Class (CommonState) import Text.Pandoc.Definition (Pandoc, pandocTypesVersion) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Marshal.CommonState (pushCommonState) import Text.Pandoc.Lua.Marshal.Pandoc (pushPandoc) import Text.Pandoc.Lua.Marshal.ReaderOptions (pushReaderOptionsReadonly) import Text.Pandoc.Lua.Marshal.WriterOptions (pushWriterOptions) import Text.Pandoc.Lua.PandocLua () import Text.Pandoc.Options (ReaderOptions, WriterOptions) import Text.Pandoc.Version (pandocVersion) import qualified Data.Text as Text -- | Permissible global Lua variables. data Global = FORMAT Text.Text | PANDOC_API_VERSION | PANDOC_DOCUMENT Pandoc | PANDOC_READER_OPTIONS ReaderOptions | PANDOC_WRITER_OPTIONS WriterOptions | PANDOC_SCRIPT_FILE FilePath | PANDOC_STATE CommonState | PANDOC_VERSION -- Cannot derive instance of Data because of CommonState -- | Set all given globals. setGlobals :: [Global] -> LuaE PandocError () setGlobals = mapM_ setGlobal setGlobal :: Global -> LuaE PandocError () setGlobal global = case global of -- This could be simplified if Global was an instance of Data. FORMAT format -> do Lua.pushText format Lua.setglobal "FORMAT" PANDOC_API_VERSION -> do pushVersion pandocTypesVersion Lua.setglobal "PANDOC_API_VERSION" PANDOC_DOCUMENT doc -> do pushPandoc doc Lua.setglobal "PANDOC_DOCUMENT" PANDOC_READER_OPTIONS ropts -> do pushReaderOptionsReadonly ropts Lua.setglobal "PANDOC_READER_OPTIONS" PANDOC_WRITER_OPTIONS wopts -> do pushWriterOptions wopts Lua.setglobal "PANDOC_WRITER_OPTIONS" PANDOC_SCRIPT_FILE filePath -> do Lua.pushString filePath Lua.setglobal "PANDOC_SCRIPT_FILE" PANDOC_STATE commonState -> do pushCommonState commonState Lua.setglobal "PANDOC_STATE" PANDOC_VERSION -> do pushVersion pandocVersion Lua.setglobal "PANDOC_VERSION" pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Init.hs0000644000000000000000000002125707346545000020235 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {- | Module : Text.Pandoc.Lua Copyright : Copyright © 2017-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Functions to initialize the Lua interpreter. -} module Text.Pandoc.Lua.Init ( runLua , runLuaNoEnv , runLuaWith ) where import Control.Monad (forM, forM_, when) import Control.Monad.Catch (throwM, try) import Control.Monad.Trans (MonadIO (..)) import Data.Maybe (catMaybes) import Data.Version (makeVersion) import HsLua as Lua hiding (status, try) import Text.Pandoc.Class (PandocMonad (..)) import Text.Pandoc.Data (readDataFile) import Text.Pandoc.Error (PandocError (PandocLuaError)) import Text.Pandoc.Lua.Global (Global (..), setGlobals) import Text.Pandoc.Lua.Marshal.List (newListMetatable, pushListModule) import Text.Pandoc.Lua.PandocLua (PandocLua (..), liftPandocLua) import qualified Data.ByteString.Char8 as Char8 import qualified Data.Text as T import qualified Lua.LPeg as LPeg import qualified HsLua.Aeson import qualified HsLua.Module.DocLayout as Module.Layout import qualified HsLua.Module.Path as Module.Path import qualified HsLua.Module.Zip as Module.Zip import qualified Text.Pandoc.Lua.Module.CLI as Pandoc.CLI import qualified Text.Pandoc.Lua.Module.Format as Pandoc.Format import qualified Text.Pandoc.Lua.Module.JSON as Pandoc.JSON import qualified Text.Pandoc.Lua.Module.MediaBag as Pandoc.MediaBag import qualified Text.Pandoc.Lua.Module.Pandoc as Module.Pandoc import qualified Text.Pandoc.Lua.Module.Scaffolding as Pandoc.Scaffolding import qualified Text.Pandoc.Lua.Module.Structure as Pandoc.Structure import qualified Text.Pandoc.Lua.Module.System as Pandoc.System import qualified Text.Pandoc.Lua.Module.Template as Pandoc.Template import qualified Text.Pandoc.Lua.Module.Text as Pandoc.Text import qualified Text.Pandoc.Lua.Module.Types as Pandoc.Types import qualified Text.Pandoc.Lua.Module.Utils as Pandoc.Utils -- | Run the Lua interpreter, using pandoc's default way of environment -- initialization. runLua :: (PandocMonad m, MonadIO m) => LuaE PandocError a -> m (Either PandocError a) runLua action = do runPandocLuaWith Lua.run . try $ do initLuaState liftPandocLua action runLuaWith :: (PandocMonad m, MonadIO m) => GCManagedState -> LuaE PandocError a -> m (Either PandocError a) runLuaWith luaState action = do runPandocLuaWith (withGCManagedState luaState) . try $ do initLuaState liftPandocLua action -- | Like 'runLua', but ignores all environment variables like @LUA_PATH@. runLuaNoEnv :: (PandocMonad m, MonadIO m) => LuaE PandocError a -> m (Either PandocError a) runLuaNoEnv action = do runPandocLuaWith Lua.run . try $ do liftPandocLua $ do -- This is undocumented, but works -- the code is adapted from the -- `lua.c` sources for the default interpreter. Lua.pushboolean True Lua.setfield Lua.registryindex "LUA_NOENV" initLuaState liftPandocLua action -- | Modules that are loaded at startup and assigned to fields in the -- pandoc module. -- -- Note that @pandoc.List@ is not included here for technical reasons; -- it must be handled separately. loadedModules :: [Module PandocError] loadedModules = [ Pandoc.CLI.documentedModule , Pandoc.Format.documentedModule , Pandoc.JSON.documentedModule , Pandoc.MediaBag.documentedModule , Pandoc.Scaffolding.documentedModule , Pandoc.Structure.documentedModule , Pandoc.System.documentedModule , Pandoc.Template.documentedModule , Pandoc.Text.documentedModule , Pandoc.Types.documentedModule , Pandoc.Utils.documentedModule , Module.Layout.documentedModule { moduleName = "pandoc.layout" } `allSince` [2,18] , Module.Path.documentedModule { moduleName = "pandoc.path" } `allSince` [2,12] , Module.Zip.documentedModule { moduleName = "pandoc.zip" } `allSince` [3,0] ] where allSince mdl version = mdl { moduleFunctions = map (`since` makeVersion version) $ moduleFunctions mdl } -- | Initialize the lua state with all required values initLuaState :: PandocLua () initLuaState = do liftPandocLua Lua.openlibs initJsonMetatable initPandocModule installLpegSearcher setGlobalModules loadInitScript "init.lua" where initPandocModule :: PandocLua () initPandocModule = liftPandocLua $ do -- Push module table registerModule Module.Pandoc.documentedModule -- load modules and add them to the `pandoc` module table. forM_ loadedModules $ \mdl -> do registerModule mdl -- pandoc.text must be require-able as 'text' for backwards compat. when (moduleName mdl == "pandoc.text") $ do getfield registryindex loaded pushvalue (nth 2) setfield (nth 2) "text" pop 1 -- _LOADED -- Shorten name, drop everything before the first dot (if any). let fieldname (Name mdlname) = Name . maybe mdlname snd . Char8.uncons . snd $ Char8.break (== '.') mdlname Lua.setfield (nth 2) (fieldname $ moduleName mdl) -- pandoc.List is low-level and must be opened differently. requirehs "pandoc.List" (const pushListModule) setfield (nth 2) "List" -- assign module to global variable Lua.setglobal "pandoc" loadInitScript :: FilePath -> PandocLua () loadInitScript scriptFile = do script <- readDataFile scriptFile status <- liftPandocLua $ Lua.dostring script when (status /= Lua.OK) . liftPandocLua $ do err <- popException let prefix = "Couldn't load '" <> T.pack scriptFile <> "':\n" throwM . PandocLuaError . (prefix <>) $ case err of PandocLuaError msg -> msg _ -> T.pack $ show err setGlobalModules :: PandocLua () setGlobalModules = liftPandocLua $ do let globalModules = [ ("lpeg", LPeg.luaopen_lpeg_ptr) -- must be loaded first , ("re", LPeg.luaopen_re_ptr) -- re depends on lpeg ] loadedBuiltInModules <- fmap catMaybes . forM globalModules $ \(pkgname, luaopen) -> do Lua.pushcfunction luaopen usedBuiltIn <- Lua.pcall 0 1 Nothing >>= \case OK -> do -- all good, loading succeeded -- register as loaded module so later modules can rely on this Lua.getfield Lua.registryindex Lua.loaded Lua.pushvalue (Lua.nth 2) Lua.setfield (Lua.nth 2) pkgname Lua.pop 1 -- pop _LOADED return True _ -> do -- built-in library failed, load system lib Lua.pop 1 -- ignore error message -- Try loading via the normal package loading mechanism. Lua.getglobal "require" Lua.pushName pkgname Lua.call 1 1 -- Throws an exception if loading failed again! return False -- Module on top of stack. Register as global Lua.setglobal pkgname return $ if usedBuiltIn then Just pkgname else Nothing -- Remove module entry from _LOADED table in registry if we used a -- built-in library. This ensures that later calls to @require@ will -- prefer the shared library, if any. forM_ loadedBuiltInModules $ \pkgname -> do Lua.getfield Lua.registryindex Lua.loaded Lua.pushnil Lua.setfield (Lua.nth 2) pkgname Lua.pop 1 -- registry installLpegSearcher :: PandocLua () installLpegSearcher = liftPandocLua $ do Lua.getglobal' "package.searchers" Lua.pushHaskellFunction $ Lua.state >>= liftIO . LPeg.lpeg_searcher Lua.rawseti (Lua.nth 2) . (+1) . fromIntegral =<< Lua.rawlen (Lua.nth 2) Lua.pop 1 -- remove 'package.searchers' from stack -- | Setup the metatable that's assigned to Lua tables that were created -- from/via JSON arrays. initJsonMetatable :: PandocLua () initJsonMetatable = liftPandocLua $ do newListMetatable HsLua.Aeson.jsonarray (pure ()) Lua.pop 1 -- | Evaluate a @'PandocLua'@ computation, running all contained Lua -- operations. runPandocLuaWith :: (PandocMonad m, MonadIO m) => (forall b. LuaE PandocError b -> IO b) -> PandocLua a -> m a runPandocLuaWith runner pLua = do origState <- getCommonState globals <- defaultGlobals (result, newState) <- liftIO . runner . unPandocLua $ do putCommonState origState liftPandocLua $ setGlobals globals r <- pLua c <- getCommonState return (r, c) putCommonState newState return result -- | Global variables which should always be set. defaultGlobals :: PandocMonad m => m [Global] defaultGlobals = do commonState <- getCommonState return [ PANDOC_API_VERSION , PANDOC_STATE commonState , PANDOC_VERSION ] pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Marshal/0000755000000000000000000000000007346545000020356 5ustar0000000000000000pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Marshal/Chunks.hs0000644000000000000000000001065407346545000022153 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Marshal.Chunks Copyright : © 2022 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Marshaling chunks, i.e., pandoc subdocuments. -} module Text.Pandoc.Lua.Marshal.Chunks ( peekChunk , pushChunk , peekChunkedDoc , pushChunkedDoc ) where import Control.Monad ((<$!>)) import Data.Tree (Tree (..)) import HsLua import Text.Pandoc.Chunks (Chunk (..), ChunkedDoc (..), SecInfo (..)) import Text.Pandoc.Lua.Marshal.AST -- | Retrieves a 'Chunk' from the Lua stack. peekChunk :: LuaError e => Peeker e Chunk peekChunk = peekUD typeChunk -- | Pushes a 'Chunk' to the top of the Lua stack. pushChunk :: LuaError e => Pusher e Chunk pushChunk = pushUD typeChunk typeChunk :: LuaError e => DocumentedType e Chunk typeChunk = deftype "pandoc.Chunk" [ operation Tostring $ lambda ### liftPure show <#> udparam typeChunk "chunk" "chunk to print in native format" =#> functionResult pushString "string" "Haskell representation" ] [ property "heading" "heading text" (pushInlines, chunkHeading) (peekInlinesFuzzy, \chunk inlns -> chunk{ chunkHeading = inlns }) , property "id" "identifier" (pushText, chunkId) (peekText, \chunk ident -> chunk{ chunkId = ident }) , property "level" "level of topmost heading in chunk" (pushIntegral, chunkLevel) (peekIntegral, \chunk level -> chunk{ chunkLevel = level }) , property "number" "chunk number" (pushIntegral, chunkNumber) (peekIntegral, \chunk number -> chunk{ chunkNumber = number }) , property "section_number" "hierarchical section number" (pushMaybe pushText, chunkSectionNumber) (peekMaybe peekText, \chunk secnum -> chunk{ chunkSectionNumber = secnum }) , property "path" "target filepath for this chunk" (pushString, chunkPath) (peekString, \chunk path -> chunk{ chunkPath = path }) , property "up" "link to the enclosing section chunk, if any" (pushMaybe pushChunk, chunkUp) (peekMaybe peekChunk, \chunk up -> chunk{ chunkUp = up }) , property "prev" "link to the previous section, if any" (pushMaybe pushChunk, chunkPrev) (peekMaybe peekChunk, \chunk prev -> chunk{ chunkPrev = prev }) , property "next" "link to the next section, if any" (pushMaybe pushChunk, chunkNext) (peekMaybe peekChunk, \chunk next' -> chunk{ chunkNext = next' }) , property "unlisted" ( "whether the section in this chunk should be listed in the TOC" <> "even if the chunk has no section number" ) (pushBool, chunkUnlisted) (peekBool, \chunk unlisted -> chunk { chunkUnlisted = unlisted }) , property "contents" "the chunk's block contents" (pushBlocks, chunkContents) (peekBlocksFuzzy, \chunk blks -> chunk{ chunkContents = blks }) ] -- | Retrieves a 'ChunkedDoc' from the Lua stack. peekChunkedDoc :: LuaError e => Peeker e ChunkedDoc peekChunkedDoc = peekUD typeChunkedDoc -- | Pushes a 'ChunkedDoc to the top of the Lua stack. pushChunkedDoc :: LuaError e => Pusher e ChunkedDoc pushChunkedDoc = pushUD typeChunkedDoc -- | Lua type for 'ChunkedDoc' values. typeChunkedDoc :: LuaError e => DocumentedType e ChunkedDoc typeChunkedDoc = deftype "pandoc.ChunkedDoc" [] [ readonly "chunks" "list of chunks that make up the document" (pushList pushChunk, chunkedChunks) , readonly "meta" "the document's metadata" (pushMeta, chunkedMeta) , readonly "toc" "table of contents information" (pushTocTree, chunkedTOC) ] -- | Pushes a TOC tree to the stack. The resulting object is a list with -- the top-level entry placed at index @0@ and all subentries as the -- rest of the list. pushTocTree :: LuaError e => Pusher e (Tree SecInfo) pushTocTree (Node secInfo subSecInfo) = do pushList pushTocTree subSecInfo pushSecInfo secInfo rawseti (nth 2) 0 pushSecInfo :: LuaError e => Pusher e SecInfo pushSecInfo = pushAsTable [ ("title" , pushInlines . secTitle) , ("number" , maybe pushnil pushText . secNumber) , ("id" , pushText . secId) , ("path" , pushText . secPath) , ("level" , pushIntegral . secLevel) ] peekMaybe :: LuaError e => Peeker e a -> Peeker e (Maybe a) peekMaybe p idx = liftLua (isnoneornil idx) >>= \case True -> pure Nothing False -> Just <$!> p idx pushMaybe :: LuaError e => Pusher e a -> Pusher e (Maybe a) pushMaybe = maybe pushnil pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Marshal/CommonState.hs0000644000000000000000000000504307346545000023145 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Marshal.CommonState Copyright : © 2012-2023 John MacFarlane © 2017-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Instances to marshal (push) and unmarshal (peek) the common state. -} module Text.Pandoc.Lua.Marshal.CommonState ( typeCommonState , peekCommonState , pushCommonState ) where import HsLua import Text.Pandoc.Class (CommonState (..)) import Text.Pandoc.Logging (LogMessage, showLogMessage) import Text.Pandoc.Lua.Marshal.List (pushPandocList) import qualified Data.Aeson as Aeson -- | Lua type used for the @CommonState@ object. typeCommonState :: LuaError e => DocumentedType e CommonState typeCommonState = deftype "pandoc CommonState" [] [ readonly "input_files" "input files passed to pandoc" (pushPandocList pushString, stInputFiles) , readonly "output_file" "the file to which pandoc will write" (maybe pushnil pushString, stOutputFile) , readonly "log" "list of log messages" (pushPandocList (pushUD typeLogMessage), stLog) , readonly "request_headers" "headers to add for HTTP requests" (pushPandocList (pushPair pushText pushText), stRequestHeaders) , readonly "resource_path" "path to search for resources like included images" (pushPandocList pushString, stResourcePath) , readonly "source_url" "absolute URL + dir of 1st source file" (maybe pushnil pushText, stSourceURL) , readonly "user_data_dir" "directory to search for data files" (maybe pushnil pushString, stUserDataDir) , readonly "trace" "controls whether tracing messages are issued" (pushBool, stTrace) , readonly "verbosity" "verbosity level" (pushString . show, stVerbosity) ] peekCommonState :: LuaError e => Peeker e CommonState peekCommonState = peekUD typeCommonState pushCommonState :: LuaError e => Pusher e CommonState pushCommonState = pushUD typeCommonState typeLogMessage :: LuaError e => DocumentedType e LogMessage typeLogMessage = deftype "pandoc LogMessage" [ operation Index $ defun "__tostring" ### liftPure showLogMessage <#> udparam typeLogMessage "msg" "object" =#> functionResult pushText "string" "stringified log message" , operation (CustomOperation "__tojson") $ lambda ### liftPure Aeson.encode <#> udparam typeLogMessage "msg" "object" =#> functionResult pushLazyByteString "string" "JSON encoded object" ] mempty -- no members pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Marshal/Context.hs0000644000000000000000000000435507346545000022345 0ustar0000000000000000{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.Marshaling.Context Copyright : © 2012-2023 John MacFarlane © 2017-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Marshaling instance for doctemplates Context and its components. -} module Text.Pandoc.Lua.Marshal.Context ( peekContext , pushContext ) where import Control.Monad (when, (<$!>)) import Data.Text (Text) import HsLua as Lua import HsLua.Module.DocLayout (peekDoc, pushDoc) import Text.DocTemplates (Context(..), Val(..)) instance Pushable (Context Text) where push = pushContext instance Pushable (Val Text) where push = pushVal -- | Retrieves a template context from the Lua stack. peekContext :: LuaError e => Peeker e (Context Text) peekContext idx = Context <$!> peekMap peekText peekVal idx -- | Pushes a template context to the Lua stack. pushContext :: LuaError e => Pusher e (Context Text) pushContext ctx = do pushMap pushText pushVal $ unContext ctx created <- Lua.newmetatable "pandoc Context" when created $ do pushName "__concat" pushHaskellFunction $ do c1 <- forcePeek $ peekContext (nthBottom 1) c2 <- forcePeek $ peekContext (nthBottom 2) pushContext (c1 <> c2) return 1 rawset (nth 3) setmetatable (nth 2) pushVal :: LuaError e => Pusher e (Val Text) pushVal = \case NullVal -> Lua.pushnil BoolVal b -> Lua.pushBool b MapVal ctx -> pushContext ctx ListVal xs -> pushList pushVal xs SimpleVal d -> pushDoc d peekVal :: LuaError e => Peeker e (Val Text) peekVal idx = liftLua (ltype idx) >>= \case TypeNil -> pure NullVal TypeBoolean -> BoolVal <$!> peekBool idx TypeNumber -> SimpleVal <$!> peekDoc idx TypeString -> SimpleVal <$!> peekDoc idx TypeTable -> do len <- liftLua $ Lua.rawlen idx if len <= 0 then MapVal <$!> peekContext idx else ListVal <$!> peekList peekVal idx TypeUserdata -> SimpleVal <$!> peekDoc idx _ -> failPeek =<< typeMismatchMessage "Doc, string, boolean, table, or nil" idx pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Marshal/Format.hs0000644000000000000000000001221107346545000022137 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.Marshaling.Format Copyright : © 2022-2023 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Marshaling functions and instance for format related types, including 'Extensions' and 'ExtensionConfig'. -} module Text.Pandoc.Lua.Marshal.Format ( peekExtensions , pushExtensions , peekExtensionsConfig , pushExtensionsConfig , peekFlavoredFormat ) where import Control.Applicative ((<|>)) import Control.Monad ((<$!>)) import Data.Maybe (fromMaybe) import HsLua import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.Extensions ( Extension, Extensions, extensionsFromList, extensionsToList , getDefaultExtensions, readExtension, showExtension ) import Text.Pandoc.Format ( ExtensionsConfig (..), ExtensionsDiff (..), FlavoredFormat (..) , diffExtensions, parseFlavoredFormat) import Text.Pandoc.Lua.PandocLua (PandocLua (unPandocLua)) -- | Retrieves a single 'Extension' from the Lua stack. peekExtension :: LuaError e => Peeker e Extension peekExtension idx = do extString <- peekString idx return $ readExtension extString {-# INLINE peekExtension #-} -- | Pushes an individual 'Extension' to the Lua stack. pushExtension :: LuaError e => Pusher e Extension pushExtension = pushText . showExtension {-# INLINE pushExtension #-} -- | Retrieves an 'Extensions' set from the Lua stack. peekExtensions :: LuaError e => Peeker e Extensions peekExtensions = fmap extensionsFromList . peekList peekExtension {-# INLINE peekExtensions #-} -- | Pushes a set of 'Extensions' to the top of the Lua stack. pushExtensions :: LuaError e => Pusher e Extensions pushExtensions = pushViaJSON {-# INLINE pushExtensions #-} instance Peekable Extensions where safepeek = peekExtensions instance Pushable Extensions where push = pushExtensions -- | Retrieves an 'ExtensionsConfig' value from the Lua stack. peekExtensionsConfig :: LuaError e => Peeker e ExtensionsConfig peekExtensionsConfig idx = do diff <- peekExtensionsDiff idx return $ ExtensionsConfig { extsDefault = extsToEnable diff , extsSupported = extsToEnable diff <> extsToDisable diff } -- | Pushes an 'ExtensionsConfig' value as a table with that maps -- extensions to their default enabled/disabled status. pushExtensionsConfig :: LuaError e => Pusher e ExtensionsConfig pushExtensionsConfig (ExtensionsConfig def supported) = pushKeyValuePairs pushExtension pushBool $ map (,False) (extensionsToList supported) ++ map (,True) (extensionsToList def) instance Peekable ExtensionsConfig where safepeek = peekExtensionsConfig peekExtensionsDiff :: LuaError e => Peeker e ExtensionsDiff peekExtensionsDiff = typeChecked "table" istable $ \idx -> (do en <- peekFieldRaw (emptyOr (fmap Just . peekExtensions)) "enable" idx di <- peekFieldRaw (emptyOr (fmap Just . peekExtensions)) "disable" idx if (en, di) == (Nothing, Nothing) then failPeek "At least on of 'enable' and 'disable' must be set" else return $ ExtensionsDiff (fromMaybe mempty en) (fromMaybe mempty di)) <|> -- two lists of extensions; the first is list assumed to contain those -- extensions to be enabled (uncurry ExtensionsDiff <$!> peekPair peekExtensions peekExtensions idx) <|> (do let exts <- peekKeyValuePairs peekExtension peekEnabled idx let enabled = extensionsFromList . map fst $ filter snd exts let disabled = extensionsFromList . map fst $ filter (not . snd) exts return $ ExtensionsDiff enabled disabled) -- | Retrieves the activation status of an extension. True or the string -- @'enable'@ for activated, False or 'disable' for disabled. peekEnabled :: LuaError e => Peeker e Bool peekEnabled idx' = liftLua (ltype idx') >>= \case TypeBoolean -> peekBool idx' TypeString -> peekText idx' >>= \case "disable" -> pure False "enable" -> pure True _ -> failPeek "expected 'disable' or 'enable'" _ -> failPeek "expected boolean or string" -- | Retrieves a flavored format from the Lua stack. peekFlavoredFormat :: Peeker PandocError FlavoredFormat peekFlavoredFormat idx = retrieving "flavored format" $ liftLua (ltype idx) >>= \case TypeString -> peekText idx >>= liftLua . unPandocLua . parseFlavoredFormat TypeTable -> do let diffFor format idx' = peekExtensionsDiff idx' <|> (getDefaultExtensions format `diffExtensions`) <$> (typeChecked "table" istable peekExtensions idx') format <- peekFieldRaw peekText "format" idx extsDiff <- peekFieldRaw (emptyOr (diffFor format)) "extensions" idx return (FlavoredFormat format extsDiff) _ -> failPeek =<< typeMismatchMessage "string or table" idx -- | Returns 'mempty' if the given stack index is @nil@, and the result -- of the peeker otherwise. emptyOr :: Monoid a => Peeker e a -> Peeker e a emptyOr p idx = do nil <- liftLua (isnil idx) if nil then pure mempty else p idx pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Marshal/PandocError.hs0000644000000000000000000000337407346545000023137 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Lua.Marshal.PandocError Copyright : © 2020-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Marshal of @'PandocError'@ values. -} module Text.Pandoc.Lua.Marshal.PandocError ( peekPandocError , pushPandocError , typePandocError ) where import HsLua (LuaError, Peeker, Pusher, liftLua, pushText) import HsLua.Packaging import Text.Pandoc.Error (PandocError (PandocLuaError), renderError) import qualified HsLua as Lua import qualified Text.Pandoc.UTF8 as UTF8 -- | Lua userdata type definition for PandocError. typePandocError :: LuaError e => DocumentedType e PandocError typePandocError = deftype "PandocError" [ operation Tostring $ defun "__tostring" ### liftPure (\case PandocLuaError e -> e err -> renderError err) <#> udparam typePandocError "obj" "PandocError object" =#> functionResult pushText "string" "string representation of error." ] mempty -- no members -- | Peek a @'PandocError'@ element to the Lua stack. pushPandocError :: LuaError e => Pusher e PandocError pushPandocError = pushUD typePandocError -- | Retrieve a @'PandocError'@ from the Lua stack. peekPandocError :: LuaError e => Peeker e PandocError peekPandocError idx = Lua.retrieving "PandocError" $ liftLua (Lua.ltype idx) >>= \case Lua.TypeUserdata -> peekUD typePandocError idx _ -> do msg <- liftLua $ do Lua.pushvalue idx Lua.state >>= \l -> Lua.liftIO (Lua.popErrorMessage l) return $ PandocLuaError (UTF8.toText msg) pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Marshal/ReaderOptions.hs0000644000000000000000000001224207346545000023471 0ustar0000000000000000{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.Marshaling.ReaderOptions Copyright : © 2012-2023 John MacFarlane © 2017-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Marshaling instance for ReaderOptions and its components. -} module Text.Pandoc.Lua.Marshal.ReaderOptions ( peekReaderOptions , pushReaderOptions , pushReaderOptionsReadonly ) where import Data.Default (def) import HsLua as Lua import Text.Pandoc.Lua.Marshal.Format (peekExtensions, pushExtensions) import Text.Pandoc.Lua.Marshal.List (pushPandocList) import Text.Pandoc.Options (ReaderOptions (..)) -- -- Reader Options -- -- | Retrieve a ReaderOptions value, either from a normal ReaderOptions -- value, from a read-only object, or from a table with the same -- keys as a ReaderOptions object. peekReaderOptions :: LuaError e => Peeker e ReaderOptions peekReaderOptions = retrieving "ReaderOptions" . \idx -> liftLua (ltype idx) >>= \case TypeUserdata -> choice [ peekUD typeReaderOptions , peekUD typeReaderOptionsReadonly ] idx TypeTable -> peekReaderOptionsTable idx _ -> failPeek =<< typeMismatchMessage "ReaderOptions userdata or table" idx -- | Pushes a ReaderOptions value as userdata object. pushReaderOptions :: LuaError e => Pusher e ReaderOptions pushReaderOptions = pushUD typeReaderOptions -- | Pushes a ReaderOptions object, but makes it read-only. pushReaderOptionsReadonly :: LuaError e => Pusher e ReaderOptions pushReaderOptionsReadonly = pushUD typeReaderOptionsReadonly -- | ReaderOptions object type for read-only values. typeReaderOptionsReadonly :: LuaError e => DocumentedType e ReaderOptions typeReaderOptionsReadonly = deftype "ReaderOptions (read-only)" [ operation Tostring $ lambda ### liftPure show <#> udparam typeReaderOptions "opts" "options to print in native format" =#> functionResult pushString "string" "Haskell representation" , operation Newindex $ lambda ### (failLua "This ReaderOptions value is read-only.") =?> "Throws an error when called, i.e., an assignment is made." ] readerOptionsMembers -- | 'ReaderOptions' object type. typeReaderOptions :: LuaError e => DocumentedType e ReaderOptions typeReaderOptions = deftype "ReaderOptions" [ operation Tostring $ lambda ### liftPure show <#> udparam typeReaderOptions "opts" "options to print in native format" =#> functionResult pushString "string" "Haskell representation" ] readerOptionsMembers -- | Member properties of 'ReaderOptions' Lua values. readerOptionsMembers :: LuaError e => [Member e (DocumentedFunction e) ReaderOptions] readerOptionsMembers = [ property "abbreviations" "" (pushSet pushText, readerAbbreviations) (peekSet peekText, \opts x -> opts{ readerAbbreviations = x }) , property "columns" "" (pushIntegral, readerColumns) (peekIntegral, \opts x -> opts{ readerColumns = x }) , property "default_image_extension" "" (pushText, readerDefaultImageExtension) (peekText, \opts x -> opts{ readerDefaultImageExtension = x }) , property "extensions" "" (pushExtensions, readerExtensions) (peekExtensions, \opts x -> opts{ readerExtensions = x }) , property "indented_code_classes" "" (pushPandocList pushText, readerIndentedCodeClasses) (peekList peekText, \opts x -> opts{ readerIndentedCodeClasses = x }) , property "standalone" "" (pushBool, readerStandalone) (peekBool, \opts x -> opts{ readerStandalone = x }) , property "strip_comments" "" (pushBool, readerStripComments) (peekBool, \opts x -> opts{ readerStripComments = x }) , property "tab_stop" "" (pushIntegral, readerTabStop) (peekIntegral, \opts x -> opts{ readerTabStop = x }) , property "track_changes" "" (pushViaJSON, readerTrackChanges) (choice [peekRead, peekViaJSON], \opts x -> opts{ readerTrackChanges = x }) ] -- | Retrieves a 'ReaderOptions' object from a table on the stack, using -- the default values for all missing fields. -- -- Internally, this pushes the default reader options, sets each -- key/value pair of the table in the userdata value, then retrieves the -- object again. This will update all fields and complain about unknown -- keys. peekReaderOptionsTable :: LuaError e => Peeker e ReaderOptions peekReaderOptionsTable idx = retrieving "ReaderOptions (table)" $ do liftLua $ do absidx <- absindex idx pushUD typeReaderOptions def let setFields = do next absidx >>= \case False -> return () -- all fields were copied True -> do pushvalue (nth 2) *> insert (nth 2) settable (nth 4) -- set in userdata object setFields pushnil -- first key setFields peekUD typeReaderOptions top `lastly` pop 1 instance Pushable ReaderOptions where push = pushReaderOptions pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Marshal/Reference.hs0000644000000000000000000000627207346545000022617 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.Marshaling.ReaderOptions Copyright : © 2012-2023 John MacFarlane © 2017-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Marshal citeproc 'Reference' values. -} module Text.Pandoc.Lua.Marshal.Reference ( pushReference ) where import Citeproc.Types ( Date (..), DateParts (..), ItemId (..), Name (..), Reference (..) , Val (..), Variable, fromVariable ) import Control.Monad (forM_) import HsLua hiding (Name, Reference, pushName, peekName) import Text.Pandoc.Builder (Inlines, toList) import Text.Pandoc.Lua.Marshal.Inline (pushInlines) import Text.Pandoc.Lua.Marshal.List (pushPandocList) import qualified Data.Map as Map -- | Pushes a ReaderOptions value as userdata object. pushReference :: LuaError e => Pusher e (Reference Inlines) pushReference reference = do pushAsTable [ ("id", pushItemId . referenceId) , ("type", pushText . referenceType) ] reference forM_ (Map.toList $ referenceVariables reference) $ \(var, val) -> do pushVariable var pushVal val rawset (nth 3) -- | Pushes an 'ItemId' as a string. pushItemId :: Pusher e ItemId pushItemId = pushText . unItemId -- | Pushes a person's 'Name' as a table. pushName :: LuaError e => Pusher e Name pushName = pushAsTable [ ("family" , pushTextOrNil . nameFamily) , ("given" , pushTextOrNil . nameGiven) , ("dropping-particle" , pushTextOrNil . nameDroppingParticle) , ("non-dropping-particle" , pushTextOrNil . nameNonDroppingParticle) , ("suffix" , pushTextOrNil . nameSuffix) , ("literal" , pushTextOrNil . nameLiteral) , ("comma-suffix" , pushBoolOrNil . nameCommaSuffix) , ("static-ordering" , pushBoolOrNil . nameStaticOrdering) ] where pushTextOrNil = \case Nothing -> pushnil Just xs -> pushText xs -- | Pushes a boolean, but uses @nil@ instead of @false@; table fields -- are not set unless the value is true. pushBoolOrNil :: Pusher e Bool pushBoolOrNil = \case False -> pushnil True -> pushBool True -- | Pushes a 'Variable' as string. pushVariable :: Pusher e Variable pushVariable = pushText . fromVariable -- | Pushes a 'Val', i.e., a variable value. pushVal :: LuaError e => Pusher e (Val Inlines) pushVal = \case TextVal t -> pushText t FancyVal inlns -> pushInlines $ toList inlns NumVal i -> pushIntegral i NamesVal names -> pushPandocList pushName names DateVal date -> pushDate date _ -> pushText mempty -- | Pushes a 'Date' as table. pushDate :: LuaError e => Pusher e Date pushDate = pushAsTable [ ("date-parts", pushPandocList pushDateParts . dateParts) , ("circa", pushBoolOrNil . dateCirca) , ("season", maybe pushnil pushIntegral . dateSeason) , ("literal", maybe pushnil pushText . dateLiteral) ] where -- date parts are lists of Int values pushDateParts (DateParts dp) = pushPandocList pushIntegral dp pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Marshal/Sources.hs0000644000000000000000000000346007346545000022340 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.Marshaling.Sources Copyright : © 2021-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Marshal 'Sources'. -} module Text.Pandoc.Lua.Marshal.Sources ( peekSources , pushSources ) where import Control.Monad ((<$!>)) import Data.Text (Text) import HsLua as Lua import Text.Pandoc.Lua.Marshal.List (newListMetatable) import Text.Pandoc.Sources (Sources (..), toSources) import Text.Parsec (SourcePos, sourceName) -- | Pushes the 'Sources' as a list of lazy Lua objects. pushSources :: LuaError e => Pusher e Sources pushSources (Sources srcs) = do pushList (pushUD typeSource) srcs newListMetatable "pandoc Sources" $ do pushName "__tostring" pushHaskellFunction $ do sources <- forcePeek $ peekList (peekUD typeSource) (nthBottom 1) pushText . mconcat $ map snd sources return 1 rawset (nth 3) setmetatable (nth 2) -- | Retrieves sources from the stack. peekSources :: LuaError e => Peeker e Sources peekSources idx = liftLua (ltype idx) >>= \case TypeString -> toSources <$!> peekText idx TypeTable -> Sources <$!> peekList (peekUD typeSource) idx _ -> Sources . (:[]) <$!> peekUD typeSource idx -- | Source object type. typeSource :: LuaError e => DocumentedType e (SourcePos, Text) typeSource = deftype "pandoc input source" [ operation Tostring $ lambda ### liftPure snd <#> udparam typeSource "srcs" "Source to print in native format" =#> functionResult pushText "string" "Haskell representation" ] [ readonly "name" "source name" (pushString, sourceName . fst) , readonly "text" "source text" (pushText, snd) ] pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Marshal/Template.hs0000644000000000000000000000262607346545000022473 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.Marshal.Template Copyright : © 2021-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Marshal 'Template' 'Text'. -} module Text.Pandoc.Lua.Marshal.Template ( pushTemplate , peekTemplate , typeTemplate ) where import Data.Text (Text) import HsLua as Lua import HsLua.Core.Utf8 as Lua import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.PandocLua (unPandocLua) import Text.Pandoc.Templates (Template, compileTemplate, runWithDefaultPartials) -- | Pushes a 'Template' as a an opaque userdata value. pushTemplate :: LuaError e => Pusher e (Template Text) pushTemplate = pushUD typeTemplate -- | Retrieves a 'Template' 'Text' value from the stack. peekTemplate :: Peeker PandocError (Template Text) peekTemplate idx = liftLua (ltype idx) >>= \case TypeString -> do let path = "templates/default.custom" let liftPM = liftLua . unPandocLua tmpl <- peekText idx liftPM (runWithDefaultPartials (compileTemplate path tmpl)) >>= \case Left e -> failPeek (Lua.fromString e) Right t -> pure t _ -> peekUD typeTemplate idx -- | Template object type. typeTemplate :: LuaError e => DocumentedType e (Template Text) typeTemplate = deftype "pandoc Template" [] [] pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Marshal/WriterOptions.hs0000644000000000000000000002136107346545000023545 0ustar0000000000000000{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.Marshaling.WriterOptions Copyright : © 2021-2023 Albert Krewinkel, John MacFarlane License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Marshaling instance for WriterOptions and its components. -} module Text.Pandoc.Lua.Marshal.WriterOptions ( peekWriterOptions , pushWriterOptions ) where import Control.Applicative (optional) import Data.Default (def) import HsLua as Lua import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Marshal.Context (peekContext, pushContext) import Text.Pandoc.Lua.Marshal.Format (peekExtensions, pushExtensions) import Text.Pandoc.Lua.Marshal.List (pushPandocList) import Text.Pandoc.Lua.Marshal.Template (peekTemplate, pushTemplate) import Text.Pandoc.Options (WriterOptions (..)) -- -- Writer Options -- -- | Retrieve a WriterOptions value, either from a normal WriterOptions -- value, from a read-only object, or from a table with the same -- keys as a WriterOptions object. peekWriterOptions :: Peeker PandocError WriterOptions peekWriterOptions = retrieving "WriterOptions" . \idx -> liftLua (ltype idx) >>= \case TypeUserdata -> peekUD typeWriterOptions idx TypeTable -> peekWriterOptionsTable idx _ -> failPeek =<< typeMismatchMessage "WriterOptions userdata or table" idx -- | Pushes a WriterOptions value as userdata object. pushWriterOptions :: Pusher PandocError WriterOptions pushWriterOptions = pushUD typeWriterOptions -- | 'WriterOptions' object type. typeWriterOptions :: DocumentedType PandocError WriterOptions typeWriterOptions = deftype "WriterOptions" [ operation Tostring $ lambda ### liftPure show <#> udparam typeWriterOptions "opts" "options to print in native format" =#> functionResult pushString "string" "Haskell representation" ] [ property "chunk_template" "Templates used to generate chunked HTML filenames (string)" (pushViaJSON, writerChunkTemplate) (peekViaJSON, \opts x -> opts{ writerChunkTemplate = x }) , property "cite_method" "How to print cites" (pushViaJSON, writerCiteMethod) (peekViaJSON, \opts x -> opts{ writerCiteMethod = x }) , property "columns" "Characters in a line (for text wrapping)" (pushIntegral, writerColumns) (peekIntegral, \opts x -> opts{ writerColumns = x }) , property "dpi" "DPI for pixel to/from inch/cm conversions" (pushIntegral, writerDpi) (peekIntegral, \opts x -> opts{ writerDpi = x }) , property "email_obfuscation" "How to obfuscate emails" (pushViaJSON, writerEmailObfuscation) (peekViaJSON, \opts x -> opts{ writerEmailObfuscation = x }) , property "split_level" "Level at which EPUB or chunked HTML documents are split into files" (pushIntegral, writerSplitLevel) (peekIntegral, \opts x -> opts{ writerSplitLevel = x }) , property "epub_chapter_level" "Deprecated synonym for split_level" (pushIntegral, writerSplitLevel) (peekIntegral, \opts x -> opts{ writerSplitLevel = x }) , property "epub_fonts" "Paths to fonts to embed" (pushPandocList pushString, writerEpubFonts) (peekList peekString, \opts x -> opts{ writerEpubFonts = x }) , property "epub_metadata" "Metadata to include in EPUB" (maybe pushnil pushText, writerEpubMetadata) (optional . peekText, \opts x -> opts{ writerEpubMetadata = x }) , property "epub_subdirectory" "Subdir for epub in OCF" (pushText, writerEpubSubdirectory) (peekText, \opts x -> opts{ writerEpubSubdirectory = x }) , property "extensions" "Markdown extensions that can be used" (pushExtensions, writerExtensions) (peekExtensions, \opts x -> opts{ writerExtensions = x }) , property "highlight_style" "Style to use for highlighting (nil = no highlighting)" (maybe pushnil pushViaJSON, writerHighlightStyle) (optional . peekViaJSON, \opts x -> opts{ writerHighlightStyle = x }) , property "html_math_method" "How to print math in HTML" (pushViaJSON, writerHTMLMathMethod) (peekViaJSON, \opts x -> opts{ writerHTMLMathMethod = x }) , property "html_q_tags" "Use @@ tags for quotes in HTML" (pushBool, writerHtmlQTags) (peekBool, \opts x -> opts{ writerHtmlQTags = x }) , property "identifier_prefix" "Prefix for section & note ids in HTML and for footnote marks in markdown" (pushText, writerIdentifierPrefix) (peekText, \opts x -> opts{ writerIdentifierPrefix = x }) , property "incremental" "True if lists should be incremental" (pushBool, writerIncremental) (peekBool, \opts x -> opts{ writerIncremental = x }) , property "listings" "Use listings package for code" (pushBool, writerListings) (peekBool, \opts x -> opts{ writerListings = x }) , property "number_offset" "Starting number for section, subsection, ..." (pushPandocList pushIntegral, writerNumberOffset) (peekList peekIntegral, \opts x -> opts{ writerNumberOffset = x }) , property "number_sections" "Number sections in LaTeX" (pushBool, writerNumberSections) (peekBool, \opts x -> opts{ writerNumberSections = x }) , property "prefer_ascii" "Prefer ASCII representations of characters when possible" (pushBool, writerPreferAscii) (peekBool, \opts x -> opts{ writerPreferAscii = x }) , property "reference_doc" "Path to reference document if specified" (maybe pushnil pushString, writerReferenceDoc) (optional . peekString, \opts x -> opts{ writerReferenceDoc = x }) , property "reference_links" "Use reference links in writing markdown, rst" (pushBool, writerReferenceLinks) (peekBool, \opts x -> opts{ writerReferenceLinks = x }) , property "reference_location" "Location of footnotes and references for writing markdown" (pushViaJSON, writerReferenceLocation) (peekViaJSON, \opts x -> opts{ writerReferenceLocation = x }) , property "section_divs" "Put sections in div tags in HTML" (pushBool, writerSectionDivs) (peekBool, \opts x -> opts{ writerSectionDivs = x }) , property "setext_headers" "Use setext headers for levels 1-2 in markdown" (pushBool, writerSetextHeaders) (peekBool, \opts x -> opts{ writerSetextHeaders = x }) , property "slide_level" "Force header level of slides" (maybe pushnil pushIntegral, writerSlideLevel) (optional . peekIntegral, \opts x -> opts{ writerSlideLevel = x }) -- , property "syntax_map" "Syntax highlighting definition" -- (pushViaJSON, writerSyntaxMap) -- (peekViaJSON, \opts x -> opts{ writerSyntaxMap = x }) -- :: SyntaxMap , property "tab_stop" "Tabstop for conversion btw spaces and tabs" (pushIntegral, writerTabStop) (peekIntegral, \opts x -> opts{ writerTabStop = x }) , property "table_of_contents" "Include table of contents" (pushBool, writerTableOfContents) (peekBool, \opts x -> opts{ writerTableOfContents = x }) , property "template" "Template to use" (maybe pushnil pushTemplate, writerTemplate) (optional . peekTemplate, \opts x -> opts{ writerTemplate = x }) -- :: Maybe (Template Text) , property "toc_depth" "Number of levels to include in TOC" (pushIntegral, writerTOCDepth) (peekIntegral, \opts x -> opts{ writerTOCDepth = x }) , property "top_level_division" "Type of top-level divisions" (pushViaJSON, writerTopLevelDivision) (peekViaJSON, \opts x -> opts{ writerTopLevelDivision = x }) , property "variables" "Variables to set in template" (pushContext, writerVariables) (peekContext, \opts x -> opts{ writerVariables = x }) , property "wrap_text" "Option for wrapping text" (pushViaJSON, writerWrapText) (peekViaJSON, \opts x -> opts{ writerWrapText = x }) ] -- | Retrieves a 'WriterOptions' object from a table on the stack, using -- the default values for all missing fields. -- -- Internally, this pushes the default writer options, sets each -- key/value pair of the table in the userdata value, then retrieves the -- object again. This will update all fields and complain about unknown -- keys. peekWriterOptionsTable :: Peeker PandocError WriterOptions peekWriterOptionsTable idx = retrieving "WriterOptions (table)" $ do liftLua $ do absidx <- absindex idx pushUD typeWriterOptions def let setFields = do next absidx >>= \case False -> return () -- all fields were copied True -> do pushvalue (nth 2) *> insert (nth 2) settable (nth 4) -- set in userdata object setFields pushnil -- first key setFields peekUD typeWriterOptions top `lastly` pop 1 pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Module/0000755000000000000000000000000007346545000020214 5ustar0000000000000000pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Module/CLI.hs0000644000000000000000000001017707346545000021165 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Module.CLI Copyright : © 2022-2023 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Command line helpers -} module Text.Pandoc.Lua.Module.CLI ( documentedModule ) where import Control.Applicative ((<|>)) import Data.Version (makeVersion) import HsLua import HsLua.REPL (defaultConfig, replWithEnv, setup) import Text.Pandoc.App (defaultOpts, options, parseOptionsFromArgs) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.PandocLua () import qualified Data.Text as T -- | Push the pandoc.types module on the Lua stack. documentedModule :: Module PandocError documentedModule = Module { moduleName = "pandoc.cli" , moduleDescription = "Command line options and argument parsing." , moduleFields = [ Field { fieldName = "default_options" , fieldType = "table" , fieldDescription = "Default CLI options, using a JSON-like " <> "representation." , fieldPushValue = pushViaJSON defaultOpts } ] , moduleFunctions = [ defun "parse_options" ### parseOptions <#> parameter peekArgs "{string,...}" "args" "list of command line arguments" =#> functionResult pushViaJSON "table" "parsed options, using their JSON-like representation." #? T.unlines [ "Parses command line arguments into pandoc options." , "Typically this function will be used in stand-alone pandoc Lua" , "scripts, taking the list of arguments from the global `arg`." ] `since` makeVersion [3, 0] , repl `since` makeVersion [3, 1, 2] ] , moduleOperations = [] , moduleTypeInitializers = [] } where peekArgs idx = (,) <$> (liftLua (rawgeti idx 0) *> (peekString top <|> pure "") `lastly` pop 1) <*> peekList peekString idx parseOptions (prg, args) = liftIO (parseOptionsFromArgs options defaultOpts prg args) >>= \case Left e -> failLua $ "Cannot process info option: " ++ show e Right opts -> pure opts -- | Starts a REPL. repl :: DocumentedFunction PandocError repl = defun "repl" ### (\menvIdx -> do let repl' = case menvIdx of Nothing -> replWithEnv Nothing Just envIdx -> do settop envIdx fillWithGlobals envIdx replWithEnv . Just =<< ref registryindex setup defaultConfig repl') <#> opt (parameter (typeChecked "table" istable pure) "table" "env" ("Extra environment; the global environment is merged into this" <> " table.")) =?> T.unlines [ "The result(s) of the last evaluated input, or nothing if the last" , "input resulted in an error." ] #? T.unlines [ "Starts a read-eval-print loop (REPL). The function returns all" , "values of the last evaluated input. Exit the REPL by pressing" , "`ctrl-d` or `ctrl-c`; press `F1` to get a list of all key" , "bindings." , "" , "The REPL is started in the global namespace, unless the `env`" , "parameter is specified. In that case, the global namespace is" , "merged into the given table and the result is used as `_ENV` value" , "for the repl." , "" , "Specifically, local variables *cannot* be accessed, unless they" , "are explicitly passed via the `env` parameter; e.g." , "" , " function Pandoc (doc)" , " -- start repl, allow to access the `doc` parameter" , " -- in the repl" , " return pandoc.cli.repl{ doc = doc }" , " end" , "" , "**Note**: it seems that the function exits immediately on Windows," , "without prompting for user input." ] where fillWithGlobals idx = do -- Copy all global values into the table pushglobaltable pushnil let copyval = next (nth 2) >>= \case False -> return () True -> do pushvalue (nth 2) insert (nth 2) rawset idx copyval copyval pop 1 -- global table pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Module/Format.hs0000644000000000000000000000643207346545000022005 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Module.Format Copyright : © 2022-2023 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Lua module to handle pandoc templates. -} module Text.Pandoc.Lua.Module.Format ( documentedModule ) where import Data.Version (makeVersion) import HsLua import Text.Pandoc.Error (PandocError) import Text.Pandoc.Extensions (getAllExtensions, getDefaultExtensions) import Text.Pandoc.Format (formatFromFilePaths, formatName, getExtensionsConfig) import Text.Pandoc.Lua.Marshal.Format (pushExtensions, pushExtensionsConfig) import Text.Pandoc.Lua.PandocLua () import qualified Data.Text as T -- | The "pandoc.format" module. documentedModule :: Module PandocError documentedModule = Module { moduleName = "pandoc.format" , moduleDescription = T.unlines [ "Information about the formats supported by pandoc." ] , moduleFields = [] , moduleOperations = [] , moduleFunctions = functions , moduleTypeInitializers = [] } -- | Extension module functions. functions :: [DocumentedFunction PandocError] functions = [ defun "all_extensions" ### liftPure getAllExtensions <#> parameter peekText "string" "format" "format name" =#> functionResult pushExtensions "FormatExtensions" "all extensions supported for `format`" #? T.unlines [ "Returns the list of all valid extensions for a format." , "No distinction is made between input and output; an extension" , "can have an effect when reading a format but not when" , "writing it, or *vice versa*." ] `since` makeVersion [3,0] , defun "default_extensions" ### liftPure getDefaultExtensions <#> parameter peekText "string" "format" "format name" =#> functionResult pushExtensions "FormatExtensions" "default extensions enabled for `format`" #? T.unlines [ "Returns the list of default extensions of the given format; this" , "function does not check if the format is supported, it will return" , "a fallback list of extensions even for unknown formats." ] `since` makeVersion [3,0] , defun "extensions" ### liftPure getExtensionsConfig <#> textParam "format" "format identifier" =#> functionResult pushExtensionsConfig "table" "extensions config" #? T.unlines [ "Returns the extension configuration for the given format." , "The configuration is represented as a table with all supported" , "extensions as keys and their default status as value, with" , "`true` indicating that the extension is enabled by default," , "while `false` marks a supported extension that's disabled." , "" , "This function can be used to assign a value to the `Extensions`" , "global in custom readers and writers." ] `since` makeVersion [3,0] , defun "from_path" ### liftPure formatFromFilePaths <#> parameter (choice [ fmap (:[]) . peekString, peekList peekString]) "string|{string,...}" "path" "file path, or list of paths" =#> functionResult (maybe pushnil (pushText . formatName)) "string|nil" "format determined by heuristic" `since` makeVersion [3,1,2] ] pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Module/JSON.hs0000644000000000000000000000704007346545000021322 0ustar0000000000000000{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-| Module : Text.Pandoc.Lua.Module.JSON Copyright : © 2022-2023 Albert Krewinkel License : MIT Maintainer : Albert Krewinkel Lua module to work with JSON. -} module Text.Pandoc.Lua.Module.JSON ( -- * Module documentedModule -- ** Functions , decode , encode ) where import Prelude hiding (null) import Data.Maybe (fromMaybe) import Data.Monoid (Alt (..)) import Data.Version (makeVersion) import HsLua.Aeson import HsLua.Core import HsLua.Marshalling import HsLua.Packaging import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.PandocLua () import Text.Pandoc.Lua.Marshal.AST import qualified Data.Aeson as Aeson import qualified Data.Text as T -- | The @aeson@ module specification. documentedModule :: Module PandocError documentedModule = Module { moduleName = "pandoc.json" , moduleDescription = "JSON module to work with JSON; " <> "based on the Aeson Haskell package." , moduleFields = fields , moduleFunctions = functions , moduleOperations = [] , moduleTypeInitializers = [] } -- -- Fields -- -- | Exported fields. fields :: LuaError e => [Field e] fields = [ null ] -- | The value used to represent the JSON @null@. null :: LuaError e => Field e null = Field { fieldName = "null" , fieldType = "light userdata" , fieldDescription = "Value used to represent the `null` JSON value." , fieldPushValue = pushValue Aeson.Null } -- -- Functions -- functions :: [DocumentedFunction PandocError] functions = [ decode `since` makeVersion [3, 1, 1] , encode `since` makeVersion [3, 1, 1] ] -- | Decode a JSON string into a Lua object. decode :: DocumentedFunction PandocError decode = defun "decode" ### (\str usePandocTypes -> fromMaybe pushnil . getAlt . mconcat . map Alt $ (if usePandocTypes == Just False then [] else [ pushInline <$> Aeson.decode str , pushBlock <$> Aeson.decode str , pushPandoc <$> Aeson.decode str , pushInlines <$> Aeson.decode str , pushBlocks <$> Aeson.decode str ]) ++ [pushValue <$> Aeson.decode str]) <#> parameter peekLazyByteString "string" "str" "JSON string" <#> opt (parameter peekBool "boolean" "pandoc_types" "whether to use pandoc types when possible.") =#> functionResult pure "any" "decoded object" #? T.unlines [ "Creates a Lua object from a JSON string. The function returns an" , "[[Inline]], [[Block]], [[Pandoc]], [[Inlines]], or [[Blocks]] element" , "if the input can be decoded into represent any of those types." , "Otherwise the default decoding is applied, using tables, booleans," , "numbers, and [null](#pandoc.json.null) to represent the JSON value." , "" , "The special handling of AST elements can be disabled by setting" , "`pandoc_types` to `false`." ] -- | Encode a Lua object as JSON. encode :: LuaError e => DocumentedFunction e encode = defun "encode" ### liftPure Aeson.encode <#> parameter peekValue "any" "object" "object to convert" =#> functionResult pushLazyByteString "string" "JSON encoding of the given `object`" #? T.unlines ["Encodes a Lua object as JSON string." , "" , "If the object has a metamethod with name `__tojson`, then the" , "result is that of a call to that method with `object` passed as" , "the sole argument. The result of that call is expected to be a" , "valid JSON string, but this not checked." ] pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Module/MediaBag.hs0000644000000000000000000002420007346545000022177 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Module.MediaBag Copyright : Copyright © 2017-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel The Lua module @pandoc.mediabag@. -} module Text.Pandoc.Lua.Module.MediaBag ( documentedModule ) where import Prelude hiding (lookup) import Data.Maybe (fromMaybe) import Data.Version (makeVersion) import HsLua ( LuaE, DocumentedFunction, Module (..) , (<#>), (###), (=#>), (=?>), (#?), defun, functionResult , opt, parameter, since, stringParam, textParam) import Text.Pandoc.Class ( CommonState (..), fetchItem, fillMediaBag , getMediaBag, modifyCommonState, setMediaBag) import Text.Pandoc.Class.IO (writeMedia) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Marshal.Pandoc (peekPandoc, pushPandoc) import Text.Pandoc.Lua.Marshal.List (pushPandocList) import Text.Pandoc.Lua.Orphans () import Text.Pandoc.Lua.PandocLua (unPandocLua) import Text.Pandoc.MIME (MimeType) import qualified Data.ByteString.Lazy as BL import qualified Data.Text as T import qualified HsLua as Lua import qualified Text.Pandoc.MediaBag as MB -- -- MediaBag submodule -- documentedModule :: Module PandocError documentedModule = Module { moduleName = "pandoc.mediabag" , moduleDescription = T.unlines [ "The `pandoc.mediabag` module allows accessing pandoc's media" , "storage. The \"media bag\" is used when pandoc is called with the" , "`--extract-media` or (for HTML only) `--embed-resources` option." , "" , "The module is loaded as part of module `pandoc` and can either" , "be accessed via the `pandoc.mediabag` field, or explicitly" , "required, e.g.:" , "" , " local mb = require 'pandoc.mediabag'" ] , moduleFields = [] , moduleFunctions = [ delete `since` makeVersion [2,7,3] , empty `since` makeVersion [2,7,3] , fetch `since` makeVersion [2,0] , fill `since` makeVersion [2,19] , insert `since` makeVersion [2,0] , items `since` makeVersion [2,7,3] , list `since` makeVersion [2,0] , lookup `since` makeVersion [2,0] , write `since` makeVersion [3,0] ] , moduleOperations = [] , moduleTypeInitializers = [] } -- | Delete a single item from the media bag. delete :: DocumentedFunction PandocError delete = defun "delete" ### (\fp -> unPandocLua $ modifyCommonState (\st -> st { stMediaBag = MB.deleteMedia fp (stMediaBag st) })) <#> stringParam "filepath" ("Filename of the item to deleted. The media bag will be " <> "left unchanged if no entry with the given filename exists.") =#> [] #? "Removes a single entry from the media bag." -- | Delete all items from the media bag. empty :: DocumentedFunction PandocError empty = defun "empty" ### unPandocLua (modifyCommonState (\st -> st { stMediaBag = mempty })) =#> [] #? "Clear-out the media bag, deleting all items." -- | Fill the mediabag with all images in the document that aren't -- present yet. fill :: DocumentedFunction PandocError fill = defun "fill" ### unPandocLua . fillMediaBag <#> parameter peekPandoc "Pandoc" "doc" "document from which to fill the mediabag" =#> functionResult pushPandoc "Pandoc" "modified document" #? ("Fills the mediabag with the images in the given document.\n" <> "An image that cannot be retrieved will be replaced with a Span\n" <> "of class \"image\" that contains the image description.\n" <> "\n" <> "Images for which the mediabag already contains an item will\n" <> "not be processed again.") -- | Insert a new item into the media bag. insert :: DocumentedFunction PandocError insert = defun "insert" ### (\fp mmime contents -> unPandocLua $ do mb <- getMediaBag setMediaBag $ MB.insertMedia fp mmime contents mb return (Lua.NumResults 0)) <#> stringParam "filepath" "filename and path relative to the output folder." <#> opt (textParam "mimetype" "the item's MIME type; omit if unknown or unavailable.") <#> parameter Lua.peekLazyByteString "string" "contents" "the binary contents of the file." =#> [] #? T.unlines [ "Adds a new entry to pandoc's media bag. Replaces any existing" , "media bag entry the same `filepath`." , "" , "Usage:" , "" , " local fp = 'media/hello.txt'" , " local mt = 'text/plain'" , " local contents = 'Hello, World!'" , " pandoc.mediabag.insert(fp, mt, contents)" ] -- | Returns iterator values to be used with a Lua @for@ loop. items :: DocumentedFunction PandocError items = defun "items" ### (do mb <- unPandocLua getMediaBag let pushItem (fp, mimetype, contents) = do Lua.pushString fp Lua.pushText mimetype Lua.pushByteString $ BL.toStrict contents return (Lua.NumResults 3) Lua.pushIterator pushItem (MB.mediaItems mb)) =?> T.unlines [ "Iterator triple:" , "" , "- The iterator function; must be called with the iterator" , " state and the current iterator value." , "- Iterator state -- an opaque value to be passed to the" , " iterator function." , "- Initial iterator value." ] #? T.unlines [ "Returns an iterator triple to be used with Lua's generic `for`" , "statement. The iterator returns the filepath, MIME type, and" , "content of a media bag item on each invocation. Items are" , "processed one-by-one to avoid excessive memory use." , "" , "This function should be used only when full access to all items," , "including their contents, is required. For all other cases," , "[`list`](#pandoc.mediabag.list) should be preferred." , "" , "Usage:" , "" , " for fp, mt, contents in pandoc.mediabag.items() do" , " -- print(fp, mt, contents)" , " end" ] -- | Function to lookup a value in the mediabag. lookup :: DocumentedFunction PandocError lookup = defun "lookup" ### (\fp -> unPandocLua (MB.lookupMedia fp <$> getMediaBag)) <#> stringParam "filepath" "name of the file to look up." =#> mconcat [ functionResult (maybe Lua.pushnil (Lua.pushText . MB.mediaMimeType)) "string" "The entry's MIME type, or nil if the file was not found." , functionResult (maybe Lua.pushnil (Lua.pushLazyByteString . MB.mediaContents)) "string" "Contents of the file, or nil if the file was not found." ] #? T.unlines [ "Lookup a media item in the media bag, and return its MIME type" , "and contents." , "" , "Usage:" , "" , " local filename = 'media/diagram.png'" , " local mt, contents = pandoc.mediabag.lookup(filename)" ] -- | Function listing all mediabag items. list :: DocumentedFunction PandocError list = defun "list" ### (unPandocLua (MB.mediaDirectory <$> getMediaBag)) =#> functionResult (pushPandocList pushEntry) "table" ("A list of elements summarizing each entry in the media\n" <> "bag. The summary item contains the keys `path`, `type`, and\n" <> "`length`, giving the filepath, MIME type, and length of\n" <> "contents in bytes, respectively.") #? T.unlines [ "Get a summary of the current media bag contents." , "" , "Usage:" , "" , " -- calculate the size of the media bag." , " local mb_items = pandoc.mediabag.list()" , " local sum = 0" , " for i = 1, #mb_items do" , " sum = sum + mb_items[i].length" , " end" , " print(sum)" ] where pushEntry :: (FilePath, MimeType, Int) -> LuaE PandocError () pushEntry (fp, mimeType, contentLength) = do Lua.newtable Lua.pushName "path" *> Lua.pushString fp *> Lua.rawset (-3) Lua.pushName "type" *> Lua.pushText mimeType *> Lua.rawset (-3) Lua.pushName "length" *> Lua.pushIntegral contentLength *> Lua.rawset (-3) -- | Lua function to retrieve a new item. fetch :: DocumentedFunction PandocError fetch = defun "fetch" ### (unPandocLua . fetchItem) <#> textParam "source" "path to a resource; either a local file path or URI" =#> ( functionResult (Lua.pushText . fromMaybe "" . snd) "string" "The entry's MIME type, or `nil` if the file was not found." <> functionResult (Lua.pushByteString . fst) "string" "Contents of the file, or `nil` if the file was not found." ) #? T.unlines [ "Fetches the given source from a URL or local file. Returns two" , "values: the contents of the file and the MIME type (or an empty" , "string)." , "" , "The function will first try to retrieve `source` from the" , "mediabag; if that fails, it will try to download it or read it" , "from the local file system while respecting pandoc's \"resource" , "path\" setting." , "" , "Usage:" , "" , " local diagram_url = 'https://pandoc.org/diagram.jpg'" , " local mt, contents = pandoc.mediabag.fetch(diagram_url)" ] -- | Extract the mediabag or just a single entry. write :: DocumentedFunction PandocError write = defun "write" ### (\dir mfp -> do mb <- unPandocLua getMediaBag case mfp of Nothing -> unPandocLua $ mapM_ (writeMedia dir) (MB.mediaItems mb) Just fp -> do case MB.lookupMedia fp mb of Nothing -> Lua.failLua ("Resource not in mediabag: " <> fp) Just item -> unPandocLua $ do let triple = ( MB.mediaPath item , MB.mediaMimeType item , MB.mediaContents item ) writeMedia dir triple) <#> stringParam "dir" "path of the target directory" <#> opt (stringParam "fp" "canonical name (relative path) of resource") =#> [] #? T.unlines [ "Writes the contents of mediabag to the given target directory. If" , "`fp` is given, then only the resource with the given name will be" , "extracted. Omitting that parameter means that the whole mediabag" , "gets extracted. An error is thrown if `fp` is given but cannot be" , "found in the mediabag." ] `since` makeVersion [3, 0] pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Module/Pandoc.hs0000644000000000000000000002727307346545000021767 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Lua.Module.Pandoc Copyright : Copyright © 2017-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Pandoc module for lua. -} module Text.Pandoc.Lua.Module.Pandoc ( documentedModule ) where import Prelude hiding (read) import Control.Applicative ((<|>)) import Control.Monad (forM_, when) import Control.Monad.Catch (catch, throwM) import Data.Data (Data, dataTypeConstrs, dataTypeOf, showConstr) import Data.Default (Default (..)) import Data.Maybe (fromMaybe) import Data.Proxy (Proxy (Proxy)) import HsLua import System.Exit (ExitCode (..)) import Text.Pandoc.Definition import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.Format (parseFlavoredFormat) import Text.Pandoc.Lua.Orphans () import Text.Pandoc.Lua.Marshal.AST import Text.Pandoc.Lua.Marshal.Format (peekFlavoredFormat) import Text.Pandoc.Lua.Marshal.Filter (peekFilter) import Text.Pandoc.Lua.Marshal.ReaderOptions ( peekReaderOptions , pushReaderOptions) import Text.Pandoc.Lua.Marshal.Sources (peekSources) import Text.Pandoc.Lua.Marshal.WriterOptions ( peekWriterOptions , pushWriterOptions) import Text.Pandoc.Lua.Module.Utils (sha1) import Text.Pandoc.Lua.PandocLua (PandocLua (unPandocLua)) import Text.Pandoc.Lua.Writer.Classic (runCustom) import Text.Pandoc.Options ( ReaderOptions (readerExtensions) , WriterOptions (writerExtensions) ) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.Readers (Reader (..), getReader, readers) import Text.Pandoc.Sources (toSources) import Text.Pandoc.Writers (Writer (..), getWriter, writers) import qualified HsLua as Lua import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BSL import qualified Data.Set as Set import qualified Data.Text as T import qualified Text.Pandoc.UTF8 as UTF8 documentedModule :: Module PandocError documentedModule = Module { moduleName = "pandoc" , moduleDescription = T.unlines [ "Lua functions for pandoc scripts; includes constructors for" , "document elements, functions to parse text in a given" , "format, and functions to filter and modify a subtree." ] , moduleFields = readersField : writersField : stringConstants ++ [inlineField, blockField] , moduleOperations = [] , moduleFunctions = mconcat [ functions , otherConstructors , blockConstructors , inlineConstructors , metaValueConstructors ] , moduleTypeInitializers = [ initType typePandoc , initType typeBlock , initType typeInline ] } -- | Set of input formats accepted by @read@. readersField :: Field PandocError readersField = Field { fieldName = "readers" , fieldType = "table" , fieldDescription = T.unlines [ "Set of formats that pandoc can parse. All keys in this table can" , "be used as the `format` value in `pandoc.read`." ] , fieldPushValue = pushSet pushText $ Set.fromList (map fst (readers @PandocLua)) } -- | Set of input formats accepted by @write@. writersField :: Field PandocError writersField = Field { fieldName = "writers" , fieldType = "table" , fieldDescription = T.unlines [ "Set of formats that pandoc can generate. All keys in this table" , "can be used as the `format` value in `pandoc.write`." ] , fieldPushValue = pushSet pushText $ Set.fromList (map fst (writers @PandocLua)) } -- | Inline table field inlineField :: Field PandocError inlineField = Field { fieldName = "Inline" , fieldType = "table" , fieldDescription = "Inline constructors, nested under 'constructors'." -- the nesting happens for historical reasons and should probably be -- changed. , fieldPushValue = pushWithConstructorsSubtable inlineConstructors } -- | @Block@ module field blockField :: Field PandocError blockField = Field { fieldName = "Block" , fieldType = "table" , fieldDescription = "Inline constructors, nested under 'constructors'." -- the nesting happens for historical reasons and should probably be -- changed. , fieldPushValue = pushWithConstructorsSubtable blockConstructors } pushWithConstructorsSubtable :: [DocumentedFunction PandocError] -> LuaE PandocError () pushWithConstructorsSubtable constructors = do newtable -- Field table newtable -- constructor table pushName "constructor" *> pushvalue (nth 2) *> rawset (nth 4) forM_ constructors $ \fn -> do pushName (functionName fn) pushDocumentedFunction fn rawset (nth 3) pop 1 -- pop constructor table otherConstructors :: [DocumentedFunction PandocError] otherConstructors = [ mkPandoc , mkMeta , mkAttr , mkAttributeList , mkBlocks , mkCitation , mkCell , mkRow , mkTableHead , mkTableFoot , mkInlines , mkListAttributes , mkSimpleTable , defun "ReaderOptions" ### liftPure id <#> parameter peekReaderOptions "ReaderOptions|table" "opts" "reader options" =#> functionResult pushReaderOptions "ReaderOptions" "new object" #? "Creates a new ReaderOptions value." , defun "WriterOptions" ### liftPure id <#> parameter peekWriterOptions "WriterOptions|table" "opts" "writer options" =#> functionResult pushWriterOptions "WriterOptions" "new object" #? "Creates a new WriterOptions value." ] stringConstants :: [Field e] stringConstants = let constrs :: forall a. Data a => Proxy a -> [String] constrs _ = map showConstr . dataTypeConstrs . dataTypeOf @a $ undefined nullaryConstructors = mconcat [ constrs (Proxy @ListNumberStyle) , constrs (Proxy @ListNumberDelim) , constrs (Proxy @QuoteType) , constrs (Proxy @MathType) , constrs (Proxy @Alignment) , constrs (Proxy @CitationMode) ] toField s = Field { fieldName = T.pack s , fieldType = "string" , fieldDescription = T.pack s , fieldPushValue = pushString s } in map toField nullaryConstructors functions :: [DocumentedFunction PandocError] functions = [ defun "pipe" ### (\command args input -> do (ec, output) <- Lua.liftIO $ pipeProcess Nothing command args input `catch` (throwM . PandocIOError "pipe") case ec of ExitSuccess -> 1 <$ Lua.pushLazyByteString output ExitFailure n -> do pushPipeError (PipeError (T.pack command) n output) Lua.error) <#> parameter peekString "string" "command" "path to executable" <#> parameter (peekList peekString) "{string,...}" "args" "list of arguments" <#> parameter peekLazyByteString "string" "input" "input passed to process via stdin" =?> "output string, or error triple" , defun "read" ### (\content mformatspec mreaderOptions -> unPandocLua $ do flvrd <- maybe (parseFlavoredFormat "markdown") pure mformatspec let readerOpts = fromMaybe def mreaderOptions getReader flvrd >>= \case (TextReader r, es) -> r readerOpts{readerExtensions = es} (case content of Left bs -> toSources $ UTF8.toText bs Right sources -> sources) (ByteStringReader r, es) -> case content of Left bs -> r readerOpts{readerExtensions = es} (BSL.fromStrict bs) Right _ -> throwM $ PandocLuaError "Cannot use bytestring reader with Sources") <#> parameter (\idx -> (Left <$> peekByteString idx) <|> (Right <$> peekSources idx)) "string|Sources" "content" "text to parse" <#> opt (parameter peekFlavoredFormat "string|table" "formatspec" "format and extensions") <#> opt (parameter peekReaderOptions "ReaderOptions" "reader_options" "reader options") =#> functionResult pushPandoc "Pandoc" "result document" , sha1 , defun "walk_block" ### walkElement <#> parameter peekBlockFuzzy "Block" "block" "element to traverse" <#> parameter peekFilter "Filter" "lua_filter" "filter functions" =#> functionResult pushBlock "Block" "modified Block" , defun "walk_inline" ### walkElement <#> parameter peekInlineFuzzy "Inline" "inline" "element to traverse" <#> parameter peekFilter "Filter" "lua_filter" "filter functions" =#> functionResult pushInline "Inline" "modified Inline" , defun "write" ### (\doc mformatspec mwriterOpts -> unPandocLua $ do flvrd <- maybe (parseFlavoredFormat "markdown") pure mformatspec let writerOpts = fromMaybe def mwriterOpts getWriter flvrd >>= \case (TextWriter w, es) -> Right <$> w writerOpts{ writerExtensions = es } doc (ByteStringWriter w, es) -> Left <$> w writerOpts{ writerExtensions = es } doc) <#> parameter peekPandoc "Pandoc" "doc" "document to convert" <#> opt (parameter peekFlavoredFormat "string|table" "formatspec" "format and extensions") <#> opt (parameter peekWriterOptions "WriterOptions" "writer_options" "writer options") =#> functionResult (either pushLazyByteString pushText) "string" "result document" , defun "write_classic" ### (\doc mwopts -> runCustom (fromMaybe def mwopts) doc) <#> parameter peekPandoc "Pandoc" "doc" "document to convert" <#> opt (parameter peekWriterOptions "WriterOptions" "writer_options" "writer options") =#> functionResult pushText "string" "rendered document" #? (T.unlines [ "Runs a classic custom Lua writer, using the functions defined" , "in the current environment." ]) ] where walkElement x f = walkInlineSplicing f x >>= walkInlinesStraight f >>= walkBlockSplicing f >>= walkBlocksStraight f data PipeError = PipeError { pipeErrorCommand :: T.Text , pipeErrorCode :: Int , pipeErrorOutput :: BL.ByteString } peekPipeError :: LuaError e => StackIndex -> LuaE e PipeError peekPipeError idx = PipeError <$> (Lua.getfield idx "command" *> Lua.peek (-1) <* Lua.pop 1) <*> (Lua.getfield idx "error_code" *> Lua.peek (-1) <* Lua.pop 1) <*> (Lua.getfield idx "output" *> Lua.peek (-1) <* Lua.pop 1) pushPipeError :: LuaError e => Pusher e PipeError pushPipeError pipeErr = do pushAsTable [ ("command" , pushText . pipeErrorCommand) , ("error_code" , pushIntegral . pipeErrorCode) , ("output" , pushLazyByteString . pipeErrorOutput) ] pipeErr pushPipeErrorMetaTable Lua.setmetatable (nth 2) where pushPipeErrorMetaTable :: LuaError e => LuaE e () pushPipeErrorMetaTable = do v <- Lua.newmetatable "pandoc pipe error" when v $ do pushName "__tostring" pushHaskellFunction pipeErrorMessage rawset (nth 3) pipeErrorMessage :: LuaError e => LuaE e NumResults pipeErrorMessage = do (PipeError cmd errorCode output) <- peekPipeError (nthBottom 1) pushByteString . BSL.toStrict . BSL.concat $ [ BSL.pack "Error running " , BSL.pack $ T.unpack cmd , BSL.pack " (error code " , BSL.pack $ show errorCode , BSL.pack "): " , if output == mempty then BSL.pack "" else output ] return (NumResults 1) pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Module/Scaffolding.hs0000644000000000000000000000333607346545000022774 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Module.Scaffolding Copyright : Copyright © 2022-2023 Albert Krewinkel, John MacFarlane License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Scaffolding for custom Writers. -} module Text.Pandoc.Lua.Module.Scaffolding ( documentedModule ) where import HsLua import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Writer.Scaffolding (pushWriterScaffolding) import qualified Data.Text as T -- | The "pandoc.template" module. documentedModule :: Module PandocError documentedModule = Module { moduleName = "pandoc.scaffolding" , moduleDescription = T.unlines [ "Scaffolding for custom writers." ] , moduleFields = [writerScaffolding] , moduleOperations = [] , moduleFunctions = [] , moduleTypeInitializers = [] } -- | Template module functions. writerScaffolding :: Field PandocError writerScaffolding = Field { fieldName = "Writer" , fieldType = "table" , fieldDescription = T.unlines [ "An object to be used as a `Writer` function; the construct handles" , "most of the boilerplate, expecting only render functions for all" , "AST elements" ] , fieldPushValue = do pushWriterScaffolding -- pretend that it's a submodule so we get better error messages getfield registryindex loaded pushvalue (nth 2) setfield (nth 2) (submod "Writer") -- same for fields "Block" and "Inline" getfield (nth 2) "Inline" *> setfield (nth 2) (submod "Writer.Inline") getfield (nth 2) "Block" *> setfield (nth 2) (submod "Writer.Block") pop 1 -- remove "LOADED_TABLE" } where submod x = moduleName documentedModule <> "." <> x pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Module/Structure.hs0000644000000000000000000002065007346545000022553 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Module.Structure Copyright : © 2023 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Command line helpers -} module Text.Pandoc.Lua.Module.Structure ( documentedModule ) where import Control.Applicative ((<|>), optional) import Data.Default (Default (..)) import Data.Maybe (fromMaybe) import Data.Version (makeVersion) import HsLua ( DocumentedFunction, LuaError, Module (..), Peeker , (###), (<#>), (=#>), (#?) , defun, functionResult, getfield, isnil, lastly, liftLua , opt, liftPure, parameter , peekBool, peekIntegral , peekFieldRaw, peekText, pop, pushIntegral, since, top ) import Text.Pandoc.Chunks ( ChunkedDoc (..), PathTemplate (..) , tocToList, splitIntoChunks ) import Text.Pandoc.Definition (Pandoc (..), Block) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.PandocLua () import Text.Pandoc.Lua.Marshal.AST ( peekBlocksFuzzy, peekPandoc , pushBlock, pushBlocks ) import Text.Pandoc.Lua.Marshal.Chunks import Text.Pandoc.Lua.Marshal.WriterOptions ( peekWriterOptions ) import Text.Pandoc.Options (WriterOptions (writerTOCDepth, writerNumberSections)) import Text.Pandoc.Slides (getSlideLevel, prepSlides) import Text.Pandoc.Writers.Shared (toTableOfContents) import qualified Data.Text as T import qualified Text.Pandoc.Shared as Shared -- | Push the pandoc.structure module on the Lua stack. documentedModule :: Module PandocError documentedModule = Module { moduleName = "pandoc.structure" , moduleDescription = "Access to the higher-level document structure, including " <> "hierarchical sections and the table of contents." , moduleFields = [] , moduleFunctions = [ make_sections `since` makeVersion [3,0] , slide_level `since` makeVersion [3,0] , split_into_chunks `since` makeVersion [3,0] , table_of_contents `since` makeVersion [3,0] ] , moduleOperations = [] , moduleTypeInitializers = [] } make_sections :: LuaError e => DocumentedFunction e make_sections = defun "make_sections" ### (\blks mopts -> let (numSects, baseLevel, mslideLevel) = fromMaybe (defNumSec, Nothing, Nothing) mopts blks' = case mslideLevel of Just l | l <= 0 -> prepSlides (getSlideLevel blks) blks Just sl -> prepSlides sl blks Nothing -> blks in pure $ Shared.makeSections numSects baseLevel blks') <#> parameter peekBodyBlocks "Blocks|Pandoc" "blocks" "document blocks to process" <#> opt (parameter peekOpts "table" "opts" "options") =#> functionResult pushBlocks "Blocks" "processed blocks" #? T.unlines [ "Puts [[Blocks]] into a hierarchical structure: a list of sections" , "(each a Div with class \"section\" and first element a Header)." , "" , "The optional `opts` argument can be a table; two settings are" , "recognized: If `number_sections` is true, a `number` attribute" , "containing the section number will be added to each `Header`. If" , "`base_level` is an integer, then `Header` levels will be" , "reorganized so that there are no gaps, with numbering levels" , "shifted by the given value. Finally, an integer `slide_level`" , "value triggers the creation of slides at that heading level." , "" , "Note that a [[WriterOptions]] object can be passed as the opts" , "table; this will set the `number_section` and `slide_level` values" , "to those defined on the command line." , "" , "Usage:" , "" , " local blocks = {" , " pandoc.Header(2, pandoc.Str 'first')," , " pandoc.Header(2, pandoc.Str 'second')," , " }" , " local opts = PANDOC_WRITER_OPTIONS" , " local newblocks = pandoc.structure.make_sections(blocks, opts)" ] where defNumSec = False peekOpts idx = do numberSections <- fromMaybe defNumSec <$> do liftLua $ getfield idx "number_sections" optional (peekBool top `lastly` pop 1) baseLevel <- do liftLua $ getfield idx "base_level" optional (peekIntegral top `lastly` pop 1) slideLevel <- do liftLua $ getfield idx "slide_level" optional (peekIntegral top `lastly` pop 1) return (numberSections, baseLevel, slideLevel) slide_level :: LuaError e => DocumentedFunction e slide_level = defun "slide_level" ### liftPure getSlideLevel <#> parameter peekBodyBlocks "Blocks|Pandoc" "blocks" "document body" =#> functionResult pushIntegral "integer" "slide level" #? T.unlines [ "Find level of header that starts slides (defined as the least" , "header level that occurs before a non-header/non-hrule in the" , "blocks)." ] -- | Split 'Pandoc' into 'Chunk's. split_into_chunks :: LuaError e => DocumentedFunction e split_into_chunks = defun "split_into_chunks" ### (\doc mopts -> pure $ let defOpts = (defPathTmpl, defNumSects, Nothing, defLvl) (pathTempl, numberSect, mbBaseLevel, chunkLevel) = fromMaybe defOpts mopts in splitIntoChunks pathTempl numberSect mbBaseLevel chunkLevel doc) <#> parameter peekPandoc "Pandoc" "doc" "document to split" <#> opt (parameter peekSplitOpts "table" "opts" optionsDescr) =#> functionResult pushChunkedDoc "ChunkedDoc" "" #? T.unlines [ "Converts a [[Pandoc]] document into a [[ChunkedDoc]]." ] where defPathTmpl = PathTemplate "chunk-%n" defNumSects = False defLvl = 1 peekSplitOpts idx = (,,,) <$> peekFieldRaw ((fmap PathTemplate . peekText) `orDefault` defPathTmpl) "path_template" idx <*> peekFieldRaw (peekBool `orDefault` defNumSects) "number_sections" idx <*> peekFieldRaw (optional . peekIntegral) "base_heading_level" idx <*> peekFieldRaw (peekIntegral `orDefault` defLvl) "chunk_level" idx orDefault p defaultValue idx' = liftLua (isnil idx') >>= \case True -> pure defaultValue False -> p idx' optionsDescr = T.unlines [ "Splitting options." , "" , "The following options are supported:" , "" , " `path_template`" , " : template used to generate the chunks' filepaths" , " `%n` will be replaced with the chunk number (padded with" , " leading 0s to 3 digits), `%s` with the section number of" , " the heading, `%h` with the (stringified) heading text," , " `%i` with the section identifier. For example," , " `\"section-%s-%i.html\"` might be resolved to" , " `\"section-1.2-introduction.html\"`." , "" , " Default is `\"chunk-%n\"` (string)" , "" , " `number_sections`" , " : whether sections should be numbered; default is `false`" , " (boolean)" , "" , " `chunk_level`" , " : The heading level the document should be split into" , " chunks. The default is to split at the top-level, i.e.," , " `1`. (integer)" , "" , " `base_heading_level`" , " : The base level to be used for numbering. Default is `nil`" , " (integer|nil)" ] -- | Generate a table of contents. table_of_contents :: DocumentedFunction PandocError table_of_contents = defun "table_of_contents" ### (\tocSource mwriterOpts -> pure $ let writerOpts = fromMaybe def mwriterOpts in case tocSource of Left blks -> toTableOfContents writerOpts blks Right tree -> tocToList (writerNumberSections writerOpts) (writerTOCDepth writerOpts) tree ) <#> parameter peekTocSource "Blocks|Pandoc|ChunkedDoc" "toc_source" "list of command line arguments" <#> opt (parameter peekWriterOptions "WriterOptions" "opts" "options") =#> functionResult pushBlock "Block" "Table of contents as a BulletList object" #? T.unlines [ "Generates a table of contents for the given object." ] where peekTocSource idx = (Left <$> peekBodyBlocks idx) <|> (Right . chunkedTOC <$> peekChunkedDoc idx) -- | Retrieves the body blocks of a 'Pandoc' object or from a list of -- blocks. peekBodyBlocks :: LuaError e => Peeker e [Block] peekBodyBlocks idx = ((\(Pandoc _ blks) -> blks) <$> peekPandoc idx) <|> peekBlocksFuzzy idx pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Module/System.hs0000644000000000000000000000323407346545000022036 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Lua.Module.System Copyright : © 2019-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Pandoc's system Lua module. -} module Text.Pandoc.Lua.Module.System ( documentedModule ) where import Data.Version (makeVersion) import HsLua import HsLua.Module.System ( arch, cputime, env, getwd, ls, mkdir, os, rmdir , with_env, with_tmpdir, with_wd) import qualified HsLua.Module.System as MSys -- | Push the pandoc.system module on the Lua stack. documentedModule :: forall e. LuaError e => Module e documentedModule = Module { moduleName = "pandoc.system" , moduleDescription = moduleDescription @e MSys.documentedModule , moduleFields = [ arch , os ] , moduleFunctions = [ cputime `since` v[3,1,1] , setName "environment" env `since` v[2,7,3] , setName "get_working_directory" getwd `since` v[2,8] , setName "list_directory" ls `since` v[2,19] , setName "make_directory" mkdir `since` v[2,19] , setName "remove_directory" rmdir `since` v[2,19] , setName "with_environment" with_env `since` v[2,7,3] , setName "with_temporary_directory" with_tmpdir `since` v[2,8] , setName "with_working_directory" with_wd `since` v[2,7,3] ] , moduleOperations = [] , moduleTypeInitializers = [] } where v = makeVersion pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Module/Template.hs0000644000000000000000000001036007346545000022323 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Module.Template Copyright : Copyright © 2022-2023 Albert Krewinkel, John MacFarlane License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Lua module to handle pandoc templates. -} module Text.Pandoc.Lua.Module.Template ( documentedModule ) where import Data.Version (makeVersion) import HsLua import HsLua.Module.DocLayout (peekDoc, pushDoc) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Marshal.AST (peekMeta, pushBlocks, pushInlines) import Text.Pandoc.Lua.Marshal.Context (peekContext, pushContext) import Text.Pandoc.Lua.Marshal.Template (typeTemplate, peekTemplate, pushTemplate) import Text.Pandoc.Lua.PandocLua (PandocLua (unPandocLua), liftPandocLua) import Text.Pandoc.Writers.Shared (metaToContext') import Text.Pandoc.Templates ( compileTemplate, getDefaultTemplate, renderTemplate , runWithPartials, runWithDefaultPartials ) import qualified Data.Text as T -- | The "pandoc.template" module. documentedModule :: Module PandocError documentedModule = Module { moduleName = "pandoc.template" , moduleDescription = T.unlines [ "Lua functions for pandoc templates." ] , moduleFields = [] , moduleOperations = [] , moduleFunctions = functions , moduleTypeInitializers = [initType typeTemplate] } -- | Template module functions. functions :: [DocumentedFunction PandocError] functions = [ defun "apply" ### liftPure2 renderTemplate <#> parameter peekTemplate "Template" "template" "template to apply" <#> parameter peekContext "table" "context" "variable values" =#> functionResult pushDoc "Doc" "rendered template" #? T.unlines [ "Applies a context with variable assignments to a template," , "returning the rendered template. The `context` parameter must be a" , "table with variable names as keys and [Doc], string, boolean, or" , "table as values, where the table can be either be a list of the" , "aforementioned types, or a nested context." ] `since` makeVersion [3,0] , defun "compile" ### (\template mfilepath -> unPandocLua $ case mfilepath of Just fp -> runWithPartials (compileTemplate fp template) Nothing -> runWithDefaultPartials (compileTemplate "templates/default" template)) <#> parameter peekText "string" "template" "template string" <#> opt (stringParam "templ_path" "template path") =#> functionResult (either failLua pushTemplate) "pandoc Template" "compiled template" `since` makeVersion [2,17] , defun "default" ### (\mformat -> unPandocLua $ do let getFORMAT = liftPandocLua $ do getglobal "FORMAT" forcePeek $ peekText top `lastly` pop 1 format <- maybe getFORMAT pure mformat getDefaultTemplate format) <#> opt (textParam "writer" "writer for which the template should be returned.") =#> functionResult pushText "string" "string representation of the writer's default template" `since` makeVersion [2,17] , defun "meta_to_context" ### (\meta blockWriterIdx inlineWriterIdx -> unPandocLua $ do let blockWriter blks = liftPandocLua $ do pushvalue blockWriterIdx pushBlocks blks callTrace 1 1 forcePeek $ peekDoc top let inlineWriter blks = liftPandocLua $ do pushvalue inlineWriterIdx pushInlines blks callTrace 1 1 forcePeek $ peekDoc top metaToContext' blockWriter inlineWriter meta) <#> parameter peekMeta "Meta" "meta" "document metadata" <#> parameter pure "function" "blocks_writer" "converter from Blocks to Doc values" <#> parameter pure "function" "inlines_writer" "converter from Inlines to Doc values" =#> functionResult pushContext "table" "template context" #? T.unlines [ "Creates template context from the document's [Meta]{#type-meta}" , "data, using the given functions to convert [Blocks] and [Inlines]" , "to [Doc] values." ] `since` makeVersion [3,0] ] pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Module/Text.hs0000644000000000000000000000257107346545000021501 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} {-| Module : Text.Pandoc.Lua.Module.Text Copyright : © 2023 Albert Krewinkel License : MIT Maintainer : Albert Krewinkel Lua module to work with UTF-8 strings. -} module Text.Pandoc.Lua.Module.Text ( documentedModule ) where import Data.Version (makeVersion) import HsLua import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.PandocLua () import qualified Data.Text as T import qualified HsLua.Module.Text as TM -- | The @aeson@ module specification. documentedModule :: Module PandocError documentedModule = TM.documentedModule { moduleName = "pandoc.text" , moduleFunctions = [ TM.fromencoding `since` v[3,0] , TM.len `since` v[2,0,3] , TM.lower `since` v[2,0,3] , TM.reverse `since` v[2,0,3] , TM.sub `since` v[2,0,3] , TM.toencoding `since` v[3,0] , TM.upper `since` v[2,0,3] ] , moduleDescription = T.unlines [ "UTF-8 aware text manipulation functions, implemented in Haskell." , "" , "The text module can also be loaded under the name `text`, although" , "this is discouraged and deprecated." , "" , "``` lua" , "-- uppercase all regular text in a document:" , "function Str (s)" , " s.text = pandoc.text.upper(s.text)" , " return s" , "end" , "```" ] } where v = makeVersion pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Module/Types.hs0000644000000000000000000000276007346545000021661 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Module.Types Copyright : © 2019-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Pandoc data type constructors. -} module Text.Pandoc.Lua.Module.Types ( documentedModule ) where import Data.Version (makeVersion) import HsLua ( Module (..), (###), (<#>), (=#>) , defun, functionResult, parameter, since) import HsLua.Module.Version (peekVersionFuzzy, pushVersion) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.PandocLua () -- | Push the pandoc.types module on the Lua stack. documentedModule :: Module PandocError documentedModule = Module { moduleName = "pandoc.types" , moduleDescription = "Constructors for types that are not part of the pandoc AST." , moduleFields = [] , moduleFunctions = [ defun "Version" ### return <#> parameter peekVersionFuzzy "string|integer|{integer,...}|Version" "version_specifier" (mconcat [ "either a version string like `'2.7.3'`, " , "a single integer like `2`, " , "list of integers like `{2,7,3}`, " , "or a Version object" ]) =#> functionResult pushVersion "Version" "A new Version object." `since` makeVersion [2,7,3] ] , moduleOperations = [] , moduleTypeInitializers = [] } pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Module/Utils.hs0000644000000000000000000003457207346545000021663 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Lua.Module.Utils Copyright : Copyright © 2017-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Utility module for Lua, exposing internal helper functions. -} module Text.Pandoc.Lua.Module.Utils ( documentedModule , sha1 ) where import Control.Applicative ((<|>)) import Control.Monad ((<$!>)) import Data.Data (showConstr, toConstr) import Data.Default (def) import Data.Maybe (fromMaybe) import Data.Version (Version, makeVersion) import HsLua as Lua import HsLua.Module.Version (peekVersionFuzzy, pushVersion) import Text.Pandoc.Citeproc (getReferences, processCitations) import Text.Pandoc.Definition import Text.Pandoc.Error (PandocError) import Text.Pandoc.Filter (applyJSONFilter) import Text.Pandoc.Lua.Marshal.AST import Text.Pandoc.Lua.Marshal.Reference import Text.Pandoc.Lua.PandocLua (PandocLua (unPandocLua)) import qualified Data.Digest.Pure.SHA as SHA import qualified Data.ByteString.Lazy as BSL import qualified Data.Map as Map import qualified Data.Text as T import qualified Text.Pandoc.Builder as B import qualified Text.Pandoc.Shared as Shared import qualified Text.Pandoc.UTF8 as UTF8 import qualified Text.Pandoc.Writers.Shared as Shared -- | Push the "pandoc.utils" module to the Lua stack. documentedModule :: Module PandocError documentedModule = Module { moduleName = "pandoc.utils" , moduleDescription = T.unlines [ "This module exposes internal pandoc functions and utility" , "functions." ] , moduleFields = [] , moduleOperations = [] , moduleTypeInitializers = [] , moduleFunctions = -- FIXME: order alphabetically [ blocks_to_inlines `since` v[2,2,3] , citeproc `since` v[2,19,1] , equals `since` v[2,5] , from_simple_table `since` v[2,11] , make_sections `since` v[2,8] , references `since` v[2,17] , run_json_filter `since` v[2,1,1] , normalize_date `since` v[2,0,6] , sha1 `since` v[2,0,6] , stringify `since` v[2,0,6] , to_roman_numeral `since` v[2,0,6] , to_simple_table `since` v[2,11] , type' `since` v[2,17] , defun "Version" ### liftPure (id @Version) <#> parameter peekVersionFuzzy "version string, list of integers, or integer" "v" "version description" =#> functionResult pushVersion "Version" "new Version object" #? "Creates a Version object." ] } where v = makeVersion blocks_to_inlines :: LuaError e => DocumentedFunction e blocks_to_inlines = defun "blocks_to_inlines" ### (\blks mSep -> do let sep = maybe Shared.defaultBlocksSeparator B.fromList mSep return $ B.toList (Shared.blocksToInlinesWithSep sep blks)) <#> parameter (peekList peekBlock) "Blocks" "blocks" "List of [[Block]] elements to be flattened." <#> opt (parameter (peekList peekInline) "Inlines" "sep" ("List of [[Inline]] elements inserted as separator between\n" <> "two consecutive blocks; defaults to `{pandoc.LineBreak()}`.")) =#> functionResult pushInlines "Inlines" "" #? T.unlines [ "Squash a list of blocks into a list of inlines." , "" , "Usage" , "" , " local blocks = {" , " pandoc.Para{ pandoc.Str 'Paragraph1' }," , " pandoc.Para{ pandoc.Emph 'Paragraph2' }" , " }" , " local inlines = pandoc.utils.blocks_to_inlines(blocks)" , " assert(" , " inlines == pandoc.Inlines {" , " pandoc.Str 'Paragraph1'," , " pandoc.Linebreak()," , " pandoc.Emph{ pandoc.Str 'Paragraph2' }" , " }" , " )" ] citeproc :: DocumentedFunction PandocError citeproc = defun "citeproc" ### unPandocLua . processCitations <#> parameter peekPandoc "Pandoc" "doc" "document" =#> functionResult pushPandoc "Pandoc" "processed document" #? T.unlines [ "Process the citations in the file, replacing them with " , "rendered citations and adding a bibliography. " , "See the manual section on citation rendering for details." , "" , "Usage:" , "" , " -- Lua filter that behaves like `--citeproc`" , " function Pandoc (doc)" , " return pandoc.utils.citeproc(doc)" , " end" ] equals :: LuaError e => DocumentedFunction e equals = defun "equals" ### equal <#> parameter pure "any" "element1" "" <#> parameter pure "any" "element2" "" =#> functionResult pushBool "boolean" "Whether the two objects represent the same element" #? T.unlines [ "Test equality of AST elements. Elements in Lua are considered" , "equal if and only if the objects obtained by unmarshaling are" , "equal." , "" , "**This function is deprecated.** Use the normal Lua `==` equality" , "operator instead." ] -- | Converts an old/simple table into a normal table block element. from_simple_table :: LuaError e => DocumentedFunction e from_simple_table = defun "from_simple_table" ### liftPure (\(SimpleTable capt aligns widths head' body) -> Table nullAttr (Caption Nothing [Plain capt | not (null capt)]) (zipWith (\a w -> (a, toColWidth w)) aligns widths) (TableHead nullAttr [blockListToRow head' | not (null head') ]) [TableBody nullAttr 0 [] $ map blockListToRow body | not (null body)] (TableFoot nullAttr [])) <#> parameter peekSimpleTable "SimpleTable" "simple_tbl" "" =#> functionResult pushBlock "Block" "table block element" #? T.unlines [ "Creates a [[Table]] block element from a [[SimpleTable]]. This is" , "useful for dealing with legacy code which was written for pandoc" , "versions older than 2.10." , "" , "Usage:" , "" , " local simple = pandoc.SimpleTable(table)" , " -- modify, using pre pandoc 2.10 methods" , " simple.caption = pandoc.SmallCaps(simple.caption)" , " -- create normal table block again" , " table = pandoc.utils.from_simple_table(simple)" ] where blockListToRow :: [[Block]] -> Row blockListToRow = Row nullAttr . map (B.simpleCell . B.fromList) toColWidth :: Double -> ColWidth toColWidth 0 = ColWidthDefault toColWidth w = ColWidth w make_sections :: LuaError e => DocumentedFunction e make_sections = defun "make_sections" ### liftPure3 Shared.makeSections <#> parameter peekBool "boolean" "number_sections" ("whether section divs should get an additional `number`\n" <> "attribute containing the section number.") <#> parameter (\i -> (Nothing <$ peekNil i) <|> (Just <$!> peekIntegral i)) "integer|nil" "baselevel" "shift top-level headings to this level" <#> parameter (peekList peekBlock) "Blocks" "blocks" "list of blocks to process" =#> functionResult pushBlocks "Blocks" "blocks with sections" #? T.unlines [ "Converts a list of [[Block]] elements into sections." , "`Div`s will be created beginning at each `Header`" , "and containing following content until the next `Header`" , "of comparable level. If `number_sections` is true," , "a `number` attribute will be added to each `Header`" , "containing the section number. If `base_level` is" , "non-null, `Header` levels will be reorganized so" , "that there are no gaps, and so that the base level" , "is the level specified." ] normalize_date :: DocumentedFunction e normalize_date = defun "normalize_date" ### liftPure Shared.normalizeDate <#> parameter peekText "string" "date" "the date string" =#> functionResult (maybe pushnil pushText) "string or nil" "normalized date, or nil if normalization failed." #? T.unwords [ "Parse a date and convert (if possible) to \"YYYY-MM-DD\" format. We" , "limit years to the range 1601-9999 (ISO 8601 accepts greater than" , "or equal to 1583, but MS Word only accepts dates starting 1601)." , "Returns nil instead of a string if the conversion failed." ] -- | List of references in CSL format. references :: DocumentedFunction PandocError references = defun "references" ### (unPandocLua . getReferences Nothing) <#> parameter peekPandoc "Pandoc" "doc" "document" =#> functionResult (pushPandocList pushReference) "table" "lift of references." #? T.unlines [ "Get references defined inline in the metadata and via an external" , "bibliography. Only references that are actually cited in the" , "document (either with a genuine citation or with `nocite`) are" , "returned. URL variables are converted to links." , "" , "The structure used represent reference values corresponds to that" , "used in CSL JSON; the return value can be use as `references`" , "metadata, which is one of the values used by pandoc and citeproc" , "when generating bibliographies." , "" , "Usage:" , "" , " -- Include all cited references in document" , " function Pandoc (doc)" , " doc.meta.references = pandoc.utils.references(doc)" , " doc.meta.bibliography = nil" , " return doc" , " end" ] run_json_filter :: DocumentedFunction PandocError run_json_filter = defun "run_json_filter" ### (\doc filterPath margs -> do args <- case margs of Just xs -> return xs Nothing -> do Lua.getglobal "FORMAT" (forcePeek ((:[]) <$!> peekString top) <* pop 1) applyJSONFilter def args filterPath doc ) <#> parameter peekPandoc "Pandoc" "doc" "the Pandoc document to filter" <#> parameter peekString "string" "filter" "filter to run" <#> opt (parameter (peekList peekString) "{string,...}" "args" "list of arguments passed to the filter. Defaults to `{FORMAT}`.") =#> functionResult pushPandoc "Pandoc" "filtered document" #? "Filter the given doc by passing it through a JSON filter." -- | Documented Lua function to compute the hash of a string. sha1 :: DocumentedFunction e sha1 = defun "sha1" ### liftPure (SHA.showDigest . SHA.sha1) <#> parameter (fmap BSL.fromStrict . peekByteString) "string" "input" "" =#> functionResult pushString "string" "hexadecimal hash value" #? "Computes the SHA1 hash of the given string input." -- | Convert pandoc structure to a string with formatting removed. -- Footnotes are skipped (since we don't want their contents in link -- labels). stringify :: LuaError e => DocumentedFunction e stringify = defun "stringify" ### (\idx -> forcePeek . retrieving "stringifyable element" $ choice [ (fmap Shared.stringify . peekPandoc) , (fmap Shared.stringify . peekInline) , (fmap Shared.stringify . peekBlock) , (fmap Shared.stringify . peekCitation) , (fmap stringifyMetaValue . peekMetaValue) , (fmap (const "") . peekAttr) , (fmap (const "") . peekListAttributes) ] idx) <#> parameter pure "AST element" "element" "some pandoc AST element" =#> functionResult pushText "string" "A plain string representation of the given element." #? T.unlines [ "Converts the given element (Pandoc, Meta, Block, or Inline) into" , "a string with all formatting removed." ] where stringifyMetaValue :: MetaValue -> T.Text stringifyMetaValue mv = case mv of MetaBool b -> T.toLower $ T.pack (show b) MetaString s -> s MetaList xs -> mconcat $ map stringifyMetaValue xs MetaMap m -> mconcat $ map (stringifyMetaValue . snd) (Map.toList m) _ -> Shared.stringify mv to_roman_numeral :: LuaError e => DocumentedFunction e to_roman_numeral = defun "to_roman_numeral" ### liftPure Shared.toRomanNumeral <#> parameter (peekIntegral @Int) "integer" "n" "positive integer below 4000" =#> functionResult pushText "string" "A roman numeral." #? T.unlines [ "Converts an integer < 4000 to uppercase roman numeral." , "" , "Usage:" , "" , " local to_roman_numeral = pandoc.utils.to_roman_numeral" , " local pandoc_birth_year = to_roman_numeral(2006)" , " -- pandoc_birth_year == 'MMVI'" ] -- | Converts a table into an old/simple table. to_simple_table :: DocumentedFunction PandocError to_simple_table = defun "to_simple_table" ### (\case Table _attr caption specs thead tbodies tfoot -> do let (capt, aligns, widths, headers, rows) = Shared.toLegacyTable caption specs thead tbodies tfoot return $ SimpleTable capt aligns widths headers rows blk -> Lua.failLua $ mconcat [ "Expected Table, got ", showConstr (toConstr blk), "." ]) <#> parameter peekTable "Block" "tbl" "a table" =#> functionResult pushSimpleTable "SimpleTable" "SimpleTable object" #? T.unlines [ "Converts a table into an old/simple table." , "" , "Usage:" , "" , " local simple = pandoc.utils.to_simple_table(table)" , " -- modify, using pre pandoc 2.10 methods" , " simple.caption = pandoc.SmallCaps(simple.caption)" , " -- create normal table block again" , " table = pandoc.utils.from_simple_table(simple)" ] where peekTable :: LuaError e => Peeker e Block peekTable idx = peekBlock idx >>= \case t@(Table {}) -> return t b -> Lua.failPeek $ mconcat [ "Expected Table, got " , UTF8.fromString $ showConstr (toConstr b) , "." ] type' :: DocumentedFunction e type' = defun "type" ### (\idx -> getmetafield idx "__name" >>= \case TypeString -> fromMaybe mempty <$> tostring top _ -> ltype idx >>= typename) <#> parameter pure "any" "value" "any Lua value" =#> functionResult pushByteString "string" "type of the given value" #? T.unlines [ "Pandoc-friendly version of Lua's default `type` function, returning" , "type information similar to what is presented in the manual." , "" , "The function works by checking the metafield `__name`. If the" , "argument has a string-valued metafield `__name`, then it returns" , "that string. Otherwise it behaves just like the normal `type`" , "function." , "" , "Usage:" , " -- Prints one of 'string', 'boolean', 'Inlines', 'Blocks'," , " -- 'table', and 'nil', corresponding to the Haskell constructors" , " -- MetaString, MetaBool, MetaInlines, MetaBlocks, MetaMap," , " -- and an unset value, respectively." , " function Meta (meta)" , " print('type of metavalue `author`:', pandoc.utils.type(meta.author))" , " end" ] pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Orphans.hs0000644000000000000000000000514207346545000020737 0ustar0000000000000000{-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE FlexibleInstances #-} {- | Module : Text.Pandoc.Lua.Orphans Copyright : © 2012-2023 John MacFarlane © 2017-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Orphan instances for Lua's Pushable and Peekable type classes. -} module Text.Pandoc.Lua.Orphans () where import Data.Version (Version) import HsLua import HsLua.Module.Version (peekVersionFuzzy) import Text.Pandoc.Definition import Text.Pandoc.Lua.Marshal.AST import Text.Pandoc.Lua.Marshal.CommonState () import Text.Pandoc.Lua.Marshal.Context () import Text.Pandoc.Lua.Marshal.PandocError() import Text.Pandoc.Lua.Marshal.ReaderOptions () import Text.Pandoc.Lua.Marshal.Sources (pushSources) import Text.Pandoc.Sources (Sources) instance Pushable Pandoc where push = pushPandoc instance Pushable Meta where push = pushMeta instance Pushable MetaValue where push = pushMetaValue instance Pushable Block where push = pushBlock instance {-# OVERLAPPING #-} Pushable [Block] where push = pushBlocks instance Pushable Alignment where push = pushString . show instance Pushable CitationMode where push = pushCitationMode instance Pushable Format where push = pushFormat instance Pushable ListNumberDelim where push = pushString . show instance Pushable ListNumberStyle where push = pushString . show instance Pushable MathType where push = pushMathType instance Pushable QuoteType where push = pushQuoteType instance Pushable Cell where push = pushCell instance Pushable Inline where push = pushInline instance {-# OVERLAPPING #-} Pushable [Inline] where push = pushInlines instance Pushable Citation where push = pushCitation instance Pushable Row where push = pushRow instance Pushable TableBody where push = pushTableBody instance Pushable TableFoot where push = pushTableFoot instance Pushable TableHead where push = pushTableHead -- These instances exist only for testing. It's a hack to avoid making -- the marshalling modules public. instance Peekable Inline where safepeek = peekInline instance Peekable Block where safepeek = peekBlock instance Peekable Cell where safepeek = peekCell instance Peekable Meta where safepeek = peekMeta instance Peekable Pandoc where safepeek = peekPandoc instance Peekable Row where safepeek = peekRow instance Peekable Version where safepeek = peekVersionFuzzy instance {-# OVERLAPPING #-} Peekable Attr where safepeek = peekAttr instance Pushable Sources where push = pushSources pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/PandocLua.hs0000644000000000000000000000614707346545000021201 0ustar0000000000000000{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.PandocLua Copyright : © 2020-2023 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel PandocMonad instance which allows execution of Lua operations and which uses Lua to handle state. -} module Text.Pandoc.Lua.PandocLua ( PandocLua (..) , liftPandocLua ) where import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow) import Control.Monad.Except (MonadError (catchError, throwError)) import Control.Monad.IO.Class (MonadIO) import HsLua as Lua import Text.Pandoc.Class (PandocMonad (..)) import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.Lua.Marshal.CommonState (peekCommonState, pushCommonState) import Text.Pandoc.Lua.Marshal.PandocError (peekPandocError, pushPandocError) import qualified Control.Monad.Catch as Catch import qualified Data.Text as T import qualified Text.Pandoc.Class.IO as IO -- | Type providing access to both, pandoc and Lua operations. newtype PandocLua a = PandocLua { unPandocLua :: LuaE PandocError a } deriving ( Applicative , Functor , Monad , MonadCatch , MonadIO , MonadMask , MonadThrow ) -- | Lift a @'Lua'@ operation into the @'PandocLua'@ type. liftPandocLua :: LuaE PandocError a -> PandocLua a liftPandocLua = PandocLua instance {-# OVERLAPPING #-} Exposable PandocError (PandocLua NumResults) where partialApply _narg = liftLua . unPandocLua instance Pushable a => Exposable PandocError (PandocLua a) where partialApply _narg x = 1 <$ (liftLua (unPandocLua x >>= Lua.push)) instance MonadError PandocError PandocLua where catchError = Catch.catch throwError = Catch.throwM instance PandocMonad PandocLua where lookupEnv = IO.lookupEnv getCurrentTime = IO.getCurrentTime getCurrentTimeZone = IO.getCurrentTimeZone newStdGen = IO.newStdGen newUniqueHash = IO.newUniqueHash openURL = IO.openURL readFileLazy = IO.readFileLazy readFileStrict = IO.readFileStrict readStdinStrict = IO.readStdinStrict glob = IO.glob fileExists = IO.fileExists getDataFileName = IO.getDataFileName getModificationTime = IO.getModificationTime getCommonState = PandocLua $ do Lua.getglobal "PANDOC_STATE" forcePeek $ peekCommonState Lua.top `lastly` pop 1 putCommonState cst = PandocLua $ do pushCommonState cst Lua.setglobal "PANDOC_STATE" logOutput = IO.logOutput -- | Retrieve a @'PandocError'@ from the Lua stack. popPandocError :: LuaE PandocError PandocError popPandocError = do errResult <- runPeek $ peekPandocError top `lastly` pop 1 case resultToEither errResult of Right x -> return x Left err -> return $ PandocLuaError (T.pack err) -- | Conversions between Lua errors and 'PandocError' exceptions. instance LuaError PandocError where popException = popPandocError pushException = pushPandocError luaException = PandocLuaError . T.pack pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Writer/0000755000000000000000000000000007346545000020243 5ustar0000000000000000pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Writer/Classic.hs0000644000000000000000000002064007346545000022162 0ustar0000000000000000{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Lua.Writer.Classic Copyright : Copyright (C) 2012-2023 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of Pandoc documents using a \"classic\" custom Lua writer. -} module Text.Pandoc.Lua.Writer.Classic ( runCustom ) where import Control.Applicative (optional) import Control.Arrow ((***)) import Data.List (intersperse) import Data.Maybe (fromMaybe) import qualified Data.Text as T import Data.Text (Text, pack) import HsLua as Lua hiding (Operation (Div)) #if !MIN_VERSION_hslua(2,2,0) import HsLua.Aeson (peekViaJSON) #endif import Text.DocLayout (literal, render) import Text.DocTemplates (Context) import Text.Pandoc.Definition import Text.Pandoc.Lua.Marshal.Attr (pushAttributeList) import Text.Pandoc.Lua.Orphans () import Text.Pandoc.Options import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Shared -- | List of key-value pairs that is pushed to Lua as AttributeList -- userdata. newtype AttributeList = AttributeList [(Text, Text)] instance Pushable AttributeList where push (AttributeList kvs) = pushAttributeList kvs attrToMap :: Attr -> AttributeList attrToMap (id',classes,keyvals) = AttributeList $ ("id", id') : ("class", T.unwords classes) : keyvals newtype Stringify a = Stringify a instance Pushable (Stringify Format) where push (Stringify (Format f)) = Lua.push (T.toLower f) instance Pushable (Stringify [Inline]) where push (Stringify ils) = Lua.push =<< inlineListToCustom ils instance Pushable (Stringify [Block]) where push (Stringify blks) = Lua.push =<< blockListToCustom blks instance Pushable (Stringify MetaValue) where push (Stringify (MetaMap m)) = Lua.push (fmap Stringify m) push (Stringify (MetaList xs)) = Lua.push (map Stringify xs) push (Stringify (MetaBool x)) = Lua.push x push (Stringify (MetaString s)) = Lua.push s push (Stringify (MetaInlines ils)) = Lua.push (Stringify ils) push (Stringify (MetaBlocks bs)) = Lua.push (Stringify bs) instance Pushable (Stringify Citation) where push (Stringify cit) = pushAsTable [ ("citationId", push . citationId) , ("citationPrefix", push . Stringify . citationPrefix) , ("citationSuffix", push . Stringify . citationSuffix) , ("citationMode", push . citationMode) , ("citationNoteNum", push . citationNoteNum) , ("citationHash", push . citationHash) ] cit -- | Key-value pair, pushed as a table with @a@ as the only key and @v@ as the -- associated value. newtype KeyValue a b = KeyValue (a, b) instance (Pushable a, Pushable b) => Pushable (KeyValue a b) where push (KeyValue (k, v)) = do Lua.newtable Lua.push k Lua.push v Lua.rawset (Lua.nth 3) -- | Convert Pandoc to custom markup using a classic Lua writer. runCustom :: LuaError e => WriterOptions -> Pandoc -> LuaE e Text runCustom opts doc@(Pandoc meta _) = do (body, context) <- docToCustom opts doc -- convert metavalues to a template context (variables) metaContext <- metaToContext opts (fmap (literal . pack) . blockListToCustom) (fmap (literal . pack) . inlineListToCustom) meta -- merge contexts from metadata and variables let renderContext = context <> metaContext return $ case writerTemplate opts of Nothing -> body Just tpl -> render Nothing $ renderTemplate tpl $ setField "body" body renderContext -- | Converts a Pandoc value to custom markup using a classic Lua writer. docToCustom :: forall e. LuaError e => WriterOptions -> Pandoc -> LuaE e (Text, Context Text) docToCustom opts (Pandoc (Meta metamap) blocks) = do body <- blockListToCustom blocks -- invoke doesn't work with multiple return values, so we have to call -- `Doc` manually. Lua.getglobal "Doc" -- function push body -- argument 1 push (fmap Stringify metamap) -- argument 2 push (writerVariables opts) -- argument 3 call 3 2 rendered <- peek (nth 2) -- first return value context <- forcePeek . optional $ peekViaJSON top -- snd return value return (rendered, fromMaybe mempty context) -- | Convert Pandoc block element to Custom. blockToCustom :: forall e. LuaError e => Block -- ^ Block element -> LuaE e String blockToCustom (Plain inlines) = invoke "Plain" (Stringify inlines) blockToCustom (Para [Image attr txt (src,tit)]) = invoke "CaptionedImage" src tit (Stringify txt) (attrToMap attr) blockToCustom (Para inlines) = invoke "Para" (Stringify inlines) blockToCustom (LineBlock linesList) = invoke "LineBlock" (map (Stringify) linesList) blockToCustom (RawBlock format str) = invoke "RawBlock" (Stringify format) str blockToCustom HorizontalRule = invoke "HorizontalRule" blockToCustom (Header level attr inlines) = invoke "Header" level (Stringify inlines) (attrToMap attr) blockToCustom (CodeBlock attr str) = invoke "CodeBlock" str (attrToMap attr) blockToCustom (BlockQuote blocks) = invoke "BlockQuote" (Stringify blocks) blockToCustom (Figure attr (Caption _ cbody) content) = invoke "Figure" (Stringify cbody) (Stringify content) (attrToMap attr) blockToCustom (Table _ blkCapt specs thead tbody tfoot) = let (capt, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot aligns' = map show aligns capt' = Stringify capt headers' = map (Stringify) headers rows' = map (map (Stringify)) rows in invoke "Table" capt' aligns' widths headers' rows' blockToCustom (BulletList items) = invoke "BulletList" (map (Stringify) items) blockToCustom (OrderedList (num,sty,delim) items) = invoke "OrderedList" (map (Stringify) items) num (show sty) (show delim) blockToCustom (DefinitionList items) = invoke "DefinitionList" (map (KeyValue . (Stringify *** map (Stringify))) items) blockToCustom (Div attr items) = invoke "Div" (Stringify items) (attrToMap attr) -- | Convert list of Pandoc block elements to Custom. blockListToCustom :: forall e. LuaError e => [Block] -- ^ List of block elements -> LuaE e String blockListToCustom xs = do blocksep <- invoke "Blocksep" bs <- mapM blockToCustom xs return $ mconcat $ intersperse blocksep bs -- | Convert list of Pandoc inline elements to Custom. inlineListToCustom :: forall e. LuaError e => [Inline] -> LuaE e String inlineListToCustom lst = do xs <- mapM (inlineToCustom @e) lst return $ mconcat xs -- | Convert Pandoc inline element to Custom. inlineToCustom :: forall e. LuaError e => Inline -> LuaE e String inlineToCustom (Str str) = invoke "Str" str inlineToCustom Space = invoke "Space" inlineToCustom SoftBreak = invoke "SoftBreak" inlineToCustom (Emph lst) = invoke "Emph" (Stringify lst) inlineToCustom (Underline lst) = invoke "Underline" (Stringify lst) inlineToCustom (Strong lst) = invoke "Strong" (Stringify lst) inlineToCustom (Strikeout lst) = invoke "Strikeout" (Stringify lst) inlineToCustom (Superscript lst) = invoke "Superscript" (Stringify lst) inlineToCustom (Subscript lst) = invoke "Subscript" (Stringify lst) inlineToCustom (SmallCaps lst) = invoke "SmallCaps" (Stringify lst) inlineToCustom (Quoted SingleQuote lst) = invoke "SingleQuoted" (Stringify lst) inlineToCustom (Quoted DoubleQuote lst) = invoke "DoubleQuoted" (Stringify lst) inlineToCustom (Cite cs lst) = invoke "Cite" (Stringify lst) (map (Stringify) cs) inlineToCustom (Code attr str) = invoke "Code" str (attrToMap attr) inlineToCustom (Math DisplayMath str) = invoke "DisplayMath" str inlineToCustom (Math InlineMath str) = invoke "InlineMath" str inlineToCustom (RawInline format str) = invoke "RawInline" (Stringify format) str inlineToCustom LineBreak = invoke "LineBreak" inlineToCustom (Link attr txt (src,tit)) = invoke "Link" (Stringify txt) src tit (attrToMap attr) inlineToCustom (Image attr alt (src,tit)) = invoke "Image" (Stringify alt) src tit (attrToMap attr) inlineToCustom (Note contents) = invoke "Note" (Stringify contents) inlineToCustom (Span attr items) = invoke "Span" (Stringify items) (attrToMap attr) pandoc-lua-engine-0.2.0.1/src/Text/Pandoc/Lua/Writer/Scaffolding.hs0000644000000000000000000002750507346545000023027 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Writer.Scaffolding Copyright : © 2022-2023 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Conversion of Pandoc documents using a custom Lua writer. -} module Text.Pandoc.Lua.Writer.Scaffolding ( pushWriterScaffolding ) where import Control.Monad ((<$!>), void) import Data.ByteString (ByteString) import Data.Data (dataTypeConstrs, dataTypeOf, showConstr, toConstr) import Data.Default (def) import Data.List (intersperse) import Data.Maybe (fromMaybe) import Data.Text (Text) import Data.String (IsString (fromString)) import HsLua import HsLua.Module.DocLayout (peekDoc, pushDoc) import Text.DocLayout (Doc, blankline, render) import Text.DocTemplates (Context) import Text.Pandoc.Definition import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.Options (WriterOptions (..), WrapOption(..)) import Text.Pandoc.Lua.PandocLua () import Text.Pandoc.Lua.Marshal.AST import Text.Pandoc.Lua.Marshal.Context (peekContext) import Text.Pandoc.Lua.Marshal.WriterOptions ( peekWriterOptions , pushWriterOptions) import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Shared (metaToContext, setField) import qualified Data.Text as T import qualified Text.Pandoc.UTF8 as UTF8 -- | Convert Pandoc to custom markup. pushWriterScaffolding :: LuaE PandocError NumResults pushWriterScaffolding = do newtable *> pushWriterMT *> setmetatable (nth 2) writer <- toWriterTable top addField "Blocks" $ pushDocumentedFunction (blocksFn writer) addField "Inlines" $ pushDocumentedFunction (inlinesFn writer) addField "Block" $ newtable *> pushBlockMT writer *> setmetatable (nth 2) addField "Inline" $ newtable *> pushInlineMT writer *> setmetatable (nth 2) addField "Pandoc" $ pushDocumentedFunction $ lambda ### (\(Pandoc _ blks) -> do pushWriterTable writer getfield' top "Blocks" pushBlocks blks callTrace 1 1 pure (NumResults 1)) <#> parameter peekPandoc "Pandoc" "doc" "" =?> "rendered doc" freeWriter writer return 1 where blocksFn w = lambda ### (\blocks msep -> blockListToCustom w msep blocks) <#> parameter peekBlocks "Blocks" "blocks" "" <#> opt (parameter peekDocFuzzy "Doc" "sep" "") =#> functionResult pushDoc "Doc" "" inlinesFn w = lambda ### inlineListToCustom w <#> parameter peekInlines "Inlines" "inlines" "" =#> functionResult pushDoc "Doc" "" pushBlockMT writer = do newtable addField "__call" $ pushDocumentedFunction $ lambda ### blockToCustom <#> parameter peekWriter "table" "writer" "" <#> parameter peekBlockFuzzy "Block" "block" "" =#> functionResult pushDoc "Doc" "rendered blocks" addField "__index" $ -- lookup missing fields in the main Writer table pushWriterTable writer pushInlineMT writer = do newtable addField "__call" $ pushDocumentedFunction $ lambda ### inlineToCustom <#> parameter peekWriter "table" "writer" "" <#> parameter peekInlineFuzzy "Inline" "inline" "" =#> functionResult pushDoc "Doc" "rendered inline" addField "__index" $ do -- lookup missing fields in the main Writer table pushWriterTable writer pushWriterMT :: LuaE PandocError () pushWriterMT = do newtable addField "__call" $ pushDocumentedFunction $ lambda ### (\writer doc mopts -> runWriter writer doc mopts) <#> parameter peekWriter "table" "writer" "" <#> parameter peekPandoc "Pandoc" "doc" "" <#> opt (parameter peekWriterOptions "WriterOptions" "opts" "") =#> functionResult pushText "string" "rendered document" addField "__index" . pushDocumentedFunction $ lambda ### (\_writer key -> handleMissingField key) <#> parameter pure "table" "writer" "" <#> parameter (liftLua . tostring') "string" "key" "" =#> functionResult (const pushnil) "string" "" addField :: LuaError e => Name -> LuaE e a -> LuaE e () addField name action = do pushName name action rawset (nth 3) getfield' :: LuaError e => StackIndex -> Name -> LuaE e HsLua.Type getfield' idx name = do aidx <- absindex idx pushName name rawget aidx >>= \case TypeNil -> pop 1 *> getfield aidx name ty -> pure ty -- | A writer table is just an absolute stack index. newtype WriterTable = WriterTable Reference toWriterTable :: LuaError e => StackIndex -> LuaE e WriterTable toWriterTable idx = WriterTable <$!> do pushvalue idx ref registryindex peekWriter :: LuaError e => Peeker e WriterTable peekWriter = liftLua . toWriterTable pushWriterTable :: LuaError e => Pusher e WriterTable pushWriterTable (WriterTable wref) = void $ getref registryindex wref writerOptionsField :: Name writerOptionsField = "Pandoc Writer WriterOptions" freeWriter :: WriterTable -> LuaE e () freeWriter (WriterTable wref) = unref registryindex wref pushOpts :: LuaE PandocError () pushOpts = void $ getfield' registryindex writerOptionsField runWriter :: WriterTable -> Pandoc -> Maybe WriterOptions -> LuaE PandocError Text runWriter writer doc@(Pandoc meta _blks) mopts = do let opts = fromMaybe def mopts pushWriterOptions opts *> setfield registryindex writerOptionsField (body, mcontext) <- runPeek (pandocToCustom writer doc) >>= force . \case Failure msg contexts -> Failure (cleanupTrace msg) contexts s -> s -- convert metavalues to a template context (variables) defaultContext <- metaToContext opts (blockListToCustom writer Nothing) (inlineListToCustom writer) meta let context = setField "body" body $ fromMaybe defaultContext mcontext let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing return $ render colwidth $ case writerTemplate opts of Nothing -> body Just tpl -> renderTemplate tpl context -- | Keep exactly one traceback and clean it up. This wouldn't be -- necessary if the @pcallTrace@ function would do nothing whenever the -- error already included a trace, but that would require some bigger -- changes; removing the additional traces in this post-process step is -- much easier (for now). cleanupTrace :: ByteString -> ByteString cleanupTrace msg = UTF8.fromText . T.intercalate "\n" $ let tmsg = T.lines $ UTF8.toText msg traceStart = (== "stack traceback:") in case break traceStart tmsg of (x, t:traces) -> (x <>) . (t:) $ let (firstTrace, rest) = break traceStart traces isPeekContext = ("\twhile " `T.isPrefixOf`) isUnknownCFn = (== "\t[C]: in ?") in filter (not . isUnknownCFn) firstTrace <> filter isPeekContext rest _ -> tmsg -- | Pushes the field in the writer table. getWriterField :: LuaError e => WriterTable -> Name -> LuaE e HsLua.Type getWriterField writer name = do pushWriterTable writer getfield' top name <* remove (nth 2) -- | Looks up @Writer.subtable.field@; tries @Writer.field@ as a fallback if the -- subtable field is @nil@. getNestedWriterField :: LuaError e => WriterTable -> Name -> Name -> LuaE e HsLua.Type getNestedWriterField writer subtable field = do pushWriterTable writer getfield' top subtable >>= \case TypeNil -> TypeNil <$ remove (nth 2) -- remove Writer table _ -> getfield' top field -- remove Writer and subtable <* remove (nth 3) <* remove (nth 2) pandocToCustom :: WriterTable -> Pandoc -> Peek PandocError (Doc Text, Maybe (Context Text)) pandocToCustom writer doc = withContext "rendering Pandoc" $ do callStatus <- liftLua $ do getWriterField writer "Pandoc" pushPandoc doc pushOpts pcallTrace 2 2 case callStatus of OK -> ((,) <$> peekDocFuzzy (nth 2) <*> orNil peekContext top) `lastly` pop 2 _ -> failPeek =<< liftLua (tostring' top) blockToCustom :: WriterTable -> Block -> LuaE PandocError (Doc Text) blockToCustom writer blk = forcePeek $ renderBlock writer blk renderBlock :: WriterTable -> Block -> Peek PandocError (Doc Text) renderBlock writer blk = do let constrName = fromString . showConstr . toConstr $ blk withContext ("rendering Block `" <> constrName <> "`") $ liftLua (getNestedWriterField writer "Block" constrName) >>= \case TypeNil -> failPeek =<< typeMismatchMessage "function or Doc" top _ -> callOrDoc (pushBlock blk) inlineToCustom :: WriterTable -> Inline -> LuaE PandocError (Doc Text) inlineToCustom writer inln = forcePeek $ renderInline writer inln renderInline :: WriterTable -> Inline -> Peek PandocError (Doc Text) renderInline writer inln = do let constrName = fromString . showConstr . toConstr $ inln withContext ("rendering Inline `" <> constrName <> "`") $ do liftLua (getNestedWriterField writer "Inline" constrName) >>= \case TypeNil -> failPeek =<< typeMismatchMessage "function or Doc" top _ -> callOrDoc (pushInline inln) -- | If the value at the top of the stack can be called as a function, -- then push the element and writer options to the stack and call it; -- otherwise treat it as a plain Doc value callOrDoc :: LuaE PandocError () -> Peek PandocError (Doc Text) callOrDoc pushElement = do liftLua (ltype top) >>= \case TypeFunction -> peekCall _ -> liftLua (getmetafield top "__call") >>= \case TypeNil -> peekDocFuzzy top _ -> liftLua (pop 1) *> peekCall where peekCall :: Peek PandocError (Doc Text) peekCall = liftLua (pushElement *> pushOpts *> pcallTrace 2 1) >>= \case OK -> peekDocFuzzy top _ -> failPeek =<< liftLua (tostring' top) blockListToCustom :: WriterTable -> Maybe (Doc Text) -> [Block] -> LuaE PandocError (Doc Text) blockListToCustom writer msep blocks = forcePeek $ renderBlockList writer msep blocks inlineListToCustom :: WriterTable -> [Inline] -> LuaE PandocError (Doc Text) inlineListToCustom writer inlines = forcePeek $ renderInlineList writer inlines renderBlockList :: WriterTable -> Maybe (Doc Text) -> [Block] -> Peek PandocError (Doc Text) renderBlockList writer msep blocks = withContext "rendering Blocks" $ do let addSeps = intersperse $ fromMaybe blankline msep mconcat . addSeps <$> mapM (renderBlock writer) blocks renderInlineList :: WriterTable -> [Inline] -> Peek PandocError (Doc Text) renderInlineList writer inlines = withContext "rendering Inlines" $ do mconcat <$> mapM (renderInline writer) inlines orNil :: Peeker e a -> Peeker e (Maybe a) orNil p idx = liftLua (ltype idx) >>= \case TypeNil -> pure Nothing TypeNone -> pure Nothing _ -> Just <$> p idx peekDocFuzzy :: LuaError e => Peeker e (Doc Text) peekDocFuzzy idx = liftLua (ltype idx) >>= \case TypeTable -> mconcat <$!> peekList peekDoc idx _ -> peekDoc idx handleMissingField :: LuaError e => ByteString -> LuaE e () handleMissingField key' = let key = UTF8.toString key' blockNames = map (fromString . show) . dataTypeConstrs . dataTypeOf $ HorizontalRule inlineNames = map (fromString . show) . dataTypeConstrs . dataTypeOf $ Space mtypeName = case () of _ | key `elem` blockNames -> Just "Block" _ | key `elem` inlineNames -> Just "Inline" _ -> Nothing in case mtypeName of Just typeName -> failLua $ "No render function for " <> typeName <> " value " <> "'" <> key <> "';\ndefine a function `Writer." <> typeName <> "." <> key <> "` that returns " <> "a string or Doc." _ -> pure () pandoc-lua-engine-0.2.0.1/test/Tests/0000755000000000000000000000000007346545000015410 5ustar0000000000000000pandoc-lua-engine-0.2.0.1/test/Tests/Lua.hs0000644000000000000000000002247107346545000016473 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {- | Module : Tests.Lua Copyright : © 2017-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Unit and integration tests for pandoc's Lua subsystem. -} module Tests.Lua ( runLuaTest, tests ) where import HsLua as Lua hiding (Operation (Div), error) import System.FilePath (()) import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit ((@=?), Assertion, HasCallStack, assertEqual, testCase) import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder (bulletList, definitionList, displayMath, divWith, doc, doubleQuoted, emph, header, lineBlock, linebreak, math, orderedList, para, plain, rawBlock, singleQuoted, space, str, strong, HasMeta (setMeta)) import Text.Pandoc.Class (runIOorExplode, setUserDataDir) import Text.Pandoc.Definition (Attr, Block (BlockQuote, Div, Para), Pandoc, Inline (Emph, Str), pandocTypesVersion) import Text.Pandoc.Error (PandocError (PandocLuaError)) import Text.Pandoc.Lua (Global (..), applyFilter, runLua, setGlobals) import Text.Pandoc.Options (def) import Text.Pandoc.Version (pandocVersionText) import qualified Control.Monad.Catch as Catch import qualified Data.Text as T import qualified Data.Text.Encoding as TE tests :: [TestTree] tests = [ testCase "macro expansion via filter" $ assertFilterConversion "a '{{helloworld}}' string is expanded" "strmacro.lua" (doc . para $ str "{{helloworld}}") (doc . para . emph $ str "Hello, World") , testCase "convert all plains to paras" $ assertFilterConversion "plains become para" "plain-to-para.lua" (doc $ bulletList [plain (str "alfa"), plain (str "bravo")]) (doc $ bulletList [para (str "alfa"), para (str "bravo")]) , testCase "convert display math to inline math" $ assertFilterConversion "display math becomes inline math" "math.lua" (doc $ para (displayMath "5+5")) (doc $ para (math "5+5")) , testCase "make hello world document" $ assertFilterConversion "Document contains 'Hello, World!'" "hello-world-doc.lua" (doc . para $ str "Hey!" <> linebreak <> str "What's up?") (doc . para $ str "Hello," <> space <> str "World!") , testCase "implicit doc filter" $ assertFilterConversion "Document contains 'Hello, World!'" "implicit-doc-filter.lua" (doc . plain $ linebreak) (doc . para $ str "Hello," <> space <> str "World!") , testCase "parse raw markdown blocks" $ assertFilterConversion "raw markdown block is converted" "markdown-reader.lua" (doc $ rawBlock "markdown" "*charly* **delta**") (doc . para $ emph "charly" <> space <> strong "delta") , testCase "allow shorthand functions for quote types" $ assertFilterConversion "single quoted becomes double quoted string" "single-to-double-quoted.lua" (doc . para . singleQuoted $ str "simple") (doc . para . doubleQuoted $ str "simple") , testCase "Count inlines via metatable catch-all" $ assertFilterConversion "filtering with metatable catch-all failed" "metatable-catch-all.lua" (doc . para $ "four words, three spaces") (doc . para $ str "7") , testCase "Count blocks via Block-specific catch-all" $ assertFilterConversion "filtering with Block catch-all failed" "block-count.lua" (doc $ para "one" <> para "two") (doc $ para "2") , testCase "Smart constructors" $ assertFilterConversion "smart constructors returned a wrong result" "smart-constructors.lua" (doc $ para "") (doc $ mconcat [ bulletList [para "Hello", para "World"] , definitionList [("foo", [para "placeholder"])] , lineBlock ["Moin", "Welt"] , orderedList [plain "one", plain "two"] ]) , testCase "Convert header upper case" $ assertFilterConversion "converting header to upper case failed" "uppercase-header.lua" (doc $ header 1 "les états-unis" <> para "text") (doc $ header 1 "LES ÉTATS-UNIS" <> para "text") , testCase "Attribute lists are convenient to use" $ let kv_before = [("one", "1"), ("two", "2"), ("three", "3")] kv_after = [("one", "eins"), ("three", "3"), ("five", "5")] in assertFilterConversion "Attr doesn't behave as expected" "attr-test.lua" (doc $ divWith ("", [], kv_before) (para "nil")) (doc $ divWith ("", [], kv_after) (para "nil")) , testCase "Filter list of inlines" $ assertFilterConversion "List of inlines" "inlines-filter.lua" (doc $ para ("Hello," <> linebreak <> "World! Wassup?")) (doc $ para "Hello, World! Wassup?") , testCase "Filter list of blocks" $ assertFilterConversion "List of blocks" "blocks-filter.lua" (doc $ para "one." <> para "two." <> para "three.") (doc $ plain "3") , testCase "Filter Meta" $ let setMetaBefore = setMeta "old" ("old" :: T.Text) . setMeta "bool" False setMetaAfter = setMeta "new" ("new" :: T.Text) . setMeta "bool" True in assertFilterConversion "Meta filtering" "meta.lua" (setMetaBefore . doc $ mempty) (setMetaAfter . doc $ mempty) , testCase "Script filename is set" $ assertFilterConversion "unexpected script name" "script-name.lua" (doc $ para "ignored") (doc $ para (str $ T.pack $ "lua" "script-name.lua")) , testCase "Pandoc version is set" . runLuaTest $ do Lua.getglobal "PANDOC_VERSION" Lua.liftIO . assertEqual "pandoc version is wrong" (TE.encodeUtf8 pandocVersionText) =<< Lua.tostring' Lua.top , testCase "Pandoc types version is set" . runLuaTest $ do Lua.getglobal "PANDOC_API_VERSION" Lua.liftIO . assertEqual "pandoc-types version is wrong" pandocTypesVersion =<< Lua.peek Lua.top , testCase "require file" $ assertFilterConversion "requiring file failed" "require-file.lua" (doc $ para "ignored") (doc $ para (str . T.pack $ "lua" "require-file.lua")) , testCase "Allow singleton inline in constructors" . runLuaTest $ do Lua.liftIO . assertEqual "Not the expected Emph" (Emph [Str "test"]) =<< do Lua.OK <- Lua.dostring "return pandoc.Emph" Lua.push @Inline (Str "test") Lua.call 1 1 Lua.peek @Inline top Lua.liftIO . assertEqual "Unexpected element" (Para [Str "test"]) =<< do Lua.getglobal' "pandoc.Para" Lua.pushString "test" Lua.call 1 1 Lua.peek @Block top Lua.liftIO . assertEqual "Unexptected element" (BlockQuote [Para [Str "foo"]]) =<< ( do Lua.getglobal' "pandoc.BlockQuote" Lua.push (Para [Str "foo"]) _ <- Lua.call 1 1 Lua.peek @Block Lua.top ) , testCase "Elements with Attr have `attr` accessor" . runLuaTest $ do Lua.push (Div ("hi", ["moin"], []) [Para [Str "ignored"]]) Lua.getfield Lua.top "attr" Lua.liftIO . assertEqual "no accessor" (("hi", ["moin"], []) :: Attr) =<< Lua.peek @Attr Lua.top , testCase "module `pandoc.system` is present" . runLuaTest $ do Lua.getglobal' "pandoc.system" ty <- Lua.ltype Lua.top Lua.liftIO $ assertEqual "module should be a table" Lua.TypeTable ty , testGroup "global modules" [ testCase "module 'lpeg' is loaded into a global" . runLuaTest $ do s <- Lua.dostring "assert(type(lpeg)=='table')" Lua.liftIO $ Lua.OK @=? s , testCase "module 're' is loaded into a global" . runLuaTest $ do s <- Lua.dostring "assert(type(re)=='table')" Lua.liftIO $ Lua.OK @=? s , testCase "module 'lpeg' is available via `require`" . runLuaTest $ do s <- Lua.dostring "package.path = ''; package.cpath = ''; require 'lpeg'" Lua.liftIO $ Lua.OK @=? s , testCase "module 're' is available via `require`" . runLuaTest $ do s <- Lua.dostring "package.path = ''; package.cpath = ''; require 're'" Lua.liftIO $ Lua.OK @=? s ] , testCase "informative error messages" . runLuaTest $ do Lua.pushboolean True -- Lua.newtable eitherPandoc <- Catch.try (peek @Pandoc Lua.top) case eitherPandoc of Left (PandocLuaError msg) -> do let expectedMsg = "Pandoc expected, got boolean\n" <> "\twhile retrieving Pandoc" Lua.liftIO $ assertEqual "unexpected error message" expectedMsg msg Left e -> error ("Expected a Lua error, but got " <> show e) Right _ -> error "Getting a Pandoc element from a bool should fail." ] assertFilterConversion :: String -> FilePath -> Pandoc -> Pandoc -> Assertion assertFilterConversion msg filterPath docIn expectedDoc = do actualDoc <- runIOorExplode $ do setUserDataDir (Just "../data") applyFilter def ["HTML"] ("lua" filterPath) docIn assertEqual msg expectedDoc actualDoc runLuaTest :: HasCallStack => Lua.LuaE PandocError a -> IO a runLuaTest op = runIOorExplode $ do res <- runLua $ do setGlobals [ PANDOC_WRITER_OPTIONS def ] op case res of Left e -> error (show e) Right x -> return x pandoc-lua-engine-0.2.0.1/test/Tests/Lua/0000755000000000000000000000000007346545000016131 5ustar0000000000000000pandoc-lua-engine-0.2.0.1/test/Tests/Lua/Module.hs0000644000000000000000000000320707346545000017714 0ustar0000000000000000{- | Module : Tests.Lua.Module Copyright : © 2019-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Lua module tests -} module Tests.Lua.Module (tests) where import System.FilePath (()) import Test.Tasty (TestName, TestTree) import Test.Tasty.Lua (testLuaFile) import Tests.Lua (runLuaTest) tests :: [TestTree] tests = [ testPandocLua "pandoc" ("lua" "module" "pandoc.lua") , testPandocLua "pandoc.List" ("lua" "module" "pandoc-list.lua") , testPandocLua "pandoc.format" ("lua" "module" "pandoc-format.lua") , testPandocLua "pandoc.json" ("lua" "module" "pandoc-json.lua") , testPandocLua "pandoc.mediabag" ("lua" "module" "pandoc-mediabag.lua") , testPandocLua "pandoc.path" ("lua" "module" "pandoc-path.lua") , testPandocLua "pandoc.structure" ("lua" "module" "pandoc-structure.lua") , testPandocLua "pandoc.template" ("lua" "module" "pandoc-template.lua") , testPandocLua "pandoc.text" ("lua" "module" "pandoc-text.lua") , testPandocLua "pandoc.types" ("lua" "module" "pandoc-types.lua") , testPandocLua "pandoc.utils" ("lua" "module" "pandoc-utils.lua") , testPandocLua "globals" ("lua" "module" "globals.lua") ] testPandocLua :: TestName -> FilePath -> TestTree testPandocLua = testLuaFile runLuaTest pandoc-lua-engine-0.2.0.1/test/Tests/Lua/Reader.hs0000644000000000000000000000222007346545000017663 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {- | Module : Tests.Lua.Reader Copyright : © 2022-2023 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Tests for custom Lua readers. -} module Tests.Lua.Reader (tests) where import Control.Arrow ((>>>)) import Data.Char (chr) import Data.Default (Default (def)) import Text.Pandoc.Class (runIOorExplode) import Text.Pandoc.Lua (loadCustom) import Text.Pandoc.Readers (Reader (ByteStringReader)) import Text.Pandoc.Scripting (customReader) import Test.Tasty (TestTree) import Test.Tasty.HUnit ((@?=), testCase) import qualified Data.ByteString.Lazy as BL import qualified Data.Text as T import qualified Text.Pandoc.Builder as B tests :: [TestTree] tests = [ testCase "read binary to code block" $ do input <- BL.readFile "bytestring.bin" doc <- runIOorExplode $ loadCustom "bytestring-reader.lua" >>= (customReader >>> \case Just (ByteStringReader f) -> f def input _ -> error "Expected a bytestring reader") let bytes = mconcat $ map (B.str . T.singleton . chr) [0..255] doc @?= B.doc (B.plain bytes) ] pandoc-lua-engine-0.2.0.1/test/Tests/Lua/Writer.hs0000644000000000000000000000735007346545000017746 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Lua.Writer Copyright : © 2019-2023 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Tests for custom Lua writers. -} module Tests.Lua.Writer (tests) where import Data.Default (Default (def)) import Data.Maybe (fromMaybe) import Text.Pandoc.Class (runIOorExplode, readFileStrict) import Text.Pandoc.Extensions (Extension (..), extensionsFromList) import Text.Pandoc.Format (ExtensionsDiff (..), FlavoredFormat (..), applyExtensionsDiff) import Text.Pandoc.Lua (loadCustom) import Text.Pandoc.Options (WriterOptions (..)) import Text.Pandoc.Readers (readNative) import Text.Pandoc.Scripting (CustomComponents (..)) import Text.Pandoc.Writers (Writer (ByteStringWriter, TextWriter)) import Test.Tasty (TestTree) import Test.Tasty.Golden (goldenVsString) import Test.Tasty.HUnit (testCase, (@?=)) import qualified Data.ByteString.Lazy as BL import qualified Text.Pandoc.Builder as B import qualified Text.Pandoc.UTF8 as UTF8 tests :: [TestTree] tests = [ goldenVsString "default testsuite" "writer.custom" (runIOorExplode $ do source <- UTF8.toText <$> readFileStrict "testsuite.native" doc <- readNative def source txt <- customWriter <$> loadCustom "sample.lua" >>= \case Just (TextWriter f) -> f def doc _ -> error "Expected a text writer" pure $ BL.fromStrict (UTF8.fromText txt)) , goldenVsString "tables testsuite" "tables.custom" (runIOorExplode $ do source <- UTF8.toText <$> readFileStrict "tables.native" doc <- readNative def source txt <- writeCustom "sample.lua" >>= \case (TextWriter f, _, _) -> f def doc _ -> error "Expected a text writer" pure $ BL.fromStrict (UTF8.fromText txt)) , goldenVsString "bytestring writer" "bytestring.bin" (runIOorExplode $ writeCustom "bytestring.lua" >>= \case (ByteStringWriter f, _, _) -> f def mempty _ -> error "Expected a bytestring writer") , goldenVsString "template" "writer-template.out.txt" (runIOorExplode $ do (_, _, template) <- writeCustom "writer-template.lua" pure . BL.fromStrict . UTF8.fromText $ fromMaybe "" template) , testCase "preset extensions" $ do let format = FlavoredFormat "extensions.lua" mempty result <- runIOorExplode $ writeCustom "extensions.lua" >>= \case (TextWriter write, extsConf, _) -> do exts <- applyExtensionsDiff extsConf format write def{writerExtensions = exts} (B.doc mempty) _ -> error "Expected a text writer" result @?= "smart extension is enabled;\ncitations extension is disabled\n" , testCase "modified extensions" $ do let ediff = ExtensionsDiff { extsToEnable = extensionsFromList [Ext_citations] , extsToDisable = mempty } let format = FlavoredFormat "extensions.lua" ediff result <- runIOorExplode $ writeCustom "extensions.lua" >>= \case (TextWriter write, extsConf, _) -> do exts <- applyExtensionsDiff extsConf format write def{writerExtensions = exts} (B.doc mempty) _ -> error "Expected a text writer" result @?= "smart extension is enabled;\ncitations extension is enabled\n" ] where writeCustom fp = do components <- loadCustom fp let exts = fromMaybe mempty (customExtensions components) case customWriter components of Nothing -> error "Expected a writer to be defined" Just w -> return (w, exts, customTemplate components) pandoc-lua-engine-0.2.0.1/test/0000755000000000000000000000000007346545000014306 5ustar0000000000000000pandoc-lua-engine-0.2.0.1/test/bytestring-reader.lua0000644000000000000000000000033207346545000020441 0ustar0000000000000000function ByteStringReader (input, opts) local chars = pandoc.List{} for i = 1, #input do chars:insert(utf8.char(input:byte(i,i))) end return pandoc.Pandoc(pandoc.Plain(pandoc.Str(table.concat(chars)))) end pandoc-lua-engine-0.2.0.1/test/bytestring.bin0000644000000000000000000000040007346545000017164 0ustar0000000000000000  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~pandoc-lua-engine-0.2.0.1/test/bytestring.lua0000644000000000000000000000024107346545000017200 0ustar0000000000000000function ByteStringWriter (doc, opts) local buffer = {} for i=0, 255 do table.insert(buffer, string.char(i)) end return table.concat(buffer, '') end pandoc-lua-engine-0.2.0.1/test/extensions.lua0000644000000000000000000000050607346545000017211 0ustar0000000000000000function Writer (doc, opts) local output = 'smart extension is %s;\ncitations extension is %s\n' local status = function (ext) return opts.extensions:includes(ext) and 'enabled' or 'disabled' end return output:format(status('smart'), status('citations')) end Extensions = { smart = true, citations = false, } pandoc-lua-engine-0.2.0.1/test/lua/0000755000000000000000000000000007346545000015067 5ustar0000000000000000pandoc-lua-engine-0.2.0.1/test/lua/attr-test.lua0000644000000000000000000000025607346545000017524 0ustar0000000000000000function Div (div) div.attributes.five = ("%d"):format(div.attributes.two + div.attributes.three) div.attributes.two = nil div.attributes.one = "eins" return div end pandoc-lua-engine-0.2.0.1/test/lua/block-count.lua0000644000000000000000000000026307346545000020013 0ustar0000000000000000local num_blocks = 0 function Block(el) num_blocks = num_blocks + 1 end function Pandoc(blocks, meta) return pandoc.Pandoc { pandoc.Para{pandoc.Str(num_blocks)} } end pandoc-lua-engine-0.2.0.1/test/lua/blocks-filter.lua0000644000000000000000000000051207346545000020330 0ustar0000000000000000function Blocks (blks) -- verify that this looks like a `pandoc.List` if not blks.find or not blks.map or not blks.filter then error("table doesn't seem to be an instance of pandoc.List") end -- return plain block containing the number of elements in the list return {pandoc.Plain {pandoc.Str(tostring(#blks))}} end pandoc-lua-engine-0.2.0.1/test/lua/hello-world-doc.lua0000644000000000000000000000036207346545000020566 0ustar0000000000000000return { { Pandoc = function(doc) local meta = {} local hello = { pandoc.Str "Hello,", pandoc.Space(), pandoc.Str "World!" } local blocks = { pandoc.Para(hello) } return pandoc.Pandoc(blocks, meta) end } } pandoc-lua-engine-0.2.0.1/test/lua/implicit-doc-filter.lua0000644000000000000000000000030607346545000021431 0ustar0000000000000000function Pandoc (doc) local meta = {} local hello = { pandoc.Str "Hello,", pandoc.Space(), pandoc.Str "World!" } local blocks = { pandoc.Para(hello) } return pandoc.Pandoc(blocks, meta) end pandoc-lua-engine-0.2.0.1/test/lua/inlines-filter.lua0000644000000000000000000000102107346545000020510 0ustar0000000000000000function isWorldAfterSpace (fst, snd) return fst and fst.t == 'LineBreak' and snd and snd.t == 'Str' and snd.text == 'World!' end function Inlines (inlns) -- verify that this looks like a `pandoc.List` if not inlns.find or not inlns.map or not inlns.filter then error("table doesn't seem to be an instance of pandoc.List") end -- Remove spaces before string "World" for i = #inlns-1,1,-1 do if isWorldAfterSpace(inlns[i], inlns[i+1]) then inlns[i] = pandoc.Space() end end return inlns end pandoc-lua-engine-0.2.0.1/test/lua/markdown-reader.lua0000644000000000000000000000033607346545000020656 0ustar0000000000000000return { { RawBlock = function (elem) if elem.format == "markdown" then local pd = pandoc.read(elem.text, "markdown") return pd.blocks[1] else return elem end end, } } pandoc-lua-engine-0.2.0.1/test/lua/math.lua0000644000000000000000000000024507346545000016524 0ustar0000000000000000return { { Math = function (elem) if elem.mathtype == "DisplayMath" then elem.mathtype = "InlineMath" end return elem end, } } pandoc-lua-engine-0.2.0.1/test/lua/meta.lua0000644000000000000000000000015607346545000016522 0ustar0000000000000000function Meta (meta) meta.old = nil meta.new = "new" meta.bool = (meta.bool == false) return meta end pandoc-lua-engine-0.2.0.1/test/lua/metatable-catch-all.lua0000644000000000000000000000054207346545000021357 0ustar0000000000000000local num_inlines = 0 function catch_all(el) if el.tag and pandoc.Inline.constructor[el.tag] then num_inlines = num_inlines + 1 end end function Pandoc(blocks, meta) return pandoc.Pandoc { pandoc.Para{pandoc.Str(num_inlines)} } end return { setmetatable( {Pandoc = Pandoc}, {__index = function(_) return catch_all end} ) } pandoc-lua-engine-0.2.0.1/test/lua/module/0000755000000000000000000000000007346545000016354 5ustar0000000000000000pandoc-lua-engine-0.2.0.1/test/lua/module/globals.lua0000644000000000000000000001040207346545000020477 0ustar0000000000000000local tasty = require 'tasty' local test = tasty.test_case local group = tasty.test_group local assert = tasty.assert -- These tests exist mainly to catch changes to the JSON representation of -- WriterOptions and its components. UPDATE THE DOCS if anything changes. return { group 'PANDOC_WRITER_OPTIONS' { test('chunk_template', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.chunk_template), 'string') end), test('cite_method', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.cite_method), 'string') end), test('columns', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.columns), 'number') end), test('dpi', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.dpi), 'number') end), test('email_obfuscation', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.email_obfuscation), 'string') end), test('split_level', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.split_level), 'number') end), test('epub_fonts', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.epub_fonts), 'table') end), test('epub_metadata', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.epub_metadata), 'nil') end), test('epub_subdirectory', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.epub_subdirectory), 'string') end), test('extensions', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.extensions), 'table') for _, v in ipairs(PANDOC_WRITER_OPTIONS.extensions) do assert.are_equal(type(v), 'string') end end), test('highlight_style', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.highlight_style), 'table') end), test('html_math_method', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.html_math_method), 'string') end), test('html_q_tags', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.html_q_tags), 'boolean') end), test('identifier_prefix', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.identifier_prefix), 'string') end), test('incremental', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.incremental), 'boolean') end), test('listings', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.listings), 'boolean') end), test('number_offset', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.number_offset), 'table') for _, v in ipairs(PANDOC_WRITER_OPTIONS.number_offset) do assert.are_equal(type(v), 'number') end end), test('number_sections', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.number_sections), 'boolean') end), test('prefer_ascii', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.prefer_ascii), 'boolean') end), test('reference_doc', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.reference_doc), 'nil') end), test('reference_links', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.reference_links), 'boolean') end), test('reference_location', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.reference_location), 'string') end), test('section_divs', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.section_divs), 'boolean') end), test('setext_headers', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.setext_headers), 'boolean') end), test('slide_level', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.slide_level), 'nil') end), test('tab_stop', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.tab_stop), 'number') end), test('table_of_contents', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.table_of_contents), 'boolean') end), test('toc_depth', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.toc_depth), 'number') end), test('top_level_division', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.top_level_division), 'string') end), test('variables', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.variables), 'table') end), test('wrap_text', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.wrap_text), 'string') end), } } pandoc-lua-engine-0.2.0.1/test/lua/module/pandoc-format.lua0000644000000000000000000000230407346545000021610 0ustar0000000000000000local tasty = require 'tasty' local test = tasty.test_case local group = tasty.test_group local assert = tasty.assert local format = require 'pandoc.format' return { group 'default_extensions' { test('docx', function () local docx_default_exts = { 'auto_identifiers', } assert.are_same(format.default_extensions('docx'), docx_default_exts) end), }, group 'all_extensions' { test('docx', function () local docx_default_exts = { 'ascii_identifiers', 'auto_identifiers', 'citations', 'east_asian_line_breaks', 'empty_paragraphs', 'gfm_auto_identifiers', 'native_numbering', 'styles', } assert.are_same(format.all_extensions('docx'), docx_default_exts) end), }, group 'extensions' { test('org', function () local org_default_exts = { ascii_identifiers = false, auto_identifiers = true, citations = true, east_asian_line_breaks = false, fancy_lists = false, gfm_auto_identifiers = false, smart = false, task_lists = true, } assert.are_same(format.extensions 'org', org_default_exts) end), }, } pandoc-lua-engine-0.2.0.1/test/lua/module/pandoc-json.lua0000644000000000000000000000540407346545000021275 0ustar0000000000000000-- -- Tests for the system module -- local json = require 'pandoc.json' local tasty = require 'tasty' local group = tasty.test_group local test = tasty.test_case local assert = tasty.assert return { -- Check existence of static fields group 'static fields' { test('null', function () assert.are_equal(type(json.null), 'userdata') end), }, group 'encode' { test('string', function () assert.are_equal(json.encode 'one\ntwo', '"one\\ntwo"') end), test('null', function () assert.are_equal(json.encode(json.null), 'null') end), test('number', function () assert.are_equal(json.encode(42), '42') end), test('object', function () assert.are_same(json.encode{a = 5}, '{"a":5}') end), test('object with metamethod', function () local obj = setmetatable( {title = 23}, { __tojson = function (obj) return '"Nichts ist so wie es scheint"' end } ) assert.are_same(json.encode(obj), [["Nichts ist so wie es scheint"]]) end), test('Inline (Space)', function () assert.are_equal( json.encode(pandoc.Space()), '{"t":"Space"}' ) end), test('Block (HorizontalRule)', function () assert.are_equal( json.encode(pandoc.HorizontalRule()), '{"t":"HorizontalRule"}' ) end), test('Inlines list', function () assert.are_equal( json.encode(pandoc.Inlines{pandoc.Space()}), '[{"t":"Space"}]' ) end), test('Pandoc', function () assert.are_equal( type(json.encode(pandoc.Pandoc{'Hello from Lua!'})), 'string' ) end), test('Nested Inline', function () assert.are_equal( json.encode({spc = pandoc.Space()}), '{"spc":{"t":"Space"}}' ) end) }, group 'decode' { test('string', function () assert.are_equal(json.decode '"one\\ntwo"', 'one\ntwo') end), test('null', function () assert.are_equal(json.decode 'null', json.null) end), test('number', function () assert.are_equal(json.decode '42', 42) end), test('object', function () assert.are_same(json.decode '{"a":5}', {a = 5}) end), test('Inline (Space)', function () assert.are_equal(json.decode '{"t":"Space"}', pandoc.Space()) end), test('Inline (Str)', function () assert.are_equal(json.decode '{"t":"Str", "c":"a"}', pandoc.Str 'a') end), test('disabled AST check', function () assert.are_same( json.decode('{"t":"Str", "c":"a"}', false), {t = 'Str', c = 'a'} ) end), test('Inlines list', function () assert.are_equal( json.decode '[{"t":"Space"}]', pandoc.Inlines{pandoc.Space()} ) end) }, } pandoc-lua-engine-0.2.0.1/test/lua/module/pandoc-list.lua0000644000000000000000000001173307346545000021301 0ustar0000000000000000local tasty = require 'tasty' local List = require 'pandoc.List' local assert = tasty.assert local test = tasty.test_case local group = tasty.test_group return { group 'List as function' { test('equivalent to List:new', function (x) local new = List:new {'ramen'} local list = List {'ramen'} assert.are_same(new, list) assert.are_equal(getmetatable(new), getmetatable(list)) end) }, group 'clone' { test('changing the clone does not affect original', function () local orig = List:new {23, 42} local copy = orig:clone() copy[1] = 5 assert.are_same({23, 42}, orig) assert.are_same({5, 42}, copy) end), test('result is a list', function () local orig = List:new {23, 42} assert.are_equal(List, getmetatable(orig:clone())) end), }, group 'extend' { test('extends list with other list', function () local primes = List:new {2, 3, 5, 7} primes:extend {11, 13, 17} assert.are_same({2, 3, 5, 7, 11, 13, 17}, primes) end) }, group 'filter' { test('keep elements for which property is truthy', function () local is_small_prime = function (x) return List.includes({2, 3, 5, 7}, x) end local numbers = List:new {4, 7, 2, 9, 5, 11} assert.are_same({7, 2, 5}, numbers:filter(is_small_prime)) end), }, group 'find' { test('returns element and index if found', function () local list = List:new {5, 23, 71} local elem, idx = list:find(71) assert.are_same(71, elem) assert.are_same(3, idx) end), test('respects start index', function () local list = List:new {19, 23, 29, 71} assert.are_equal(23, list:find(23, 1)) assert.are_equal(23, list:find(23, 2)) assert.is_nil(list:find(23, 3)) end), test('returns nil if element not found', function () assert.is_nil((List:new {18, 20, 22, 0, 24}):find('0')) end), }, group 'find_if' { test('returns element and index if found', function () local perm_prime = List:new {2, 3, 5, 7, 11, 13, 17, 31, 37, 71} local elem, idx = perm_prime:find_if(function (x) return x >= 10 end) assert.are_same(11, elem) assert.are_same(5, idx) end), test('returns nil if element not found', function () local is_null = function (n) return List.includes({23,35,46,59}, n) end assert.is_nil((List:new {18, 20, 22, 24, 27}):find_if(is_null)) end), }, group 'includes' { test('finds elements in list', function () local lst = List:new {'one', 'two', 'three'} assert.is_truthy(lst:includes('one')) assert.is_truthy(lst:includes('two')) assert.is_truthy(lst:includes('three')) assert.is_falsy(lst:includes('four')) end) }, group 'insert' { test('insert value at end of list.', function () local count_norsk = List {'en', 'to', 'tre'} count_norsk:insert('fire') assert.are_same({'en', 'to', 'tre', 'fire'}, count_norsk) end), test('insert value in the middle of list.', function () local count_norsk = List {'fem', 'syv'} count_norsk:insert(2, 'seks') assert.are_same({'fem', 'seks', 'syv'}, count_norsk) end) }, group 'map' { test('applies function to elements', function () local primes = List:new {2, 3, 5, 7} local squares = primes:map(function (x) return x^2 end) assert.are_same({4, 9, 25, 49}, squares) end), test('leaves original list unchanged', function () local primes = List:new {2, 3, 5, 7} local squares = primes:map(function (x) return x^2 end) assert.are_same({2, 3, 5, 7}, primes) end) }, group 'new' { test('make table usable as list', function () local test = List:new{1, 1, 2, 3, 5} assert.are_same( {1, 1, 4, 9, 25}, test:map(function (x) return x^2 end) ) end), test('return empty list if no argument is given', function () assert.are_same({}, List:new()) end), test('metatable of result is pandoc.List', function () local test = List:new{5} assert.are_equal(List, getmetatable(test)) end) }, group 'remove' { test('remove value at end of list.', function () local understand = List {'jeg', 'forstår', 'ikke'} local norsk_not = understand:remove() assert.are_same({'jeg', 'forstår'}, understand) assert.are_equal('ikke', norsk_not) end), test('remove value at beginning of list.', function () local count_norsk = List {'en', 'to', 'tre'} count_norsk:remove(1) assert.are_same({'to', 'tre'}, count_norsk) end) }, group 'sort' { test('sort numeric list', function () local numbers = List {71, 5, -1, 42, 23, 0, 1} numbers:sort() assert.are_same({-1, 0, 1, 5, 23, 42, 71}, numbers) end), test('reverse-sort numeric', function () local numbers = List {71, 5, -1, 42, 23, 0, 1} numbers:sort(function (x, y) return x > y end) assert.are_same({71, 42, 23, 5, 1, 0, -1}, numbers) end) }, } pandoc-lua-engine-0.2.0.1/test/lua/module/pandoc-mediabag.lua0000644000000000000000000000623207346545000022055 0ustar0000000000000000local tasty = require 'tasty' local test = tasty.test_case local group = tasty.test_group local assert = tasty.assert local mediabag = require 'pandoc.mediabag' return { group 'insert' { test('insert adds an item to the mediabag', function () local fp = "media/hello.txt" local mt = "text/plain" local contents = "Hello, World!" assert.are_same(mediabag.list(), {}) mediabag.insert(fp, mt, contents) assert.are_same( mediabag.list(), {{['path'] = fp, ['type'] = mt, ['length'] = 13}} ) mediabag.empty() -- clean up end), test('is idempotent', function () local fp = "media/hello.txt" local mt = "text/plain" local contents = "Hello, World!" mediabag.insert(fp, mt, contents) mediabag.insert(fp, mt, contents) assert.are_same( mediabag.list(), {{['path'] = fp, ['type'] = mt, ['length'] = 13}} ) mediabag.empty() -- clean up end), }, group 'delete' { test('removes an item', function () assert.are_same(mediabag.list(), {}) mediabag.insert('test.html', 'text/html', '') mediabag.insert('test.css', 'text/plain', 'aside { color: red; }') assert.are_equal(#mediabag.list(), 2) mediabag.delete('test.html') assert.are_same( mediabag.list(), {{['path'] = 'test.css', ['type'] = 'text/plain', ['length'] = 21}} ) mediabag.empty() -- clean up end), }, group 'fetch' { test('populates media bag', function () local filename = 'lua/module/sample.svg' local mime, contents = mediabag.fetch(filename) assert.are_equal(mime, 'image/svg+xml') assert.are_equal(contents:sub(1,5), 'Really?'}, ['test.css'] = {'text/plain', 'aside { color: red; }'}, ['test.js'] = {'application/javascript', 'alert("HI MOM!")'} } -- fill mediabag for name, v in pairs(input_items) do mediabag.insert(name, v[1], v[2]) end local seen_items = {} for fp, mt, c in mediabag.items() do seen_items[fp] = {mt, c} end assert.are_same(seen_items, input_items) mediabag.empty() -- clean up end) }, group 'lookup' { test('returns MIME type and contents', function () mediabag.insert('test.html', 'text/html', '') local mime, contents = mediabag.lookup('test.html') assert.are_equal(mime, 'text/html') assert.are_equal(contents, '') mediabag.empty() -- clean up end), }, } pandoc-lua-engine-0.2.0.1/test/lua/module/pandoc-path.lua0000644000000000000000000000213307346545000021254 0ustar0000000000000000local tasty = require 'tasty' local path = require 'pandoc.path' local assert = tasty.assert local test = tasty.test_case local group = tasty.test_group return { group 'path separator' { test('is string', function () assert.are_same(type(path.separator), 'string') end), test('is slash or backslash', function () assert.is_truthy(path.separator:match '^[/\\]$') end), }, group 'search path separator' { test('is string', function () assert.are_same(type(path.search_path_separator), 'string') end), test('is colon or semicolon', function () assert.is_truthy(path.search_path_separator:match '^[:;]$') end) }, group 'module' { test('check function existence', function () local functions = { 'directory', 'filename', 'is_absolute', 'is_relative', 'join', 'make_relative', 'normalize', 'split', 'split_extension', 'split_search_path', } for _, f in ipairs(functions) do assert.are_equal(type(path[f]), 'function') end end) } } pandoc-lua-engine-0.2.0.1/test/lua/module/pandoc-structure.lua0000644000000000000000000001167207346545000022370 0ustar0000000000000000local tasty = require 'tasty' local structure = require 'pandoc.structure' local path = require 'pandoc.path' local system = require 'pandoc.system' local assert = tasty.assert local test = tasty.test_case local group = tasty.test_group return { test('is table', function () assert.are_equal(type(structure), 'table') end), group 'make_sections' { test('sanity check', function () local blks = { pandoc.Header(1, {pandoc.Str 'First'}), pandoc.Header(2, {pandoc.Str 'Second'}), pandoc.Header(2, {pandoc.Str 'Third'}), } local opts = PANDOC_WRITER_OPTIONS local hblks = structure.make_sections(blks, opts) assert.are_equal('Div', hblks[1].t) assert.are_equal('Header', hblks[1].content[1].t) end), test('respects number_sections', function () local blks = { pandoc.Header(1, {pandoc.Str 'First'}), pandoc.Para 'Vestibulum convallis, lorem a tempus semper.' } local hblks = structure.make_sections(blks, {number_sections = true}) assert.are_equal('Div', hblks[1].t) assert.are_equal('Header', hblks[1].content[1].t) assert.are_equal('1', hblks[1].content[1].attributes['number']) end), test('respects base_level', function () local blks = { pandoc.Header(1, {pandoc.Str 'First'}), pandoc.Para 'Curabitur lacinia pulvinar nibh.', pandoc.Header(3, {pandoc.Str 'First'}), -- Skipping level 2 } local opts = { number_sections = true, base_level = 1 } local hblks = structure.make_sections(blks, opts) assert.are_equal('Div', hblks[1].t) assert.are_equal('Header', hblks[1].content[1].t) assert.are_equal('1', hblks[1].content[1].attributes['number']) assert.are_equal('1.1', hblks[1].content[3].attributes['number']) end) }, group 'split_into_chunks' { test('is function', function () assert.are_equal(type(structure.split_into_chunks), 'function') end), test('returns a chunked doc', function () assert.are_equal( pandoc.utils.type(structure.split_into_chunks(pandoc.Pandoc{})), 'pandoc.ChunkedDoc' ) end), }, group 'table_of_contents' { test('is function', function () assert.are_equal(type(structure.table_of_contents), 'function') end), test('returns a bullet list', function () assert.are_equal( pandoc.utils.type(structure.table_of_contents{}), 'Block' ) assert.are_equal( structure.table_of_contents{}.t, 'BulletList' ) end), test('returns a toc for a list of blocks', function () local body = pandoc.Blocks{ pandoc.Header(1, 'First'), pandoc.Para('A sentence placed below the first structure.'), pandoc.Header(2, 'Subsection'), pandoc.Para('Mauris ac felis vel velit tristique imperdiet.'), pandoc.Header(1, 'Second'), pandoc.Para('Integer placerat tristique nisl.') } assert.are_equal( structure.table_of_contents(body), pandoc.BulletList{ {pandoc.Plain('First'), pandoc.BulletList{{pandoc.Plain 'Subsection'}} }, {pandoc.Plain('Second')} } ) end), test('returns a toc for a chunked doc', function () local doc = pandoc.Pandoc { pandoc.Header(1, 'First', {id='first'}), pandoc.Para('A sentence placed below the first structure.'), pandoc.Header(2, 'Subsection', {id='subsection'}), pandoc.Para('Mauris ac felis vel velit tristique imperdiet.'), pandoc.Header(1, 'Second', {id='second'}), pandoc.Para('Integer placerat tristique nisl.') } local chunked = structure.split_into_chunks(doc, {chunk_level = 2}) assert.are_equal( structure.table_of_contents(chunked), pandoc.BulletList{ {pandoc.Plain({pandoc.Link('First', 'chunk-001', '', {id='toc-first'})}), pandoc.BulletList{{pandoc.Plain({pandoc.Link('Subsection', 'chunk-002', '', {id='toc-subsection'})})}} }, {pandoc.Plain({pandoc.Link('Second', 'chunk-003', '', {id='toc-second'})})} } ) end), test('respects toc-depth option', function () local doc = pandoc.Pandoc { pandoc.Header(1, 'First', {id='first'}), pandoc.Para('A sentence placed below the first structure.'), pandoc.Header(2, 'Subsection', {id='subsection'}), pandoc.Para('Mauris ac felis vel velit tristique imperdiet.'), pandoc.Header(1, 'Second', {id='second'}), pandoc.Para('Integer placerat tristique nisl.') } local chunked = structure.split_into_chunks(doc) assert.are_equal( structure.table_of_contents(chunked, {toc_depth = 1}), pandoc.BulletList{ {pandoc.Plain({pandoc.Link('First', 'chunk-001', '', {id='toc-first'})})}, {pandoc.Plain({pandoc.Link('Second', 'chunk-002', '', {id='toc-second'})})} } ) end), }, } pandoc-lua-engine-0.2.0.1/test/lua/module/pandoc-template.lua0000644000000000000000000000532407346545000022140 0ustar0000000000000000local tasty = require 'tasty' local template = require 'pandoc.template' local assert = tasty.assert local test = tasty.test_case local group = tasty.test_group return { test('is table', function () assert.are_equal(type(template), 'table') end), group 'default' { test('is function', function () assert.are_equal(type(template.default), 'function') end), test('returns a string for known format', function () assert.are_equal( pandoc.utils.type(template.default 'json'), 'string' ) assert.are_equal( pandoc.utils.type(template.default 'markdown'), 'string' ) end), test('fails on unknown format', function () local success, msg = pcall(function () return pandoc.utils.type(template.default 'nosuchformat') end) assert.is_falsy(success) end), }, group 'compile' { test('is function', function () assert.are_equal(type(template.compile), 'function') end), test('returns a Template', function () assert.are_equal( pandoc.utils.type(template.compile('$title$')), 'pandoc Template' ) end), test('returns a Template', function () local templ_path = pandoc.path.join{'lua', 'module', 'default.test'} assert.are_equal( pandoc.utils.type(template.compile('${ partial() }', templ_path)), 'pandoc Template' ) end), test('fails if template has non-existing partial', function () assert.error_matches( function () return template.compile('${ nosuchpartial() }') end, 'Could not find data file' ) end), test('works with default template that uses partials', function () local jats_template = template.default 'jats' assert.are_equal(type(jats_template), 'string') assert.are_equal( pandoc.utils.type(template.compile(jats_template)), 'pandoc Template' ) end), }, group 'apply' { test('is function', function () assert.are_equal(type(template.apply), 'function') end), test('returns a Doc value', function () local tmpl = template.compile('placeholder') assert.are_equal( pandoc.utils.type(template.apply(tmpl, {})), 'Doc' ) end), test('applies the given context', function () local tmpl = template.compile('song: $title$') local context = {title = 'Along Comes Mary'} assert.are_equal( template.apply(tmpl, context):render(), 'song: Along Comes Mary' ) end), test('accepts string as template', function () local context = {number = '2'} assert.are_equal( template.apply('Song $number$', context):render(), 'Song 2' ) end) }, } pandoc-lua-engine-0.2.0.1/test/lua/module/pandoc-text.lua0000644000000000000000000000250707346545000021311 0ustar0000000000000000-- -- Tests for the pandoc.text module -- local text = require 'pandoc.text' local tasty = require 'tasty' local group = tasty.test_group local test = tasty.test_case local assert = tasty.assert assert.is_function = function (x) assert.are_equal(type(x), 'function') end -- We rely mostly on the tests in the `hslua-module-text` module. The -- only thing we need to test is whether `pandoc.text` is available, -- whether all functions are defined, and whether `require 'text'` works -- (for backwards compatibility). return { group 'module' { test('is table', function () assert.are_equal(type(text), 'table') end), test('can be required as "text"', function () assert.are_equal(require 'text', require 'pandoc.text') end) }, group 'functions' { test('fromencoding', function () assert.is_function(text.fromencoding) end), test('len', function () assert.is_function(text.len) end), test('lower', function () assert.is_function(text.lower) end), test('reverse', function () assert.is_function(text.reverse) end), test('sub', function () assert.is_function(text.sub) end), test('toencoding', function () assert.is_function(text.toencoding) end), test('upper', function () assert.is_function(text.upper) end), }, } pandoc-lua-engine-0.2.0.1/test/lua/module/pandoc-types.lua0000644000000000000000000000565607346545000021501 0ustar0000000000000000local tasty = require 'tasty' local types = require 'pandoc.types' local Version = types.Version local assert = tasty.assert local test = tasty.test_case local group = tasty.test_group return { group 'Version' { group 'constructor' { test('has type `userdata`', function () assert.are_same(type(Version {2}), 'userdata') end), test('accepts list of integers', function () assert.are_same(type(Version {2, 7, 3}), 'userdata') end), test('accepts a single integer', function () assert.are_same(Version(5), Version {5}) end), test('accepts version as string', function () assert.are_same( Version '4.45.1', Version {4, 45, 1} ) end), test('non-version string is rejected', function () local success, msg = pcall(function () Version '11friends' end) assert.is_falsy(success) assert.is_truthy(tostring(msg):match('11friends')) end) }, group 'comparison' { test('smaller (equal) than', function () assert.is_truthy(Version {2, 58, 3} < Version {2, 58, 4}) assert.is_falsy(Version {2, 60, 1} < Version {2, 59, 2}) assert.is_truthy(Version {0, 14, 3} < Version {0, 14, 3, 1}) assert.is_truthy(Version {3, 58, 3} <= Version {4}) assert.is_truthy(Version {0, 14, 3} <= Version {0, 14, 3, 1}) end), test('larger (equal) than', function () assert.is_truthy(Version{2,58,3} > Version {2, 57, 4}) assert.is_truthy(Version{2,58,3} > Version {2, 58, 2}) assert.is_truthy(Version {0, 8} >= Version {0, 8}) assert.is_falsy(Version {0, 8} >= Version {0, 8, 2}) end), test('equality', function () assert.is_truthy(Version '8.8', Version {8, 8}) end), test('second argument can be a version string', function () assert.is_truthy(Version '8' < '9.1') assert.is_falsy(Version '8.8' < '8.7') end), }, group 'conversion to string' { test('converting from and to string is a noop', function () local version_string = '1.19.4' assert.are_equal(tostring(Version(version_string)), version_string) end) }, group 'convenience functions' { test('throws error if version is too old', function () local actual = Version {2, 8} local expected = Version {2, 9} assert.error_matches( function () actual:must_be_at_least(expected) end, 'expected version 2.9 or newer, got 2.8' ) end), test('does nothing if expected version is older than actual', function () local actual = Version '2.9' local expected = Version '2.8' actual:must_be_at_least(expected) end), test('does nothing if expected version equals to actual', function () local actual = Version '2.8' local expected = Version '2.8' actual:must_be_at_least(expected) end) } } } pandoc-lua-engine-0.2.0.1/test/lua/module/pandoc-utils.lua0000644000000000000000000002423607346545000021470 0ustar0000000000000000local tasty = require 'tasty' local utils = require 'pandoc.utils' local assert = tasty.assert local test = tasty.test_case local group = tasty.test_group return { group 'blocks_to_inlines' { test('default separator', function () local blocks = { pandoc.Para { pandoc.Str 'Paragraph1' }, pandoc.Para { pandoc.Emph { pandoc.Str 'Paragraph2' } } } local expected = { pandoc.Str 'Paragraph1', pandoc.LineBreak(), pandoc.Emph { pandoc.Str 'Paragraph2' } } assert.are_same( expected, utils.blocks_to_inlines(blocks) ) end), test('custom separator', function () local blocks = { pandoc.Para{ pandoc.Str 'Paragraph1' }, pandoc.Para{ pandoc.Emph 'Paragraph2' } } local expected = { pandoc.Str 'Paragraph1', pandoc.LineBreak(), pandoc.Emph { pandoc.Str 'Paragraph2' } } assert.are_same( expected, utils.blocks_to_inlines(blocks, { pandoc.LineBreak() }) ) end) }, group 'equals' { test('compares Pandoc elements', function () assert.is_truthy( utils.equals(pandoc.Pandoc{'foo'}, pandoc.Pandoc{'foo'}) ) end), test('compares Block elements', function () assert.is_truthy( utils.equals(pandoc.Plain{'foo'}, pandoc.Plain{'foo'}) ) assert.is_falsy( utils.equals(pandoc.Para{'foo'}, pandoc.Plain{'foo'}) ) end), test('compares Inline elements', function () assert.is_truthy( utils.equals(pandoc.Emph{'foo'}, pandoc.Emph{'foo'}) ) assert.is_falsy( utils.equals(pandoc.Emph{'foo'}, pandoc.Strong{'foo'}) ) end), test('compares Inline with Block elements', function () assert.is_falsy( utils.equals(pandoc.Emph{'foo'}, pandoc.Plain{'foo'}) ) assert.is_falsy( utils.equals(pandoc.Para{'foo'}, pandoc.Strong{'foo'}) ) end), test('compares Pandoc with Block elements', function () assert.is_falsy( utils.equals(pandoc.Pandoc{'foo'}, pandoc.Plain{'foo'}) ) assert.is_falsy( utils.equals(pandoc.Para{'foo'}, pandoc.Pandoc{'foo'}) ) end), }, group 'make_sections' { test('sanity check', function () local blks = { pandoc.Header(1, {pandoc.Str 'First'}), pandoc.Header(2, {pandoc.Str 'Second'}), pandoc.Header(2, {pandoc.Str 'Third'}), } local hblks = utils.make_sections(true, 1, blks) assert.are_equal('Div', hblks[1].t) assert.are_equal('Header', hblks[1].content[1].t) assert.are_equal('1', hblks[1].content[1].attributes['number']) end) }, group 'normalize_date' { test('09 Nov 1989', function () assert.are_equal('1989-11-09', utils.normalize_date '09 Nov 1989') end), test('12/31/2017', function () assert.are_equal('2017-12-31', utils.normalize_date '12/31/2017') end), }, group 'references' { test('gets references from doc', function () local ref = { ['author'] = { {given = 'Max', family = 'Mustermann'} }, ['container-title'] = pandoc.Inlines('JOSS'), ['id'] = 'test', ['issued'] = {['date-parts'] = {{2021}}}, ['title'] = pandoc.Inlines{ pandoc.Quoted('DoubleQuote', 'Interesting'), pandoc.Space(), 'work' }, ['type'] = 'article-journal', } local nocite = pandoc.Cite( '@test', {pandoc.Citation('test', 'NormalCitation')} ) local doc = pandoc.Pandoc({}, {nocite = nocite, references = {ref}}) assert.are_same({ref}, pandoc.utils.references(doc)) end) }, group 'sha1' { test('hashing', function () local ref_hash = '0a0a9f2a6772942557ab5355d76af442f8f65e01' assert.are_equal(ref_hash, utils.sha1 'Hello, World!') end) }, group 'stringify' { test('Inline', function () local inline = pandoc.Emph{ pandoc.Str 'Cogito', pandoc.Space(), pandoc.Str 'ergo', pandoc.Space(), pandoc.Str 'sum.', } assert.are_equal('Cogito ergo sum.', utils.stringify(inline)) end), test('Block', function () local block = pandoc.Para{ pandoc.Str 'Make', pandoc.Space(), pandoc.Str 'it', pandoc.Space(), pandoc.Str 'so.', } assert.are_equal('Make it so.', utils.stringify(block)) end), test('boolean', function () assert.are_equal('true', utils.stringify(true)) assert.are_equal('false', utils.stringify(false)) end), test('number', function () assert.are_equal('5', utils.stringify(5)) assert.are_equal('23.23', utils.stringify(23.23)) end), test('Attr', function () local attr = pandoc.Attr('foo', {'bar'}, {a = 'b'}) assert.are_equal('', utils.stringify(attr)) end), test('List', function () local list = pandoc.List{pandoc.Str 'a', pandoc.Blocks('b')} assert.are_equal('ab', utils.stringify(list)) end), test('Blocks', function () local blocks = pandoc.Blocks{pandoc.Para 'a', pandoc.Header(1, 'b')} assert.are_equal('ab', utils.stringify(blocks)) end), test('Inlines', function () local inlines = pandoc.Inlines{pandoc.Str 'a', pandoc.Subscript('b')} assert.are_equal('ab', utils.stringify(inlines)) end), test('Meta', function () local meta = pandoc.Meta{ a = pandoc.Inlines 'funny and ', b = 'good movie', c = pandoc.List{pandoc.Inlines{pandoc.Str '!'}} } assert.are_equal('funny and good movie!', utils.stringify(meta)) end), }, group 'to_roman_numeral' { test('convertes number', function () assert.are_equal('MDCCCLXXXVIII', utils.to_roman_numeral(1888)) end), test('fails on non-convertible argument', function () assert.is_falsy(pcall(utils.to_roman_numeral, 'not a number')) end) }, group 'type' { test('nil', function () assert.are_equal(utils.type(nil), 'nil') end), test('boolean', function () assert.are_equal(utils.type(true), 'boolean') assert.are_equal(utils.type(false), 'boolean') end), test('number', function () assert.are_equal(utils.type(5), 'number') assert.are_equal(utils.type(-3.02), 'number') end), test('string', function () assert.are_equal(utils.type(''), 'string') assert.are_equal(utils.type('asdf'), 'string') end), test('plain table', function () assert.are_equal(utils.type({}), 'table') end), test('List', function () assert.are_equal(utils.type(pandoc.List{}), 'List') end), test('Inline', function () assert.are_equal(utils.type(pandoc.Str 'a'), 'Inline') assert.are_equal(utils.type(pandoc.Emph 'emphasized'), 'Inline') end), test('Inlines', function () assert.are_equal(utils.type(pandoc.Inlines{pandoc.Str 'a'}), 'Inlines') assert.are_equal(utils.type(pandoc.Inlines{pandoc.Emph 'b'}), 'Inlines') end), test('Blocks', function () assert.are_equal(utils.type(pandoc.Para 'a'), 'Block') assert.are_equal(utils.type(pandoc.CodeBlock 'true'), 'Block') end), test('Inlines', function () assert.are_equal(utils.type(pandoc.Blocks{'a'}), 'Blocks') assert.are_equal(utils.type(pandoc.Blocks{pandoc.CodeBlock 'b'}), 'Blocks') end), }, group 'to_simple_table' { test('convertes Table', function () function simple_cell (blocks) return { attr = pandoc.Attr(), alignment = "AlignDefault", contents = blocks, col_span = 1, row_span = 1, } end local tbl = pandoc.Table( {long = {pandoc.Plain { pandoc.Str "the", pandoc.Space(), pandoc.Str "caption"}}}, {{pandoc.AlignDefault, nil}}, pandoc.TableHead{pandoc.Row{simple_cell{pandoc.Plain "head1"}}}, {{ attr = pandoc.Attr(), body = {pandoc.Row{simple_cell{pandoc.Plain "cell1"}}}, head = {}, row_head_columns = 0 }}, pandoc.TableFoot(), pandoc.Attr() ) local stbl = utils.to_simple_table(tbl) assert.are_equal('SimpleTable', stbl.t) assert.are_equal('head1', utils.stringify(stbl.headers[1])) assert.are_equal('cell1', utils.stringify(stbl.rows[1][1])) assert.are_equal('the caption', utils.stringify(pandoc.Span(stbl.caption))) end), test('fails on para', function () assert.is_falsy(pcall(utils.to_simple_table, pandoc.Para "nope")) end), }, group 'from_simple_table' { test('converts SimpleTable to Table', function () local caption = {pandoc.Str "Overview"} local aligns = {pandoc.AlignDefault, pandoc.AlignDefault} local widths = {0, 0} -- let pandoc determine col widths local headers = { {pandoc.Plain "Language"}, {pandoc.Plain "Typing"} } local rows = { {{pandoc.Plain "Haskell"}, {pandoc.Plain "static"}}, {{pandoc.Plain "Lua"}, {pandoc.Plain "Dynamic"}}, } local simple_table = pandoc.SimpleTable( caption, aligns, widths, headers, rows ) local tbl = utils.from_simple_table(simple_table) assert.are_equal("Table", tbl.t) assert.are_same( {pandoc.Plain(caption)}, tbl.caption.long ) -- reversible assert.are_same(simple_table, utils.to_simple_table(tbl)) end), test('empty caption', function () local simple_table = pandoc.SimpleTable( {}, {pandoc.AlignDefault}, {0}, {{pandoc.Plain 'a'}}, {{{pandoc.Plain 'b'}}} ) local tbl = utils.from_simple_table(simple_table) assert.are_equal( pandoc.Blocks{}, tbl.caption.long ) assert.is_nil(tbl.caption.short) end), test('empty body', function () local simple_table = pandoc.SimpleTable( pandoc.Inlines('a nice caption'), {pandoc.AlignDefault}, {0}, {{pandoc.Plain 'a'}}, {} ) local tbl = utils.from_simple_table(simple_table) tbl.bodies:map(print) assert.are_same(pandoc.List(), tbl.bodies) end), } } pandoc-lua-engine-0.2.0.1/test/lua/module/pandoc.lua0000644000000000000000000003576007346545000020336 0ustar0000000000000000local tasty = require 'tasty' local test = tasty.test_case local group = tasty.test_group local assert = tasty.assert function os_is_windows () return package.config:sub(1,1) == '\\' end -- Constructor behavior is tested in the hslua-pandoc-types module, so -- we just make sure the functions are present. return { group 'Constructors' { group 'Misc' { test('pandoc.Attr is a function', function () assert.are_equal(type(pandoc.Attr), 'function') end), test('pandoc.AttributeList is a function', function () assert.are_equal(type(pandoc.AttributeList), 'function') end), test('pandoc.Blocks is a function', function () assert.are_equal(type(pandoc.Blocks), 'function') end), test('pandoc.Citation is a function', function () assert.are_equal(type(pandoc.Citation), 'function') end), test('pandoc.Inlines is a function', function () assert.are_equal(type(pandoc.Inlines), 'function') end), test('pandoc.SimpleTable is a function', function () assert.are_equal(type(pandoc.SimpleTable), 'function') end), test('pandoc.Meta is a function', function () assert.are_equal(type(pandoc.Meta), 'function') end), test('pandoc.Pandoc is a function', function () assert.are_equal(type(pandoc.Pandoc), 'function') end), }, group "Inline elements" { test('pandoc.AttributeList is a function', function () assert.are_equal(type(pandoc.Cite), 'function') end), test('pandoc.AttributeList is a function', function () assert.are_equal(type(pandoc.Code), 'function') end), test('pandoc.Emph is a function', function () assert.are_equal(type(pandoc.Emph), 'function') end), test('pandoc.Image is a function', function () assert.are_equal(type(pandoc.Image), 'function') end), test('pandoc.Link is a function', function () assert.are_equal(type(pandoc.Link), 'function') end), test('pandoc.Math is a function', function () assert.are_equal(type(pandoc.Math), 'function') end), test('pandoc.Note is a function', function () assert.are_equal(type(pandoc.Note), 'function') end), test('pandoc.Quoted is a function', function () assert.are_equal(type(pandoc.Quoted), 'function') end), test('pandoc.SmallCaps is a function', function () assert.are_equal(type(pandoc.SmallCaps), 'function') end), test('pandoc.SoftBreak is a function', function () assert.are_equal(type(pandoc.SoftBreak), 'function') end), test('pandoc.Span is a function', function () assert.are_equal(type(pandoc.Span), 'function') end), test('pandoc.Str is a function', function () assert.are_equal(type(pandoc.Str), 'function') end), test('pandoc.Strikeout is a function', function () assert.are_equal(type(pandoc.Strikeout), 'function') end), test('pandoc.Strong is a function', function () assert.are_equal(type(pandoc.Strong), 'function') end), test('pandoc.Subscript is a function', function () assert.are_equal(type(pandoc.Subscript), 'function') end), test('pandoc.Superscript is a function', function () assert.are_equal(type(pandoc.Superscript), 'function') end), test('pandoc.Underline is a function', function () assert.are_equal(type(pandoc.Underline), 'function') end), }, group "Block elements" { test('pandoc.BlockQuote is a function', function () assert.are_equal(type(pandoc.BlockQuote), 'function') end), test('pandoc.BulletList is a function', function () assert.are_equal(type(pandoc.BulletList), 'function') end), test('pandoc.CodeBlock is a function', function () assert.are_equal(type(pandoc.CodeBlock), 'function') end), test('pandoc.DefinitionList is a function', function () assert.are_equal(type(pandoc.DefinitionList), 'function') end), test('pandoc.Div is a function', function () assert.are_equal(type(pandoc.Div), 'function') end), test('pandoc.Header is a function', function () assert.are_equal(type(pandoc.Header), 'function') end), test('pandoc.LineBlock is a function', function () assert.are_equal(type(pandoc.LineBlock), 'function') end), test('pandoc.Figure is a function', function () assert.are_equal(type(pandoc.Figure), 'function') end), test('pandoc.OrderedList is a function', function () assert.are_equal(type(pandoc.OrderedList), 'function') end), test('pandoc.Para is a function', function () assert.are_equal(type(pandoc.Para), 'function') end), test('pandoc.Plain is a function', function () assert.are_equal(type(pandoc.Plain), 'function') end), test('pandoc.RawBlock is a function', function () assert.are_equal(type(pandoc.Plain), 'function') end), test('pandoc.Table is a function', function () assert.are_equal(type(pandoc.Table), 'function') end), } }, group 'MetaValue elements' { test('MetaList elements behave like lists', function () local metalist = pandoc.MetaList{} assert.are_equal(type(metalist.insert), 'function') assert.are_equal(type(metalist.remove), 'function') end), test('`tag` is an alias for `t``', function () assert.are_equal((pandoc.MetaList{}).tag, (pandoc.MetaList{}).t) assert.are_equal((pandoc.MetaMap{}).tag, (pandoc.MetaMap{}).t) assert.are_equal((pandoc.MetaInlines{}).tag, (pandoc.MetaInlines{}).t) assert.are_equal((pandoc.MetaBlocks{}).tag, (pandoc.MetaBlocks{}).t) end), }, group 'Meta' { test('inline list is treated as MetaInlines', function () local meta = pandoc.Pandoc({}, {test = {pandoc.Emph 'check'}}).meta assert.are_same(meta.test, {pandoc.Emph{pandoc.Str 'check'}}) end), test('inline element is treated as MetaInlines singleton', function () local meta = pandoc.Pandoc({}, {test = pandoc.Emph 'check'}).meta assert.are_same(meta.test, {pandoc.Emph{pandoc.Str 'check'}}) end), test('block list is treated as MetaBlocks', function () local meta = pandoc.Pandoc({}, {test = {pandoc.Plain 'check'}}).meta assert.are_same(meta.test, {pandoc.Plain{pandoc.Str 'check'}}) end), test('block element is treated as MetaBlocks singleton', function () local meta = pandoc.Pandoc({}, {test = pandoc.Plain 'check'}).meta assert.are_same(meta.test, {pandoc.Plain{pandoc.Str 'check'}}) end), }, group 'Other types' { group 'ReaderOptions' { test('returns a userdata value', function () local opts = pandoc.ReaderOptions {} assert.are_equal(type(opts), 'userdata') end), test('can construct from table', function () local opts = pandoc.ReaderOptions {columns = 66} assert.are_equal(opts.columns, 66) end), test('can construct from other ReaderOptions value', function () local orig = pandoc.ReaderOptions{columns = 65} local copy = pandoc.ReaderOptions(orig) for k, v in pairs(orig) do assert.are_same(copy[k], v) end assert.are_equal(copy.columns, 65) end), }, }, group 'clone' { test('clones Attr', function () local attr = pandoc.Attr('test', {'my-class'}, {foo = 'bar'}) local cloned = attr:clone() attr.identifier = '' attr.classes = {} attr.attributes = {} assert.are_same(cloned.identifier, 'test') assert.are_same(cloned.classes, {'my-class'}) assert.are_same(cloned.attributes.foo, 'bar') end), test('clones ListAttributes', function () local la = pandoc.ListAttributes(2, pandoc.DefaultStyle, pandoc.Period) local cloned = la:clone() la.start = 9 assert.are_same(cloned.start, 2) end), test('clones Para', function () local para = pandoc.Para {pandoc.Str 'Hello'} local cloned = para:clone() para.content[1].text = 'bye' assert.are_same(cloned, pandoc.Para {pandoc.Str 'Hello'}) end), test('clones Str', function () local str = pandoc.Str 'Hello' local cloned = str:clone() str.text = 'bye' assert.are_same(cloned.text, 'Hello') end), test('clones Citation', function () local cite = pandoc.Citation('leibniz', pandoc.AuthorInText) local cloned = cite:clone() cite.id = 'newton' assert.are_same(cloned.id, 'leibniz') assert.are_same(cite.id, 'newton') assert.are_same(cite.mode, cloned.mode) end), }, group 'pipe' { test('external string processing', function () if os_is_windows() then local pipe_result = pandoc.pipe('find', {'hi'}, 'hi') assert.are_equal('hi', pipe_result:match '%a+') else local pipe_result = pandoc.pipe('tr', {'a', 'b'}, 'abc') assert.are_equal('bbc', pipe_result:match '%a+') end end), test('failing pipe', function () if os_is_windows() then local success, err = pcall(pandoc.pipe, 'find', {'/a'}, 'hi') assert.is_falsy(success) assert.are_equal('find', err.command) assert.is_truthy(err.error_code ~= 0) else local success, err = pcall(pandoc.pipe, 'false', {}, 'abc') assert.is_falsy(success) assert.are_equal('false', err.command) assert.are_equal(1, err.error_code) assert.are_equal('', err.output) end end) }, group 'read' { test('Markdown', function () local valid_markdown = '*Hello*, World!\n' local expected = pandoc.Pandoc({ pandoc.Para { pandoc.Emph { pandoc.Str 'Hello' }, pandoc.Str ',', pandoc.Space(), pandoc.Str 'World!' } }) assert.are_same(expected, pandoc.read(valid_markdown)) end), test('unsupported extension', function () assert.error_matches( function () pandoc.read('foo', 'gfm+empty_paragraphs') end, 'The extension empty_paragraphs is not supported for gfm' ) end), test('read with other indented code classes', function() local indented_code = ' return true' local expected = pandoc.Pandoc({ pandoc.CodeBlock('return true', {class='foo'}) }) assert.are_same( expected, pandoc.read(indented_code, 'markdown', {indented_code_classes={'foo'}}) ) end), test('can read epub', function () local epub = io.open('lua/module/tiny.epub', 'rb') local blocks = pandoc.read(epub:read'a', 'epub').blocks assert.are_equal( blocks[#blocks], pandoc.Para { pandoc.Emph 'EPUB' } ) end), test('failing read', function () assert.error_matches( function () pandoc.read('foo', 'nosuchreader') end, 'Unknown input format nosuchreader' ) end), group 'extensions' { test('string spec', function () local doc = pandoc.read('"vice versa"', 'markdown-smart') assert.are_equal(doc, pandoc.Pandoc{pandoc.Para '"vice versa"'}) end), test('unsupported extension', function () assert.error_matches( function () pandoc.read('foo', 'gfm+empty_paragraphs') end, 'The extension empty_paragraphs is not supported for gfm' ) end), test('unknown extension', function () local format_spec = { format = 'markdown', extensions = {'nope'}} assert.error_matches( function () pandoc.read('x', format_spec) end, 'The extension nope is not supported for markdown' ) end), test('fails on invalid extension', function () local format_spec = { format = 'markdown', extensions = {'nope'}} assert.error_matches( function () pandoc.read('nu-uh', format_spec) end, 'The extension nope is not supported for markdown' ) end), }, }, group 'walk_block' { test('block walking order', function () local acc = {} local nested_nums = pandoc.Div { pandoc.Para{pandoc.Str'1'}, pandoc.Div{ pandoc.Para{pandoc.Str'2'}, pandoc.Para{pandoc.Str'3'} }, pandoc.Para{pandoc.Str'4'} } pandoc.walk_block( nested_nums, {Para = function (p) table.insert(acc, p.content[1].text) end} ) assert.are_equal('1234', table.concat(acc)) end) }, group 'walk_inline' { test('inline walking order', function () local acc = {} local nested_nums = pandoc.Span { pandoc.Str'1', pandoc.Emph { pandoc.Str'2', pandoc.Str'3' }, pandoc.Str'4' } pandoc.walk_inline( nested_nums, {Str = function (s) table.insert(acc, s.text) end} ) assert.are_equal('1234', table.concat(acc)) end) }, group 'write' { test('string spec', function () local doc = pandoc.Pandoc{pandoc.Quoted('DoubleQuote', 'vice versa')} local plain = pandoc.write(doc, 'plain+smart') assert.are_equal(plain, '"vice versa"\n') end), test('table format spec with extensions list', function () local doc = pandoc.Pandoc{pandoc.Quoted('DoubleQuote', 'vice versa')} local format_spec = { format = 'plain', extensions = {'smart'}} local plain = pandoc.write(doc, format_spec) assert.are_equal(plain, '"vice versa"\n') end), test('table format spec with `enable`/`disable` diff', function () local diff = { enable = {'smart'} } local doc = pandoc.Pandoc{pandoc.Quoted('DoubleQuote', 'vice versa')} local format_spec = { format = 'plain', extensions = diff} local plain = pandoc.write(doc, format_spec) assert.are_equal(plain, '"vice versa"\n') end), test('table format spec with set-like diff', function () local diff = { smart = true, auto_identifiers = false } local doc = pandoc.Pandoc{pandoc.Quoted('DoubleQuote', 'vice versa')} local format_spec = { format = 'plain', extensions = diff} local plain = pandoc.write(doc, format_spec) assert.are_equal(plain, '"vice versa"\n') end), test('fails on invalid extension', function () local doc = pandoc.Pandoc{'nope'} local format_spec = { format = 'plain', extensions = {'nope'}} assert.error_matches( function () pandoc.write(doc, format_spec) end, 'The extension nope is not supported for plain' ) end), }, group 'Marshal' { group 'Inlines' { test('Strings are broken into words', function () assert.are_equal( pandoc.Emph 'Nice, init?', pandoc.Emph{pandoc.Str 'Nice,', pandoc.Space(), pandoc.Str 'init?'} ) end) }, group 'Blocks' { test('Strings are broken into words and wrapped in Plain', function () assert.are_equal( pandoc.Div{ pandoc.Plain{pandoc.Str 'Nice,', pandoc.Space(), pandoc.Str 'init?'} }, pandoc.Div{'Nice, init?'} ) end) } } } pandoc-lua-engine-0.2.0.1/test/lua/module/partial.test0000644000000000000000000000000007346545000020677 0ustar0000000000000000pandoc-lua-engine-0.2.0.1/test/lua/module/sample.svg0000644000000000000000000000237407346545000020364 0ustar0000000000000000 Single-Source-Publishing Logo pandoc-lua-engine-0.2.0.1/test/lua/module/tiny.epub0000644000000000000000000000603107346545000020214 0ustar0000000000000000PK `Soa,mimetypeapplication/epub+zipPK kSEPUB/PK JS}n##EPUB/content.opf urn:uuid:7f93e293-2638-4f9d-b8c9-608ac258f6f6Testen-US2021-12-30T16:03:00Z PK US EPUB/text/PK USpȼEPUB/text/a.xhtml Test

EPUB

PK kSŝ EPUB/toc.ncx TestTest PK 3S;SSEPUB/nav.xhtml Test PK S META-INF/PK SMETA-INF/container.xml PK `Soa,mimetypePK kSA:EPUB/PK JS}n##]EPUB/content.opfPK US AEPUB/text/PK USpȼEPUB/text/a.xhtmlPK kSŝ EPUB/toc.ncxPK 3S;SS+EPUB/nav.xhtmlPK S AMETA-INF/PK SMETA-INF/container.xmlPK  pandoc-lua-engine-0.2.0.1/test/lua/plain-to-para.lua0000644000000000000000000000013107346545000020231 0ustar0000000000000000return { { Plain = function (elem) return pandoc.Para(elem.content) end, } } pandoc-lua-engine-0.2.0.1/test/lua/require-file.lua0000644000000000000000000000010207346545000020154 0ustar0000000000000000package.path = package.path .. ';lua/?.lua' require 'script-name' pandoc-lua-engine-0.2.0.1/test/lua/script-name.lua0000644000000000000000000000011307346545000020007 0ustar0000000000000000function Para (_) return pandoc.Para{pandoc.Str(PANDOC_SCRIPT_FILE)} end pandoc-lua-engine-0.2.0.1/test/lua/single-to-double-quoted.lua0000644000000000000000000000025207346545000022241 0ustar0000000000000000return { { Quoted = function (elem) if elem.quotetype == "SingleQuote" then elem.quotetype = "DoubleQuote" end return elem end, } } pandoc-lua-engine-0.2.0.1/test/lua/smallcaps-title.lua0000644000000000000000000000061507346545000020672 0ustar0000000000000000return { { Meta = function(meta) -- The call to `MetaInlines` is redundant and used for testing purposes -- only. The explicit use of a MetaValue constructor is only useful when -- used with an empty table: `MetaInlines{}` is read differently than -- `MetaBlocks{}`. meta.title = pandoc.MetaInlines{pandoc.SmallCaps(meta.title)} return meta end } } pandoc-lua-engine-0.2.0.1/test/lua/smart-constructors.lua0000644000000000000000000000055607346545000021474 0ustar0000000000000000-- Test that constructors are "smart" in that they autoconvert -- types where sensible. function Para (_) return { pandoc.BulletList{pandoc.Para "Hello", pandoc.Para "World"}, pandoc.DefinitionList{{"foo", pandoc.Para "placeholder"}}, pandoc.LineBlock{"Moin", "Welt"}, pandoc.OrderedList{pandoc.Plain{pandoc.Str "one"}, pandoc.Plain "two"} } end pandoc-lua-engine-0.2.0.1/test/lua/strmacro.lua0000644000000000000000000000030207346545000017417 0ustar0000000000000000return { { Str = function (elem) if elem.text == "{{helloworld}}" then return pandoc.Emph {pandoc.Str "Hello, World"} else return elem end end, } } pandoc-lua-engine-0.2.0.1/test/lua/undiv.lua0000644000000000000000000000005107346545000016713 0ustar0000000000000000function Div(el) return el.content end pandoc-lua-engine-0.2.0.1/test/lua/uppercase-header.lua0000644000000000000000000000030007346545000021000 0ustar0000000000000000local text = require 'text' local function str_to_uppercase (s) return pandoc.Str(text.upper(s.text)) end function Header (el) return pandoc.walk_block(el, {Str = str_to_uppercase}) end pandoc-lua-engine-0.2.0.1/test/sample.lua0000644000000000000000000002315707346545000016302 0ustar0000000000000000-- This is a sample custom writer for pandoc. It produces output -- that is very similar to that of pandoc's HTML writer. -- There is one new feature: code blocks marked with class 'dot' -- are piped through graphviz and images are included in the HTML -- output using 'data:' URLs. The image format can be controlled -- via the `image_format` metadata field. -- -- Invoke with: pandoc -t sample.lua -- -- Note: you need not have lua installed on your system to use this -- custom writer. However, if you do have lua installed, you can -- use it to test changes to the script. 'lua sample.lua' will -- produce informative error messages if your code contains -- syntax errors. function Writer (doc, opts) PANDOC_DOCUMENT = doc PANDOC_WRITER_OPTIONS = opts loadfile(PANDOC_SCRIPT_FILE)() return pandoc.write_classic(doc, opts) end local pipe = pandoc.pipe local stringify = (require 'pandoc.utils').stringify -- Choose the image format based on the value of the -- `image_format` environment variable. local image_format = os.getenv 'image_format' or 'png' local image_mime_type = ({ jpeg = 'image/jpeg', jpg = 'image/jpeg', gif = 'image/gif', png = 'image/png', svg = 'image/svg+xml', })[image_format] or error('unsupported image format `' .. image_format .. '`') -- Character escaping local function escape(s, in_attribute) return s:gsub('[<>&"\']', function(x) if x == '<' then return '<' elseif x == '>' then return '>' elseif x == '&' then return '&' elseif in_attribute and x == '"' then return '"' elseif in_attribute and x == "'" then return ''' else return x end end) end -- Helper function to convert an attributes table into -- a string that can be put into HTML tags. local function attributes(attr) local attr_table = {} for x,y in pairs(attr) do if y and y ~= '' then table.insert(attr_table, ' ' .. x .. '="' .. escape(y,true) .. '"') end end return table.concat(attr_table) end -- Table to store footnotes, so they can be included at the end. local notes = {} -- Blocksep is used to separate block elements. function Blocksep() return '\n\n' end -- This function is called once for the whole document. Parameters: -- body is a string, metadata is a table, variables is a table. -- This gives you a fragment. You could use the metadata table to -- fill variables in a custom lua template. Or, pass `--template=...` -- to pandoc, and pandoc will do the template processing as usual. function Doc(body, metadata, variables) local buffer = {} local function add(s) table.insert(buffer, s) end add(body) if #notes > 0 then add('
    ') for _,note in pairs(notes) do add(note) end add('
') end return table.concat(buffer,'\n') .. '\n' end -- The functions that follow render corresponding pandoc elements. -- s is always a string, attr is always a table of attributes, and -- items is always an array of strings (the items in a list). -- Comments indicate the types of other variables. function Str(s) return escape(s) end function Space() return ' ' end function SoftBreak() return '\n' end function LineBreak() return '
' end function Emph(s) return '' .. s .. '' end function Strong(s) return '' .. s .. '' end function Subscript(s) return '' .. s .. '' end function Superscript(s) return '' .. s .. '' end function SmallCaps(s) return '' .. s .. '' end function Strikeout(s) return '' .. s .. '' end function Link(s, tgt, tit, attr) return '' .. s .. '' end function Image(s, src, tit, attr) return '' end function Code(s, attr) return '' .. escape(s) .. '' end function InlineMath(s) return '\\(' .. escape(s) .. '\\)' end function DisplayMath(s) return '\\[' .. escape(s) .. '\\]' end function SingleQuoted(s) return '‘' .. s .. '’' end function DoubleQuoted(s) return '“' .. s .. '”' end function Note(s) local num = #notes + 1 -- insert the back reference right before the final closing tag. s = string.gsub(s, '(.*)' .. s .. '') -- return the footnote reference, linked to the note. return '' .. num .. '' end function Span(s, attr) return '' .. s .. '' end function RawInline(format, str) if format == 'html' then return str else return '' end end function Cite(s, cs) local ids = {} for _,cit in ipairs(cs) do table.insert(ids, cit.citationId) end return '' .. s .. '' end function Plain(s) return s end function Para(s) return '

' .. s .. '

' end -- lev is an integer, the header level. function Header(lev, s, attr) return '' .. s .. '' end function BlockQuote(s) return '
\n' .. s .. '\n
' end function HorizontalRule() return "
" end function LineBlock(ls) return '
' .. table.concat(ls, '\n') .. '
' end function CodeBlock(s, attr) -- If code block has class 'dot', pipe the contents through dot -- and base64, and include the base64-encoded png as a data: URL. if attr.class and string.match(' ' .. attr.class .. ' ',' dot ') then local img = pipe('base64', {}, pipe('dot', {'-T' .. image_format}, s)) return '' -- otherwise treat as code (one could pipe through a highlighter) else return '
' .. escape(s) ..
           '
' end end function BulletList(items) local buffer = {} for _, item in pairs(items) do table.insert(buffer, '
  • ' .. item .. '
  • ') end return '
      \n' .. table.concat(buffer, '\n') .. '\n
    ' end function OrderedList(items) local buffer = {} for _, item in pairs(items) do table.insert(buffer, '
  • ' .. item .. '
  • ') end return '
      \n' .. table.concat(buffer, '\n') .. '\n
    ' end function DefinitionList(items) local buffer = {} for _,item in pairs(items) do local k, v = next(item) table.insert(buffer, '
    ' .. k .. '
    \n
    ' .. table.concat(v, '
    \n
    ') .. '
    ') end return '
    \n' .. table.concat(buffer, '\n') .. '\n
    ' end -- Convert pandoc alignment to something HTML can use. -- align is AlignLeft, AlignRight, AlignCenter, or AlignDefault. local function html_align(align) if align == 'AlignLeft' then return 'left' elseif align == 'AlignRight' then return 'right' elseif align == 'AlignCenter' then return 'center' else return 'left' end end function CaptionedImage(src, tit, caption, attr) if #caption == 0 then return '

    ' else local ecaption = escape(caption) return '
    \n' .. ecaption  .. '' .. '
    ' .. ecaption .. '
    \n
    ' end end function Figure(caption, contents, attr) return '\n' .. contents .. '\n
    ' .. caption .. '
    \n' .. '' end -- Caption is a string, aligns is an array of strings, -- widths is an array of floats, headers is an array of -- strings, rows is an array of arrays of strings. function Table(caption, aligns, widths, headers, rows) local buffer = {} local function add(s) table.insert(buffer, s) end add('') if caption ~= '' then add('') end if widths and widths[1] ~= 0 then for _, w in pairs(widths) do add('') end end local header_row = {} local empty_header = true for i, h in pairs(headers) do local align = html_align(aligns[i]) table.insert(header_row,'') empty_header = empty_header and h == '' end if not empty_header then add('') for _,h in pairs(header_row) do add(h) end add('') end local class = 'even' for _, row in pairs(rows) do class = (class == 'even' and 'odd') or 'even' add('') for i,c in pairs(row) do add('') end add('') end add('
    ' .. escape(caption) .. '
    ' .. h .. '
    ' .. c .. '
    ') return table.concat(buffer,'\n') end function RawBlock(format, str) if format == 'html' then return str else return '' end end function Div(s, attr) return '\n' .. s .. '' end -- The following code will produce runtime warnings when you haven't defined -- all of the functions you need for the custom writer, so it's useful -- to include when you're working on a writer. local meta = {} meta.__index = function(_, key) io.stderr:write(string.format("WARNING: Undefined function '%s'\n",key)) return function() return '' end end setmetatable(_G, meta) pandoc-lua-engine-0.2.0.1/test/tables.custom0000644000000000000000000001015507346545000017016 0ustar0000000000000000

    Simple table with caption:

    Demonstration of simple table syntax.
    Right Left Center Default
    12 12 12 12
    123 123 123 123
    1 1 1 1

    Simple table without caption:

    Right Left Center Default
    12 12 12 12
    123 123 123 123
    1 1 1 1

    Simple table indented two spaces:

    Demonstration of simple table syntax.
    Right Left Center Default
    12 12 12 12
    123 123 123 123
    1 1 1 1

    Multiline table with caption:

    Here’s the caption. It may span multiple lines.
    Centered Header Left Aligned Right Aligned Default aligned
    First row 12.0 Example of a row that spans multiple lines.
    Second row 5.0 Here’s another one. Note the blank line between rows.

    Multiline table without caption:

    Centered Header Left Aligned Right Aligned Default aligned
    First row 12.0 Example of a row that spans multiple lines.
    Second row 5.0 Here’s another one. Note the blank line between rows.

    Table without column headers:

    12 12 12 12
    123 123 123 123
    1 1 1 1

    Multiline table without column headers:

    First row 12.0 Example of a row that spans multiple lines.
    Second row 5.0 Here’s another one. Note the blank line between rows.
    pandoc-lua-engine-0.2.0.1/test/tables.native0000644000000000000000000006153307346545000017000 0ustar0000000000000000[ Para [ Str "Simple" , Space , Str "table" , Space , Str "with" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Demonstration" , Space , Str "of" , Space , Str "simple" , Space , Str "table" , Space , Str "syntax." ] ]) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Simple" , Space , Str "table" , Space , Str "without" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Simple" , Space , Str "table" , Space , Str "indented" , Space , Str "two" , Space , Str "spaces:" ] , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Demonstration" , Space , Str "of" , Space , Str "simple" , Space , Str "table" , Space , Str "syntax." ] ]) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Multiline" , Space , Str "table" , Space , Str "with" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Here\8217s" , Space , Str "the" , Space , Str "caption." , SoftBreak , Str "It" , Space , Str "may" , Space , Str "span" , Space , Str "multiple" , Space , Str "lines." ] ]) [ ( AlignCenter , ColWidth 0.15 ) , ( AlignLeft , ColWidth 0.1375 ) , ( AlignRight , ColWidth 0.1625 ) , ( AlignLeft , ColWidth 0.35 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Centered" , SoftBreak , Str "Header" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" , SoftBreak , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" , SoftBreak , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" , Space , Str "aligned" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "First" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Example" , Space , Str "of" , Space , Str "a" , Space , Str "row" , Space , Str "that" , Space , Str "spans" , SoftBreak , Str "multiple" , Space , Str "lines." ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Here\8217s" , Space , Str "another" , Space , Str "one." , Space , Str "Note" , SoftBreak , Str "the" , Space , Str "blank" , Space , Str "line" , Space , Str "between" , Space , Str "rows." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Multiline" , Space , Str "table" , Space , Str "without" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidth 0.15 ) , ( AlignLeft , ColWidth 0.1375 ) , ( AlignRight , ColWidth 0.1625 ) , ( AlignLeft , ColWidth 0.35 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Centered" , SoftBreak , Str "Header" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" , SoftBreak , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" , SoftBreak , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" , Space , Str "aligned" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "First" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Example" , Space , Str "of" , Space , Str "a" , Space , Str "row" , Space , Str "that" , Space , Str "spans" , SoftBreak , Str "multiple" , Space , Str "lines." ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Here\8217s" , Space , Str "another" , Space , Str "one." , Space , Str "Note" , SoftBreak , Str "the" , Space , Str "blank" , Space , Str "line" , Space , Str "between" , Space , Str "rows." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Table" , Space , Str "without" , Space , Str "column" , Space , Str "headers:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignRight , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Multiline" , Space , Str "table" , Space , Str "without" , Space , Str "column" , Space , Str "headers:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidth 0.15 ) , ( AlignLeft , ColWidth 0.1375 ) , ( AlignRight , ColWidth 0.1625 ) , ( AlignDefault , ColWidth 0.35 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "First" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Example" , Space , Str "of" , Space , Str "a" , Space , Str "row" , Space , Str "that" , Space , Str "spans" , SoftBreak , Str "multiple" , Space , Str "lines." ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Here\8217s" , Space , Str "another" , Space , Str "one." , Space , Str "Note" , SoftBreak , Str "the" , Space , Str "blank" , Space , Str "line" , Space , Str "between" , Space , Str "rows." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] pandoc-lua-engine-0.2.0.1/test/test-pandoc-lua-engine.hs0000644000000000000000000000111407346545000021102 0ustar0000000000000000module Main (main) where import Test.Tasty (TestTree, defaultMain, testGroup) import qualified Tests.Lua import qualified Tests.Lua.Module import qualified Tests.Lua.Reader import qualified Tests.Lua.Writer import System.Directory (withCurrentDirectory) main :: IO () main = withCurrentDirectory "test" $ defaultMain tests tests :: TestTree tests = testGroup "pandoc Lua engine" [ testGroup "Lua filters" Tests.Lua.tests , testGroup "Lua modules" Tests.Lua.Module.tests , testGroup "Custom writers" Tests.Lua.Writer.tests , testGroup "Custom readers" Tests.Lua.Reader.tests ] pandoc-lua-engine-0.2.0.1/test/testsuite.native0000644000000000000000000014330507346545000017555 0ustar0000000000000000Pandoc Meta { unMeta = fromList [ ( "author" , MetaList [ MetaInlines [ Str "John" , Space , Str "MacFarlane" ] , MetaInlines [ Str "Anonymous" ] ] ) , ( "date" , MetaInlines [ Str "July" , Space , Str "17," , Space , Str "2006" ] ) , ( "title" , MetaInlines [ Str "Pandoc" , Space , Str "Test" , Space , Str "Suite" ] ) ] } [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "set" , Space , Str "of" , Space , Str "tests" , Space , Str "for" , Space , Str "pandoc." , Space , Str "Most" , Space , Str "of" , Space , Str "them" , Space , Str "are" , Space , Str "adapted" , Space , Str "from" , SoftBreak , Str "John" , Space , Str "Gruber\8217s" , Space , Str "markdown" , Space , Str "test" , Space , Str "suite." ] , HorizontalRule , Header 1 ( "headers" , [] , [] ) [ Str "Headers" ] , Header 2 ( "level-2-with-an-embedded-link" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "link" ] ( "/url" , "" ) ] , Header 3 ( "level-3-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "3" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Header 4 ( "level-4" , [] , [] ) [ Str "Level" , Space , Str "4" ] , Header 5 ( "level-5" , [] , [] ) [ Str "Level" , Space , Str "5" ] , Header 1 ( "level-1" , [] , [] ) [ Str "Level" , Space , Str "1" ] , Header 2 ( "level-2-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Header 3 ( "level-3" , [] , [] ) [ Str "Level" , Space , Str "3" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] , Header 2 ( "level-2" , [] , [] ) [ Str "Level" , Space , Str "2" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] , HorizontalRule , Header 1 ( "paragraphs" , [] , [] ) [ Str "Paragraphs" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "regular" , Space , Str "paragraph." ] , Para [ Str "In" , Space , Str "Markdown" , Space , Str "1.0.0" , Space , Str "and" , Space , Str "earlier." , Space , Str "Version" , SoftBreak , Str "8." , Space , Str "This" , Space , Str "line" , Space , Str "turns" , Space , Str "into" , Space , Str "a" , Space , Str "list" , Space , Str "item." , SoftBreak , Str "Because" , Space , Str "a" , Space , Str "hard-wrapped" , Space , Str "line" , Space , Str "in" , Space , Str "the" , SoftBreak , Str "middle" , Space , Str "of" , Space , Str "a" , Space , Str "paragraph" , Space , Str "looked" , Space , Str "like" , Space , Str "a" , SoftBreak , Str "list" , Space , Str "item." ] , Para [ Str "Here\8217s" , Space , Str "one" , Space , Str "with" , Space , Str "a" , Space , Str "bullet." , SoftBreak , Str "*" , Space , Str "criminey." ] , Para [ Str "There" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "hard" , Space , Str "line" , Space , Str "break" , LineBreak , Str "here." ] , HorizontalRule , Header 1 ( "block-quotes" , [] , [] ) [ Str "Block" , Space , Str "Quotes" ] , Para [ Str "E-mail" , Space , Str "style:" ] , BlockQuote [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "block" , Space , Str "quote." , SoftBreak , Str "It" , Space , Str "is" , Space , Str "pretty" , Space , Str "short." ] ] , BlockQuote [ Para [ Str "Code" , Space , Str "in" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" ] , CodeBlock ( "" , [] , [] ) "sub status {\n print \"working\";\n}" , Para [ Str "A" , Space , Str "list:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "item" , Space , Str "one" ] ] , [ Plain [ Str "item" , Space , Str "two" ] ] ] , Para [ Str "Nested" , Space , Str "block" , Space , Str "quotes:" ] , BlockQuote [ Para [ Str "nested" ] ] , BlockQuote [ Para [ Str "nested" ] ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" , Space , Str "2" , SoftBreak , Str ">" , Space , Str "1." ] , Para [ Str "And" , Space , Str "a" , Space , Str "following" , Space , Str "paragraph." ] , HorizontalRule , Header 1 ( "code-blocks" , [] , [] ) [ Str "Code" , Space , Str "Blocks" ] , Para [ Str "Code:" ] , CodeBlock ( "" , [] , [] ) "---- (should be four hyphens)\n\nsub status {\n print \"working\";\n}\n\nthis code block is indented by one tab" , Para [ Str "And:" ] , CodeBlock ( "" , [] , [] ) " this code block is indented by two tabs\n\nThese should not be escaped: \\$ \\\\ \\> \\[ \\{" , HorizontalRule , Header 1 ( "lists" , [] , [] ) [ Str "Lists" ] , Header 2 ( "unordered" , [] , [] ) [ Str "Unordered" ] , Para [ Str "Asterisks" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "asterisk" , Space , Str "1" ] ] , [ Plain [ Str "asterisk" , Space , Str "2" ] ] , [ Plain [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Asterisks" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "asterisk" , Space , Str "1" ] ] , [ Para [ Str "asterisk" , Space , Str "2" ] ] , [ Para [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "Plus" , Space , Str "1" ] ] , [ Plain [ Str "Plus" , Space , Str "2" ] ] , [ Plain [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "Plus" , Space , Str "1" ] ] , [ Para [ Str "Plus" , Space , Str "2" ] ] , [ Para [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "Minus" , Space , Str "1" ] ] , [ Plain [ Str "Minus" , Space , Str "2" ] ] , [ Plain [ Str "Minus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "Minus" , Space , Str "1" ] ] , [ Para [ Str "Minus" , Space , Str "2" ] ] , [ Para [ Str "Minus" , Space , Str "3" ] ] ] , Header 2 ( "ordered" , [] , [] ) [ Str "Ordered" ] , Para [ Str "Tight:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "First" ] ] , [ Plain [ Str "Second" ] ] , [ Plain [ Str "Third" ] ] ] , Para [ Str "and:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "One" ] ] , [ Plain [ Str "Two" ] ] , [ Plain [ Str "Three" ] ] ] , Para [ Str "Loose" , Space , Str "using" , Space , Str "tabs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second" ] ] , [ Para [ Str "Third" ] ] ] , Para [ Str "and" , Space , Str "using" , Space , Str "spaces:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "One" ] ] , [ Para [ Str "Two" ] ] , [ Para [ Str "Three" ] ] ] , Para [ Str "Multiple" , Space , Str "paragraphs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "Item" , Space , Str "1," , Space , Str "graf" , Space , Str "one." ] , Para [ Str "Item" , Space , Str "1." , Space , Str "graf" , Space , Str "two." , Space , Str "The" , Space , Str "quick" , Space , Str "brown" , Space , Str "fox" , Space , Str "jumped" , Space , Str "over" , Space , Str "the" , Space , Str "lazy" , Space , Str "dog\8217s" , SoftBreak , Str "back." ] ] , [ Para [ Str "Item" , Space , Str "2." ] ] , [ Para [ Str "Item" , Space , Str "3." ] ] ] , Header 2 ( "nested" , [] , [] ) [ Str "Nested" ] , BulletList [ [ Plain [ Str "Tab" ] , BulletList [ [ Plain [ Str "Tab" ] , BulletList [ [ Plain [ Str "Tab" ] ] ] ] ] ] ] , Para [ Str "Here\8217s" , Space , Str "another:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "First" ] ] , [ Plain [ Str "Second:" ] , BulletList [ [ Plain [ Str "Fee" ] ] , [ Plain [ Str "Fie" ] ] , [ Plain [ Str "Foe" ] ] ] ] , [ Plain [ Str "Third" ] ] ] , Para [ Str "Same" , Space , Str "thing" , Space , Str "but" , Space , Str "with" , Space , Str "paragraphs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second:" ] , BulletList [ [ Plain [ Str "Fee" ] ] , [ Plain [ Str "Fie" ] ] , [ Plain [ Str "Foe" ] ] ] ] , [ Para [ Str "Third" ] ] ] , Header 2 ( "tabs-and-spaces" , [] , [] ) [ Str "Tabs" , Space , Str "and" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , SoftBreak , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , SoftBreak , Str "indented" , Space , Str "with" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , SoftBreak , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , SoftBreak , Str "indented" , Space , Str "with" , Space , Str "spaces" ] ] ] ] ] , Header 2 ( "fancy-list-markers" , [] , [] ) [ Str "Fancy" , Space , Str "list" , Space , Str "markers" ] , OrderedList ( 2 , Decimal , TwoParens ) [ [ Para [ Str "begins" , Space , Str "with" , Space , Str "2" ] ] , [ Para [ Str "and" , Space , Str "now" , Space , Str "3" ] , Para [ Str "with" , Space , Str "a" , Space , Str "continuation" ] , OrderedList ( 4 , LowerRoman , Period ) [ [ Plain [ Str "sublist" , Space , Str "with" , Space , Str "roman" , Space , Str "numerals," , SoftBreak , Str "starting" , Space , Str "with" , Space , Str "4" ] ] , [ Plain [ Str "more" , Space , Str "items" ] , OrderedList ( 1 , UpperAlpha , TwoParens ) [ [ Plain [ Str "a" , Space , Str "subsublist" ] ] , [ Plain [ Str "a" , Space , Str "subsublist" ] ] ] ] ] ] ] , Para [ Str "Nesting:" ] , OrderedList ( 1 , UpperAlpha , Period ) [ [ Plain [ Str "Upper" , Space , Str "Alpha" ] , OrderedList ( 1 , UpperRoman , Period ) [ [ Plain [ Str "Upper" , Space , Str "Roman." ] , OrderedList ( 6 , Decimal , TwoParens ) [ [ Plain [ Str "Decimal" , Space , Str "start" , Space , Str "with" , Space , Str "6" ] , OrderedList ( 3 , LowerAlpha , OneParen ) [ [ Plain [ Str "Lower" , Space , Str "alpha" , Space , Str "with" , Space , Str "paren" ] ] ] ] ] ] ] ] ] , Para [ Str "Autonumbering:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Autonumber." ] ] , [ Plain [ Str "More." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Nested." ] ] ] ] ] , Para [ Str "Should" , Space , Str "not" , Space , Str "be" , Space , Str "a" , Space , Str "list" , Space , Str "item:" ] , Para [ Str "M.A.\160\&2007" ] , Para [ Str "B." , Space , Str "Williams" ] , HorizontalRule , Header 1 ( "definition-lists" , [] , [] ) [ Str "Definition" , Space , Str "Lists" ] , Para [ Str "Tight" , Space , Str "using" , Space , Str "spaces:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Plain [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Plain [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Plain [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Tight" , Space , Str "using" , Space , Str "tabs:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Plain [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Plain [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Plain [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Loose:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Para [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Multiple" , Space , Str "blocks" , Space , Str "with" , Space , Str "italics:" ] , DefinitionList [ ( [ Emph [ Str "apple" ] ] , [ [ Para [ Str "red" , Space , Str "fruit" ] , Para [ Str "contains" , Space , Str "seeds," , SoftBreak , Str "crisp," , Space , Str "pleasant" , Space , Str "to" , Space , Str "taste" ] ] ] ) , ( [ Emph [ Str "orange" ] ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] , CodeBlock ( "" , [] , [] ) "{ orange code block }" , BlockQuote [ Para [ Str "orange" , Space , Str "block" , Space , Str "quote" ] ] ] ] ) ] , Para [ Str "Multiple" , Space , Str "definitions," , Space , Str "tight:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Plain [ Str "red" , Space , Str "fruit" ] ] , [ Plain [ Str "computer" ] ] ] ) , ( [ Str "orange" ] , [ [ Plain [ Str "orange" , Space , Str "fruit" ] ] , [ Plain [ Str "bank" ] ] ] ) ] , Para [ Str "Multiple" , Space , Str "definitions," , Space , Str "loose:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] , [ Para [ Str "computer" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] ] , [ Para [ Str "bank" ] ] ] ) ] , Para [ Str "Blank" , Space , Str "line" , Space , Str "after" , Space , Str "term," , Space , Str "indented" , Space , Str "marker," , Space , Str "alternate" , Space , Str "markers:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] , [ Para [ Str "computer" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "sublist" ] ] , [ Plain [ Str "sublist" ] ] ] ] ] ) ] , Header 1 ( "html-blocks" , [] , [] ) [ Str "HTML" , Space , Str "Blocks" ] , Para [ Str "Simple" , Space , Str "block" , Space , Str "on" , Space , Str "one" , Space , Str "line:" ] , Div ( "" , [] , [] ) [ Plain [ Str "foo" ] ] , Para [ Str "And" , Space , Str "nested" , Space , Str "without" , Space , Str "indentation:" ] , Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Para [ Str "foo" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "bar" ] ] ] , Para [ Str "Interpreted" , Space , Str "markdown" , Space , Str "in" , Space , Str "a" , Space , Str "table:" ] , RawBlock (Format "html") "" , RawBlock (Format "html") "" , RawBlock (Format "html") "" , RawBlock (Format "html") "" , RawBlock (Format "html") "" , RawBlock (Format "html") "
    " , Plain [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] ] , RawBlock (Format "html") "" , Plain [ Str "And" , Space , Str "this" , Space , Str "is" , Space , Strong [ Str "strong" ] ] , RawBlock (Format "html") "
    " , RawBlock (Format "html") "" , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "simple" , Space , Str "block:" ] , Div ( "" , [] , [] ) [ Para [ Str "foo" ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "code" , Space , Str "block," , Space , Str "though:" ] , CodeBlock ( "" , [] , [] ) "
    \n foo\n
    " , Para [ Str "As" , Space , Str "should" , Space , Str "this:" ] , CodeBlock ( "" , [] , [] ) "
    foo
    " , Para [ Str "Now," , Space , Str "nested:" ] , Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Plain [ Str "foo" ] ] ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "just" , Space , Str "be" , Space , Str "an" , Space , Str "HTML" , Space , Str "comment:" ] , RawBlock (Format "html") "" , Para [ Str "Multiline:" ] , RawBlock (Format "html") "" , RawBlock (Format "html") "" , Para [ Str "Code" , Space , Str "block:" ] , CodeBlock ( "" , [] , [] ) "" , Para [ Str "Just" , Space , Str "plain" , Space , Str "comment," , Space , Str "with" , Space , Str "trailing" , Space , Str "spaces" , Space , Str "on" , Space , Str "the" , Space , Str "line:" ] , RawBlock (Format "html") "" , Para [ Str "Code:" ] , CodeBlock ( "" , [] , [] ) "
    " , Para [ Str "Hr\8217s:" ] , RawBlock (Format "html") "
    " , RawBlock (Format "html") "
    " , RawBlock (Format "html") "
    " , RawBlock (Format "html") "
    " , RawBlock (Format "html") "
    " , RawBlock (Format "html") "
    " , RawBlock (Format "html") "
    " , RawBlock (Format "html") "
    " , RawBlock (Format "html") "
    " , HorizontalRule , Header 1 ( "inline-markup" , [] , [] ) [ Str "Inline" , Space , Str "Markup" ] , Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Emph [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "This" , Space , Str "is" , Space , Strong [ Str "strong" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Strong [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "An" , Space , Emph [ Link ( "" , [] , [] ) [ Str "emphasized" , Space , Str "link" ] ( "/url" , "" ) ] , Str "." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Str "This" , Space , Str "is" , Space , Str "code:" , Space , Code ( "" , [] , [] ) ">" , Str "," , Space , Code ( "" , [] , [] ) "$" , Str "," , Space , Code ( "" , [] , [] ) "\\" , Str "," , Space , Code ( "" , [] , [] ) "\\$" , Str "," , Space , Code ( "" , [] , [] ) "" , Str "." ] , Para [ Strikeout [ Str "This" , Space , Str "is" , Space , Emph [ Str "strikeout" ] , Str "." ] ] , Para [ Str "Superscripts:" , Space , Str "a" , Superscript [ Str "bc" ] , Str "d" , Space , Str "a" , Superscript [ Emph [ Str "hello" ] ] , Space , Str "a" , Superscript [ Str "hello\160there" ] , Str "." ] , Para [ Str "Subscripts:" , Space , Str "H" , Subscript [ Str "2" ] , Str "O," , Space , Str "H" , Subscript [ Str "23" ] , Str "O," , Space , Str "H" , Subscript [ Str "many\160of\160them" ] , Str "O." ] , Para [ Str "These" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "superscripts" , Space , Str "or" , Space , Str "subscripts," , SoftBreak , Str "because" , Space , Str "of" , Space , Str "the" , Space , Str "unescaped" , Space , Str "spaces:" , Space , Str "a^b" , Space , Str "c^d," , Space , Str "a~b" , Space , Str "c~d." ] , HorizontalRule , Header 1 ( "smart-quotes-ellipses-dashes" , [] , [] ) [ Str "Smart" , Space , Str "quotes," , Space , Str "ellipses," , Space , Str "dashes" ] , Para [ Quoted DoubleQuote [ Str "Hello," ] , Space , Str "said" , Space , Str "the" , Space , Str "spider." , Space , Quoted DoubleQuote [ Quoted SingleQuote [ Str "Shelob" ] , Space , Str "is" , Space , Str "my" , Space , Str "name." ] ] , Para [ Quoted SingleQuote [ Str "A" ] , Str "," , Space , Quoted SingleQuote [ Str "B" ] , Str "," , Space , Str "and" , Space , Quoted SingleQuote [ Str "C" ] , Space , Str "are" , Space , Str "letters." ] , Para [ Quoted SingleQuote [ Str "Oak," ] , Space , Quoted SingleQuote [ Str "elm," ] , Space , Str "and" , Space , Quoted SingleQuote [ Str "beech" ] , Space , Str "are" , Space , Str "names" , Space , Str "of" , Space , Str "trees." , SoftBreak , Str "So" , Space , Str "is" , Space , Quoted SingleQuote [ Str "pine." ] ] , Para [ Quoted SingleQuote [ Str "He" , Space , Str "said," , Space , Quoted DoubleQuote [ Str "I" , Space , Str "want" , Space , Str "to" , Space , Str "go." ] ] , Space , Str "Were" , Space , Str "you" , Space , Str "alive" , Space , Str "in" , Space , Str "the" , SoftBreak , Str "70\8217s?" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "quoted" , Space , Quoted SingleQuote [ Code ( "" , [] , [] ) "code" ] , Space , Str "and" , Space , Str "a" , Space , Quoted DoubleQuote [ Link ( "" , [] , [] ) [ Str "quoted" , Space , Str "link" ] ( "http://example.com/?foo=1&bar=2" , "" ) ] , Str "." ] , Para [ Str "Some" , Space , Str "dashes:" , Space , Str "one\8212two" , Space , Str "\8212" , Space , Str "three\8212four" , Space , Str "\8212" , Space , Str "five." ] , Para [ Str "Dashes" , Space , Str "between" , Space , Str "numbers:" , Space , Str "5\8211\&7," , Space , Str "255\8211\&66," , Space , Str "1987\8211\&1999." ] , Para [ Str "Ellipses\8230and\8230and\8230." ] , HorizontalRule , Header 1 ( "latex" , [] , [] ) [ Str "LaTeX" ] , BulletList [ [ Plain [ RawInline (Format "tex") "\\cite[22-23]{smith.1899}" ] ] , [ Plain [ Math InlineMath "2+2=4" ] ] , [ Plain [ Math InlineMath "x \\in y" ] ] , [ Plain [ Math InlineMath "\\alpha \\wedge \\omega" ] ] , [ Plain [ Math InlineMath "223" ] ] , [ Plain [ Math InlineMath "p" , Str "-Tree" ] ] , [ Plain [ Str "Here\8217s" , Space , Str "some" , Space , Str "display" , Space , Str "math:" , SoftBreak , Math DisplayMath "\\frac{d}{dx}f(x)=\\lim_{h\\to 0}\\frac{f(x+h)-f(x)}{h}" ] ] , [ Plain [ Str "Here\8217s" , Space , Str "one" , Space , Str "that" , Space , Str "has" , Space , Str "a" , Space , Str "line" , Space , Str "break" , Space , Str "in" , Space , Str "it:" , Space , Math InlineMath "\\alpha + \\omega \\times x^2" , Str "." ] ] ] , Para [ Str "These" , Space , Str "shouldn\8217t" , Space , Str "be" , Space , Str "math:" ] , BulletList [ [ Plain [ Str "To" , Space , Str "get" , Space , Str "the" , Space , Str "famous" , Space , Str "equation," , Space , Str "write" , Space , Code ( "" , [] , [] ) "$e = mc^2$" , Str "." ] ] , [ Plain [ Str "$22,000" , Space , Str "is" , Space , Str "a" , Space , Emph [ Str "lot" ] , Space , Str "of" , Space , Str "money." , Space , Str "So" , Space , Str "is" , Space , Str "$34,000." , SoftBreak , Str "(It" , Space , Str "worked" , Space , Str "if" , Space , Quoted DoubleQuote [ Str "lot" ] , Space , Str "is" , Space , Str "emphasized.)" ] ] , [ Plain [ Str "Shoes" , Space , Str "($20)" , Space , Str "and" , Space , Str "socks" , Space , Str "($5)." ] ] , [ Plain [ Str "Escaped" , Space , Code ( "" , [] , [] ) "$" , Str ":" , Space , Str "$73" , Space , Emph [ Str "this" , Space , Str "should" , Space , Str "be" , Space , Str "emphasized" ] , Space , Str "23$." ] ] ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "LaTeX" , Space , Str "table:" ] , RawBlock (Format "tex") "\\begin{tabular}{|l|l|}\\hline\nAnimal & Number \\\\ \\hline\nDog & 2 \\\\\nCat & 1 \\\\ \\hline\n\\end{tabular}" , HorizontalRule , Header 1 ( "special-characters" , [] , [] ) [ Str "Special" , Space , Str "Characters" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "unicode:" ] , BulletList [ [ Plain [ Str "I" , Space , Str "hat:" , Space , Str "\206" ] ] , [ Plain [ Str "o" , Space , Str "umlaut:" , Space , Str "\246" ] ] , [ Plain [ Str "section:" , Space , Str "\167" ] ] , [ Plain [ Str "set" , Space , Str "membership:" , Space , Str "\8712" ] ] , [ Plain [ Str "copyright:" , Space , Str "\169" ] ] ] , Para [ Str "AT&T" , Space , Str "has" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "their" , Space , Str "name." ] , Para [ Str "AT&T" , Space , Str "is" , Space , Str "another" , Space , Str "way" , Space , Str "to" , Space , Str "write" , Space , Str "it." ] , Para [ Str "This" , Space , Str "&" , Space , Str "that." ] , Para [ Str "4" , Space , Str "<" , Space , Str "5." ] , Para [ Str "6" , Space , Str ">" , Space , Str "5." ] , Para [ Str "Backslash:" , Space , Str "\\" ] , Para [ Str "Backtick:" , Space , Str "`" ] , Para [ Str "Asterisk:" , Space , Str "*" ] , Para [ Str "Underscore:" , Space , Str "_" ] , Para [ Str "Left" , Space , Str "brace:" , Space , Str "{" ] , Para [ Str "Right" , Space , Str "brace:" , Space , Str "}" ] , Para [ Str "Left" , Space , Str "bracket:" , Space , Str "[" ] , Para [ Str "Right" , Space , Str "bracket:" , Space , Str "]" ] , Para [ Str "Left" , Space , Str "paren:" , Space , Str "(" ] , Para [ Str "Right" , Space , Str "paren:" , Space , Str ")" ] , Para [ Str "Greater-than:" , Space , Str ">" ] , Para [ Str "Hash:" , Space , Str "#" ] , Para [ Str "Period:" , Space , Str "." ] , Para [ Str "Bang:" , Space , Str "!" ] , Para [ Str "Plus:" , Space , Str "+" ] , Para [ Str "Minus:" , Space , Str "-" ] , HorizontalRule , Header 1 ( "links" , [] , [] ) [ Str "Links" ] , Header 2 ( "explicit" , [] , [] ) [ Str "Explicit" ] , Para [ Str "Just" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "URL" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title preceded by two spaces" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title preceded by a tab" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title with \"quotes\" in it" ) ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title with single quotes" ) ] , Para [ Link ( "" , [] , [] ) [ Str "with_underscore" ] ( "/url/with_underscore" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Email" , Space , Str "link" ] ( "mailto:nobody@nowhere.net" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Empty" ] ( "" , "" ) , Str "." ] , Header 2 ( "reference" , [] , [] ) [ Str "Reference" ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "With" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "[brackets]" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "b" ] ( "/url/" , "" ) , Space , Str "by" , Space , Str "itself" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "once" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "twice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "thrice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "This" , Space , Str "should" , Space , Str "[not][]" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , CodeBlock ( "" , [] , [] ) "[not]: /url" , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "Title with \"quotes\" inside" ) , Str "." ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "biz" ] ( "/url/" , "Title with \"quote\" inside" ) , Str "." ] , Header 2 ( "with-ampersands" , [] , [] ) [ Str "With" , Space , Str "ampersands" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "the" , Space , Str "URL" ] ( "http://example.com/?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "amersand" , Space , Str "in" , Space , Str "the" , Space , Str "link" , Space , Str "text:" , Space , Link ( "" , [] , [] ) [ Str "AT&T" ] ( "http://att.com/" , "AT&T" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "inline" , Space , Str "link" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "inline" , Space , Str "link" , Space , Str "in" , Space , Str "pointy" , Space , Str "braces" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] , Header 2 ( "autolinks" , [] , [] ) [ Str "Autolinks" ] , Para [ Str "With" , Space , Str "an" , Space , Str "ampersand:" , Space , Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/?foo=1&bar=2" ] ( "http://example.com/?foo=1&bar=2" , "" ) ] , BulletList [ [ Plain [ Str "In" , Space , Str "a" , Space , Str "list?" ] ] , [ Plain [ Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , [ Plain [ Str "It" , Space , Str "should." ] ] ] , Para [ Str "An" , Space , Str "e-mail" , Space , Str "address:" , Space , Link ( "" , [ "email" ] , [] ) [ Str "nobody@nowhere.net" ] ( "mailto:nobody@nowhere.net" , "" ) ] , BlockQuote [ Para [ Str "Blockquoted:" , Space , Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , Para [ Str "Auto-links" , Space , Str "should" , Space , Str "not" , Space , Str "occur" , Space , Str "here:" , Space , Code ( "" , [] , [] ) "" ] , CodeBlock ( "" , [] , [] ) "or here: " , HorizontalRule , Header 1 ( "images" , [] , [] ) [ Str "Images" ] , Para [ Str "From" , Space , Quoted DoubleQuote [ Str "Voyage" , Space , Str "dans" , Space , Str "la" , Space , Str "Lune" ] , Space , Str "by" , Space , Str "Georges" , Space , Str "Melies" , Space , Str "(1902):" ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "lalune" ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "lalune" ] ( "lalune.jpg" , "Voyage dans la Lune" ) ] ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "movie" , Space , Image ( "" , [] , [] ) [ Str "movie" ] ( "movie.jpg" , "" ) , Space , Str "icon." ] , HorizontalRule , Header 1 ( "footnotes" , [] , [] ) [ Str "Footnotes" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "footnote" , Space , Str "reference," , Note [ Para [ Str "Here" , Space , Str "is" , Space , Str "the" , Space , Str "footnote." , Space , Str "It" , Space , Str "can" , Space , Str "go" , Space , Str "anywhere" , Space , Str "after" , Space , Str "the" , Space , Str "footnote" , SoftBreak , Str "reference." , Space , Str "It" , Space , Str "need" , Space , Str "not" , Space , Str "be" , Space , Str "placed" , Space , Str "at" , Space , Str "the" , Space , Str "end" , Space , Str "of" , Space , Str "the" , Space , Str "document." ] ] , Space , Str "and" , Space , Str "another." , Note [ Para [ Str "Here\8217s" , Space , Str "the" , Space , Str "long" , Space , Str "note." , Space , Str "This" , Space , Str "one" , Space , Str "contains" , Space , Str "multiple" , SoftBreak , Str "blocks." ] , Para [ Str "Subsequent" , Space , Str "blocks" , Space , Str "are" , Space , Str "indented" , Space , Str "to" , Space , Str "show" , Space , Str "that" , Space , Str "they" , Space , Str "belong" , Space , Str "to" , Space , Str "the" , SoftBreak , Str "footnote" , Space , Str "(as" , Space , Str "with" , Space , Str "list" , Space , Str "items)." ] , CodeBlock ( "" , [] , [] ) " { }" , Para [ Str "If" , Space , Str "you" , Space , Str "want," , Space , Str "you" , Space , Str "can" , Space , Str "indent" , Space , Str "every" , Space , Str "line," , Space , Str "but" , Space , Str "you" , Space , Str "can" , Space , Str "also" , Space , Str "be" , SoftBreak , Str "lazy" , Space , Str "and" , Space , Str "just" , Space , Str "indent" , Space , Str "the" , Space , Str "first" , Space , Str "line" , Space , Str "of" , Space , Str "each" , Space , Str "block." ] ] , SoftBreak , Str "This" , Space , Str "should" , Space , Emph [ Str "not" ] , Space , Str "be" , Space , Str "a" , Space , Str "footnote" , Space , Str "reference," , Space , Str "because" , Space , Str "it" , SoftBreak , Str "contains" , Space , Str "a" , Space , Str "space.[^my" , Space , Str "note]" , Space , Str "Here" , Space , Str "is" , Space , Str "an" , Space , Str "inline" , Space , Str "note." , Note [ Para [ Str "This" , SoftBreak , Str "is" , Space , Emph [ Str "easier" ] , Space , Str "to" , Space , Str "type." , Space , Str "Inline" , Space , Str "notes" , Space , Str "may" , Space , Str "contain" , SoftBreak , Link ( "" , [] , [] ) [ Str "links" ] ( "http://google.com" , "" ) , Space , Str "and" , Space , Code ( "" , [] , [] ) "]" , Space , Str "verbatim" , Space , Str "characters," , SoftBreak , Str "as" , Space , Str "well" , Space , Str "as" , Space , Str "[bracketed" , Space , Str "text]." ] ] ] , BlockQuote [ Para [ Str "Notes" , Space , Str "can" , Space , Str "go" , Space , Str "in" , Space , Str "quotes." , Note [ Para [ Str "In" , Space , Str "quote." ] ] ] ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "And" , Space , Str "in" , Space , Str "list" , Space , Str "items." , Note [ Para [ Str "In" , Space , Str "list." ] ] ] ] ] , Para [ Str "This" , Space , Str "paragraph" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "part" , Space , Str "of" , Space , Str "the" , Space , Str "note," , Space , Str "as" , Space , Str "it" , Space , Str "is" , Space , Str "not" , Space , Str "indented." ] ] pandoc-lua-engine-0.2.0.1/test/writer-template.lua0000644000000000000000000000022507346545000020135 0ustar0000000000000000function Writer (doc, opts) return pandoc.write(doc, 'gfm', opts) end function Template () return '\n$body$\n\n' end pandoc-lua-engine-0.2.0.1/test/writer-template.out.txt0000644000000000000000000000004407346545000021000 0ustar0000000000000000 $body$ pandoc-lua-engine-0.2.0.1/test/writer.custom0000644000000000000000000003400007346545000017053 0ustar0000000000000000

    This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.


    Headers

    Level 3 with emphasis

    Level 4

    Level 5

    Level 1

    Level 2 with emphasis

    Level 3

    with no blank line

    Level 2

    with no blank line


    Paragraphs

    Here’s a regular paragraph.

    In Markdown 1.0.0 and earlier. Version 8. This line turns into a list item. Because a hard-wrapped line in the middle of a paragraph looked like a list item.

    Here’s one with a bullet. * criminey.

    There should be a hard line break
    here.


    Block Quotes

    E-mail style:

    This is a block quote. It is pretty short.

    Code in a block quote:

    sub status {
        print "working";
    }

    A list:

    1. item one
    2. item two

    Nested block quotes:

    nested

    nested

    This should not be a block quote: 2 > 1.

    And a following paragraph.


    Code Blocks

    Code:

    ---- (should be four hyphens)
    
    sub status {
        print "working";
    }
    
    this code block is indented by one tab

    And:

        this code block is indented by two tabs
    
    These should not be escaped:  \$ \\ \> \[ \{

    Lists

    Unordered

    Asterisks tight:

    • asterisk 1
    • asterisk 2
    • asterisk 3

    Asterisks loose:

    • asterisk 1

    • asterisk 2

    • asterisk 3

    Pluses tight:

    • Plus 1
    • Plus 2
    • Plus 3

    Pluses loose:

    • Plus 1

    • Plus 2

    • Plus 3

    Minuses tight:

    • Minus 1
    • Minus 2
    • Minus 3

    Minuses loose:

    • Minus 1

    • Minus 2

    • Minus 3

    Ordered

    Tight:

    1. First
    2. Second
    3. Third

    and:

    1. One
    2. Two
    3. Three

    Loose using tabs:

    1. First

    2. Second

    3. Third

    and using spaces:

    1. One

    2. Two

    3. Three

    Multiple paragraphs:

    1. Item 1, graf one.

      Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.

    2. Item 2.

    3. Item 3.

    Nested

    • Tab
      • Tab
        • Tab

    Here’s another:

    1. First
    2. Second:
      • Fee
      • Fie
      • Foe
    3. Third

    Same thing but with paragraphs:

    1. First

    2. Second:

      • Fee
      • Fie
      • Foe
    3. Third

    Tabs and spaces

    • this is a list item indented with tabs

    • this is a list item indented with spaces

      • this is an example list item indented with tabs

      • this is an example list item indented with spaces

    Fancy list markers

    1. begins with 2

    2. and now 3

      with a continuation

      1. sublist with roman numerals, starting with 4
      2. more items
        1. a subsublist
        2. a subsublist

    Nesting:

    1. Upper Alpha
      1. Upper Roman.
        1. Decimal start with 6
          1. Lower alpha with paren

    Autonumbering:

    1. Autonumber.
    2. More.
      1. Nested.

    Should not be a list item:

    M.A. 2007

    B. Williams


    Definition Lists

    Tight using spaces:

    apple
    red fruit
    orange
    orange fruit
    banana
    yellow fruit

    Tight using tabs:

    apple
    red fruit
    orange
    orange fruit
    banana
    yellow fruit

    Loose:

    apple

    red fruit

    orange

    orange fruit

    banana

    yellow fruit

    Multiple blocks with italics:

    apple

    red fruit

    contains seeds, crisp, pleasant to taste

    orange

    orange fruit

    { orange code block }

    orange block quote

    Multiple definitions, tight:

    apple
    red fruit
    computer
    orange
    orange fruit
    bank

    Multiple definitions, loose:

    apple

    red fruit

    computer

    orange

    orange fruit

    bank

    Blank line after term, indented marker, alternate markers:

    apple

    red fruit

    computer

    orange

    orange fruit

    1. sublist
    2. sublist

    HTML Blocks

    Simple block on one line:

    foo

    And nested without indentation:

    foo

    bar

    Interpreted markdown in a table:

    This is emphasized And this is strong

    Here’s a simple block:

    foo

    This should be a code block, though:

    <div>
        foo
    </div>

    As should this:

    <div>foo</div>

    Now, nested:

    foo

    This should just be an HTML comment:

    Multiline:

    Code block:

    <!-- Comment -->

    Just plain comment, with trailing spaces on the line:

    Code:

    <hr />

    Hr’s:











    Inline Markup

    This is emphasized, and so is this.

    This is strong, and so is this.

    An emphasized link.

    This is strong and em.

    So is this word.

    This is strong and em.

    So is this word.

    This is code: >, $, \, \$, <html>.

    This is strikeout.

    Superscripts: abcd ahello ahello there.

    Subscripts: H2O, H23O, Hmany of themO.

    These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.


    Smart quotes, ellipses, dashes

    “Hello,” said the spider. “‘Shelob’ is my name.”

    ‘A’, ‘B’, and ‘C’ are letters.

    ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’

    ‘He said, “I want to go.”’ Were you alive in the 70’s?

    Here is some quoted ‘code’ and a “quoted link”.

    Some dashes: one—two — three—four — five.

    Dashes between numbers: 5–7, 255–66, 1987–1999.

    Ellipses…and…and….


    LaTeX

    • \(2+2=4\)
    • \(x \in y\)
    • \(\alpha \wedge \omega\)
    • \(223\)
    • \(p\)-Tree
    • Here’s some display math: \[\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}\]
    • Here’s one that has a line break in it: \(\alpha + \omega \times x^2\).

    These shouldn’t be math:

    • To get the famous equation, write $e = mc^2$.
    • $22,000 is a lot of money. So is $34,000. (It worked if “lot” is emphasized.)
    • Shoes ($20) and socks ($5).
    • Escaped $: $73 this should be emphasized 23$.

    Here’s a LaTeX table:


    Special Characters

    Here is some unicode:

    • I hat: Î
    • o umlaut: ö
    • section: §
    • set membership: ∈
    • copyright: ©

    AT&T has an ampersand in their name.

    AT&T is another way to write it.

    This & that.

    4 < 5.

    6 > 5.

    Backslash: \

    Backtick: `

    Asterisk: *

    Underscore: _

    Left brace: {

    Right brace: }

    Left bracket: [

    Right bracket: ]

    Left paren: (

    Right paren: )

    Greater-than: >

    Hash: #

    Period: .

    Bang: !

    Plus: +

    Minus: -


    Links

    Explicit

    Just a URL.

    URL and title.

    URL and title.

    URL and title.

    URL and title

    URL and title

    with_underscore

    Email link

    Empty.

    Reference

    Foo bar.

    With embedded [brackets].

    b by itself should be a link.

    Indented once.

    Indented twice.

    Indented thrice.

    This should [not][] be a link.

    [not]: /url

    Foo bar.

    Foo biz.

    With ampersands

    Here’s a link with an ampersand in the URL.

    Here’s a link with an amersand in the link text: AT&T.

    Here’s an inline link.

    Here’s an inline link in pointy braces.

    With an ampersand: http://example.com/?foo=1&bar=2

    An e-mail address:

    Blockquoted: http://example.com/

    Auto-links should not occur here: <http://example.com/>

    or here: <http://example.com/>

    Images

    From “Voyage dans la Lune” by Georges Melies (1902):

    lalune

    Here is a movie icon.


    Footnotes

    Here is a footnote reference,1 and another.2 This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note.3

    Notes can go in quotes.4

    1. And in list items.5

    This paragraph should not be part of the note, as it is not indented.

    1. Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.

    2. Here’s the long note. This one contains multiple blocks.

      Subsequent blocks are indented to show that they belong to the footnote (as with list items).

        { <code> }

      If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.

    3. This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text].

    4. In quote.

    5. In list.