antlr3 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|