Class: NonHaml::NonHamlParser
- Inherits:
-
Object
- Object
- NonHaml::NonHamlParser
- Defined in:
- lib/non-haml/parser.rb
Instance Attribute Summary collapse
-
#base_dir ⇒ Object
Returns the value of attribute base_dir.
-
#last_ok_line ⇒ Object
Returns the value of attribute last_ok_line.
-
#out ⇒ Object
Returns the value of attribute out.
Instance Method Summary collapse
- #concat(spaces = nil, text = nil) ⇒ Object
- #control_indent ⇒ Object
- #current_filename ⇒ Object
- #dedent(indent, suppress_end = false) ⇒ Object
- #evaluate(code) ⇒ Object
- #filename ⇒ Object
- #generate(out_name, in_name, context_or_vars, base_dir, verbose) ⇒ Object
-
#initialize ⇒ NonHamlParser
constructor
A new instance of NonHamlParser.
- #parse(text, base_control_indent = 0, base_indent = 0) ⇒ Object
- #pop_filename ⇒ Object
- #prepare_context(_context_or_vars) ⇒ Object
- #push_filename(new_name) ⇒ Object
- #store(text) ⇒ Object
Constructor Details
#initialize ⇒ NonHamlParser
Returns a new instance of NonHamlParser.
25 26 27 |
# File 'lib/non-haml/parser.rb', line 25 def initialize self.out = "" end |
Instance Attribute Details
#base_dir ⇒ Object
Returns the value of attribute base_dir.
24 25 26 |
# File 'lib/non-haml/parser.rb', line 24 def base_dir @base_dir end |
#last_ok_line ⇒ Object
Returns the value of attribute last_ok_line.
24 25 26 |
# File 'lib/non-haml/parser.rb', line 24 def last_ok_line @last_ok_line end |
#out ⇒ Object
Returns the value of attribute out.
24 25 26 |
# File 'lib/non-haml/parser.rb', line 24 def out @out end |
Instance Method Details
#concat(spaces = nil, text = nil) ⇒ Object
29 30 31 32 33 34 35 36 37 |
# File 'lib/non-haml/parser.rb', line 29 def concat spaces=nil, text=nil if spaces.nil? and text.nil? self.out << "\n" else text.to_s.lines do |l| self.out << "#{' '*spaces}#{l.rstrip}\n" end end end |
#control_indent ⇒ Object
67 68 69 |
# File 'lib/non-haml/parser.rb', line 67 def control_indent ' ' * (@block_starts.length + @base_control_indent) end |
#current_filename ⇒ Object
53 54 55 |
# File 'lib/non-haml/parser.rb', line 53 def current_filename "#{base_dir}#{filename}" end |
#dedent(indent, suppress_end = false) ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/non-haml/parser.rb', line 71 def dedent indent, suppress_end=false dedented = false @block_starts.reverse.take_while{|x| x >= indent}.each do |x| # Close some blocks to get back to the right indentation level. @block_starts.pop unless suppress_end and %w{if elsif else}.include?(@statements.pop) and x == indent store control_indent + 'end' end dedented = true # return status so we know whether to skip a blank line. end dedented end |
#evaluate(code) ⇒ Object
170 171 172 |
# File 'lib/non-haml/parser.rb', line 170 def evaluate(code) eval(code, @context) end |
#filename ⇒ Object
39 40 41 42 |
# File 'lib/non-haml/parser.rb', line 39 def filename # Retrieves the current filename. @filenames.last end |
#generate(out_name, in_name, context_or_vars, base_dir, verbose) ⇒ Object
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/non-haml/parser.rb', line 190 def generate out_name, in_name, context_or_vars, base_dir, verbose self.base_dir = base_dir push_filename(in_name) context = prepare_context(context_or_vars) source = File.read(current_filename) parsed = parse(source) if verbose parsed.lines.each_with_index do |l,i| print Color.blue '%3d ' % (i + 1) puts l end end begin evaluate(parsed) rescue Exception => e # Interrupt everything, give more info, then dump out the old exception. $stderr.puts "In #{current_filename}:" $stderr.puts Color.red " #{e.class.name}: #{Color.blue e.to_s}" File.read(current_filename).lines.each_with_index.drop([last_ok_line - 2, 0].max).first(5).each do |line,i| if i == last_ok_line $stderr.print Color.red ' %3d ' % (i + 1) $stderr.print Color.red line else $stderr.print ' %3d ' % (i + 1) $stderr.print line end end raise e else File.open(out_name, "w") do |f| f.write(out) end end end |
#parse(text, base_control_indent = 0, base_indent = 0) ⇒ Object
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/non-haml/parser.rb', line 57 def parse text, base_control_indent=0, base_indent=0 @s = "" def store text @s << "#{text}\n" end # Number of indents at the start of blocks. @block_starts = [] @statements = [] @base_control_indent = base_control_indent def control_indent ' ' * (@block_starts.length + @base_control_indent) end def dedent indent, suppress_end=false dedented = false @block_starts.reverse.take_while{|x| x >= indent}.each do |x| # Close some blocks to get back to the right indentation level. @block_starts.pop unless suppress_end and %w{if elsif else}.include?(@statements.pop) and x == indent store control_indent + 'end' end dedented = true # return status so we know whether to skip a blank line. end dedented end text.lines.with_index do |line,i| line.rstrip! line =~ /^( *)(.*)$/ indent, line = $1.length, $2 indent += base_indent if (line =~ /^- *((if|unless|for|elsif|else)\b.*)$/) or (line =~ /^- *(.*\bdo\b *(|.*|)?)$/) # Entering a block. if %w{elsif else}.include? $2 store control_indent + "self.last_ok_line = #{i}" dedent indent, %w{elsif else}.include?($2) else dedent indent, %w{elsif else}.include?($2) store control_indent + "self.last_ok_line = #{i}" end store control_indent + $1 @block_starts << indent @statements << $2 # Output should have same indent as block. concat_indent = indent elsif line =~ /= ?non_haml ['"](.*)['"]/ store control_indent + "self.last_ok_line = #{i}" file = base_dir + $1 if File.readable? file store control_indent + "push_filename '#{$1}'" @s += parse open(file).read, control_indent.length, indent else store control_indent + "raise Errno::ENOENT, '\"#{$1}\"'" end store control_indent + "pop_filename" elsif line.strip.length.zero? # Blank line. Output and move on. Don't change indent. Only do this # for if blocks though, so 'if false' doesn't generate optional blank # line and 'if true' does. #if @statements.last == 'if' or @statements.empty? # XXX disabled temporarily because it sucked. store control_indent + "self.last_ok_line = #{i}" #if @statements.empty? store "#{control_indent}concat" #end else dedented = dedent indent store control_indent + "self.last_ok_line = #{i}" # Now deal with whatever we have left. if line =~ /^- *(.*)$/ # Generic Ruby statement that isn't entering/leaving a block. store control_indent + $1 elsif line =~ /^= *(.*)$/ # Concatenate this for evaluation. target_indent = indent - control_indent.length # Deal with blank lines. content = $1 content = '""' if content.empty? store "#{control_indent}concat(#{target_indent}, (#{content}))" elsif dedented and line.strip.empty? puts 'skipping' # Skip up to one blank line after dedenting. next else # Concatenate this for output. target_indent = indent - control_indent.length # Replace #{} blocks, but completely quote the rest. # TODO clean up more nicely if we fail here!!! to_sub = [] line.gsub!('%', '%%') line.gsub!(/#\{(.*?)\}/){to_sub << $1; '%s'} if to_sub.empty? # Must do pretend substitutions to get around %% characters. subst = " % []" else # Include brackets around all quantities, so bracket-free # functions get the right arguments. subst = " % [#{to_sub.map{|x| "(#{x})"}.join ', '}]" end store "#{control_indent}concat #{target_indent}, (%q##{line.gsub('#', '\\#')}##{subst})" end end end dedent 0 @s end |
#pop_filename ⇒ Object
49 50 51 |
# File 'lib/non-haml/parser.rb', line 49 def pop_filename @filenames.pop end |
#prepare_context(_context_or_vars) ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/non-haml/parser.rb', line 174 def prepare_context(_context_or_vars) case _context_or_vars when Hash @context = binding _context_or_vars.each do |name, value| evaluate("#{name} = nil") setter = evaluate("lambda{|v| #{name} = v}") setter.call(value) end else raise TypeError, "cannot use context of type #{_context_or_vars.class}" end evaluate("concat = nil") setter = evaluate("lambda{|v| concat = v}") end |
#push_filename(new_name) ⇒ Object
44 45 46 47 |
# File 'lib/non-haml/parser.rb', line 44 def push_filename new_name @filenames ||= [] @filenames << new_name end |
#store(text) ⇒ Object
59 60 61 |
# File 'lib/non-haml/parser.rb', line 59 def store text @s << "#{text}\n" end |