Class: Lmt::Tangle::Tangler
- Inherits:
-
Object
- Object
- Lmt::Tangle::Tangler
- Defined in:
- lib/lmt/lmt.rb
Defined Under Namespace
Classes: ConditionalProcessor
Instance Method Summary collapse
- #apply_filters(strings, filters) ⇒ Object
- #expand_macros(lines, depth = 0) ⇒ Object
- #handle_extensions_and_conditionals(lines) ⇒ Object
- #include_includes(lines, current_file = @input, depth = 0) ⇒ Object
-
#initialize(input, include_path = []) ⇒ Tangler
constructor
A new instance of Tangler.
- #parse_blocks(lines) ⇒ Object
- #read_file(file) ⇒ Object
- #tangle ⇒ Object
- #unescape_double_parens(block) ⇒ Object
- #write(output) ⇒ Object
Constructor Details
#initialize(input, include_path = []) ⇒ Tangler
Returns a new instance of Tangler.
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 |
# File 'lib/lmt/lmt.rb', line 125 def initialize(input, include_path = []) @include_path = include_path @extension_context = Context.new() @extension_context.filters = { 'ruby_escape' => LineFilter.new do |line| line.dump[1..-2] end, 'double_quote' => LineFilter.new do |line| before_white = /^\s*/.match(line)[0] after_white = /\s*$/.match(line)[0] "#{before_white}\"#{line.strip}\"#{after_white}" end, 'add_comma' => LineFilter.new do |line| before_white = /^\s*/.match(line)[0] after_white = /\s*$/.match(line)[0] "#{before_white}#{line.strip},#{after_white}" end, 'indent_continuation' => Filter.new do |lines| [lines[0], *lines[1..-1].map {|l| " #{l}"}] end, 'indent_lines' => LineFilter.new do |line| " #{line}" end } @input = input @block = "" @blocks = {} @tangled = false end |
Instance Method Details
#apply_filters(strings, filters) ⇒ Object
263 264 265 266 267 268 269 |
# File 'lib/lmt/lmt.rb', line 263 def apply_filters(strings, filters) filters.map do |filter_name| filters_map[filter_name] end.inject(strings) do |strings, filter| filter.filter(strings) end end |
#expand_macros(lines, depth = 0) ⇒ Object
252 253 254 255 256 257 258 259 260 261 |
# File 'lib/lmt/lmt.rb', line 252 def (lines, depth = 0) throw "too deep macro expansion {depth}" if depth > 1000 lines.map do |line| begin (line, depth) rescue Exception => e raise Exception, "Failed to process line: #{line}", e.backtrace end end.flatten(1) end |
#handle_extensions_and_conditionals(lines) ⇒ Object
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 |
# File 'lib/lmt/lmt.rb', line 192 def handle_extensions_and_conditionals(lines) extension_expression = /^(\s*)``` ruby !/ condition_processor = ConditionalProcessor.new(@extension_context) extension_exit_expression = /```/ in_extension_block = false current_extension_block = [] other_lines = lines.lazy .find_all do |line| condition_processor.should_output(line) end.find_all do |line| unless in_extension_block in_extension_block = line =~ extension_expression if in_extension_block current_extension_block = [] end !in_extension_block else in_extension_block = !(line =~ extension_exit_expression) if in_extension_block current_extension_block << line else @extension_context.get_binding.eval(current_extension_block.join) end false end end.force condition_processor.check_block_balance() other_lines end |
#include_includes(lines, current_file = @input, depth = 0) ⇒ Object
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/lmt/lmt.rb', line 172 def include_includes(lines, current_file = @input, depth = 0) raise "too many includes" if depth > 1000 include_exp = /^!\s+include\s+\[.*\]\((.*)\)\s*$/ include_path_exp = /^!\s+include-path\s+(.*)\s*$/ lines.map do |line| include_path_match = include_path_exp.match(line) include_match = include_exp.match(line) if include_path_match path = resolve_include(include_path_match[1], current_file)[0] @include_path << path [line] elsif include_match file = resolve_include(include_match[1], current_file)[0] include_includes(read_file(file), file, depth + 1) else [line] end end.flatten(1) end |
#parse_blocks(lines) ⇒ Object
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/lmt/lmt.rb', line 225 def parse_blocks(lines) code_block_exp = /^(\s*)``` ?([\w]*) ?(=?)([-\w]*)?/ in_block = false blocks = lines.find_all do |line| in_block = !in_block if line =~ code_block_exp in_block end.slice_before do |line| code_block_exp =~ line end.map do |(header, *rest)| white_space, language, replacement_mark, name = code_block_exp.match(header)[1..-1] [name, replacement_mark, rest] end.group_by do |(name, _, _)| name end.transform_values do |bodies| last_replacement_index = get_last_replacement_index(bodies) bodies[last_replacement_index..-1].map { |(_, _, body)| body} .flatten(1) end.transform_values do |body_lines| body_lines[-1] = body_lines[-1].chomp if body_lines[-1] body_lines end throw "Missing code fence" if in_block main = blocks[""] blocks.delete("") [main, blocks] end |
#read_file(file) ⇒ Object
166 167 168 169 170 |
# File 'lib/lmt/lmt.rb', line 166 def read_file(file) File.open(file, 'r') do |f| f.readlines end end |
#tangle ⇒ Object
155 156 157 158 159 160 161 162 163 164 |
# File 'lib/lmt/lmt.rb', line 155 def tangle() contents = handle_extensions_and_conditionals( include_includes(read_file(@input))) @block, @blocks = @extension_context.parse_hook(*parse_blocks(contents)) if @block @block = (@block) @block = unescape_double_parens(@block) end @tangled = true end |
#unescape_double_parens(block) ⇒ Object
270 271 272 273 274 275 276 |
# File 'lib/lmt/lmt.rb', line 270 def unescape_double_parens(block) block.map do |l| l = l.gsub("\\⦅", "⦅") l = l.gsub("\\⦆", "⦆") l end end |
#write(output) ⇒ Object
278 279 280 281 282 283 284 |
# File 'lib/lmt/lmt.rb', line 278 def write(output) tangle() unless @tangled if @block fout = File.open(output, 'w') @block.each {|line| fout << line} end end |