Module: Bcpm::Regen

Defined in:
lib/bcpm/regen.rb

Overview

Source code auto-generation capabilities.

Class Method Summary collapse

Class Method Details

.run(file_names) ⇒ Object

Re-generates automatically generated source code files.



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
47
48
49
50
51
52
53
54
55
56
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
# File 'lib/bcpm/regen.rb', line 7

def self.run(file_names)
  source_lines = {}
  vars = {}

  # Read in source blocks.
  file_names.each do |file_name|
    lines = File.open(file_name, 'rb') { |f| f.read.split "\n" }
    
    current_block = nil
    lines.each do |line|
      if current_block
        if /^\s+\/\/\$\s+\-gen\:source(\s.*)?$/ =~ line
          current_block = nil
        else
          source_lines[current_block] << line
        end
      else
        block_match = /^\s+\/\/\$\s+\+gen\:source\s+(\S+)\s+(.*)$/.match line
        if block_match
          current_block = block_match[1]
          if source_lines[current_block]
            print "Duplicate source block #{current_block}\n"
            exit 1
          end
          source_lines[current_block] ||= []
          vars[current_block] = block_match[2].scan /\S+/
        end
      end
    end
    if current_block
      print "Un-closed source block #{current_block}\n"
      exit
    end
  end
  
  # Replace target blocks.
  file_names.each do |file_name|
    lines = File.open(file_name, 'rb') { |f| f.read.split "\n" }
    output_lines = []
    
    current_block = nil
    disabled = false
    lines.each do |line|
      if current_block
        if /^\s+\/\/\$\s+\-gen\:target(\s.*)?$/ =~ line
          output_lines << line
          current_block = nil
        end
      else
        block_match = /^\s+\/\/\$\s+\+gen\:target\s+(\S+)\s+(.*)$/.match line
        output_lines << line
    
        if block_match
          current_block = block_match[1]
          source_vars = vars[current_block]
          if !source_vars
            print "Missing source block #{current_block}\n"
            exit 1
          end
          target_vars = block_match[2].scan /\S+/
          if target_vars.length != source_vars.length
            print "Source/target variable mismatch.\n"
            print "Source: #{vars.join(' ')}\nTarget: #{vars.join(' ')}\n"
            exit 1
          end

          source_target = Hash[source_vars.zip(target_vars)]
          regexp = Regexp.new source_vars.map { |var| "(#{var})" }.join('|')
          new_lines = source_lines[current_block].map &:dup
          disabled = false
          new_lines.each do |line|
            if disabled
              disabled = false if /^\s+\/\/\$\s+\-gen\:off(\s.*)?$/ =~ line
            else
              if /^\s+\/\/\$\s+\+gen\:off(\s.*)?$/ =~ line
                disabled = true
              else
                line.gsub!(regexp) { |match| source_target[match] }
              end
            end
          end
          output_lines.concat new_lines
        end
      end
    end      
    if current_block
      print "Un-closed target block #{current_block}\n"
      exit
    end
    
    File.open(file_name, 'wb') { |f| f.write output_lines.join("\n") }
  end
end