Browse CVS RepositoryView of /rdleaves/RDLeaves/lib/rdleaves/rdsyntax.rb
Revision 1.27 -
(download)
(annotate)
Sun Jul 2 13:19:27 2006 UTC (3 years, 4 months ago) by zunda Branch: MAIN CVS Tags: RENEW_070204_bp, HEAD Branch point for: RENEW_070204 Changes since 1.26: +11 -1 lines * Sun Jul 2 2006 zunda <zunda at freeshell.org> - lib/rdleaves/rdsyntax.rb: modified INLINE_END to work with ')))' - lib/rdleaves/rdsyntax.rb: added syntax for inline plugin - ((( ~ ))) # rdsyntax.rb: RD syntax accoding to # http://www2.pos.to/~tosh/ruby/rdtool/ja/doc/rd-draft.html # # Copyright:: Copyright (C) 2005 zunda <zunda at freeshell.org> # License:: GPL # module RDsyntax # an element, i.e. a block or an inline, in RD class Element # First Line attr_reader :start_line # parent RDpage attr_reader :parent # content of element as an array of Element attr_reader :elements # name tag attr_reader :tag def initialize( parent, line ) @parent = parent @elements = Array.new @start_line = line @tag = nil end def append( element ) @elements.push( element ) end def get_tag @elements.each do |e| e.get_tag end end def parse_inline?; true; end end class BlockElement < Element def only_block?; true; end private def self::_match?( head, regex ) /\A#{regex}\Z/ =~ head end end class RootElement < BlockElement def self::match?( head, base, env_block ); false; end def accept?( line ); true; end def wrapwith; nil; end end class ParagraphElement < RootElement def only_block?; false; end def accept?( line ) @start_line.dangling?( line ) and \ line.tokens[1].empty? and \ not line.tokens[3].strip.empty? end end class WhiteLineElement < RootElement def only_block?; false; end def accept?( line ) @start_line.same_level?( line ) and \ line.tokens[1].empty? and \ line.tokens[3].strip.empty? end end module BlockElements @@heads = nil def self::blocktype( line, cur_block ) BlockElements.constants.map{ |c| BlockElements::const_get( c ) }.each do |b| return b if b::match?( line, cur_block ) end return nil end def self::heads unless @@heads @@heads = BlockElements.constants.map{ |c| BlockElements::const_get( c )::head }.reject{ |s| not s }.uniq.join( '|' ) end @@heads end class HeadElement < BlockElement def self::head; '={1,4}|\+{1,2}'; end def self::match?( line, env_block ) RootElement === env_block and \ line.tokens[0].empty? and \ self::_match?( line.tokens[1], self::head ) end def accept?( line ) @start_line.dangling?( line ) and \ not @start_line.indented?( line ) and \ not BlockElements::blocktype( line, self ) end def wrapwith; nil; end end class IncludeElement < BlockElement def only_block?; false; end def self::head; '<<<'; end def self::match?( line, env_block ) RootElement === env_block and \ line.tokens[0].empty? and \ self::_match?( line.tokens[1], self::head ) end def accept?( line ); false; end def wrapwith; nil; end end class VerbBlockElement < BlockElement def only_block?; false; end def parse_inline?; false; end def self::head; nil; end def self::match?( line, env_block ); false; end def accept?( line ); @start_line.same_verb_block?( line ); end def wrapwith; nil; end end class ItemListElement < BlockElement def self::head; nil; end def self::match?( line, env_block ); false; end def accept?( line ) @start_line.tokens[0] == line.tokens[0] and \ ItemListItemElement::match?( line, self ) end def wrapwith; nil; end end class ItemListItemElement < BlockElement def self::head; '\*'; end def self::match?( line, env_block ) self::_match?( line.tokens[1], self::head ) end def accept?( line ); @start_line.dangling?( line ); end def wrapwith; ItemListElement; end end class EnumListElement < BlockElement def self::head; nil; end def self::match?( line, env_block ); false; end def accept?( line ) @start_line.tokens[0] == line.tokens[0] and \ EnumListItemElement::match?( line, self ) end def wrapwith; nil; end end class EnumListItemElement < BlockElement def self::head; '\(\d+\)'; end def self::match?( line, env_block ) self::_match?( line.tokens[1], self::head ) end def accept?( line ); @start_line.dangling?( line ); end def wrapwith; EnumListElement; end end class DescListElement < BlockElement def self::head; nil; end def self::match?( line, env_block ); nil; end def accept?( line ) # DT (@start_line.tokens[0] == line.tokens[0] and \ DescTermElement::match?( line, self )) or \ # DD @start_line.dangling?( line ) end def wrapwith; nil; end end class DescTermElement < BlockElement def only_block?; false; end def self::head; ':'; end def self::match?( line, env_block ) self::_match?( line.tokens[1], self::head ) end def accept?( line ); false; end def wrapwith; DescListElement; end end class DescDefElement < BlockElement def self::head; nil; end def self::match?( line, env_block ) DescListElement == env_block and line.tokens[1].empty? end def accept?( line ); @start_line.same_level?( line ); end def wrapwith; DescListElement; end end class MethodListElement < BlockElement def self::head; nil; end def self::match?( line, env_block ); false; end def accept?( line ) @start_line.tokens[0] == line.tokens[0] and \ @start_line.tokens[1] == line.tokens[1] end def wrapwith; nil; end end class MethodTermElement < BlockElement def self::head; '\-\-\-'; end def self::match?( line, env_block ) self::_match?( line.tokens[1], self::head ) end def accept?( line ); false; end def wrapwith; MethodListElement; end end class MethodDefElement < BlockElement def self::head; nil; end def self::match?( line, env_block ) MethodListElement == env_block and line.tokens[1].empty? end def accept?( line ); @start_line.same_level?( line ); end def wrapwith; MethodListElement; end end end class InlineElement < Element private def self::_match?( head, char ) /\A\(\(#{Regexp.quote( char )}\Z/ =~ head end def _endwith?( tail, char ) /\A#{Regexp.quote( char )}\)\)\Z/ =~ tail end end INLINE_BEGIN = '\(\(.' INLINE_END = '.\)\)(?!\))' module InlineElements def self::inlinetype( head ) InlineElements.constants.map{ |c| InlineElements::const_get( c ) }.each do |i| return i if i::match?( head ) end return nil end class EmElement < InlineElement def self::match?( head ) self::_match?( head, '*' ) end def endwith?( tail ) _endwith?( tail, '*' ) end end class CodeElement < InlineElement def self::match?( head ) self::_match?( head, '{' ) end def endwith?( tail ) _endwith?( tail, '}' ) end def parse_inline?; false; end end class VarElement < InlineElement def self::match?( head ) self::_match?( head, '|' ) end def endwith?( tail ) _endwith?( tail, '|' ) end def parse_inline?; false; end end class KeyboardElement < InlineElement def self::match?( head ) self::_match?( head, '%' ) end def endwith?( tail ) _endwith?( tail, '%' ) end def parse_inline?; false; end end class TermElement < InlineElement def self::match?( head ) self::_match?( head, ':' ) end def endwith?( tail ) _endwith?( tail, ':' ) end end class IdElement < InlineElement def self::match?( head ) self::_match?( head, '<' ) end def endwith?( tail ) _endwith?( tail, '>' ) end def parse_inline?; false; end end class FootnoteElement < InlineElement def self::match?( head ) self::_match?( head, '-' ) end def endwith?( tail ) _endwith?( tail, '-' ) end def parse_inline?; false; end end class VerbInlineElement < InlineElement def self::match?( head ) self::_match?( head, "'" ) end def endwith?( tail ) _endwith?( tail, "'" ) end def parse_inline?; false; end end class PluginInlineElement < InlineElement def self::match?( head ) self::_match?( head, '(' ) end def endwith?( tail ) _endwith?( tail, ')' ) end def parse_inline?; false; end end end # a line in RD class Line # line number attr_reader :linenumber # raw line attr_reader :line # path attr_reader :path def initialize( line, linenumber = nil, path = nil ) @line = line @linenumber = linenumber @path = path @tokens = nil end def get_tag end # returns Array of 0:base indent, 1:head charactors, 2:space, and 3:body def tokens unless @tokens then @tokens = /\A([ \t]*)(#{BlockElements::heads})?([ \t]*)(.*)\Z/m.match( @line ).to_a.map{ |x| x ? x : '' }[1..-1] end @tokens end # true if line is in the same level as self def same_level?( line ) tokens[0] == line.tokens[0] end # true if line in the same verbatim block def same_verb_block?( line ) return false if tokens[0].size > line.tokens[0].size tokens[0] == line.tokens[0][0...tokens[0].size] end # true if line is a continuation of self def dangling?( line ) t = line.tokens [ tokens[0] + "\t", tokens[0] + ' '*tokens[1].size + tokens[2], ].each do |next_indent| return true if next_indent == t[0] end false end # true if line is indented compared to self def indented?( line ) t = line.tokens [ tokens[0] + "\t", tokens[0] + ' '*tokens[1].size + tokens[2], ].each do |next_indent| return true if next_indent == t[0][0...(next_indent.size)] and /[ \t]+\Z/ =~ t[0][next_indent.size..-1] end false end # true if line is a whiteline compared to self def whiteline?( line ) t = line.tokens tokens[0] == t[0] and t[1].empty? end def inspect "<#{self.class} #{@line.inspect}>" end end # lines in RD class Page class PageParserError < StandardError; end # Array of Line attr_reader :content # parent RDpage attr_reader :parent # parsed tree attr_reader :root # HTML attr_reader :html # name tags attr_reader :tags def initialize( parent, content ) @parent = parent @content = content @root = nil @tags = Hash.new @html = nil end # make a tree of Element def parse @root = RootElement.new( self, Line.new( '' ) ) block_stack = [ @root ] inline_stack = [] content = @content while not content.empty? do # current line line = content.shift # current block begin cur_block = block_stack[-1] unless cur_block then raise PageParserError, "#{line.path}:#{line.linenumber}: empty stack" end end while( (not cur_block.accept?( line )) and block_stack.pop ) # block type of current line block_class = BlockElements::blocktype( line, cur_block ) # Some blocks does not have a header if BlockElements::DescListElement === cur_block and (\ not block_class or \ ( not BlockElements::DescDefElement == block_class and \ not BlockElements::DescTermElement == block_class ) \ ) then wrapper_block = BlockElements::DescDefElement.new( self, line ) cur_block.append( wrapper_block ) cur_block = wrapper_block block_stack.push( cur_block ) block_class = BlockElements::blocktype( line, cur_block ) end if not block_class and \ not BlockElements::VerbBlockElement === cur_block and \ cur_block.start_line.indented?( line ) then block_class = BlockElements::VerbBlockElement end # create a new block if needed if block_class then new_block = block_class.new( self, line ) if new_block.wrapwith and not new_block.wrapwith === cur_block then wrapper_block = new_block.wrapwith.new( self, line ) cur_block.append( wrapper_block ) cur_block = wrapper_block block_stack.push( cur_block ) end cur_block.append( new_block ) cur_block = new_block block_stack.push( cur_block ) end # create Paragraph or WhiteLine if needed if cur_block.only_block? then if not line.tokens[3].strip.empty? then # paragraph wrapper_block = ParagraphElement.new( self, line ) else wrapper_block = WhiteLineElement.new( self, line ) end cur_block.append( wrapper_block ) cur_block = wrapper_block block_stack.push( cur_block ) end # put the content into the block if cur_block.parse_inline? then inline_stack.clear inline_stack.push( cur_block ) begin line.line.split( /(#{INLINE_BEGIN}|#{INLINE_END})/ ).map{ |str| Line.new( str, line.linenumber, line.path ) }.each do |line| next if line.line.empty? i = InlineElements::inlinetype( line.line ) if i and inline_stack[-1].parse_inline? then # beginning of an inline e = i.new( self, line ) inline_stack[-1].append( e ) inline_stack.push( e ) elsif inline_stack[-1] and \ InlineElement === inline_stack[-1] and \ inline_stack[-1].endwith?( line.line ) then # endding of an inline inline_stack.pop else # usual text inline_stack[-1].append( line ) end end end while( (inline_stack.size > 1) and ( line = content.shift ) ) if inline_stack.size > 1 then top = inline_stack[-1] raise PageParserError, "Inline starting with `#{top.start_line.line}' in #{top.start_line.path}:#{top.start_line.linenumber} does not end" end else cur_block.append( line ) end end self end end end
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||