summaryrefslogtreecommitdiff
path: root/lib/rdoc/markdown/literals_1_9.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rdoc/markdown/literals_1_9.rb')
-rw-r--r--lib/rdoc/markdown/literals_1_9.rb417
1 files changed, 417 insertions, 0 deletions
diff --git a/lib/rdoc/markdown/literals_1_9.rb b/lib/rdoc/markdown/literals_1_9.rb
new file mode 100644
index 0000000000..01d00e41f6
--- /dev/null
+++ b/lib/rdoc/markdown/literals_1_9.rb
@@ -0,0 +1,417 @@
+# coding: UTF-8
+# :markup: markdown
+
+##
+#--
+# This set of literals is for ruby 1.9 regular expressions and gives full
+# unicode support.
+#
+# Unlike peg-markdown, this set of literals recognizes Unicode alphanumeric
+# characters, newlines and spaces.
+class RDoc::Markdown::Literals
+ # :stopdoc:
+
+ # This is distinct from setup_parser so that a standalone parser
+ # can redefine #initialize and still have access to the proper
+ # parser setup code.
+ def initialize(str, debug=false)
+ setup_parser(str, debug)
+ end
+
+
+
+ # Prepares for parsing +str+. If you define a custom initialize you must
+ # call this method before #parse
+ def setup_parser(str, debug=false)
+ @string = str
+ @pos = 0
+ @memoizations = Hash.new { |h,k| h[k] = {} }
+ @result = nil
+ @failed_rule = nil
+ @failing_rule_offset = -1
+
+ setup_foreign_grammar
+ end
+
+ attr_reader :string
+ attr_reader :failing_rule_offset
+ attr_accessor :result, :pos
+
+
+ def current_column(target=pos)
+ if c = string.rindex("\n", target-1)
+ return target - c - 1
+ end
+
+ target + 1
+ end
+
+ def current_line(target=pos)
+ cur_offset = 0
+ cur_line = 0
+
+ string.each_line do |line|
+ cur_line += 1
+ cur_offset += line.size
+ return cur_line if cur_offset >= target
+ end
+
+ -1
+ end
+
+ def lines
+ lines = []
+ string.each_line { |l| lines << l }
+ lines
+ end
+
+
+
+ def get_text(start)
+ @string[start..@pos-1]
+ end
+
+ def show_pos
+ width = 10
+ if @pos < width
+ "#{@pos} (\"#{@string[0,@pos]}\" @ \"#{@string[@pos,width]}\")"
+ else
+ "#{@pos} (\"... #{@string[@pos - width, width]}\" @ \"#{@string[@pos,width]}\")"
+ end
+ end
+
+ def failure_info
+ l = current_line @failing_rule_offset
+ c = current_column @failing_rule_offset
+
+ if @failed_rule.kind_of? Symbol
+ info = self.class::Rules[@failed_rule]
+ "line #{l}, column #{c}: failed rule '#{info.name}' = '#{info.rendered}'"
+ else
+ "line #{l}, column #{c}: failed rule '#{@failed_rule}'"
+ end
+ end
+
+ def failure_caret
+ l = current_line @failing_rule_offset
+ c = current_column @failing_rule_offset
+
+ line = lines[l-1]
+ "#{line}\n#{' ' * (c - 1)}^"
+ end
+
+ def failure_character
+ l = current_line @failing_rule_offset
+ c = current_column @failing_rule_offset
+ lines[l-1][c-1, 1]
+ end
+
+ def failure_oneline
+ l = current_line @failing_rule_offset
+ c = current_column @failing_rule_offset
+
+ char = lines[l-1][c-1, 1]
+
+ if @failed_rule.kind_of? Symbol
+ info = self.class::Rules[@failed_rule]
+ "@#{l}:#{c} failed rule '#{info.name}', got '#{char}'"
+ else
+ "@#{l}:#{c} failed rule '#{@failed_rule}', got '#{char}'"
+ end
+ end
+
+ class ParseError < RuntimeError
+ end
+
+ def raise_error
+ raise ParseError, failure_oneline
+ end
+
+ def show_error(io=STDOUT)
+ error_pos = @failing_rule_offset
+ line_no = current_line(error_pos)
+ col_no = current_column(error_pos)
+
+ io.puts "On line #{line_no}, column #{col_no}:"
+
+ if @failed_rule.kind_of? Symbol
+ info = self.class::Rules[@failed_rule]
+ io.puts "Failed to match '#{info.rendered}' (rule '#{info.name}')"
+ else
+ io.puts "Failed to match rule '#{@failed_rule}'"
+ end
+
+ io.puts "Got: #{string[error_pos,1].inspect}"
+ line = lines[line_no-1]
+ io.puts "=> #{line}"
+ io.print(" " * (col_no + 3))
+ io.puts "^"
+ end
+
+ def set_failed_rule(name)
+ if @pos > @failing_rule_offset
+ @failed_rule = name
+ @failing_rule_offset = @pos
+ end
+ end
+
+ attr_reader :failed_rule
+
+ def match_string(str)
+ len = str.size
+ if @string[pos,len] == str
+ @pos += len
+ return str
+ end
+
+ return nil
+ end
+
+ def scan(reg)
+ if m = reg.match(@string[@pos..-1])
+ width = m.end(0)
+ @pos += width
+ return true
+ end
+
+ return nil
+ end
+
+ if "".respond_to? :getbyte
+ def get_byte
+ if @pos >= @string.size
+ return nil
+ end
+
+ s = @string.getbyte @pos
+ @pos += 1
+ s
+ end
+ else
+ def get_byte
+ if @pos >= @string.size
+ return nil
+ end
+
+ s = @string[@pos]
+ @pos += 1
+ s
+ end
+ end
+
+ def parse(rule=nil)
+ # We invoke the rules indirectly via apply
+ # instead of by just calling them as methods because
+ # if the rules use left recursion, apply needs to
+ # manage that.
+
+ if !rule
+ apply(:_root)
+ else
+ method = rule.gsub("-","_hyphen_")
+ apply :"_#{method}"
+ end
+ end
+
+ class MemoEntry
+ def initialize(ans, pos)
+ @ans = ans
+ @pos = pos
+ @result = nil
+ @set = false
+ @left_rec = false
+ end
+
+ attr_reader :ans, :pos, :result, :set
+ attr_accessor :left_rec
+
+ def move!(ans, pos, result)
+ @ans = ans
+ @pos = pos
+ @result = result
+ @set = true
+ @left_rec = false
+ end
+ end
+
+ def external_invoke(other, rule, *args)
+ old_pos = @pos
+ old_string = @string
+
+ @pos = other.pos
+ @string = other.string
+
+ begin
+ if val = __send__(rule, *args)
+ other.pos = @pos
+ other.result = @result
+ else
+ other.set_failed_rule "#{self.class}##{rule}"
+ end
+ val
+ ensure
+ @pos = old_pos
+ @string = old_string
+ end
+ end
+
+ def apply_with_args(rule, *args)
+ memo_key = [rule, args]
+ if m = @memoizations[memo_key][@pos]
+ @pos = m.pos
+ if !m.set
+ m.left_rec = true
+ return nil
+ end
+
+ @result = m.result
+
+ return m.ans
+ else
+ m = MemoEntry.new(nil, @pos)
+ @memoizations[memo_key][@pos] = m
+ start_pos = @pos
+
+ ans = __send__ rule, *args
+
+ lr = m.left_rec
+
+ m.move! ans, @pos, @result
+
+ # Don't bother trying to grow the left recursion
+ # if it's failing straight away (thus there is no seed)
+ if ans and lr
+ return grow_lr(rule, args, start_pos, m)
+ else
+ return ans
+ end
+
+ return ans
+ end
+ end
+
+ def apply(rule)
+ if m = @memoizations[rule][@pos]
+ @pos = m.pos
+ if !m.set
+ m.left_rec = true
+ return nil
+ end
+
+ @result = m.result
+
+ return m.ans
+ else
+ m = MemoEntry.new(nil, @pos)
+ @memoizations[rule][@pos] = m
+ start_pos = @pos
+
+ ans = __send__ rule
+
+ lr = m.left_rec
+
+ m.move! ans, @pos, @result
+
+ # Don't bother trying to grow the left recursion
+ # if it's failing straight away (thus there is no seed)
+ if ans and lr
+ return grow_lr(rule, nil, start_pos, m)
+ else
+ return ans
+ end
+
+ return ans
+ end
+ end
+
+ def grow_lr(rule, args, start_pos, m)
+ while true
+ @pos = start_pos
+ @result = m.result
+
+ if args
+ ans = __send__ rule, *args
+ else
+ ans = __send__ rule
+ end
+ return nil unless ans
+
+ break if @pos <= m.pos
+
+ m.move! ans, @pos, @result
+ end
+
+ @result = m.result
+ @pos = m.pos
+ return m.ans
+ end
+
+ class RuleInfo
+ def initialize(name, rendered)
+ @name = name
+ @rendered = rendered
+ end
+
+ attr_reader :name, :rendered
+ end
+
+ def self.rule_info(name, rendered)
+ RuleInfo.new(name, rendered)
+ end
+
+
+ # :startdoc:
+ # :stopdoc:
+ def setup_foreign_grammar; end
+
+ # Alphanumeric = /\p{Word}/
+ def _Alphanumeric
+ _tmp = scan(/\A(?-mix:\p{Word})/)
+ set_failed_rule :_Alphanumeric unless _tmp
+ return _tmp
+ end
+
+ # AlphanumericAscii = /[A-Za-z0-9]/
+ def _AlphanumericAscii
+ _tmp = scan(/\A(?-mix:[A-Za-z0-9])/)
+ set_failed_rule :_AlphanumericAscii unless _tmp
+ return _tmp
+ end
+
+ # BOM = "uFEFF"
+ def _BOM
+ _tmp = match_string("uFEFF")
+ set_failed_rule :_BOM unless _tmp
+ return _tmp
+ end
+
+ # Newline = /\n|\r\n?|\p{Zl}|\p{Zp}/
+ def _Newline
+ _tmp = scan(/\A(?-mix:\n|\r\n?|\p{Zl}|\p{Zp})/)
+ set_failed_rule :_Newline unless _tmp
+ return _tmp
+ end
+
+ # NonAlphanumeric = /\p{^Word}/
+ def _NonAlphanumeric
+ _tmp = scan(/\A(?-mix:\p{^Word})/)
+ set_failed_rule :_NonAlphanumeric unless _tmp
+ return _tmp
+ end
+
+ # Spacechar = /\t|\p{Zs}/
+ def _Spacechar
+ _tmp = scan(/\A(?-mix:\t|\p{Zs})/)
+ set_failed_rule :_Spacechar unless _tmp
+ return _tmp
+ end
+
+ Rules = {}
+ Rules[:_Alphanumeric] = rule_info("Alphanumeric", "/\\p{Word}/")
+ Rules[:_AlphanumericAscii] = rule_info("AlphanumericAscii", "/[A-Za-z0-9]/")
+ Rules[:_BOM] = rule_info("BOM", "\"uFEFF\"")
+ Rules[:_Newline] = rule_info("Newline", "/\\n|\\r\\n?|\\p{Zl}|\\p{Zp}/")
+ Rules[:_NonAlphanumeric] = rule_info("NonAlphanumeric", "/\\p{^Word}/")
+ Rules[:_Spacechar] = rule_info("Spacechar", "/\\t|\\p{Zs}/")
+ # :startdoc:
+end