%% name = NoraMark::Parser %% ast-location = ::NoraMark %% text = ast Text(content, line_no) %% paragraph = ast Paragraph(ids, classes, parameters, named_parameters, content, line_no) %% paragraph_group = ast ParagraphGroup(ids, classes, parameters,named_parameters, content, line_no) %% br = ast Breakline(line_no) %% block = ast Block(name, ids, classes, parameters, named_parameters, content, line_no) %% newpage = ast Newpage(ids, classes, parameters, named_parameters, content, line_no) %% inline = ast Inline(name, ids, classes, parameters, named_parameters, content, line_no) %% ul_item = ast UlItem(ids, classes, parameters, named_parameters, content, line_no) %% unordered_list = ast UnorderedList(ids, classes, parameters, named_parameters, content, line_no) %% ol_item = ast OlItem(ids, classes, parameters, named_parameters, content, line_no) %% ordered_list = ast OrderedList(ids, classes, parameters,named_parameters, content, line_no) %% dl_item = ast DLItem(ids, classes, parameters, named_parameters, content, line_no) %% definition_list = ast DefinitionList(ids, classes, parameters, named_parameters, content, line_no) %% preformatted_block = ast PreformattedBlock(name, ids, classes, parameters, named_parameters, codelanguage, content, line_no) %% frontmatter = ast Frontmatter(content, line_no) %% h_section = ast HeadedSection(level, heading, content, line_no) %% page = ast Page(content, line_no) %% root = ast Root(content) # line number ln = { current_line } # literals BOM = /\uFEFF/ Eof = !. Space = ' ' | '\t' EofComment = Space* "#" (!Eof .)* Comment = Space* "#" (!Nl .)* Nl EmptyLine* - = Space* EmptyLine = /^/ - (Nl | Comment | EofComment) Nl = /\r?\n/ Le = Nl | Eof Word = < /[\w0-9]/ ( '-' | /[\w0-9]/ )* > { text } Num = < [0-9]+ > { text.to_i } #common syntax ClassName = '.' Word:classname { classname } ClassNames = (ClassName)*:classnames { classnames } IdName = '#' Word:idname { idname } IdNames = (IdName)*:idnames { idnames } CommandName = Word:name IdNames?:idnames ClassNames?:classes ln:ln { {name: name, ids: idnames, classes: classes, ln:ln } } ParameterNormal = < /[^,)]/* > { text } ParameterQuoted = '"' < /[^"]/* > '"' - &/[,)]/ { text } ParameterSingleQuoted = "'" < /[^']/* > "'" - &/[,)]/ { text } Parameter = (ParameterQuoted | ParameterSingleQuoted | ParameterNormal ):value { value } NParameterNormal = < /[^,\]]/* > { text } NParameterQuoted = '"' < /[^"]/* > '"' - &/[,\]]/ { text } NParameterSingleQuoted = "'" < /[^']/* > "'" - &/[,\]]/ { text } NParameter = (NParameterQuoted | NParameterSingleQuoted | NParameterNormal ):value { value } Parameters = Parameter:parameter ',' - Parameters:parameters { [ parameter ] + parameters } | Parameter:parameter { [ parameter ] } NamedParameter = Word:key - ':' - NParameter:parameter { { key.to_sym => parameter } } NamedParameters = NamedParameter:parameter - ',' - NamedParameters:parameters { parameter.merge parameters } | NamedParameter:parameter { parameter } Command = CommandName:cn ('(' - Parameters:args - ')')? ('[' - NamedParameters:named_args - ']')? { cn.merge({ args: args || [] , named_args: named_args || {}}) } # paragraph ImplicitParagraph = - !ParagraphDelimiter Comment* DocumentLine:content ln:ln Comment* EofComment? ~paragraph([],[], [], [], content, ln) # explicit paragraph ExplicitParagraphCommand = Command:c &{ c[:name] == 'p' } ExplicitParagraph = - ExplicitParagraphCommand:c ':' - DocumentContent?:content Le EmptyLine* ~paragraph(c[:ids], c[:classes], c[:args], c[:named_args], content, c[:ln]) Paragraph = ExplicitParagraph | ImplicitParagraph # paragraph_group ParagraphGroup = ln:ln Paragraph+:p EmptyLine* ~paragraph_group([],[],[],[],p, ln) # explicit block BlockHead = Command:command - '{' - Nl EmptyLine* { command } BlockEnd = - '}' - Le EmptyLine* BlockBody = (!BlockEnd Block)+:body { body } ExplicitBlock = - BlockHead:c - BlockBody:content - BlockEnd ~block(c[:name], c[:ids], c[:classes], c[:args], c[:named_args], content, c[:ln]) # preformatted block PreformattedCommand = Command:command &{ ['pre', 'code'].include? command[:name] } PreformattedCommandHeadSimple = PreformattedCommand:command - '{' - Nl { command } PreformattedCommandHeadComplex = PreformattedCommand:command - '{//' Word?:codelanguage - Nl { command.merge({codelanguage: codelanguage}) } PreformattedCommandHead = PreformattedCommandHeadComplex | PreformattedCommandHeadSimple PreformatEndSimple = - '}' - Le EmptyLine* PreformatEndComplex = - '//}' - Le EmptyLine* PreformattedBlockSimple = - PreformattedCommandHeadSimple:c (!PreformatEndSimple (CharString Nl))+:content PreformatEndSimple ~preformatted_block(c[:name], c[:ids], c[:classes], c[:args], c[:named_args], c[:codelanguage], content, c[:ln]) PreformattedBlockComplex = - PreformattedCommandHeadComplex:c (!PreformatEndComplex (CharString Nl))+:content PreformatEndComplex ~preformatted_block(c[:name], c[:ids], c[:classes], c[:args], c[:named_args], c[:codelanguage], content, c[:ln]) PreformattedBlock = PreformattedBlockComplex | PreformattedBlockSimple # inline command Inline = ImgInline | CommonInline CommonInline = '[' Command:c '{' - DocumentContentExcept('}'):content '}' ']' ~inline(c[:name], c[:ids], c[:classes], c[:args], c[:named_args], content, c[:ln]) ImgCommand = Command:c &{ c[:name] == 'img' && c[:args].size == 2} ImgInline = '[' ImgCommand:c ']' ~inline(c[:name], c[:ids], c[:classes], c[:args], c[:named_args], nil, c[:ln]) # special line commands CommandNameForSpecialLineCommand = NewpageCommand | ExplicitParagraphCommand # newpage NewpageCommand = Command:command &{ command[:name] == 'newpage' } Newpage = - NewpageCommand:c ':' - DocumentContent?:content - Nl ~newpage(c[:ids],c[:classes],c[:args], c[:named_args], content, c[:ln]) # unordered list UnorderedList = ln:ln UnorderedItem+:items ~unordered_list([],[],[],[], items, ln) UnorderedItem = ln:ln '*:' - DocumentContent:content Le ~ul_item([], [], [], [], content, ln) # ordered list OrderedList = ln:ln OrderedItem+:items ~ordered_list([],[],[], [], items, ln) OrderedItem = ln:ln Num ':' - DocumentContent:content Le ~ol_item([], [], [], [], content, ln) # definition list DefinitionList = ln:ln DefinitionItem+:items ~definition_list([], [], [], [], items, ln) DefinitionItem = - ln:ln ';:' - DocumentContentExcept(':'):term ':' - DocumentContent:definition Le ~dl_item([], [], [term], [], definition, ln) # long definition list LongDefinitionList = ln:ln LongDefinitionItem+:items ~definition_list([], [], [], [], items, ln) LongDefinitionItem = - ln:ln ';:' - DocumentContentExcept('{'):term '{' - Nl - BlockBody:definition - BlockEnd ~dl_item([], [], [term], [], definition, ln) ItemsList = (UnorderedList | OrderedList | DefinitionList | LongDefinitionList) # generic line command LineCommand = - !CommandNameForSpecialLineCommand Command:c ':' - DocumentContent?:content - Le EmptyLine* ~block(c[:name], c[:ids], c[:classes], c[:args], c[:named_args], content, c[:ln]) # blocks LineBlock = ItemsList | LineCommand Block = (PreformattedBlock | HeadedSection | LineBlock | ExplicitBlock | ParagraphGroup ):block EmptyLine* {block} BlockDelimiter = BlockHead | BlockEnd ParagraphDelimiter = BlockDelimiter | PreformattedCommandHead | LineBlock | Newpage | HeadedStart # markdown-style headings HStartMark(n) = < '='+ ':' > &{ text.length - 1 == n } HMarkupTerminator(n) = - < '='+ ':' > &{ text.length - 1 <= n } | - Eof HStart(n) = - HStartMark(n) ln:ln - DocumentContent:s Le { { level: n, heading: s, ln: ln} } HSection(n) = HStart(n):h EmptyLine* (!HMarkupTerminator(n) Block)*:content EmptyLine* ~h_section(h[:level], h[:heading], content, h[:ln]) HeadedStart = HStart(1) | HStart(2) | HStart(3) | HStart(4) | HStart(5) | HStart(6) HeadedSection = HSection(1) | HSection(2) | HSection(3) | HSection(4) | HSection(5) | HSection(6) # frontmatter FrontmatterSeparator = - '---' - Nl Frontmatter = FrontmatterSeparator ln:ln (!FrontmatterSeparator ( CharString Nl))+:yaml FrontmatterSeparator EmptyLine* ~frontmatter(yaml, ln) # texts Char = < /[[:print:]]/ > { text } CharString = < Char* > { text } CharExcept(e) = Char:c &{ c != e } DocumentTextExcept(e) = < (!Inline CharExcept(e))+ > ln:ln ~text(text, ln) DocumentContentExcept(e) = (Inline | DocumentTextExcept(e))+:content { content } DocumentText = < (!Inline Char)+ > ln:ln ~text(text, ln) DocumentContent = (Inline | DocumentText)+:content { content } DocumentLine = DocumentContent:content Le { content } #page Page = Frontmatter?:frontmatter - (!Newpage Block)*:blocks EmptyLine* ~page(([frontmatter] + blocks).select{ |x| !x.nil?}, 1) Pages = Page:page Newpage:newpage Pages:pages { [ page, newpage ] + pages } | Page:page { [ page ] } #root root = BOM? EmptyLine* Pages:pages EofComment? Eof ~root(pages)