antlr3 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ANTLR-LICENSE.txt +26 -0
- data/History.txt +66 -0
- data/README.txt +139 -0
- data/bin/antlr4ruby +33 -0
- data/java/RubyTarget.java +524 -0
- data/java/antlr-full-3.2.1.jar +0 -0
- data/lib/antlr3.rb +176 -0
- data/lib/antlr3/constants.rb +88 -0
- data/lib/antlr3/debug.rb +701 -0
- data/lib/antlr3/debug/event-hub.rb +210 -0
- data/lib/antlr3/debug/record-event-listener.rb +25 -0
- data/lib/antlr3/debug/rule-tracer.rb +55 -0
- data/lib/antlr3/debug/socket.rb +360 -0
- data/lib/antlr3/debug/trace-event-listener.rb +92 -0
- data/lib/antlr3/dfa.rb +247 -0
- data/lib/antlr3/dot.rb +174 -0
- data/lib/antlr3/error.rb +657 -0
- data/lib/antlr3/main.rb +561 -0
- data/lib/antlr3/modes/ast-builder.rb +41 -0
- data/lib/antlr3/modes/filter.rb +56 -0
- data/lib/antlr3/profile.rb +322 -0
- data/lib/antlr3/recognizers.rb +1280 -0
- data/lib/antlr3/streams.rb +985 -0
- data/lib/antlr3/streams/interactive.rb +91 -0
- data/lib/antlr3/streams/rewrite.rb +412 -0
- data/lib/antlr3/test/call-stack.rb +57 -0
- data/lib/antlr3/test/config.rb +23 -0
- data/lib/antlr3/test/core-extensions.rb +269 -0
- data/lib/antlr3/test/diff.rb +165 -0
- data/lib/antlr3/test/functional.rb +207 -0
- data/lib/antlr3/test/grammar.rb +371 -0
- data/lib/antlr3/token.rb +592 -0
- data/lib/antlr3/tree.rb +1415 -0
- data/lib/antlr3/tree/debug.rb +163 -0
- data/lib/antlr3/tree/visitor.rb +84 -0
- data/lib/antlr3/tree/wizard.rb +481 -0
- data/lib/antlr3/util.rb +149 -0
- data/lib/antlr3/version.rb +27 -0
- data/samples/ANTLRv3Grammar.g +621 -0
- data/samples/Cpp.g +749 -0
- data/templates/AST.stg +335 -0
- data/templates/ASTDbg.stg +40 -0
- data/templates/ASTParser.stg +153 -0
- data/templates/ASTTreeParser.stg +272 -0
- data/templates/Dbg.stg +192 -0
- data/templates/Ruby.stg +1514 -0
- data/test/functional/ast-output/auto-ast.rb +797 -0
- data/test/functional/ast-output/construction.rb +555 -0
- data/test/functional/ast-output/hetero-nodes.rb +753 -0
- data/test/functional/ast-output/rewrites.rb +1327 -0
- data/test/functional/ast-output/tree-rewrite.rb +1662 -0
- data/test/functional/debugging/debug-mode.rb +689 -0
- data/test/functional/debugging/profile-mode.rb +165 -0
- data/test/functional/debugging/rule-tracing.rb +74 -0
- data/test/functional/delegation/import.rb +379 -0
- data/test/functional/lexer/basic.rb +559 -0
- data/test/functional/lexer/filter-mode.rb +245 -0
- data/test/functional/lexer/nuances.rb +47 -0
- data/test/functional/lexer/properties.rb +104 -0
- data/test/functional/lexer/syn-pred.rb +32 -0
- data/test/functional/lexer/xml.rb +206 -0
- data/test/functional/main/main-scripts.rb +245 -0
- data/test/functional/parser/actions.rb +224 -0
- data/test/functional/parser/backtracking.rb +244 -0
- data/test/functional/parser/basic.rb +282 -0
- data/test/functional/parser/calc.rb +98 -0
- data/test/functional/parser/ll-star.rb +143 -0
- data/test/functional/parser/nuances.rb +165 -0
- data/test/functional/parser/predicates.rb +103 -0
- data/test/functional/parser/properties.rb +242 -0
- data/test/functional/parser/rule-methods.rb +132 -0
- data/test/functional/parser/scopes.rb +274 -0
- data/test/functional/token-rewrite/basic.rb +318 -0
- data/test/functional/token-rewrite/via-parser.rb +100 -0
- data/test/functional/tree-parser/basic.rb +750 -0
- data/test/unit/sample-input/file-stream-1 +2 -0
- data/test/unit/sample-input/teststreams.input2 +2 -0
- data/test/unit/test-dfa.rb +52 -0
- data/test/unit/test-exceptions.rb +44 -0
- data/test/unit/test-recognizers.rb +55 -0
- data/test/unit/test-scheme.rb +62 -0
- data/test/unit/test-streams.rb +459 -0
- data/test/unit/test-tree-wizard.rb +535 -0
- data/test/unit/test-trees.rb +854 -0
- metadata +205 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
unless defined? Call
|
4
|
+
|
5
|
+
Call = Struct.new(:file, :line, :method)
|
6
|
+
class Call
|
7
|
+
|
8
|
+
def self.parse(call_string)
|
9
|
+
parts = call_string.split(':', 3)
|
10
|
+
file = parts.shift
|
11
|
+
line = parts.shift.to_i
|
12
|
+
if parts.empty?
|
13
|
+
return Call.new(file, line)
|
14
|
+
else
|
15
|
+
mstring = parts.shift
|
16
|
+
match = mstring.match(/`(.+)'/)
|
17
|
+
method = match ? match[1] : nil
|
18
|
+
return Call.new(file, line, method)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.convert_backtrace( trace )
|
23
|
+
trace.map { |c| parse c }
|
24
|
+
end
|
25
|
+
|
26
|
+
def irb?
|
27
|
+
self.file == '(irb)'
|
28
|
+
end
|
29
|
+
|
30
|
+
def e_switch?
|
31
|
+
self.file == '-e'
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
string = '%s:%i' % [file, line]
|
36
|
+
method and string << ":in `%s'" % method
|
37
|
+
return(string)
|
38
|
+
end
|
39
|
+
|
40
|
+
def inspect
|
41
|
+
to_s.inspect
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module Kernel
|
46
|
+
def call_stack(depth = 1)
|
47
|
+
Call.convert_backtrace(caller(depth + 1))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Exception
|
52
|
+
def backtrace!
|
53
|
+
Call.convert_backtrace(backtrace)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end # unless defined? Call
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
=begin ::about::
|
5
|
+
author: Kyle Yetter <kcy5b@yahoo.com>
|
6
|
+
created on: October 11, 2009
|
7
|
+
=end
|
8
|
+
|
9
|
+
module ANTLR3
|
10
|
+
module Test
|
11
|
+
|
12
|
+
require 'rbconfig'
|
13
|
+
path_sep = RbConfig::CONFIG['PATH_SEPARATOR']
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
exec_path = ENV['PATH'].split(path_sep)
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,269 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
class String
|
5
|
+
def /(subpath)
|
6
|
+
File.join(self, subpath.to_s)
|
7
|
+
end
|
8
|
+
|
9
|
+
def here_indent(chr = '| ')
|
10
|
+
dup.here_indent!(chr)
|
11
|
+
end
|
12
|
+
|
13
|
+
def here_indent!(chr = '| ')
|
14
|
+
chr = Regexp.escape(chr)
|
15
|
+
exp = Regexp.new("^ *#{chr}")
|
16
|
+
self.gsub!(exp,'')
|
17
|
+
return self
|
18
|
+
end
|
19
|
+
|
20
|
+
def here_flow(chr = '| ')
|
21
|
+
dup.here_flow!(chr)
|
22
|
+
end
|
23
|
+
|
24
|
+
def here_flow!(chr = '| ')
|
25
|
+
here_indent!(chr).gsub!(/\n\s+/,' ')
|
26
|
+
return(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Indent left or right by n spaces.
|
30
|
+
# (This used to be called #tab and aliased as #indent.)
|
31
|
+
#
|
32
|
+
# CREDIT: Gavin Sinclair
|
33
|
+
# CREDIT: Trans
|
34
|
+
|
35
|
+
def indent(n)
|
36
|
+
if n >= 0
|
37
|
+
gsub(/^/, ' ' * n)
|
38
|
+
else
|
39
|
+
gsub(/^ {0,#{-n}}/, "")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Outdent just indents a negative number of spaces.
|
44
|
+
#
|
45
|
+
# CREDIT: Noah Gibbs
|
46
|
+
|
47
|
+
def outdent(n)
|
48
|
+
indent(-n)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the shortest length of leading whitespace for all non-blank lines
|
52
|
+
#
|
53
|
+
# n = %Q(
|
54
|
+
# a = 3
|
55
|
+
# b = 4
|
56
|
+
# ).level_of_indent #=> 2
|
57
|
+
#
|
58
|
+
# CREDIT: Kyle Yetter
|
59
|
+
def level_of_indent
|
60
|
+
self.scan(/^ *(?=\S)/).map { |space| space.length }.min || 0
|
61
|
+
end
|
62
|
+
|
63
|
+
def fixed_indent(n)
|
64
|
+
self.outdent(self.level_of_indent).indent(n)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Provides a margin controlled string.
|
68
|
+
#
|
69
|
+
# x = %Q{
|
70
|
+
# | This
|
71
|
+
# | is
|
72
|
+
# | margin controlled!
|
73
|
+
# }.margin
|
74
|
+
#
|
75
|
+
#
|
76
|
+
# NOTE: This may still need a bit of tweaking.
|
77
|
+
#
|
78
|
+
# CREDIT: Trans
|
79
|
+
|
80
|
+
def margin(n=0)
|
81
|
+
#d = /\A.*\n\s*(.)/.match( self )[1]
|
82
|
+
#d = /\A\s*(.)/.match( self)[1] unless d
|
83
|
+
d = ((/\A.*\n\s*(.)/.match(self)) ||
|
84
|
+
(/\A\s*(.)/.match(self)))[1]
|
85
|
+
return '' unless d
|
86
|
+
if n == 0
|
87
|
+
gsub(/\n\s*\Z/,'').gsub(/^\s*[#{d}]/, '')
|
88
|
+
else
|
89
|
+
gsub(/\n\s*\Z/,'').gsub(/^\s*[#{d}]/, ' ' * n)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Expands tabs to +n+ spaces. Non-destructive. If +n+ is 0, then tabs are
|
94
|
+
# simply removed. Raises an exception if +n+ is negative.
|
95
|
+
#
|
96
|
+
# Thanks to GGaramuno for a more efficient algorithm. Very nice.
|
97
|
+
#
|
98
|
+
# CREDIT: Gavin Sinclair
|
99
|
+
# CREDIT: Noah Gibbs
|
100
|
+
# CREDIT: GGaramuno
|
101
|
+
|
102
|
+
def expand_tabs(n=8)
|
103
|
+
n = n.to_int
|
104
|
+
raise ArgumentError, "n must be >= 0" if n < 0
|
105
|
+
return gsub(/\t/, "") if n == 0
|
106
|
+
return gsub(/\t/, " ") if n == 1
|
107
|
+
str = self.dup
|
108
|
+
while
|
109
|
+
str.gsub!(/^([^\t\n]*)(\t+)/) { |f|
|
110
|
+
val = ( n * $2.size - ($1.size % n) )
|
111
|
+
$1 << (' ' * val)
|
112
|
+
}
|
113
|
+
end
|
114
|
+
str
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
# The reverse of +camelcase+. Makes an underscored of a camelcase string.
|
119
|
+
#
|
120
|
+
# Changes '::' to '/' to convert namespaces to paths.
|
121
|
+
#
|
122
|
+
# Examples
|
123
|
+
# "SnakeCase".snakecase #=> "snake_case"
|
124
|
+
# "Snake-Case".snakecase #=> "snake_case"
|
125
|
+
# "SnakeCase::Errors".underscore #=> "snake_case/errors"
|
126
|
+
|
127
|
+
def snakecase
|
128
|
+
gsub(/::/, '/'). # NOT SO SURE ABOUT THIS -T
|
129
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
130
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
131
|
+
tr("-", "_").
|
132
|
+
downcase
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
class Module
|
139
|
+
# Returns the module's container module.
|
140
|
+
#
|
141
|
+
# module Example
|
142
|
+
# class Demo
|
143
|
+
# end
|
144
|
+
# end
|
145
|
+
#
|
146
|
+
# Example::Demo.modspace #=> Example
|
147
|
+
#
|
148
|
+
# See also Module#basename.
|
149
|
+
#
|
150
|
+
# CREDIT: Trans
|
151
|
+
|
152
|
+
def modspace
|
153
|
+
space = name[ 0...(name.rindex( '::' ) || 0)]
|
154
|
+
space.empty? ? Object : eval(space)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
module Kernel
|
159
|
+
autoload :Tempfile, 'tempfile'
|
160
|
+
|
161
|
+
def screen_width(out=STDERR)
|
162
|
+
default_width = ENV['COLUMNS'] || 80
|
163
|
+
tiocgwinsz = 0x5413
|
164
|
+
data = [0, 0, 0, 0].pack("SSSS")
|
165
|
+
if out.ioctl(tiocgwinsz, data) >= 0 then
|
166
|
+
rows, cols, xpixels, ypixels = data.unpack("SSSS")
|
167
|
+
if cols >= 0 then cols else default_width end
|
168
|
+
else
|
169
|
+
default_width
|
170
|
+
end
|
171
|
+
rescue Exception => e
|
172
|
+
default_width rescue (raise e)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
class File
|
178
|
+
|
179
|
+
# Given some target path string, and an optional reference path
|
180
|
+
# (Dir.pwd by default), this method returns a string containing
|
181
|
+
# the relative path of the target path from the reference path
|
182
|
+
#
|
183
|
+
# Examples:
|
184
|
+
# File.relative_path('rel/path') # => './rel/path'
|
185
|
+
# File.relative_path('/some/abs/path', '/some') # => './abs/path'
|
186
|
+
# File.relative_path('/some/file.txt', '/some/abs/path') # => '../../file.txt'
|
187
|
+
def self.relative_path(target, reference = Dir.pwd)
|
188
|
+
pair = [target, reference].map! do |path|
|
189
|
+
File.expand_path(path.to_s).split(File::Separator).tap do |list|
|
190
|
+
if list.empty? then list << String.new(File::Separator)
|
191
|
+
elsif list.first.empty? then list.first.replace(File::Separator)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
target_list, reference_list = pair
|
197
|
+
while target_list.first == reference_list.first
|
198
|
+
target_list.shift
|
199
|
+
reference_list.shift or break
|
200
|
+
end
|
201
|
+
|
202
|
+
relative_list = Array.new(reference_list.length, '..')
|
203
|
+
relative_list.empty? and relative_list << '.'
|
204
|
+
relative_list.concat(target_list).compact!
|
205
|
+
return relative_list.join(File::Separator)
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
class Dir
|
211
|
+
defined?(DOTS) or DOTS = %w(. ..).freeze
|
212
|
+
def self.children(directory)
|
213
|
+
entries = Dir.entries(directory) - DOTS
|
214
|
+
entries.map! do |entry|
|
215
|
+
File.join(directory, entry)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def self.mkpath(path)
|
220
|
+
$VERBOSE and $stderr.puts("INFO: Dir.mkpath(%p)" % path)
|
221
|
+
test(?d, path) and return(path)
|
222
|
+
parent = File.dirname(path)
|
223
|
+
test(?d, parent) or mkpath(parent)
|
224
|
+
Dir.mkdir(path)
|
225
|
+
return(path)
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
|
230
|
+
class Array
|
231
|
+
|
232
|
+
# Pad an array with a given <tt>value</tt> upto a given <tt>length</tt>.
|
233
|
+
#
|
234
|
+
# [0,1,2].pad(6,"a") #=> [0,1,2,"a","a","a"]
|
235
|
+
#
|
236
|
+
# If <tt>length</tt> is a negative number padding will be added
|
237
|
+
# to the beginning of the array.
|
238
|
+
#
|
239
|
+
# [0,1,2].pad(-6,"a") #=> ["a","a","a",0,1,2]
|
240
|
+
#
|
241
|
+
# CREDIT: Richard Laugesen
|
242
|
+
|
243
|
+
def pad(len, val=nil)
|
244
|
+
return dup if self.size >= len.abs
|
245
|
+
if len < 0
|
246
|
+
Array.new((len+size).abs,val) + self
|
247
|
+
else
|
248
|
+
self + Array.new(len-size,val)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# Like #pad but changes the array in place.
|
253
|
+
#
|
254
|
+
# a = [0,1,2]
|
255
|
+
# a.pad!(6,"x")
|
256
|
+
# a #=> [0,1,2,"x","x","x"]
|
257
|
+
#
|
258
|
+
# CREDIT: Richard Laugesen
|
259
|
+
|
260
|
+
def pad!(len, val=nil)
|
261
|
+
return self if self.size >= len.abs
|
262
|
+
if len < 0
|
263
|
+
replace Array.new((len+size).abs,val) + self
|
264
|
+
else
|
265
|
+
concat Array.new(len-size,val)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
module ANTLR3
|
5
|
+
module Test
|
6
|
+
module Diff
|
7
|
+
module_function
|
8
|
+
|
9
|
+
# trim common head and tail elements of the two lists
|
10
|
+
def width
|
11
|
+
screen_width - 20
|
12
|
+
end
|
13
|
+
|
14
|
+
def column_width
|
15
|
+
width / 2 - 1
|
16
|
+
end
|
17
|
+
|
18
|
+
def segment(list_a, list_b)
|
19
|
+
# trim leading common elements
|
20
|
+
common_head = 0
|
21
|
+
list_a.zip(list_b) do |a, b|
|
22
|
+
a == b ? (common_head += 1) : break
|
23
|
+
end
|
24
|
+
head = list_a[0...common_head]
|
25
|
+
list_a = list_a[common_head..-1].reverse!
|
26
|
+
list_b = list_b[common_head..-1].reverse!
|
27
|
+
|
28
|
+
|
29
|
+
common_tail = 0
|
30
|
+
list_a.zip(list_b) { |a,b| a == b ? (common_tail += 1) : break }
|
31
|
+
tail = list_a[0...common_tail].reverse!
|
32
|
+
list_a = list_a[common_tail..-1].reverse!
|
33
|
+
list_b = list_b[common_tail..-1].reverse!
|
34
|
+
|
35
|
+
return [head, tail, list_a, list_b]
|
36
|
+
end
|
37
|
+
|
38
|
+
def max(a,b)
|
39
|
+
case a <=> b
|
40
|
+
when -1 then b
|
41
|
+
else a
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def colorize(text, color)
|
46
|
+
lines = text.split("\n")
|
47
|
+
escape =
|
48
|
+
case color
|
49
|
+
when :white then "\e[37m"
|
50
|
+
when :magenta then "\e[35m"
|
51
|
+
when :yellow then "\e[33m"
|
52
|
+
when :green then "\e[32m"
|
53
|
+
when :black then "\e[30m"
|
54
|
+
when :red then "\e[31m"
|
55
|
+
when :cyan then "\e[36m"
|
56
|
+
when :blue then "\e[34m"
|
57
|
+
end
|
58
|
+
lines.map! { |ln| escape + ln << "\e[\0m" }.join("\n")
|
59
|
+
end
|
60
|
+
|
61
|
+
def display_shared(item)
|
62
|
+
colorize(pair(item, item), :green)
|
63
|
+
end
|
64
|
+
def display_addition(item)
|
65
|
+
colorize(pair(nil, item), :magenta)
|
66
|
+
end
|
67
|
+
def display_subtraction(item)
|
68
|
+
colorize(pair(item, nil), :red)
|
69
|
+
end
|
70
|
+
def wrapped_inspect(list)
|
71
|
+
width = column_width
|
72
|
+
list = list.map { |i| i.inspect }
|
73
|
+
list[-1] << ' ]'
|
74
|
+
|
75
|
+
lines, line, joint = [], '[ ', ''
|
76
|
+
|
77
|
+
add_line = proc do |l|
|
78
|
+
lines << l.ljust(width)
|
79
|
+
' '
|
80
|
+
end
|
81
|
+
|
82
|
+
for item in list
|
83
|
+
line << joint
|
84
|
+
leftover = width - line.length
|
85
|
+
if item.length > leftover
|
86
|
+
i = item.rindex(/\s+/, leftover - 1) || leftover
|
87
|
+
if
|
88
|
+
line = add_line[line << item[0, i]]
|
89
|
+
item = item[i..-1]
|
90
|
+
else
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
line << item
|
95
|
+
|
96
|
+
d = item.length + 2
|
97
|
+
if line.length + d > width
|
98
|
+
line << ','
|
99
|
+
lines << line
|
100
|
+
line, joint = ' ', ''
|
101
|
+
else
|
102
|
+
joint = ", "
|
103
|
+
end
|
104
|
+
end
|
105
|
+
lines << line
|
106
|
+
|
107
|
+
lines.join("\n")
|
108
|
+
end
|
109
|
+
|
110
|
+
def pair(x, y)
|
111
|
+
x = x ? wrapped_inspect(x).split("\n") : []
|
112
|
+
y = y ? wrapped_inspect(y).split("\n") : []
|
113
|
+
line_count = max(x.length, y.length)
|
114
|
+
x.pad!(line_count, '')
|
115
|
+
y.pad!(line_count, '')
|
116
|
+
|
117
|
+
x.zip(y).map! { |pair| pair.map! { |i| i.ljust(column_width) }.join(' ') }.join("\n")
|
118
|
+
end
|
119
|
+
|
120
|
+
def compute_lcs(list_a, list_b)
|
121
|
+
m, n = list_a.length, list_b.length
|
122
|
+
lcs_matrix = Array.new(m + 1) do |row|
|
123
|
+
Array.new(n + 1, 0)
|
124
|
+
end
|
125
|
+
m.times do |i| n.times do |j|
|
126
|
+
if list_a[i] == list_b[j] then lcs_matrix[i+1][j+1] = lcs_matrix[i][j] + 1
|
127
|
+
else lcs_matrix[i+1][j+1] = max(lcs_matrix[i+1][j], lcs_matrix[i][j+1])
|
128
|
+
end
|
129
|
+
end end
|
130
|
+
return lcs_matrix
|
131
|
+
end
|
132
|
+
|
133
|
+
def partial_diff(out, a, b, lcs, i = a.length, j = b.length)
|
134
|
+
if i > 0 and j > 0 and a[i-1] == b[j-1]
|
135
|
+
partial_diff(out, a, b, lcs, i-1, j-1)
|
136
|
+
out << display_shared(a[i-1])
|
137
|
+
else
|
138
|
+
if j > 0 and i.zero? || lcs[i][j-1] >= lcs[i-1][j]
|
139
|
+
partial_diff(out,a,b,lcs,i,j-1)
|
140
|
+
out << display_addition(b[j-1])
|
141
|
+
elsif i > 0 and j.zero? || lcs[i][j-1] < lcs[i-1][j]
|
142
|
+
partial_diff(out, a,b,lcs,i-1,j)
|
143
|
+
out << display_subtraction(a[i-1])
|
144
|
+
end
|
145
|
+
end
|
146
|
+
return out
|
147
|
+
end
|
148
|
+
|
149
|
+
def diff(a, b, options = {})
|
150
|
+
head, tail, a, b = segment(a, b)
|
151
|
+
lcs = compute_lcs(a,b)
|
152
|
+
out = []
|
153
|
+
out << %w(Expected Got).map! { |w| w.ljust(column_width) }.join(' ')
|
154
|
+
head.each do |item|
|
155
|
+
out << display_shared(item)
|
156
|
+
end
|
157
|
+
partial_diff(out, a, b, lcs)
|
158
|
+
tail.each do |item|
|
159
|
+
out << display_shared(item)
|
160
|
+
end
|
161
|
+
out.join("\n")
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|