packages feed

cimple-0.0.16: src/Language/Cimple/CommentParser.y

{
{-# LANGUAGE OverloadedStrings #-}
module Language.Cimple.CommentParser
    ( parseComment
    ) where

import           Data.Fix                    (Fix (..))
import           Data.Text                   (Text)
import qualified Data.Text                   as Text

import           Language.Cimple.Ast         (AssignOp (..), BinaryOp (..),
                                              Comment, CommentF (..))
import           Language.Cimple.DescribeAst (describeLexeme, sloc)
import           Language.Cimple.Lexer       (Lexeme (..))
import           Language.Cimple.ParseResult (ParseResult)
import           Language.Cimple.Tokens      (LexemeClass (..))
}

%name parseComment Comment

%expect 0

%error {parseError}
%errorhandlertype explist
%monad {ParseResult}
%tokentype {Term}
%token
    '@attention'		{ L _ CmtCommand "@attention"	}
    '@brief'			{ L _ CmtCommand "@brief"	}
    '@deprecated'		{ L _ CmtCommand "@deprecated"	}
    '@extends'			{ L _ CmtCommand "@extends"	}
    '@implements'		{ L _ CmtCommand "@implements"	}
    '@param'			{ L _ CmtCommand "@param"	}
    '@private'			{ L _ CmtCommand "@private"	}
    '@ref'			{ L _ CmtCommand "@ref"		}
    '@p'			{ L _ CmtCommand "@p"		}
    '@return'			{ L _ CmtCommand "@return"	}
    '@retval'			{ L _ CmtCommand "@retval"	}
    '@see'			{ L _ CmtCommand "@see"		}

    ' '				{ L _ CmtIndent		" "	}
    'INDENT1'			{ L _ CmtIndent		"   "	}
    'INDENT2'			{ L _ CmtIndent		"    " 	}
    'INDENT3'			{ L _ CmtIndent		"     "	}

    '('				{ L _ PctLParen			_ }
    ')'				{ L _ PctRParen			_ }
    ','				{ L _ PctComma			_ }
    ':'				{ L _ PctColon			_ }
    '/'				{ L _ PctSlash			_ }
    '='				{ L _ PctEq			_ }
    '=='			{ L _ PctEqEq			_ }
    '!='			{ L _ PctEMarkEq		_ }
    '>='			{ L _ PctGreaterEq		_ }
    ';'				{ L _ PctSemicolon		_ }
    '.'				{ L _ PctPeriod			_ }
    '...'			{ L _ PctEllipsis		_ }
    '?'				{ L _ PctQMark			_ }
    '!'				{ L _ PctEMark			_ }
    '-'				{ L _ PctMinus			_ }
    '+'				{ L _ PctPlus			_ }
    '\n'			{ L _ PpNewline			_ }
    '/**'			{ L _ CmtStartDoc		_ }
    '*/'			{ L _ CmtEnd			_ }
    LIT_INTEGER			{ L _ LitInteger		_ }
    LIT_STRING			{ L _ LitString			_ }
    CMT_ATTR			{ L _ CmtAttr			_ }
    CMT_CODE			{ L _ CmtCode			_ }
    CMT_WORD			{ L _ CmtWord			_ }
    CMT_REF			{ L _ CmtRef			_ }

%right '='
%left '.' '?' ',' ';' '!'
%left '!=' '=='
%left '>='
%left '-' '+'
%left '/'
%left '(' ')'

%%

Comment :: { NonTerm }
Comment
:	'/**' '\n' Blocks '*/'					{ Fix $ DocComment $3 }
|	'/**' DocLine '*/'					{ Fix $ DocComment $2 }
|	'/**' Command(DocLine) '*/'				{ Fix $ DocComment [$2] }
|	'/**' Command(IndentedSentence) Blocks '*/'		{ Fix $ DocComment ($2 : $3) }

Blocks :: { [NonTerm] }
Blocks
:	BlockList						{ reverse $1 }

BlockList :: { [NonTerm] }
BlockList
:	Block							{ [$1] }
|	BlockList Block						{ $2 : $1 }

Block :: { NonTerm }
Block
:	'\n'							{ Fix DocNewline }
|	' ' Command(IndentedSentence)				{ $2 }
|	' ' Paragraph						{ Fix $ DocParagraph $2 }
|	' ' NumberedListItem					{ Fix $ DocList [$2] }
|	' ' BulletListItem					{ Fix $ DocList [$2] }

Paragraph :: { [NonTerm] }
Paragraph
:	Word(NonInt) Punctuation MaybeWords			{ Fix (DocSentence [$1] $2) : $3 }
|	Word(NonInt) MaybeWords					{ prepend $1 $2 }

Punctuation :: { Term }
Punctuation
:	'.'							{ $1 }
|	','							{ $1 }
|	';'							{ $1 }
|	'?'							{ $1 }

MaybeWords :: { [NonTerm] }
MaybeWords
:								{ [] }
|	IndentedSentence					{ $1 }

IndentedSentence :: { [NonTerm] }
IndentedSentence
:	DocLine '\n'						{ $1 }
|	DocLine '\n' 'INDENT1' IndentedSentence			{ $1 ++ $4 }

DocLine :: { [NonTerm] }
DocLine
:	Words							{ [Fix $ DocLine $1] }

Command(x)
:	'@attention' x						{ Fix $ DocAttention $2 }
|	'@brief' x						{ Fix $ DocBrief $2 }
|	'@param' CMT_WORD x					{ Fix $ DocParam Nothing $2 $3 }
|	'@param' CMT_ATTR CMT_WORD x				{ Fix $ DocParam (Just $2) $3 $4 }
|	'@retval' Atom x					{ Fix $ DocRetval $2 $3 }
|	'@return' x						{ Fix $ DocReturn $2 }
|	'@return' '\n' BulletListItemII				{ Fix $ DocReturn (Fix (DocLine []) : $3) }
|	'@see' CMT_WORD x					{ Fix $ DocSee $2 $3 }
|	'@deprecated' x						{ Fix $ DocDeprecated $2 }
|	'@implements' CMT_WORD					{ Fix $ DocImplements $2 }
|	'@extends' CMT_WORD					{ Fix $ DocExtends $2 }
|	'@private'						{ Fix DocPrivate }

BulletListItem :: { NonTerm }
BulletListItem
:	'-' Words '\n' BulletICont				{ Fix $ DocULItem $2 $4 }

BulletICont :: { [NonTerm] }
BulletICont
:								{ [] }
|	'INDENT1' DocLine '\n' BulletICont			{ $2 ++ $4 }
|	BulletListItemII					{ $1 }

BulletListItemII :: { [NonTerm] }
BulletListItemII
:	'INDENT1' '-' Words '\n' BulletIICont			{ Fix (DocULItem ($3 ++ snd $5) []) : fst $5 }

BulletIICont :: { ([NonTerm], [NonTerm]) }
BulletIICont
:								{ ([], []) }
|	BulletListItemII					{ ($1, []) }
|	'INDENT3' DocLine '\n' BulletIICont			{ ([], $2) <> $4 }

NumberedListItem :: { NonTerm }
NumberedListItem
:	LIT_INTEGER '.' IndentedSentenceII			{ Fix (DocOLItem $1 $3) }

IndentedSentenceII :: { [NonTerm] }
IndentedSentenceII
:	DocLine '\n'						{ $1 }
|	DocLine '\n' 'INDENT2' IndentedSentenceII		{ $1 ++ $4 }

Words :: { [NonTerm] }
Words
:	SentenceList(WordList)					{ $1 }

WordList :: { [NonTerm] }
WordList
:	Word(Atom)						{ [$1] }
|	WordList Word(Atom)					{ $2 : $1 }

SentenceList(x)
:	x							{ reverse $1 }
|	SentenceList(x) ';'					{ [Fix (DocSentence $1 $2)] }
|	SentenceList(x) ','					{ [Fix (DocSentence $1 $2)] }
|	SentenceList(x) '.'					{ [Fix (DocSentence $1 $2)] }
|	SentenceList(x) '?'					{ [Fix (DocSentence $1 $2)] }
|	SentenceList(x) '!'					{ [Fix (DocSentence $1 $2)] }
|	SentenceList(x) ';' SentenceList(x)			{ Fix (DocSentence $1 $2) : $3 }
|	SentenceList(x) ',' SentenceList(x)			{ Fix (DocSentence $1 $2) : $3 }
|	SentenceList(x) '.' SentenceList(x)			{ Fix (DocSentence $1 $2) : $3 }
|	SentenceList(x) '?' SentenceList(x)			{ Fix (DocSentence $1 $2) : $3 }
|	SentenceList(x) '!' SentenceList(x)			{ Fix (DocSentence $1 $2) : $3 }

Word(x)
:	x							{ Fix $ DocWord $1 }
|	x ':'							{ Fix $ DocColon $1 }
|	Word(x) '=' Word(Atom)					{ Fix $ DocAssignOp AopEq $1 $3 }
|	Word(x) '+' Word(Atom)					{ Fix $ DocBinaryOp BopPlus $1 $3 }
|	Word(x) '-' Word(Atom)					{ Fix $ DocBinaryOp BopMinus $1 $3 }
|	Word(x) '/' Word(Atom)					{ Fix $ DocBinaryOp BopDiv $1 $3 }
|	Word(x) '!=' Word(Atom)					{ Fix $ DocBinaryOp BopNe $1 $3 }
|	Word(x) '>=' Word(Atom)					{ Fix $ DocBinaryOp BopGe $1 $3 }
|	Word(x) '==' Word(Atom)					{ Fix $ DocBinaryOp BopEq $1 $3 }
|	'@ref' Atom						{ Fix $ DocRef $2 }
|	'@p' Atom						{ Fix $ DocP $2 }
|	'(' Word(Atom)						{ Fix $ DocLParen $2 }
|	Word(x) ')'						{ Fix $ DocRParen $1 }

Atom :: { Term }
Atom
:	NonInt							{ $1 }
|	LIT_INTEGER						{ $1 }

NonInt :: { Term }
NonInt
:	CMT_WORD						{ $1 }
|	CMT_CODE						{ $1 }
|	LIT_STRING						{ $1 }
|	'...'							{ $1 }

{
type Term = Lexeme Text
type NonTerm = Comment Term

prepend :: NonTerm -> [NonTerm] -> [NonTerm]
prepend x [] = [x]
prepend x (Fix (DocLine xs):rest) = Fix (DocLine (x:xs)) : rest

failAt :: Lexeme Text -> String -> ParseResult a
failAt n msg =
    fail $ Text.unpack (sloc "" n) <> ": unexpected " <> describeLexeme n <> msg

parseError :: ([Lexeme Text], [String]) -> ParseResult a
parseError ([], options)  = fail $ " end of comment; expected one of " <> show options
parseError (n:_, [])      = failAt n "; expected end of comment"
parseError (n:_, options) = failAt n $ "; expected one of " <> show options
}