Class: DakeAnalyzer
- Inherits:
-
Object
- Object
- DakeAnalyzer
- Defined in:
- lib/dake/analyzer.rb
Instance Attribute Summary collapse
-
#file_target_dict ⇒ Object
readonly
Returns the value of attribute file_target_dict.
-
#file_template_dict ⇒ Object
readonly
Returns the value of attribute file_template_dict.
-
#included_files ⇒ Object
readonly
Returns the value of attribute included_files.
-
#method_dict ⇒ Object
readonly
Returns the value of attribute method_dict.
-
#step_template_dict ⇒ Object
readonly
Returns the value of attribute step_template_dict.
-
#tag_target_dict ⇒ Object
readonly
Returns the value of attribute tag_target_dict.
-
#tag_template_dict ⇒ Object
readonly
Returns the value of attribute tag_template_dict.
-
#variable_dict ⇒ Object
readonly
Returns the value of attribute variable_dict.
-
#workflow ⇒ Object
readonly
Returns the value of attribute workflow.
Instance Method Summary collapse
- #analyze ⇒ Object
- #analyze_condition(condition) ⇒ Object
- #analyze_file(file, type, step) ⇒ Object
- #analyze_method(meth) ⇒ Object
-
#analyze_option(step) ⇒ Object
method is the same as step.
- #analyze_scheme(name, step, line, column) ⇒ Object
- #analyze_step(step) ⇒ Object
-
#initialize(workflow, inclusion_stack, env = {}) ⇒ DakeAnalyzer
constructor
A new instance of DakeAnalyzer.
- #option_line_and_column(options, option_name) ⇒ Object
- #print_target(target, tag, target_step, dep_graph = nil, pad_stack = []) ⇒ Object
- #step_line_and_column(step) ⇒ Object
-
#text_eval(text, src_file = , context = @variable_dict, skip = 0) ⇒ Object
src_file is needed by prepare_command in execution phase because @inclusion_stack will have only root workflow by then.
- #text_line_and_column(text) ⇒ Object
Constructor Details
#initialize(workflow, inclusion_stack, env = {}) ⇒ DakeAnalyzer
Returns a new instance of DakeAnalyzer.
9 10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/dake/analyzer.rb', line 9 def initialize(workflow, inclusion_stack, env={}) @workflow = workflow @inclusion_stack = inclusion_stack @included_files = inclusion_stack.to_set @variable_dict = env.dup @method_dict = {} @tag_target_dict = {} @file_target_dict = {} @tag_template_dict = {} @file_template_dict = {} @step_template_dict = {} end |
Instance Attribute Details
#file_target_dict ⇒ Object (readonly)
Returns the value of attribute file_target_dict.
6 7 8 |
# File 'lib/dake/analyzer.rb', line 6 def file_target_dict @file_target_dict end |
#file_template_dict ⇒ Object (readonly)
Returns the value of attribute file_template_dict.
7 8 9 |
# File 'lib/dake/analyzer.rb', line 7 def file_template_dict @file_template_dict end |
#included_files ⇒ Object (readonly)
Returns the value of attribute included_files.
5 6 7 |
# File 'lib/dake/analyzer.rb', line 5 def included_files @included_files end |
#method_dict ⇒ Object (readonly)
Returns the value of attribute method_dict.
5 6 7 |
# File 'lib/dake/analyzer.rb', line 5 def method_dict @method_dict end |
#step_template_dict ⇒ Object (readonly)
Returns the value of attribute step_template_dict.
7 8 9 |
# File 'lib/dake/analyzer.rb', line 7 def step_template_dict @step_template_dict end |
#tag_target_dict ⇒ Object (readonly)
Returns the value of attribute tag_target_dict.
6 7 8 |
# File 'lib/dake/analyzer.rb', line 6 def tag_target_dict @tag_target_dict end |
#tag_template_dict ⇒ Object (readonly)
Returns the value of attribute tag_template_dict.
7 8 9 |
# File 'lib/dake/analyzer.rb', line 7 def tag_template_dict @tag_template_dict end |
#variable_dict ⇒ Object (readonly)
Returns the value of attribute variable_dict.
5 6 7 |
# File 'lib/dake/analyzer.rb', line 5 def variable_dict @variable_dict end |
#workflow ⇒ Object (readonly)
Returns the value of attribute workflow.
5 6 7 |
# File 'lib/dake/analyzer.rb', line 5 def workflow @workflow end |
Instance Method Details
#analyze ⇒ Object
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 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 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/dake/analyzer.rb', line 168 def analyze @workflow.tasks.each do |task| case task when VariableDef var_name = task.var_name.to_s var_value = text_eval(task.var_value) if task.type == :assign @variable_dict[var_name] = var_value elsif not @variable_dict[var_name] @variable_dict[var_name] = var_value end when Step analyze_step(task) when StepMethod analyze_method(task) when Workflow, Inclusion, Condition case task when Inclusion file_names = text_eval(task.files).split("\n") file_names.each do |file_name| src_dirname = File.dirname(@inclusion_stack[-1]) path = file_name.start_with?('/') ? file_name : File.absolute_path(file_name, src_dirname) if @included_files.include? path line, column = text_line_and_column task.files raise "Cyclical inclusion detected in #{task.src_file} at #{line}:#{column}" else @inclusion_stack.push path end begin tree = DakeParser.new.parse(File.read(path)) rescue Parslet::ParseFailed => failure line, column = text_line_and_column task.files STDERR.puts "Failed parsing #{path} included from #{task.src_file} at #{line}:#{column}" raise failure. end workflow = DakeTransform.new.apply(tree, src_file: path) if task.type == :include or task.type == :call sub_workflow = DakeAnalyzer.new(workflow, @inclusion_stack, @variable_dict).analyze else sub_workflow = DakeAnalyzer.new(workflow, @inclusion_stack, 'BASE' => File.dirname(path)).analyze end @inclusion_stack.pop end when Condition body = analyze_condition(task) next unless body sub_workflow = DakeAnalyzer.new(body, @inclusion_stack, @variable_dict).analyze when Workflow # for scope sub_workflow = DakeAnalyzer.new(task, @inclusion_stack, @variable_dict).analyze end @tag_target_dict.merge! sub_workflow.tag_target_dict do |tag, step_list1, step_list2| step_list1 + step_list2 end @file_target_dict.merge! sub_workflow.file_target_dict do |file, step1, step2| file1 = step1.targets.find { |target| target.scheme.path == file } file2 = step2.targets.find { |target| target.scheme.path == file } line1, column1 = text_line_and_column file1.name line2, column2 = text_line_and_column file2.name file2_name = file2.scheme.path raise "Output file `#{file2_name}' defined in #{step2.src_file} at #{line2}:#{column2} " + "was previously defined in #{step1.src_file} at #{line1}:#{column1}." end @file_template_dict.merge! sub_workflow.file_template_dict do |file, (scheme1, step1), (scheme2, step2)| file1 = step1.targets.find { |target| target.scheme.path == file } file2 = step2.targets.find { |target| target.scheme.path == file } line1, column1 = text_line_and_column file1.name line2, column2 = text_line_and_column file2.name file2_name = file2.scheme.path raise "Output pattern `#{file2_name}' defined in #{step2.src_file} at #{line2}:#{column2} " + "was previously defined in #{step1.src_file} at #{line1}:#{column1}." end if task.is_a? Condition or (task.is_a? Inclusion and task.type == :include) @variable_dict.merge! sub_workflow.variable_dict end end end self end |
#analyze_condition(condition) ⇒ Object
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/dake/analyzer.rb', line 147 def analyze_condition(condition) case condition.cond when EqCond lhs = text_eval condition.cond.eq_lhs rhs = text_eval condition.cond.eq_rhs if condition.not truth = lhs != rhs else truth = lhs == rhs end when DefCond var_name = condition.cond.var_name.to_s if condition.not truth = (not @variable_dict.has_key? var_name) else truth = @variable_dict.has_key? var_name end end truth ? condition.if_body : condition.else_body end |
#analyze_file(file, type, step) ⇒ Object
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 |
# File 'lib/dake/analyzer.rb', line 72 def analyze_file(file, type, step) line, column = text_line_and_column(file.name) if file.flag == '?' and type == :targets file_name = text_eval(file.name) raise "Output file `#{file_name}' in #{step.src_file} at #{line}:#{column} should not be optional." end if file.tag tag_name = text_eval(file.name, step.src_file, step.context) if file.regex if type == :prerequisites raise "Pattern `#{file_name}' in #{step.src_file} at #{line}:#{column} cannot be used in input file list." end file.scheme = DakeScheme::Regex.new('^', tag_name, step) if type == :targets @tag_template_dict[file.scheme.path] ||= [] @tag_template_dict[file.scheme.path] << step end else file.scheme = DakeScheme::Tag.new('@', tag_name, step) if type == :targets @tag_target_dict[file.scheme.path] ||= [] @tag_target_dict[file.scheme.path] << step end end return [file] else # there maybe more than one file if a file list is generated in command substitution file_names = text_eval(file.name, step.src_file, step.context).split("\n") files = file_names.map do |file_name| if file.regex scheme = DakeScheme::Regex.new('^', file_name, step) else scheme = analyze_scheme(file_name, step, line, column) end if file_names.length == 1 file_path = scheme.path if type == :targets if file.regex if @file_template_dict[file_path] raise "Output pattern `#{file_name}' in #{step.src_file} at #{line}:#{column} appears in more than one step." else @file_template_dict[file_path] = step end else if @file_target_dict[file_path] raise "Output file `#{file_name}' in #{step.src_file} at #{line}:#{column} appears in more than one step." else @file_target_dict[file_path] = step end end else if file.regex raise "Pattern `#{file_name}' in #{step.src_file} at #{line}:#{column} cannot be used in input file list." end end else # Generated file list should not be used in targets # if type == :targets # raise "File list `#{file_name}' in #{step.src_file} at #{line}:#{column} cannot be used as targets." # end end newfile = file.dup newfile.scheme = scheme newfile end return files end end |
#analyze_method(meth) ⇒ Object
48 49 50 51 52 53 54 55 56 57 |
# File 'lib/dake/analyzer.rb', line 48 def analyze_method(meth) if @method_dict[meth.name.to_s] line, column = @method_dict[meth.name.to_s].name.line_and_column raise "Method `#{meth.name.to_s}' has already beed defined in " + "#{@method_dict[meth.name.to_s].src_file} at #{line}:#{column}" end meth.context = @variable_dict.dup analyze_option(meth) @method_dict[meth.name.to_s] = meth end |
#analyze_option(step) ⇒ Object
method is the same as step
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/dake/analyzer.rb', line 22 def analyze_option(step) # method is the same as step step.option_dict = {} step..each do |option| name = option.name.to_s if option.value.is_a? Parslet::Slice value = option.value.to_s value = 'true' if value == '+' value = 'false' if value == '-' else value = text_eval(option.value, step.src_file, step.context) end if step.option_dict[name] line, column = option.name.line_and_column raise "Option `#{name}' in #{step.src_file} at #{line}:#{column} has already been set." else if name == 'protocol' and not DakeProtocol::ProtocolDict.keys.include? value line, column = option.value.line_and_column raise "Protocol `#{value}' in #{step.src_file} at #{line}:#{column} is not supported." end # TODO: should have more option check step.option_dict[name] = value end end step.option_dict['protocol'] = 'shell' unless step.option_dict['protocol'] end |
#analyze_scheme(name, step, line, column) ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/dake/analyzer.rb', line 59 def analyze_scheme(name, step, line, column) scheme_part, path_part = name.match(/(\w+:)?(.*)/).to_a[1, 2] scheme_cls = DakeScheme::SchemeDict[scheme_part ? scheme_part : 'local:'] if step raise "Scheme `#{scheme_part}' in #{step.src_file} at #{line}:#{column} is not supported." unless scheme_cls scheme_cls.new(scheme_part, path_part, step) else # for user input target name raise "Scheme `#{scheme_part}' in #{name} is not supported." unless scheme_cls dummy_step = Step.new([], [], [], {}, nil, nil, @variable_dict, nil, nil) scheme_cls.new(scheme_part, path_part, dummy_step) end end |
#analyze_step(step) ⇒ Object
141 142 143 144 145 |
# File 'lib/dake/analyzer.rb', line 141 def analyze_step(step) step.context = @variable_dict.dup # the analysis of prerequisites is deferred to the resolve phase step.targets.map! { |file| analyze_file(file, :targets, step) }.flatten! end |
#option_line_and_column(options, option_name) ⇒ Object
257 258 259 260 261 262 263 264 265 266 267 |
# File 'lib/dake/analyzer.rb', line 257 def option_line_and_column(, option_name) .each do |option| next unless option.name.to_s == option_name if option.value.is_a? Parslet::Slice return option.value.line_and_column else return text_line_and_column option.value end end nil end |
#print_target(target, tag, target_step, dep_graph = nil, pad_stack = []) ⇒ Object
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
# File 'lib/dake/analyzer.rb', line 282 def print_target(target, tag, target_step, dep_graph = nil, pad_stack = []) pad_prefix = '' unless pad_stack.empty? pad_prefix += pad_stack.reduce('') do |str, pad| next str + '│ ' if pad == '├' next str + ' ' if pad == '└' end end pwd = Pathname.new(Dir.pwd) target_path = if tag then '@' + target elsif target.start_with? '/' Pathname.new(target).relative_path_from(pwd) else target end if target_step workflow_path = Pathname.new(target_step.src_file).relative_path_from(pwd) line, column = step_line_and_column(target_step) pad = (pad_stack.empty? ? '' : pad_prefix[0...-4] + pad_stack.last + '── ') puts pad.colorize(:magenta) + "#{target_path}".colorize((tag ? :light_blue : :light_white)) + " in " + "#{workflow_path} ".colorize(:green) + "#{line}:#{column} ".colorize(:yellow) unless target_step.doc_str.empty? pad = (pad_stack.empty? ? '│ ' : pad_prefix + '│ ') target_step.doc_str.each do |doc| if target_step.prerequisites.empty? or not dep_graph puts (pad[0...-2] + (doc == target_step.doc_str.last ? '\_' : '│ ') + doc.string.to_s).colorize(:magenta) else puts (pad + doc.string.to_s).colorize(:magenta) end end end else pad = (pad_stack.empty? ? '' : pad_prefix[0...-4] + pad_stack.last + '── ') puts pad.colorize(:magenta) + "#{target_path}".colorize((tag ? :light_blue : :light_white)) + " (No step) " end if target_step and dep_graph dep_steps = dep_graph.dep_step[target_step] pairs = target_step.prerequisites.each_with_object([]) do |prereq, pairs| steps = dep_steps.find_all { |dep_step| dep_step.targets.any? { |tar| tar.scheme.path == prereq.scheme.path } } if steps.empty? pairs << [prereq, nil] else steps.each { |step| pairs << [prereq, step]} end end while pair = pairs.pop pad_stack << (pairs.empty? ? '└' : '├') prerequisite, step = pair print_target(prerequisite.scheme.path, prerequisite.tag, step, dep_graph, pad_stack) pad_stack.pop end end end |
#step_line_and_column(step) ⇒ Object
247 248 249 250 251 252 253 254 255 |
# File 'lib/dake/analyzer.rb', line 247 def step_line_and_column(step) return step.name.line_and_column if step.is_a? StepMethod first_target = step.targets[0] if first_target.name.is_a? Parslet::Slice first_target.name.line_and_column else text_line_and_column(first_target.name) end end |
#text_eval(text, src_file = , context = @variable_dict, skip = 0) ⇒ Object
src_file is needed by prepare_command in execution phase because @inclusion_stack will have only root workflow by then
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
# File 'lib/dake/analyzer.rb', line 345 def text_eval(text, src_file=@inclusion_stack[-1], context=@variable_dict, skip=0) text.items[skip..-1].reduce('') do |string, item| case item when Chars string.concat item.string.to_s when VariableSub var_name = item.var_name.to_s var_value = context[var_name] if var_value string.concat var_value else line, column = item.var_name.line_and_column raise "Variable `#{var_name}' in #{src_file} at #{line}:#{column} is not defined." end when CommandSub command = text_eval(item.cmd_text, src_file, context) stdout, stderr, status = Open3.capture3(command, :chdir=>@variable_dict['BASE']) if status.success? string.concat stdout.chomp else line, column = text_line_and_column item.cmd_text raise "Command `#{command}' in #{src_file} at #{line}:#{column} failed " + "with EXIT_STATUS:#{status.exitstatus} and STDERR:\n#{stderr}" end else # For Escaped Char string.concat item.to_s end string end end |
#text_line_and_column(text) ⇒ Object
269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/dake/analyzer.rb', line 269 def text_line_and_column(text) first_item = text.items[0] case first_item when Chars then first_item.string.line_and_column when VariableSub line, column = first_item.var_name.line_and_column [line, column - 2] when CommandSub line, column = text_line_and_column first_item.cmd_text [line, column - 2] end end |