origen 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/config/version.rb +2 -2
- data/lib/origen/application/deployer.rb +16 -17
- data/lib/origen/application/plugins_manager.rb +1 -1
- data/lib/origen/commands.rb +33 -1
- data/lib/origen/commands/interactive.rb +11 -3
- data/lib/origen/commands/rc.rb +5 -1
- data/lib/origen/commands/web.rb +2 -0
- data/lib/origen/generator/pattern.rb +25 -2
- data/lib/origen/regression_manager.rb +6 -5
- data/lib/origen/revision_control/git.rb +95 -15
- data/lib/origen/specs.rb +19 -13
- data/lib/origen/specs/checkers.rb +99 -93
- data/lib/origen/specs/creation_info.rb +23 -3
- data/lib/origen/specs/spec.rb +8 -1
- data/lib/origen/users/user.rb +13 -4
- data/lib/origen/utility.rb +1 -0
- data/lib/origen/utility/diff.rb +7 -3
- data/lib/origen/utility/file_diff.rb +213 -0
- data/templates/nanoc/Rules +13 -15
- data/templates/nanoc/layouts/bootstrap.html.erb +31 -4
- data/templates/shared/web/_logo.html +3 -3
- data/templates/web/css/landing.css +3 -0
- data/templates/web/design-engineering.html.erb +0 -0
- data/templates/web/index.html.erb +23 -0
- data/templates/web/js/jquery-singlePageNav.js +8 -0
- data/templates/web/js/landing.js +9 -0
- data/templates/web/layouts/_basic.html.erb +1 -0
- data/templates/web/layouts/_cyborg.html.erb +111 -0
- data/templates/web/layouts/_doc.html.erb +1 -0
- data/templates/web/test-engineering.html.erb +79 -0
- metadata +11 -5
- data/templates/nanoc/content/favicon.ico +0 -0
- data/templates/web/index.md.erb +0 -12
@@ -1,103 +1,109 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# a baseline value for a spec use target as it will not be
|
22
|
-
# checked against pass/fail
|
23
|
-
def limits_ok?
|
24
|
-
status = true
|
25
|
-
if (@min.exp.to_s.include? '/') || (@max.exp.to_s.include? '/')
|
26
|
-
return status
|
27
|
-
end
|
28
|
-
if @min.exp.nil? ^ @max.exp.nil?
|
29
|
-
@limit_type = :single_sided
|
30
|
-
if @typ.exp
|
31
|
-
# status = false
|
32
|
-
Origen.log.debug "Spec #{@name} has a typical limit defined with either min or max. They are mutually exclusive, use 'target' when using min or max"
|
33
|
-
end
|
34
|
-
elsif @min.exp && @max.exp
|
35
|
-
@limit_type = :double_sided
|
36
|
-
# Both min and max must be numerical to compare them
|
37
|
-
if @min.value.is_a?(Numeric) && @max.value.is_a?(Numeric)
|
38
|
-
# Check that min and max make sense
|
39
|
-
if @max.value <= @min.value || @min.value >= @max.value
|
40
|
-
status = false
|
41
|
-
Origen.log.debug "Spec #{@name} has min (#{@min.value}) and max (#{@max.value}) reversed"
|
1
|
+
module Origen
|
2
|
+
module Specs
|
3
|
+
module Checkers
|
4
|
+
# rubocop:disable Style/RescueModifier:
|
5
|
+
def name_audit(name)
|
6
|
+
return name if name.nil?
|
7
|
+
return nil unless name.is_a?(Symbol) || name.is_a?(String)
|
8
|
+
if name == :inspect
|
9
|
+
Origen.log.debug ':inspect is a reserved spec name'
|
10
|
+
return nil
|
11
|
+
end
|
12
|
+
if name.match(/^\d/)
|
13
|
+
Origen.log.debug "Spec #{name} starts with a number"
|
14
|
+
return nil
|
15
|
+
end
|
16
|
+
if name.match(/\s+/)
|
17
|
+
Origen.log.debug "Spec #{name} contains white space, removing it"
|
18
|
+
name.delete!(/\s+/)
|
19
|
+
end
|
20
|
+
name.is_a?(String) ? name.downcase.to_sym : name
|
42
21
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
22
|
+
|
23
|
+
# Check that min, max are not mixed with typ. If a user wants
|
24
|
+
# a baseline value for a spec use target as it will not be
|
25
|
+
# checked against pass/fail
|
26
|
+
def limits_ok?
|
27
|
+
status = true
|
28
|
+
if (@min.exp.to_s.include? '/') || (@max.exp.to_s.include? '/')
|
29
|
+
return status
|
30
|
+
end
|
31
|
+
if @min.exp.nil? ^ @max.exp.nil?
|
32
|
+
@limit_type = :single_sided
|
33
|
+
if @typ.exp
|
34
|
+
# status = false
|
35
|
+
Origen.log.debug "Spec #{@name} has a typical limit defined with either min or max. They are mutually exclusive, use 'target' when using min or max"
|
36
|
+
end
|
37
|
+
elsif @min.exp && @max.exp
|
38
|
+
@limit_type = :double_sided
|
39
|
+
# Both min and max must be numerical to compare them
|
40
|
+
if @min.value.is_a?(Numeric) && @max.value.is_a?(Numeric)
|
41
|
+
# Check that min and max make sense
|
42
|
+
if @max.value <= @min.value || @min.value >= @max.value
|
43
|
+
status = false
|
44
|
+
Origen.log.debug "Spec #{@name} has min (#{@min.value}) and max (#{@max.value}) reversed"
|
45
|
+
end
|
46
|
+
# Check that target is OK
|
47
|
+
unless @target.nil?
|
48
|
+
if @target.value <= @min.value || @target.value >= @max.value
|
49
|
+
status = false
|
50
|
+
Origen.log.debug "Spec #{@name} has a target (#{@target.value}) that is not within the min (#{@min.value}) and max #{@max.value}) values"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
48
54
|
end
|
55
|
+
status
|
49
56
|
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
status
|
53
|
-
end
|
54
57
|
|
55
|
-
def get_mode
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
58
|
+
def get_mode
|
59
|
+
spec_mode = nil
|
60
|
+
if current_mode.nil?
|
61
|
+
if self == Origen.top_level
|
62
|
+
spec_mode = :global
|
63
|
+
else
|
64
|
+
spec_mode = :local
|
65
|
+
end
|
66
|
+
else
|
67
|
+
spec_mode = current_mode.name
|
68
|
+
end
|
69
|
+
spec_mode
|
70
|
+
end
|
68
71
|
|
69
|
-
def evaluate_limit(limit)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
else
|
84
|
-
result = Integer(limit) rescue false
|
85
|
-
end
|
86
|
-
if result == false
|
87
|
-
# Attempt to eval the limit because users could write a limit like "3.3 + 50.mV"
|
88
|
-
# which would not work with the code above but should eval to a number 3.35
|
89
|
-
begin
|
90
|
-
result = eval(limit)
|
91
|
-
return result.round(4) if result.is_a? Numeric
|
92
|
-
rescue SyntaxError, NameError, TypeError
|
93
|
-
Origen.log.debug "Limit '#{limit}' had to be rescued, storing it as a #{limit.class}"
|
94
|
-
if limit.is_a? Symbol
|
95
|
-
return limit
|
72
|
+
def evaluate_limit(limit)
|
73
|
+
return limit if limit.is_a?(Numeric) || limit.is_a?(Symbol)
|
74
|
+
return nil if limit.nil?
|
75
|
+
limit.gsub!("\n", ' ')
|
76
|
+
limit.scrub!
|
77
|
+
result = false
|
78
|
+
if !!(limit.match(/^\d+\.\d+$/)) || !!(limit.match(/^-\d+\.\d+$/))
|
79
|
+
result = Float(limit).round(4) rescue false # Use the same four digits of accuracy as the Spec model
|
80
|
+
elsif !!(limit.match(/\d+\.\d+\s+\d+\.\d+/)) # workaround for multiple specs authoring bug
|
81
|
+
Origen.log.debug "Found two numbers without an operator in the limit string '#{limit}', choosing the first..."
|
82
|
+
first_number = limit.match(/(\d+\.\d+)\s+\d+\.\d+/).captures.first
|
83
|
+
result = Float(first_number).round(4) rescue false # Use the same four digits of accuracy as the Spec model
|
84
|
+
# elsif !!(limit.match(/^tbd$/i)) # unique case of TBD or To Be Determined, will convert to symbol
|
85
|
+
# limit = limit.downcase.to_sym
|
96
86
|
else
|
97
|
-
|
87
|
+
result = Integer(limit) rescue false
|
98
88
|
end
|
89
|
+
if result == false
|
90
|
+
# Attempt to eval the limit because users could write a limit like "3.3 + 50.mV"
|
91
|
+
# which would not work with the code above but should eval to a number 3.35
|
92
|
+
begin
|
93
|
+
result = eval(limit)
|
94
|
+
return result.round(4) if result.is_a? Numeric
|
95
|
+
rescue SyntaxError, NameError, TypeError
|
96
|
+
Origen.log.debug "Limit '#{limit}' had to be rescued, storing it as a #{limit.class}"
|
97
|
+
if limit.is_a? Symbol
|
98
|
+
return limit
|
99
|
+
else
|
100
|
+
return "#{limit}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
else
|
104
|
+
return result
|
105
|
+
end
|
106
|
+
end
|
99
107
|
end
|
100
|
-
else
|
101
|
-
return result
|
102
108
|
end
|
103
109
|
end
|
@@ -1,12 +1,32 @@
|
|
1
1
|
module Origen
|
2
2
|
module Specs
|
3
|
-
#
|
3
|
+
# Ruby Data Class that contains Creation Information for the IP Block
|
4
4
|
class Creation_Info
|
5
|
-
attr_accessor :author, :date, :revision, :source, :tool, :tool_version
|
5
|
+
attr_accessor :author, :date, :revision, :source, :tool, :tool_version, :ip_version
|
6
6
|
|
7
|
-
|
7
|
+
# Initialize the Creation Info block to store data for latest version of the file.
|
8
|
+
#
|
9
|
+
# ==== Parameters
|
10
|
+
#
|
11
|
+
# * author # Author/Subject Matter Expert for the IP Block
|
12
|
+
# * date # Date that the File was released to Downstream Audiences
|
13
|
+
# ==== Source Information
|
14
|
+
#
|
15
|
+
# * :revision # Revision Information
|
16
|
+
# * :source # Where the Information came from
|
17
|
+
#
|
18
|
+
# ==== Tool Info
|
19
|
+
#
|
20
|
+
# * :tool # Tool that created the initial XML file
|
21
|
+
# * :version # Version of the Tool that created the XML file
|
22
|
+
#
|
23
|
+
# ==== Example
|
24
|
+
#
|
25
|
+
# Creation_Info.new("author", "07/10/2015", :revision => "5.4", :source => "CSV", :tool => "oRiGeN", :tool_version => "0.0.6")
|
26
|
+
def initialize(author, date, ip_version, src_info = {}, tool_info = {})
|
8
27
|
@author = author
|
9
28
|
@date = date
|
29
|
+
@ip_version = ip_version
|
10
30
|
@revision = src_info[:revision]
|
11
31
|
@source = src_info[:source]
|
12
32
|
@tool = tool_info[:tool]
|
data/lib/origen/specs/spec.rb
CHANGED
@@ -3,7 +3,8 @@ module Origen
|
|
3
3
|
class Spec
|
4
4
|
autoload :Note, 'origen/specs/note'
|
5
5
|
autoload :Exhibit, 'origen/specs/exhibit'
|
6
|
-
|
6
|
+
include Checkers
|
7
|
+
extend Checkers
|
7
8
|
|
8
9
|
SpecAttribute = Struct.new(:name, :type, :required, :author, :description)
|
9
10
|
|
@@ -106,6 +107,12 @@ module Origen
|
|
106
107
|
fail "Spec #{name} failed the limits audit!" unless limits_ok?
|
107
108
|
end
|
108
109
|
|
110
|
+
def inspect
|
111
|
+
$dut.send(:specs_to_table_string, [self])
|
112
|
+
rescue
|
113
|
+
super
|
114
|
+
end
|
115
|
+
|
109
116
|
def method_missing(method, *args, &block)
|
110
117
|
ivar = "@#{method.to_s.gsub('=', '')}"
|
111
118
|
ivar_sym = ":#{ivar}"
|
data/lib/origen/users/user.rb
CHANGED
@@ -66,16 +66,25 @@ module Origen
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def name
|
69
|
-
@name || @id
|
69
|
+
@name || name_from_rc || @id
|
70
|
+
end
|
71
|
+
|
72
|
+
def name_from_rc
|
73
|
+
RevisionControl::Git.user_name
|
70
74
|
end
|
71
75
|
|
72
76
|
def email
|
73
|
-
|
74
|
-
|
75
|
-
|
77
|
+
@email || email_from_rc || begin
|
78
|
+
if Origen.site_config.email_domain
|
79
|
+
"#{id}@#{Origen.site_config.email_domain}"
|
80
|
+
end
|
76
81
|
end
|
77
82
|
end
|
78
83
|
|
84
|
+
def email_from_rc
|
85
|
+
RevisionControl::Git.user_email
|
86
|
+
end
|
87
|
+
|
79
88
|
# Fetch user data from the FSL application directory
|
80
89
|
#
|
81
90
|
# @example
|
data/lib/origen/utility.rb
CHANGED
data/lib/origen/utility/diff.rb
CHANGED
@@ -101,9 +101,13 @@ module Origen
|
|
101
101
|
if @comment_char
|
102
102
|
# Screen off any inline comments at the end of line
|
103
103
|
begin
|
104
|
-
[@comment_char].flatten.each do |
|
105
|
-
|
106
|
-
|
104
|
+
[@comment_char].flatten.each do |comchar|
|
105
|
+
unless line =~ /^\s*#{comchar}/
|
106
|
+
if line =~ /(.*)\s*#{comchar}.*/
|
107
|
+
return Regexp.last_match[1].strip
|
108
|
+
else
|
109
|
+
return line.strip
|
110
|
+
end
|
107
111
|
end
|
108
112
|
end
|
109
113
|
# This rescue is a crude way to guard against non-ASCII files that find
|
@@ -0,0 +1,213 @@
|
|
1
|
+
module Origen
|
2
|
+
module Utility
|
3
|
+
module FileDiff
|
4
|
+
class InputFile < Array
|
5
|
+
attr_accessor :pointer
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
self.pointer = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
def current_line
|
12
|
+
self[pointer]
|
13
|
+
end
|
14
|
+
|
15
|
+
def advance_pointer!
|
16
|
+
self.pointer += 1
|
17
|
+
end
|
18
|
+
|
19
|
+
def find_current_line_in(other)
|
20
|
+
index = (other[other.pointer..-1] || []).index(current_line)
|
21
|
+
index.nil? ? nil : other.pointer + index
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class OutputFile < Array
|
26
|
+
class Line < String
|
27
|
+
attr_accessor :type, :original_number
|
28
|
+
def initialize(type, input_file)
|
29
|
+
self.type = type
|
30
|
+
return unless input_file
|
31
|
+
replace(input_file.current_line)
|
32
|
+
self.original_number = input_file.pointer + 1
|
33
|
+
input_file.advance_pointer!
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_line(type, input_file = nil)
|
38
|
+
push(Line.new(type, input_file))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Diff Processor (Origen::Utility::Processor) provides an easy way to diff the contents of two files
|
43
|
+
# and display the differences as an HTML file or a TXT file.
|
44
|
+
# Very basic functionality, but can be expanded to add more features in the future.
|
45
|
+
# Comments are not ignored for now (maybe a future enhancement)
|
46
|
+
# Each difference is displayed in a different color in the HTML page
|
47
|
+
# Legend:
|
48
|
+
# - New: Light Green
|
49
|
+
# - Modified: Light Gray
|
50
|
+
# - Deleted: Pink
|
51
|
+
# Usage:
|
52
|
+
# processor = Origen::Utility::FileDiff::Processor.new("#{Origen.root}/left.txt", "#{Origen.root}/right.txt")
|
53
|
+
#
|
54
|
+
# To Generate a HTML file (diff.html) showing the differences
|
55
|
+
# Origen::Utility::FileDiff::Formatter::Html.new(processor.process!, "#{Origen.root}/diff.html").format
|
56
|
+
#
|
57
|
+
# To Generate a TXT file (diff.txt) showing the differences
|
58
|
+
# Origen::Utility::FileDiff::Formatter::Text.new(processor.process!, "#{Origen.root}/diff.txt").format
|
59
|
+
class Processor
|
60
|
+
attr_accessor :source, :target
|
61
|
+
attr_accessor :source_output, :target_output
|
62
|
+
def initialize(source_file_name, target_file_name)
|
63
|
+
self.source = InputFile.new
|
64
|
+
self.target = InputFile.new
|
65
|
+
self.source_output = OutputFile.new
|
66
|
+
self.target_output = OutputFile.new
|
67
|
+
IO.readlines(source_file_name).each do |line|
|
68
|
+
source << line
|
69
|
+
end
|
70
|
+
IO.readlines(target_file_name).each do |line|
|
71
|
+
target << line
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def handle_exactly_matched
|
76
|
+
source_output.add_line(:unchanged, source)
|
77
|
+
target_output.add_line(:unchanged, target)
|
78
|
+
end
|
79
|
+
|
80
|
+
def handle_line_changed
|
81
|
+
source_output.add_line(:changed, source)
|
82
|
+
target_output.add_line(:changed, target)
|
83
|
+
end
|
84
|
+
|
85
|
+
def handle_block_added(size)
|
86
|
+
size.times do
|
87
|
+
source_output.add_line(:added) # Empty line in the left side of the diff
|
88
|
+
target_output.add_line(:added, target)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def handle_block_deleted(size)
|
93
|
+
size.times do
|
94
|
+
source_output.add_line(:deleted, source)
|
95
|
+
target_output.add_line(:deleted) # Empty line in the right side of the diff
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def process!
|
100
|
+
while source.pointer < source.size && target.pointer < target.size
|
101
|
+
matched = source.find_current_line_in(target)
|
102
|
+
if matched
|
103
|
+
if matched > target.pointer
|
104
|
+
deleted = target.find_current_line_in(source)
|
105
|
+
handle_block_deleted(deleted - source.pointer) if deleted
|
106
|
+
end
|
107
|
+
handle_block_added(matched - target.pointer)
|
108
|
+
handle_exactly_matched
|
109
|
+
else
|
110
|
+
found = target.find_current_line_in(source)
|
111
|
+
if found
|
112
|
+
handle_block_deleted(found - source.pointer)
|
113
|
+
else
|
114
|
+
handle_line_changed
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
handle_block_deleted(source.size - source.pointer)
|
119
|
+
handle_block_added(target.size - target.pointer)
|
120
|
+
|
121
|
+
self
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
module Formatter
|
126
|
+
class Base
|
127
|
+
attr_accessor :source_output, :target_output, :file
|
128
|
+
def initialize(processed_diff, output_file_name)
|
129
|
+
self.source_output = processed_diff.source_output
|
130
|
+
self.target_output = processed_diff.target_output
|
131
|
+
self.file = File.open(output_file_name, 'w')
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class Html < Base
|
136
|
+
def format
|
137
|
+
tag(:style) { content('td{vertical-align: middle} pre{margin: 0px} .added{background-color: lightgreen;}.deleted{background-color: pink;}.changed{background-color: lightgray;}.line{background-color: lightblue}') }
|
138
|
+
tag :table, cellpaddig: 0, cellspacing: 0 do
|
139
|
+
source_output.each_with_index do |src, i|
|
140
|
+
tgt = target_output[i]
|
141
|
+
tag :tr do
|
142
|
+
tag(:td, class: :line) { tag(:pre) { content(src.original_number) } }
|
143
|
+
tag(:td, class: src.type) { tag(:pre) { content(src) } }
|
144
|
+
tag(:td, class: :line) { tag(:pre) { content(tgt.original_number) } }
|
145
|
+
tag(:td, class: tgt.type) { tag(:pre) { content(tgt) } }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def tag(name, options = {}, &block)
|
154
|
+
file.puts %(<#{name})
|
155
|
+
file.puts options.collect { |attribute, value| %(#{attribute}="#{value}") }
|
156
|
+
file.puts '>'
|
157
|
+
yield
|
158
|
+
file.puts "</#{name}>"
|
159
|
+
end
|
160
|
+
|
161
|
+
def content(inner_text)
|
162
|
+
file.puts(inner_text.to_s == '' ? ' ' : inner_text)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
class Text < Base
|
167
|
+
def format
|
168
|
+
pointer = 0
|
169
|
+
while pointer < target_output.size
|
170
|
+
size = 1
|
171
|
+
type = source_output[pointer].type
|
172
|
+
case type
|
173
|
+
when :added
|
174
|
+
added(pointer, size = get_block_size(pointer, :added))
|
175
|
+
when :deleted
|
176
|
+
deleted(pointer, size = get_block_size(pointer, :deleted))
|
177
|
+
when :changed
|
178
|
+
changed(pointer, size = get_block_size(pointer, :changed))
|
179
|
+
end
|
180
|
+
file.puts unless type == :unchanged
|
181
|
+
pointer += size
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
|
187
|
+
def get_block_size(pointer, type)
|
188
|
+
size = 1
|
189
|
+
size += 1 while target_output[pointer + size].type == type
|
190
|
+
size
|
191
|
+
end
|
192
|
+
|
193
|
+
def added(pointer, size)
|
194
|
+
file.puts(target_output[pointer].original_number)
|
195
|
+
0.upto(size - 1) { |i| file.puts("+ #{target_output[pointer + i]}") }
|
196
|
+
end
|
197
|
+
|
198
|
+
def deleted(pointer, size)
|
199
|
+
file.puts(source_output[pointer].original_number)
|
200
|
+
0.upto(size - 1) { |i| file.puts("- #{source_output[pointer + i]}") }
|
201
|
+
end
|
202
|
+
|
203
|
+
def changed(pointer, size)
|
204
|
+
file.puts("#{source_output[pointer].original_number},#{target_output[pointer].original_number}")
|
205
|
+
0.upto(size - 1) { |i| file.puts("source<< #{source_output[pointer + i]}") }
|
206
|
+
file.puts('=======')
|
207
|
+
0.upto(size - 1) { |i| file.puts("target>> #{target_output[pointer + i]}") }
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|