class Literate::Programming
Constants
- VERSION
Attributes
expandtab[W]
tabstop[W]
Public Class Methods
new(src = "", source: nil, tabstop: 2, expandtab: true)
click to toggle source
# File lib/literate/programming.rb, line 7 def initialize(src = "", source: nil, tabstop: 2, expandtab: true) @source = source || src @tabstop = tabstop @expandtab = expandtab @eval_context = BasicObject.new @eval_context.instance_eval <<-EOC def gensym return (0 .. 20).collect { ('a' .. 'z').to_a[::Random.rand(26)] }.join end EOC end
Public Instance Methods
<<(append)
click to toggle source
# File lib/literate/programming.rb, line 23 def <<(append) @source << append end
inspect()
click to toggle source
# File lib/literate/programming.rb, line 19 def inspect return "#<Literate::Programming:0x#{object_id.to_s(16)} tabstop=#{@tabstop} expandtab=#{@expandtab}>" end
to_md()
click to toggle source
# File lib/literate/programming.rb, line 31 def to_md convert[:md] end
to_ruby()
click to toggle source
# File lib/literate/programming.rb, line 27 def to_ruby convert[:rb] end
to_tex()
click to toggle source
# File lib/literate/programming.rb, line 35 def to_tex convert[:tex] end
Private Instance Methods
convert()
click to toggle source
# File lib/literate/programming.rb, line 40 def convert current_mode = :text md = "" tex = <<-TEX \\documentclass{report} \\usepackage{listings} \\begin{document} \\lstset{language=Ruby} TEX table = {} current_code = nil current_label = nil operator = nil @source.split($/).each do |line| old_mode = current_mode case current_mode when :text if line.match /^[ \t]*\[\[([^\]]+)\]\][ \t]*(\+?=|<<)[ \t]*$/ then current_mode = :code current_code = '' current_label = $1 case $2 when '=' operator = :assign when '+=', '<<' operator = :append end end when :code if line.match /^[ \t]*$/ then current_mode = :text else line = line.match(/^[ \t]*/).post_match end end case current_mode when :text if old_mode == :code then case operator when :assign raise if table[current_label] table[current_label] = current_code when :append table[current_label] ||= '' table[current_label] << $/ << current_code end md << '```' << $/ << $/ tex << '\\end{lstlisting}' << $/ << $/ else md << line << $/ tex << line << $/ end when :code if old_mode == :code then current_code << line << $/ md << line << $/ tex << line << $/ else md << '```ruby:' << current_label md << ' append' if operator == :append md << $/ tex << '\\begin{lstlisting}[frame=lines,caption=' << current_label tex << ' append' if operator == :append tex << ']' << $/ end end end if current_mode == :code then case operator when :assign table[current_label] = current_code when :append table[current_label] ||= '' table[current_label] << $/ << current_code end md << '```' << $/ tex << '\\end{lstlisting}' << $/ end table['*'] ||= '' tex << <<-TEX \\end{document} TEX return {rb: indent_code(expand(table, '*')), md: indent_markdown(md), tex: indent_tex(tex)} end
expand(table, label)
click to toggle source
# File lib/literate/programming.rb, line 125 def expand(table, label) @eval_context.instance_eval(expand(table, label + 'before*')) if table.member? label + 'before*' processed = '' processing = "[[#{label}]]" while processing.match /^((?:[^\[]|\[[^\[])*)\[\[([^\]]+)\]\]/m processed << $1 expanding = $2 follow = $~.post_match if table.member? expanding then processing = expand_template(table[expanding], nil).chomp elsif expanding.match /^([^:]+):([^,]+)(,[^,]+)*$/ then true_expanding = $1 + ':@' args = $~.to_a[2..-2] raise 'undefined ' + expanding unless table.member? true_expanding processing = expand_template(table[true_expanding], args).chomp else raise 'undefined ' + expanding end processing << follow end return processed << processing end
expand_template(string, args)
click to toggle source
# File lib/literate/programming.rb, line 148 def expand_template(string, args) processed = '' processing = string while processing.match /^((?:[^@]|@[^0-9@])*)@/m processed << $1 follow = $~.post_match if follow.match /^(0|[1-9][0-9]*)/m then # @num -> args[num] processed << args[$1.to_i] processing = $~.post_match elsif follow.match /^@/m then follow = $~.post_match if follow.match /^(?<paren>\((?:[^()]|\g<paren>)*\))/m then # @@(expr) -> eval(expr) post_match = $~.post_match expanding = @eval_context.instance_eval(expand_template $1, args) processed << expanding.to_s processing = post_match else processed << '@@' processing = follow end else processed << '@' processing = follow end end return processed << processing end
indent_code(code)
click to toggle source
# File lib/literate/programming.rb, line 178 def indent_code(code) indent_level_stack = [-1] ret = '' code.split($/).each do |line| ret, indent_level_stack = pretty_print_ruby ret, indent_level_stack, line end return ret end
indent_markdown(text)
click to toggle source
# File lib/literate/programming.rb, line 187 def indent_markdown(text) mode = :text ret = '' indent_level_stack = nil text.split($/).each do |line| if mode == :text and line.match /^```/ then mode = :code indent_level_stack = [-1] ret << line << $/ elsif mode == :text then ret << line << $/ elsif mode == :code and line.match /^```/ then mode = :text ret << line << $/ else ret, indent_level_stack = pretty_print_ruby ret, indent_level_stack, line end end return ret end
indent_tex(text)
click to toggle source
# File lib/literate/programming.rb, line 208 def indent_tex(text) mode = :text ret = '' indent_level_stack = nil text.split($/).each do |line| if mode == :text and line.match /^\\begin{lstlisting}/ then mode = :code indent_level_stack = [-1] ret << line << $/ elsif mode == :text then ret << line << $/ elsif mode == :code and line.match /^\\end{lstlisting}/ then mode = :text ret << line << $/ else ret, indent_level_stack = pretty_print_ruby ret, indent_level_stack, line end end return ret end
pretty_print_ruby(buffer, indent_level_stack, line)
click to toggle source
# File lib/literate/programming.rb, line 229 def pretty_print_ruby(buffer, indent_level_stack, line) if line.match /^[ \t]*(else|elsif|ensure|rescue|when)\b/ then current_indent_level = indent_level_stack[-1] elsif line.match /\b(end)\b/ then current_indent_level = indent_level_stack.pop else current_indent_level = indent_level_stack[-1] + 1 end if line.match /^[ \t]*$/ then buffer << $/ elsif @expandtab then buffer << ' ' * current_indent_level * @tabstop << line << $/ else buffer << '\t' * current_indent_level << line << $/ end if line.match /^[ \t]*(begin|class|def|for|while|module)\b/ indent_level_stack.push current_indent_level end return buffer, indent_level_stack end