Initial commit for hasciidoc
commit
28320d30ed
|
@ -0,0 +1 @@
|
|||
dist/*
|
|
@ -0,0 +1,30 @@
|
|||
Copyright (c) 2014, Levi Pearson
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* Neither the name of Levi Pearson nor the names of other
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,120 @@
|
|||
---
|
||||
title: AsciiDoc Parsing
|
||||
...
|
||||
|
||||
# Document Structure
|
||||
|
||||
A *document* is a series of *block elements*.
|
||||
|
||||
The first element is optionally a *document header*.
|
||||
|
||||
The next element is optionally a *preamble*.
|
||||
|
||||
* Document
|
||||
* Header (optional)
|
||||
Must be separated from the remainder of the document by one or more
|
||||
blank lines and cannot contain blank lines.
|
||||
Can include comments.
|
||||
Can include attribute entries, typically *doctype*, *lang*, *encoding*,
|
||||
*icons*, *data-uri*, *toc*, *numbered*
|
||||
Command-line attributes override header attributes.
|
||||
* Title
|
||||
* Info (optional)
|
||||
* AuthorInfo
|
||||
Format: firstname[ [middlename ]lastname][ <email>]
|
||||
Multi-word names should be separated by underscores,
|
||||
e.g. van_Gogh.
|
||||
If the format isn't matched, the entire line becomes a
|
||||
FirstName.
|
||||
* FirstName
|
||||
* OtherNames (optional)
|
||||
* MiddleName (optional)
|
||||
* LastName
|
||||
* EmailAddress (optional)
|
||||
* RevisionInfo (optional)
|
||||
* Either
|
||||
* RevisionNumber (optional)
|
||||
* RevisionDate
|
||||
* RevisionRemark (optional)
|
||||
* Or
|
||||
* RCS/CVS/SVN $Id$ marker.
|
||||
The *revnumber*, *revdata*, and *author* attributes are
|
||||
extracted and set and displayed in the header.
|
||||
The header author line may be omitted.
|
||||
|
||||
* Preamble (optional)
|
||||
* SectionBody
|
||||
* Section (0 or more)
|
||||
Up to 4 section levels (1-4) in addition to the document title (level 0)
|
||||
Has attributes *level* and *sectnum* implcitly defined
|
||||
* Title
|
||||
* SectionBody (optional)
|
||||
* One of (repeated 1 or more times)
|
||||
* Either
|
||||
* BlockTitle (optional)
|
||||
Format: .<title>
|
||||
* Block
|
||||
* Or
|
||||
* BlockMacro
|
||||
* Section (0 or more)
|
||||
|
||||
* Block
|
||||
Floating titles probably belong in here somewhere.
|
||||
* One of
|
||||
* Paragraph
|
||||
* DelimitedBlock
|
||||
* List
|
||||
* One of
|
||||
* BulletedList
|
||||
* ListItem (1 or more)
|
||||
* NumberedList
|
||||
* ListItem (1 or more)
|
||||
* CalloutList
|
||||
* ListItem (1 or more)
|
||||
* LabeledList
|
||||
* ListEntry (1 or more)
|
||||
* ListLabel
|
||||
* ListTerm (1 or more)
|
||||
* ListItem
|
||||
* ItemText
|
||||
* One of (repeated 0 or more times)
|
||||
* List
|
||||
* ListParagraph
|
||||
A paragraph with *listelement* option set
|
||||
* ListContinuation
|
||||
* Table
|
||||
* Or (these can appear almost anywhere)
|
||||
* BlockId
|
||||
Applies to the following *Title*, *Paragraph*, *List*,
|
||||
*DelimitedBlock*, *Table*, or *BlockMacro*
|
||||
Format: [[<id>]]
|
||||
* AttributeEntry
|
||||
* AttributeList
|
||||
Applies to the following *Block*
|
||||
Most blocks support *id*, *role*, and *reftext* attributes
|
||||
Format: [<attr>(, <attr>)*]
|
||||
|
||||
Notes:
|
||||
* Block elements are separated by line boundaries
|
||||
* *Header*, *Title*, *Paragraph*, and *ItemText* cannot contain blank lines.
|
||||
|
||||
|
||||
```.haskell
|
||||
data Document = Doc
|
||||
{ _docAttrs :: Attributes
|
||||
, _header :: Maybe DocHeader
|
||||
, _preamble :: Maybe SectionBody
|
||||
, _sections :: Seq Section
|
||||
}
|
||||
|
||||
|
||||
data Section = Sec
|
||||
{ _secAttrs :: Attributes
|
||||
, _secTitle :: Text
|
||||
, _secBody :: Maybe SectionBody
|
||||
, _secChildren :: Seq Section
|
||||
}
|
||||
|
||||
type SectionBody = Seq Block
|
||||
|
||||
```
|
|
@ -0,0 +1,40 @@
|
|||
-- Initial hasciidoc.cabal generated by cabal init. For further
|
||||
-- documentation, see http://haskell.org/cabal/users-guide/
|
||||
|
||||
name: hasciidoc
|
||||
version: 0.1.0.0
|
||||
synopsis: An asciidoc implementation
|
||||
-- description:
|
||||
license: BSD3
|
||||
license-file: LICENSE
|
||||
author: Levi Pearson
|
||||
maintainer: levipearson@gmail.com
|
||||
-- copyright:
|
||||
category: Text
|
||||
build-type: Simple
|
||||
-- extra-source-files:
|
||||
cabal-version: >=1.10
|
||||
|
||||
library
|
||||
exposed-modules: Hasciidoc
|
||||
-- other-modules:
|
||||
-- other-extensions:
|
||||
build-depends: base >=4.6 && <4.7
|
||||
, attoparsec
|
||||
, containers
|
||||
, text
|
||||
-- hs-source-dirs:
|
||||
default-language: Haskell2010
|
||||
hs-source-dirs: src
|
||||
|
||||
test-suite tests
|
||||
type: exitcode-stdio-1.0
|
||||
main-is: Main.hs
|
||||
build-depends: base >= 4.6 && < 4.7
|
||||
, attoparsec
|
||||
, text
|
||||
, hasciidoc
|
||||
build-depends: tasty
|
||||
, tasty-hspec
|
||||
hs-source-dirs: tests
|
||||
default-language: Haskell2010
|
|
@ -0,0 +1,95 @@
|
|||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
|
||||
|
||||
module Hasciidoc where
|
||||
|
||||
import Control.Applicative
|
||||
import Data.Attoparsec.Text
|
||||
import Data.Char
|
||||
import Data.Map (Map)
|
||||
import qualified Data.Map as M
|
||||
import Data.Sequence (Seq)
|
||||
import qualified Data.Sequence as S
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
|
||||
type Attributes = Map Text Text
|
||||
|
||||
data Document = Doc
|
||||
{ _docAttrs :: Attributes
|
||||
, _header :: Maybe DocHeader
|
||||
, _preamble :: Maybe SectionBody
|
||||
, _sections :: Seq Section
|
||||
} deriving (Show)
|
||||
|
||||
data DocHeader = DocHead
|
||||
{ _docTitle :: Text
|
||||
} deriving (Show)
|
||||
|
||||
data Section = Sec
|
||||
{ _secAttrs :: Attributes
|
||||
, _secTitle :: Text
|
||||
, _secBody :: Maybe SectionBody
|
||||
, _secChildren :: Seq Section
|
||||
} deriving (Show)
|
||||
|
||||
type SectionBody = Seq Block
|
||||
|
||||
data Block = Block
|
||||
{ _blockKind :: BlockKind
|
||||
, _blockAttrs :: Attributes
|
||||
, _blockTitle :: Maybe Text
|
||||
} deriving (Show)
|
||||
|
||||
data BlockKind = Paragraph
|
||||
| DelimBlock
|
||||
| List
|
||||
deriving (Show)
|
||||
|
||||
data Inlines = TextChunk Text
|
||||
deriving (Show)
|
||||
|
||||
pCountedChar :: Char -> Parser Int
|
||||
pCountedChar c = ((+) <$> (char c >> pure 1) <*> pCountedChar c)
|
||||
<|> pure 0
|
||||
|
||||
collapseSp = T.unwords . T.words
|
||||
|
||||
|
||||
pTitle :: Parser (Int, Text)
|
||||
pTitle = pOneLineTitle <|> pUnderTitle
|
||||
|
||||
pOneLineTitle :: Parser (Int, Text)
|
||||
pOneLineTitle = (,) <$ char '='
|
||||
<*> pCountedChar '='
|
||||
<* space
|
||||
<* skipSpace
|
||||
<*> (collapseSp <$> takeTill (\c -> c == '=' || isEndOfLine c))
|
||||
<* takeTill isEndOfLine
|
||||
<* satisfy isEndOfLine
|
||||
<?> "pOneLineTitle"
|
||||
|
||||
pUnderTitle :: Parser (Int, Text)
|
||||
pUnderTitle = flip (,) <$> (collapseSp <$> takeTill isEndOfLine)
|
||||
<* satisfy isEndOfLine
|
||||
<*> pTitleUnderline
|
||||
<?> "pUnderTitle"
|
||||
|
||||
pTitleUnderline :: Parser Int
|
||||
pTitleUnderline = do
|
||||
c <- anyChar
|
||||
char c
|
||||
takeTill (not . (== c))
|
||||
satisfy isEndOfLine
|
||||
titleCharLevel c
|
||||
|
||||
|
||||
titleCharLevel :: Monad m => Char -> m Int
|
||||
titleCharLevel c = case c of
|
||||
'=' -> return 0
|
||||
'-' -> return 1
|
||||
'~' -> return 2
|
||||
'^' -> return 3
|
||||
'+' -> return 4
|
||||
_ -> fail "Bad title underline character"
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module Main where
|
||||
|
||||
import Data.Attoparsec.Text
|
||||
import qualified Data.Text as T
|
||||
|
||||
import Test.Tasty
|
||||
import Test.Tasty.Hspec as HS
|
||||
|
||||
import Hasciidoc
|
||||
|
||||
main :: IO ()
|
||||
main = defaultMain tests
|
||||
|
||||
tests :: TestTree
|
||||
tests = testGroup "Tests" [specs]
|
||||
|
||||
-- Hspec Tests
|
||||
|
||||
specs :: TestTree
|
||||
specs = testGroup "Specifications"
|
||||
[ HS.testCase "Header Parsing" headerParseSpec
|
||||
]
|
||||
|
||||
headerParseSpec :: Spec
|
||||
headerParseSpec = do
|
||||
describe "Document Header" $ do
|
||||
|
||||
describe "Document Title" $ do
|
||||
|
||||
describe "pTitle" $ do
|
||||
it "parses a one-line level 0 title with no right-hand delimiter" $
|
||||
parseOnly pTitle "= This is a level 0 title\n"
|
||||
`shouldBe`
|
||||
Right (0, "This is a level 0 title")
|
||||
|
||||
it "parses a one-line level 0 title with a right-hand delimeter" $
|
||||
parseOnly pTitle "= This is another level 0 title =\n"
|
||||
`shouldBe`
|
||||
Right (0, "This is another level 0 title")
|
Loading…
Reference in New Issue