Initial commit for hasciidoc

master
Levi Pearson 2014-02-24 01:00:46 -07:00
commit 28320d30ed
7 changed files with 329 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
dist/*

30
LICENSE Normal file
View File

@ -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.

2
Setup.hs Normal file
View File

@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain

120
asciidoc.md Normal file
View File

@ -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
```

40
hasciidoc.cabal Normal file
View File

@ -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

95
src/Hasciidoc.hs Normal file
View File

@ -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"

41
tests/Main.hs Normal file
View File

@ -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")