XDCC-Fetch 1.386 → 1.409
Sign up to get free protection for your applications and to get access to all the features.
- data/XDCC-Fetch.rbw +17 -16
- data/doc/index.html +12 -1
- data/doc/xdccfetch.css +6 -1
- data/icons/locale.png +0 -0
- data/src/GUI/Application_Builder.rb +25 -9
- data/src/GUI/Context_Menu.rb +33 -9
- data/src/GUI/Download_Finished_Box.rb +13 -10
- data/src/GUI/Empty_Text_Field_Handler.rb +2 -1
- data/src/GUI/Gui_Logic.rb +36 -99
- data/src/GUI/Main_Window.rb +60 -16
- data/src/GUI/Packet_Item.rb +36 -1
- data/src/GUI/Packet_List.rb +47 -2
- data/src/GUI/Search_Engine.rb +187 -0
- data/src/GUI/Talk_Back.rb +1 -2
- data/src/GUI/Toggle_Button.rb +1 -1
- data/src/Network/DCC_File.rb +54 -44
- data/src/Network/TCP_Connection.rb +6 -5
- data/src/Network/XDCC_Download_Handler.rb +5 -1
- data/src/Translations/check_translations +5 -5
- data/src/Translations/de.rb +3 -1
- data/src/Translations/en.rb +5 -1
- data/src/Translations/pl.rb +145 -0
- data/src/Utilities/Configuration.rb +1 -1
- data/src/Utilities/Globals.rb +7 -3
- data/src/Utilities/Recursive_Open_Struct.rb +110 -11
- metadata +26 -24
- data/src/Utilities/PrettyException.rb +0 -1091
data/src/Utilities/Globals.rb
CHANGED
@@ -31,7 +31,7 @@ $cfg = Recursive_Open_Struct.new
|
|
31
31
|
$cfg.app.name = "XDCC-Fetch"
|
32
32
|
$cfg.app.version = "STONEAGE"
|
33
33
|
|
34
|
-
$cfg.app.revision = "1.
|
34
|
+
$cfg.app.revision = "1.409"
|
35
35
|
|
36
36
|
$cfg.app.developers = [
|
37
37
|
"Martin Ankerl (GUI)",
|
@@ -88,13 +88,15 @@ $cfg.icons.about = "idea.png"
|
|
88
88
|
$cfg.icons.cancel = "cancel.png"
|
89
89
|
$cfg.icons.error_big = "messagebox_critical.png"
|
90
90
|
$cfg.icons.warning_big = "messagebox_warning.png"
|
91
|
+
$cfg.icons.translations = "locale.png"
|
91
92
|
|
92
93
|
# parameters for network code
|
93
|
-
$cfg.network.tcp.connect_timeout =
|
94
|
+
$cfg.network.tcp.connect_timeout = 30
|
94
95
|
$cfg.network.tcp.connect_thread_priority = -2
|
95
96
|
$cfg.network.dcc.send_regexp = /^DCC\sSEND\s(\S+)\s(\d+)\s(\d+)\s(\d+)$/i
|
96
97
|
$cfg.network.dcc.accept_regexp = /^DCC\sACCEPT\s(\S+\s)?\s*(\d+)\s(\d+)$/i
|
97
|
-
$cfg.network.dcc.
|
98
|
+
$cfg.network.dcc.enable_verification = true
|
99
|
+
$cfg.network.dcc.verification_bytes = 20_000
|
98
100
|
$cfg.network.dcc.block_nonroutable_addresses = true
|
99
101
|
$cfg.network.ctcp.enable_replies = true
|
100
102
|
$cfg.network.irc.default_port = 6667
|
@@ -134,5 +136,7 @@ Dir['src/Translations/*.rb'].each do |language_file|
|
|
134
136
|
require language_file
|
135
137
|
end
|
136
138
|
|
139
|
+
$cfg.text = Recursive_Open_Struct.new
|
140
|
+
|
137
141
|
# prevent further modifications
|
138
142
|
$cfg.close
|
@@ -2,8 +2,6 @@
|
|
2
2
|
# Copyright:: Copyright (c) 2004-2005 Martin Ankerl
|
3
3
|
# License:: BSD
|
4
4
|
#
|
5
|
-
# All rights reserved.
|
6
|
-
#
|
7
5
|
# Redistribution and use in source and binary forms, with or without modification,
|
8
6
|
# are permitted provided that the following conditions are met:
|
9
7
|
#
|
@@ -24,6 +22,7 @@
|
|
24
22
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
25
23
|
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
26
24
|
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
25
|
+
#
|
27
26
|
|
28
27
|
|
29
28
|
# Recursive_Open_Struct provides a convenient interface to a hierarchy of configuration
|
@@ -56,22 +55,27 @@ class Recursive_Open_Struct
|
|
56
55
|
end
|
57
56
|
|
58
57
|
# automatically add parameters
|
59
|
-
def method_missing(method, params
|
58
|
+
def method_missing(method, *params) # :nodoc:
|
59
|
+
# setting or getting?
|
60
|
+
is_setting = !params.empty?
|
61
|
+
|
60
62
|
key = method.id2name
|
61
|
-
|
63
|
+
# remove trailing =
|
64
|
+
key.chop! if is_setting
|
62
65
|
|
63
66
|
# if structure is closed, disable hierarchy creation
|
64
|
-
super
|
67
|
+
super unless @methods.has_key?(key) || @open
|
65
68
|
|
66
|
-
if
|
67
|
-
# no param: create new Recursive_Open_Struct object
|
68
|
-
@methods[key] ||= Recursive_Open_Struct.new
|
69
|
-
else
|
69
|
+
if is_setting
|
70
70
|
# assigning a new value
|
71
71
|
if @methods[key].class == Recursive_Open_Struct
|
72
72
|
raise TypeError, "overwriting previously created hierarchy entry '#{key}' not allowed", caller(1)
|
73
|
-
|
74
|
-
|
73
|
+
end
|
74
|
+
@methods[key] = *params
|
75
|
+
else
|
76
|
+
# no param: create new Recursive_Open_Struct object, if nothing is set.
|
77
|
+
unless @methods.has_key?(key)
|
78
|
+
@methods[key] = Recursive_Open_Struct.new
|
75
79
|
end
|
76
80
|
end
|
77
81
|
@methods[key]
|
@@ -157,3 +161,98 @@ class Recursive_Open_Struct
|
|
157
161
|
@open = status
|
158
162
|
end
|
159
163
|
end
|
164
|
+
|
165
|
+
if __FILE__ == $0
|
166
|
+
require 'test/unit'
|
167
|
+
|
168
|
+
class TestRecursiveOpenStruct < Test::Unit::TestCase
|
169
|
+
def setup
|
170
|
+
@s = Recursive_Open_Struct.new
|
171
|
+
end
|
172
|
+
|
173
|
+
def setAndAssertValue(val)
|
174
|
+
@s.test = val
|
175
|
+
assert_equal(val, @s.test)
|
176
|
+
@s.close
|
177
|
+
assert_equal(val, @s.test)
|
178
|
+
@s.test = "asdf"
|
179
|
+
assert_equal("asdf", @s.test)
|
180
|
+
end
|
181
|
+
|
182
|
+
def testSetNil
|
183
|
+
setAndAssertValue(nil)
|
184
|
+
end
|
185
|
+
|
186
|
+
def testSimple
|
187
|
+
@s.test = "xx"
|
188
|
+
@s.close
|
189
|
+
assert_equal("xx", @s.test)
|
190
|
+
end
|
191
|
+
|
192
|
+
def testSetFalse
|
193
|
+
setAndAssertValue(false)
|
194
|
+
end
|
195
|
+
|
196
|
+
def testSetStr
|
197
|
+
setAndAssertValue("topfen")
|
198
|
+
end
|
199
|
+
|
200
|
+
def testSetClass
|
201
|
+
setAndAssertValue(String)
|
202
|
+
end
|
203
|
+
|
204
|
+
def testSetTrue
|
205
|
+
setAndAssertValue(true)
|
206
|
+
end
|
207
|
+
|
208
|
+
def testSet0
|
209
|
+
setAndAssertValue(0)
|
210
|
+
end
|
211
|
+
|
212
|
+
def testRaiseTypeError
|
213
|
+
@s.a.b = 1
|
214
|
+
assert_raise(TypeError) do
|
215
|
+
@s.a = 3
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def testAttrs
|
220
|
+
assert_equal([], @s.attrs)
|
221
|
+
@s.b = "x"
|
222
|
+
@s.a = "a"
|
223
|
+
assert_equal(["a", "b"], @s.attrs)
|
224
|
+
end
|
225
|
+
|
226
|
+
def testRecursive
|
227
|
+
@s.a.b = 1
|
228
|
+
@s.a.c = 2
|
229
|
+
assert_equal(["a"], @s.attrs)
|
230
|
+
end
|
231
|
+
|
232
|
+
def testStrange
|
233
|
+
@s.a
|
234
|
+
assert_equal(["a"], @s.attrs)
|
235
|
+
assert_equal(Recursive_Open_Struct, @s.a.class)
|
236
|
+
@s.a.x = "asfd"
|
237
|
+
assert_equal("asfd", @s.a.x)
|
238
|
+
end
|
239
|
+
|
240
|
+
def testKlammer
|
241
|
+
@s.a = "asdf"
|
242
|
+
assert_equal("asdf", @s["a"])
|
243
|
+
@s.b_x = "hog"
|
244
|
+
assert_equal("hog", @s["b_x"])
|
245
|
+
@s.c.b.a = 1234
|
246
|
+
assert_equal(1234, @s["c"]["b"]["a"])
|
247
|
+
end
|
248
|
+
|
249
|
+
def testDeep
|
250
|
+
@s.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z = false
|
251
|
+
@s.close
|
252
|
+
assert_raise(NoMethodError) do
|
253
|
+
@s.blub = "hellow"
|
254
|
+
end
|
255
|
+
assert_equal(false, @s.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: XDCC-Fetch
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: "1.
|
7
|
-
date: 2005-
|
6
|
+
version: "1.409"
|
7
|
+
date: 2005-02-13
|
8
8
|
summary: "XDCC-Fetch, written entirely in Ruby, is an intuitive, no-nonsense tool for
|
9
9
|
searching, collecting and downloading XDCC announcements within IRC channels.
|
10
10
|
XDCC-Fetch is released under the BSD license and available for free."
|
@@ -34,46 +34,46 @@ files:
|
|
34
34
|
- "./icons"
|
35
35
|
- "./icons/package.png"
|
36
36
|
- "./icons/camera_test.png"
|
37
|
+
- "./icons/cancel.png"
|
38
|
+
- "./icons/connect_no.png"
|
37
39
|
- "./icons/messagebox_warning.png"
|
38
|
-
- "./icons/
|
40
|
+
- "./icons/ark.png"
|
39
41
|
- "./icons/folder_inbox.png"
|
40
|
-
- "./icons/package_unknown.png"
|
41
42
|
- "./icons/messagebox_warning_small.png"
|
42
|
-
- "./icons/
|
43
|
-
- "./icons/ark.png"
|
44
|
-
- "./icons/messagebox_critical.png"
|
43
|
+
- "./icons/package_unknown.png"
|
45
44
|
- "./icons/exit.png"
|
46
|
-
- "./icons/cancel.png"
|
47
|
-
- "./icons/mega_ark.png"
|
48
|
-
- "./icons/fileclose.png"
|
49
45
|
- "./icons/messagebox_info.png"
|
50
|
-
- "./icons/
|
51
|
-
- "./icons/
|
46
|
+
- "./icons/package_favourite.png"
|
47
|
+
- "./icons/fileclose.png"
|
52
48
|
- "./icons/connect_creating.png"
|
49
|
+
- "./icons/messagebox_critical.png"
|
50
|
+
- "./icons/connect_established.png"
|
51
|
+
- "./icons/mega_ark.png"
|
52
|
+
- "./icons/idea.png"
|
53
|
+
- "./icons/locale.png"
|
54
|
+
- "./icons/connect_failed.png"
|
53
55
|
- "./icons/ark_big.png"
|
54
56
|
- "./icons/edit_add.png"
|
55
|
-
- "./icons/
|
56
|
-
- "./icons/connect_established.png"
|
57
|
+
- "./icons/edit_remove.png"
|
57
58
|
- "./src"
|
58
59
|
- "./src/Utilities"
|
59
|
-
- "./src/Utilities/PrettyException.rb"
|
60
60
|
- "./src/Utilities/Recursive_Open_Struct.rb"
|
61
61
|
- "./src/Utilities/Events.rb"
|
62
62
|
- "./src/Utilities/Configuration.rb"
|
63
63
|
- "./src/Utilities/Timer.rb"
|
64
64
|
- "./src/Utilities/Globals.rb"
|
65
65
|
- "./src/Network"
|
66
|
-
- "./src/Network/XDCC_Announcement.rb"
|
67
66
|
- "./src/Network/IRC_User.rb"
|
68
|
-
- "./src/Network/
|
67
|
+
- "./src/Network/XDCC_Announcement.rb"
|
69
68
|
- "./src/Network/CTCP_Handler.rb"
|
69
|
+
- "./src/Network/DCC_Parser.rb"
|
70
70
|
- "./src/Network/XDCC_Download_Handler.rb"
|
71
71
|
- "./src/Network/IRC_Server_Respond_Map.rb"
|
72
|
-
- "./src/Network/DCC_Parser.rb"
|
73
|
-
- "./src/Network/IRC_Server.rb"
|
74
72
|
- "./src/Network/IPAddr_Ext.rb"
|
75
|
-
- "./src/Network/
|
73
|
+
- "./src/Network/IRC_Server.rb"
|
76
74
|
- "./src/Network/IRC_Message.rb"
|
75
|
+
- "./src/Network/XDCC_Announcement_Storage.rb"
|
76
|
+
- "./src/Network/DCC_File.rb"
|
77
77
|
- "./src/Network/XDCC_Pack.rb"
|
78
78
|
- "./src/Network/XDCC_Parser.rb"
|
79
79
|
- "./src/Network/TCP_Connection.rb"
|
@@ -82,16 +82,17 @@ files:
|
|
82
82
|
- "./src/GUI/Talk_Back.rb"
|
83
83
|
- "./src/GUI/Application_Builder.rb"
|
84
84
|
- "./src/GUI/Main_Window.rb"
|
85
|
-
- "./src/GUI/About_Dialog.rb"
|
86
|
-
- "./src/GUI/Download_Finished_Box.rb"
|
87
85
|
- "./src/GUI/Context_Menu.rb"
|
88
86
|
- "./src/GUI/Dialog_Box.rb"
|
89
|
-
- "./src/GUI/Packet_Item.rb"
|
90
|
-
- "./src/GUI/Custom_Tabs.rb"
|
91
87
|
- "./src/GUI/Empty_Text_Field_Handler.rb"
|
92
88
|
- "./src/GUI/Speed_Widget.rb"
|
93
89
|
- "./src/GUI/Packet_List.rb"
|
94
90
|
- "./src/GUI/Gui_Logic.rb"
|
91
|
+
- "./src/GUI/Search_Engine.rb"
|
92
|
+
- "./src/GUI/Download_Finished_Box.rb"
|
93
|
+
- "./src/GUI/About_Dialog.rb"
|
94
|
+
- "./src/GUI/Packet_Item.rb"
|
95
|
+
- "./src/GUI/Custom_Tabs.rb"
|
95
96
|
- "./src/GUI/Icon_Loader.rb"
|
96
97
|
- "./src/Console"
|
97
98
|
- "./src/Console/XDCC_Pack_Match_Template.rb"
|
@@ -100,6 +101,7 @@ files:
|
|
100
101
|
- "./src/Translations"
|
101
102
|
- "./src/Translations/check_translations"
|
102
103
|
- "./src/Translations/README"
|
104
|
+
- "./src/Translations/pl.rb"
|
103
105
|
- "./src/Translations/de.rb"
|
104
106
|
- "./src/Translations/en.rb"
|
105
107
|
- "./XDCC-Fetch.rbw"
|
@@ -1,1091 +0,0 @@
|
|
1
|
-
###################################################
|
2
|
-
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
3
|
-
#
|
4
|
-
# = lib/PrettyException.rb
|
5
|
-
#
|
6
|
-
# PrettyPrint for Exceptions
|
7
|
-
#
|
8
|
-
# Author:: Dmitry V. Sabanin <sdmitry@lrn.ru>
|
9
|
-
# Revision:: $Date: 2004/12/20 05:11:50 $ $Revision: 1.16 $
|
10
|
-
# License:: LGPL
|
11
|
-
#
|
12
|
-
# Modified to use rdoc's templating by Michael Neumann (mneumann@ntecs.de).
|
13
|
-
#
|
14
|
-
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
15
|
-
###################################################
|
16
|
-
|
17
|
-
require 'rdoc/template'
|
18
|
-
require 'rbconfig'
|
19
|
-
require 'cgi'
|
20
|
-
|
21
|
-
###################################################
|
22
|
-
# Web PrettyPrint for Exceptions
|
23
|
-
#
|
24
|
-
class PrettyException
|
25
|
-
|
26
|
-
attr_writer :message
|
27
|
-
|
28
|
-
###################################################
|
29
|
-
def initialize(exception, tplpath = nil)
|
30
|
-
@tplpath = tplpath
|
31
|
-
@exception = exception
|
32
|
-
@message = nil
|
33
|
-
end
|
34
|
-
###################################################
|
35
|
-
|
36
|
-
###################################################
|
37
|
-
def build_template
|
38
|
-
tmpl =
|
39
|
-
if @tplpath and File.exists?(@tplpath)
|
40
|
-
File.read(@tplpath)
|
41
|
-
else
|
42
|
-
tplfile = File.readlines(__FILE__)
|
43
|
-
start = nil
|
44
|
-
tplfile.each_with_index do |line,idx|
|
45
|
-
if line =~ /^__END__\s*$/
|
46
|
-
start = idx + 1
|
47
|
-
break
|
48
|
-
end
|
49
|
-
end
|
50
|
-
tplfile[start..-1].join('')
|
51
|
-
end
|
52
|
-
|
53
|
-
return TemplatePage.new(tmpl)
|
54
|
-
end
|
55
|
-
###################################################
|
56
|
-
|
57
|
-
###################################################
|
58
|
-
def gen_source(file, line, mark_line = true)
|
59
|
-
source = File.readlines(file)
|
60
|
-
source = hilite_source(source)
|
61
|
-
line = line.to_i
|
62
|
-
if mark_line
|
63
|
-
show = 3
|
64
|
-
begin_from = line - show
|
65
|
-
end_at = line + show
|
66
|
-
begin_from = begin_from > 0 ? begin_from : 0
|
67
|
-
work_with = source[begin_from..end_at]
|
68
|
-
else
|
69
|
-
work_with = [ source[line-1] ]
|
70
|
-
end
|
71
|
-
work_with ||= []
|
72
|
-
res = []
|
73
|
-
work_with.each do |i, l|
|
74
|
-
l = ('<span class="hl_lineno">%.3d:</span>%s' % [i," "]) + l
|
75
|
-
if i == line and mark_line
|
76
|
-
buf = '<div class="current_line">' + l + '</div>'
|
77
|
-
else
|
78
|
-
buf = l + "\n"
|
79
|
-
end
|
80
|
-
res << buf
|
81
|
-
end
|
82
|
-
res.join
|
83
|
-
end
|
84
|
-
###################################################
|
85
|
-
|
86
|
-
###################################################
|
87
|
-
def hilite_source(src)
|
88
|
-
lx = LexerRuby::LexerOld.new
|
89
|
-
src.each do |sline|
|
90
|
-
lx.lex_line(sline)
|
91
|
-
end
|
92
|
-
res = []
|
93
|
-
lineno = 0
|
94
|
-
this_line = []
|
95
|
-
hc = false
|
96
|
-
heredoc_buf = nil
|
97
|
-
lx.result.each do |text, token|
|
98
|
-
if hc
|
99
|
-
lineno += 1
|
100
|
-
res << [lineno, this_line.join]
|
101
|
-
this_line = []
|
102
|
-
hc = false
|
103
|
-
end
|
104
|
-
if token != :heredoc and heredoc_buf and token != :any
|
105
|
-
heredoc = heredoc_buf.split(/\n/)
|
106
|
-
heredoc.each do |hd_line|
|
107
|
-
res << [lineno, ('<span class="hl_heredoc">%s</span>' % [hd_line.to_s])]
|
108
|
-
lineno += 1
|
109
|
-
end
|
110
|
-
this_line = []
|
111
|
-
heredoc_buf = nil
|
112
|
-
end
|
113
|
-
case token
|
114
|
-
when :keyword, :ident, :punct,
|
115
|
-
:comment, :ivar, :dot,
|
116
|
-
:string, :command, :number,
|
117
|
-
:gvar, :literal, :symbol
|
118
|
-
if token == :comment
|
119
|
-
hc = true
|
120
|
-
end
|
121
|
-
text = CGI.escapeHTML(text)
|
122
|
-
this_line << ('<span class="hl_%s">%s</span>' % [token.to_s, text.rstrip])
|
123
|
-
when :heredoc
|
124
|
-
text = CGI.escapeHTML(text)
|
125
|
-
heredoc_buf ||= ''
|
126
|
-
heredoc_buf << text
|
127
|
-
when :any
|
128
|
-
if text =~ /^\s*\n$/
|
129
|
-
lineno += 1
|
130
|
-
res << [lineno, this_line.join]
|
131
|
-
this_line = []
|
132
|
-
else
|
133
|
-
this_line << text
|
134
|
-
end
|
135
|
-
else
|
136
|
-
this_line << CGI.escapeHTML(text)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
res << [lineno+1, this_line.join] if this_line.length > 0
|
140
|
-
res
|
141
|
-
end
|
142
|
-
###################################################
|
143
|
-
|
144
|
-
###################################################
|
145
|
-
def print
|
146
|
-
tpl = build_template
|
147
|
-
contents = {}
|
148
|
-
contents['message'] = CGI::escapeHTML(@exception.message).to_s
|
149
|
-
contents['exception'] = @exception.class.to_s
|
150
|
-
contents['time'] = Time.now.to_s
|
151
|
-
bt = []
|
152
|
-
@exception.backtrace.each_with_index do |str,idx|
|
153
|
-
file,text = str.scan(/^(.+):(.+)(?::(.+))?$/).flatten;
|
154
|
-
file,line = file.split(/:/)
|
155
|
-
unless line
|
156
|
-
line = text
|
157
|
-
text = nil
|
158
|
-
end
|
159
|
-
is_stdlib = false
|
160
|
-
paths = [Config::CONFIG['rubylibdir'], Config::CONFIG['sitedir']]
|
161
|
-
paths.each do |dir|
|
162
|
-
is_stdlib = true if (file =~ /#{Regexp::escape(dir)}/)
|
163
|
-
end
|
164
|
-
data = { 'file' => file, 'line' => line, 'text' => text, 'from_stdlib' => is_stdlib }
|
165
|
-
if idx == 0
|
166
|
-
data['source'] = gen_source(file, line)
|
167
|
-
else
|
168
|
-
data['source'] = gen_source(file, line)
|
169
|
-
end
|
170
|
-
data['iteration_id'] = idx.to_s
|
171
|
-
bt << data
|
172
|
-
end
|
173
|
-
contents['backtrace'] = bt
|
174
|
-
tpl.write_html_on(output='', contents)
|
175
|
-
output
|
176
|
-
end
|
177
|
-
###################################################
|
178
|
-
|
179
|
-
alias_method :to_s, :print
|
180
|
-
|
181
|
-
end
|
182
|
-
|
183
|
-
class LexerBase
|
184
|
-
def initialize
|
185
|
-
@states = []
|
186
|
-
@result = []
|
187
|
-
@result_endofline = nil
|
188
|
-
end
|
189
|
-
attr_reader :states, :result, :result_endofline
|
190
|
-
|
191
|
-
def set_states(states)
|
192
|
-
@states = states
|
193
|
-
end
|
194
|
-
def set_result(result)
|
195
|
-
@result = result
|
196
|
-
end
|
197
|
-
def format(text, state_output)
|
198
|
-
@result << [text, state_output]
|
199
|
-
end
|
200
|
-
def format_end(state_output)
|
201
|
-
@result_endofline = state_output
|
202
|
-
end
|
203
|
-
def match(regexp, output)
|
204
|
-
m = regexp.match(@text)
|
205
|
-
return false unless m
|
206
|
-
txt = @text.slice!(0, m.end(0))
|
207
|
-
format(txt, output)
|
208
|
-
true
|
209
|
-
end
|
210
|
-
def lex_line(text)
|
211
|
-
raise "derived class #{self.class} must overload #lex_line."
|
212
|
-
end
|
213
|
-
def self.profile
|
214
|
-
lines = IO.readlines(__FILE__)
|
215
|
-
lexer = self.new
|
216
|
-
puts "profiling the #{self.inspect} lexer (this may take some time)"
|
217
|
-
require 'profiler'
|
218
|
-
Profiler__.start_profile
|
219
|
-
lines.each do |line|
|
220
|
-
lexer.set_states([])
|
221
|
-
lexer.set_result([])
|
222
|
-
lexer.lex_line(line)
|
223
|
-
end
|
224
|
-
Profiler__.print_profile(STDOUT)
|
225
|
-
end
|
226
|
-
def self.benchmark
|
227
|
-
n = 10000
|
228
|
-
puts "benchmarking the lexers (computing #{n} lines " +
|
229
|
-
"with GC disabled)"
|
230
|
-
require 'benchmark'
|
231
|
-
Benchmark.bm(20) do |b|
|
232
|
-
lexer = LexerRuby::LexerOld.new
|
233
|
-
#=begin
|
234
|
-
lines = IO.readlines(__FILE__)
|
235
|
-
GC.disable
|
236
|
-
b.report("#{lexer.class}") do
|
237
|
-
n.times do |i|
|
238
|
-
lexer.set_states([])
|
239
|
-
lexer.set_result([])
|
240
|
-
lexer.lex_line(lines[i%lines.size].clone)
|
241
|
-
end
|
242
|
-
end
|
243
|
-
#=begin
|
244
|
-
GC.enable
|
245
|
-
GC.start
|
246
|
-
lines = IO.readlines(__FILE__)
|
247
|
-
lexer = LexerRuby::LexerNew.new
|
248
|
-
GC.disable
|
249
|
-
b.report("#{lexer.class}") do
|
250
|
-
n.times do |i|
|
251
|
-
lexer.set_states([])
|
252
|
-
lexer.set_result([])
|
253
|
-
lexer.lex_line(lines[i%lines.size])
|
254
|
-
end
|
255
|
-
end
|
256
|
-
=begin
|
257
|
-
=end
|
258
|
-
GC.enable
|
259
|
-
GC.start
|
260
|
-
lines = IO.readlines(__FILE__)
|
261
|
-
lexer = LexerRuby::Lexer3.new
|
262
|
-
GC.disable
|
263
|
-
b.report("#{lexer.class}") do
|
264
|
-
n.times do |i|
|
265
|
-
lexer.set_states([])
|
266
|
-
lexer.set_result([])
|
267
|
-
lexer.lex_line(lines[i%lines.size])
|
268
|
-
end
|
269
|
-
end
|
270
|
-
GC.enable
|
271
|
-
end
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
module LexerText
|
276
|
-
|
277
|
-
class Lexer < LexerBase
|
278
|
-
RE_TAB = /\A\t+/
|
279
|
-
RE_NOTTAB = /\A[^\t]+/
|
280
|
-
def lex_line(text)
|
281
|
-
@text = text
|
282
|
-
until @text.empty?
|
283
|
-
if match(RE_TAB, :tab)
|
284
|
-
else
|
285
|
-
match(RE_NOTTAB, :text)
|
286
|
-
end
|
287
|
-
end
|
288
|
-
end
|
289
|
-
end # class Lexer
|
290
|
-
|
291
|
-
end # module LexerText
|
292
|
-
|
293
|
-
module LexerRuby
|
294
|
-
|
295
|
-
module State
|
296
|
-
|
297
|
-
class Base
|
298
|
-
end
|
299
|
-
|
300
|
-
class Heredoc < Base
|
301
|
-
def initialize(begin_tag, ignore_leading_spaces, interpolate=true)
|
302
|
-
@begin_tag = begin_tag
|
303
|
-
@ignore_leading_spaces = ignore_leading_spaces
|
304
|
-
@interpolate = interpolate
|
305
|
-
end
|
306
|
-
attr_reader :begin_tag, :ignore_leading_spaces, :interpolate
|
307
|
-
def ==(other)
|
308
|
-
(self.class == other.class) and
|
309
|
-
(@begin_tag == other.begin_tag) and
|
310
|
-
(@ignore_leading_spaces == other.ignore_leading_spaces) and
|
311
|
-
(@interpolate == other.interpolate)
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
class Comment < Base
|
316
|
-
def ==(other)
|
317
|
-
(self.class == other.class)
|
318
|
-
end
|
319
|
-
end
|
320
|
-
|
321
|
-
class Endoffile < Base
|
322
|
-
def ==(other)
|
323
|
-
(self.class == other.class)
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
end # module State
|
328
|
-
|
329
|
-
class NewerRubyLexer
|
330
|
-
RE_TOKENIZE = Regexp.new([
|
331
|
-
# TODO: @ivar
|
332
|
-
# TODO: @@cvar
|
333
|
-
# TODO: $gvar
|
334
|
-
# TODO: %literals %w(a b c) %Q|'"|
|
335
|
-
# TODO: :symbol
|
336
|
-
# TODO: ?x chars
|
337
|
-
# TODO: 0b01001 binary data
|
338
|
-
# TODO: 0x234af hex data
|
339
|
-
# TODO: keywords =begin defined?
|
340
|
-
# TODO: ruby puncturation .. ... && ||
|
341
|
-
# TODO: /regexp/
|
342
|
-
# TODO: __END__ tag
|
343
|
-
# TODO: illegal ruby heredoc which has space after end-tag
|
344
|
-
# TODO: illegal ruby puncturation &&& |||
|
345
|
-
# TODO: illegal ruby numbers 0x23Yab
|
346
|
-
'#.*', # # blah ## !?! comment
|
347
|
-
'<<-?[[:alpha:]]+', # <<HTML <<-XML heredoc
|
348
|
-
'<<-?\'[[:alpha:]]+\'', # <<'eof' <<-'X' heredoc single quoted
|
349
|
-
'<<-?"[[:alpha:]]+"', # <<"eof" <<-"X" heredoc double quoted
|
350
|
-
'\.[[:alpha:]][[:alnum:]_]*', # .method .dup2 method call
|
351
|
-
'[[:alpha:]][[:alnum:]_]*', # value pix2_3 identifier
|
352
|
-
'\'(?:[^\\\\]|\\\\.)*?\'', # '\'x\\' '' string single quoted
|
353
|
-
'"(?:[^\\\\]|\\\\.)*?"', # "ab" "" string double quoted
|
354
|
-
'\d+\.\d+', # 0.123 32.10 number as float
|
355
|
-
'\d+', # 42 999 number as integer
|
356
|
-
'.' # * + fallthrough
|
357
|
-
].join('|'))
|
358
|
-
def initialize
|
359
|
-
@char_to_symbol_hash = {
|
360
|
-
" " => :space,
|
361
|
-
"\t" => :tab
|
362
|
-
}
|
363
|
-
@char_to_symbol_hash.default = :should_not_happen
|
364
|
-
end
|
365
|
-
def tokenize(string)
|
366
|
-
string.scan(RE_TOKENIZE)
|
367
|
-
end
|
368
|
-
def lex(string)
|
369
|
-
tokens = tokenize(string)
|
370
|
-
states = tokens.map do |token|
|
371
|
-
# TODO: by inserting 2 parentesises in the TOKENIZER
|
372
|
-
# then I can destinguish between good/bad tokens.
|
373
|
-
# 'ab'.scan(/(a)(b)/) do |(good, bad)|
|
374
|
-
case token
|
375
|
-
when /\A(?:"|')./
|
376
|
-
:string
|
377
|
-
when /\A<<-?(?:"|'|)[[:alpha:]]/
|
378
|
-
:heredoc
|
379
|
-
when /\A\.[[:alpha:]]/
|
380
|
-
:method
|
381
|
-
when /\A[[:alpha:]]/
|
382
|
-
:ident
|
383
|
-
when /\A#/
|
384
|
-
:comment
|
385
|
-
when /\A[[:punct:]]/
|
386
|
-
:punct
|
387
|
-
when /\A[[:digit:]]/
|
388
|
-
:number
|
389
|
-
else
|
390
|
-
@char_to_symbol_hash[token]
|
391
|
-
end
|
392
|
-
end
|
393
|
-
[tokens, states]
|
394
|
-
end
|
395
|
-
end
|
396
|
-
|
397
|
-
class Lexer3 < LexerBase
|
398
|
-
def initialize
|
399
|
-
@rl = NewerRubyLexer.new
|
400
|
-
super
|
401
|
-
end
|
402
|
-
def what_to_output(hash)
|
403
|
-
@rl.what_to_output(hash)
|
404
|
-
end
|
405
|
-
def lex_line(text)
|
406
|
-
tokens, states = @rl.lex(text)
|
407
|
-
tokens.each_with_index do |token, index|
|
408
|
-
format(token, states[index])
|
409
|
-
end
|
410
|
-
true
|
411
|
-
end
|
412
|
-
end
|
413
|
-
|
414
|
-
|
415
|
-
class RubyLexer
|
416
|
-
def initialize
|
417
|
-
@result = []
|
418
|
-
end
|
419
|
-
attr_reader :result
|
420
|
-
PUNCT = ['(', ')'] +
|
421
|
-
%w(=== == =~ => = != !~ !) +
|
422
|
-
%w(<< <=> <= < >= >) +
|
423
|
-
%w({ } [ ]) +
|
424
|
-
%w(:: : ... ..) +
|
425
|
-
%w(+= + -= - ** * / %) +
|
426
|
-
%w(|| | && &) +
|
427
|
-
%w(, ;)
|
428
|
-
RE_NUMBER = /\d[\d\.]*/
|
429
|
-
RE_PUNCT = Regexp.new('(?:' +
|
430
|
-
PUNCT.map{|i| Regexp.escape(i)}.join('|') + ')')
|
431
|
-
RE_IDENT = /[[:alpha:]][\w\!]*/
|
432
|
-
RE_STRING = /".*?"|'.*?'/
|
433
|
-
RE_COMMENT = /#.*/
|
434
|
-
RE_COMMAND = /\.[[:alnum:]_]*[[:alnum:]_\?\!]/
|
435
|
-
RE_SPACE = /\x20+/
|
436
|
-
RE_NEWLINE = /\n+/
|
437
|
-
RE_TABS = /\t+/
|
438
|
-
def scan_index(string, regexp, prio, symbol)
|
439
|
-
string.scan(regexp) do
|
440
|
-
@result << [$~.begin(0), prio, $~.end(0), symbol]
|
441
|
-
end
|
442
|
-
end
|
443
|
-
def lex(string)
|
444
|
-
scan_index(string, RE_COMMENT, 0, :comment)
|
445
|
-
scan_index(string, RE_STRING, 1, :string)
|
446
|
-
scan_index(string, RE_COMMAND, 2, :command)
|
447
|
-
scan_index(string, RE_IDENT, 3, :ident)
|
448
|
-
scan_index(string, RE_NUMBER, 4, :number)
|
449
|
-
scan_index(string, RE_PUNCT, 5, :punct)
|
450
|
-
scan_index(string, RE_SPACE, 6, :space)
|
451
|
-
scan_index(string, RE_NEWLINE, 7, :newline)
|
452
|
-
scan_index(string, RE_TABS, 8, :tabs)
|
453
|
-
end
|
454
|
-
def result
|
455
|
-
# primary key = string begin position
|
456
|
-
# secondary key = priority
|
457
|
-
@result.sort!
|
458
|
-
# collect the highest precedens data
|
459
|
-
ary = [0]
|
460
|
-
@result.each do |i1, prio, i2, symbol|
|
461
|
-
next if ary.last > i1 # discard low precedens data
|
462
|
-
if ary.last < i1
|
463
|
-
ary << :any
|
464
|
-
ary << i1
|
465
|
-
end
|
466
|
-
ary << symbol
|
467
|
-
ary << i2
|
468
|
-
end
|
469
|
-
#p ary
|
470
|
-
ary
|
471
|
-
end
|
472
|
-
def self.mk(*symbols)
|
473
|
-
symbols.each do |symbol|
|
474
|
-
class_eval %{
|
475
|
-
def #{symbol.to_s}
|
476
|
-
res = []
|
477
|
-
@result.each do |i1, prio, i2, sym|
|
478
|
-
if sym == :#{symbol.to_s}
|
479
|
-
res << [i1, i2]
|
480
|
-
end
|
481
|
-
end
|
482
|
-
res
|
483
|
-
end
|
484
|
-
}
|
485
|
-
end
|
486
|
-
end
|
487
|
-
mk :number, :ident, :string, :punct
|
488
|
-
mk :comment, :command, :space, :newline, :tabs
|
489
|
-
end
|
490
|
-
|
491
|
-
class LexerNew < LexerBase
|
492
|
-
def lex_line(text)
|
493
|
-
#puts("-"*40)
|
494
|
-
#puts "text=#{text.inspect}"
|
495
|
-
rl = RubyLexer.new
|
496
|
-
rl.lex(text)
|
497
|
-
res = rl.result
|
498
|
-
#puts "res=#{res.inspect}"
|
499
|
-
i1 = res.shift
|
500
|
-
while res.size > 1
|
501
|
-
symbol = res.shift
|
502
|
-
i2 = res.shift
|
503
|
-
format(text[i1, (i2-i1)], symbol)
|
504
|
-
i1 = i2
|
505
|
-
end
|
506
|
-
#puts "result=#{@result.inspect}"
|
507
|
-
true
|
508
|
-
end
|
509
|
-
end
|
510
|
-
|
511
|
-
class LexerOld < LexerBase
|
512
|
-
RE_COMMENT = /^#.*/m
|
513
|
-
|
514
|
-
RE_TAB = /^\t+/
|
515
|
-
|
516
|
-
RE_SPACE = /^\x20+/
|
517
|
-
|
518
|
-
KEYWORDS = %w(alias and begin BEGIN break case class) +
|
519
|
-
%w(defined? def do else elsif end END ensure for if loop) +
|
520
|
-
%w(module next nil not or raise redo require rescue) +
|
521
|
-
%w(retry return self super then true false undef) +
|
522
|
-
%w(unless until yield when while)
|
523
|
-
RE_KEYWORD = Regexp.new(
|
524
|
-
'\A(?:' +
|
525
|
-
KEYWORDS.map{|txt|Regexp.escape(txt)}.join('|') +
|
526
|
-
')(?!\w)'
|
527
|
-
)
|
528
|
-
|
529
|
-
RE_SYMBOL = /\A:[[:alpha:]_][[:alnum:]_]*/
|
530
|
-
|
531
|
-
RE_STRING = /^("|\')(?:[^\\]|\\.)*?\1/
|
532
|
-
RE_STRING_INTERPOL = /\A ((?:[^\\]|\\.)*?) (\#\{ .*? \}) /x
|
533
|
-
# TODO: interpolated code can nest (hint: recursion is necessary)
|
534
|
-
def match_string
|
535
|
-
m = RE_STRING.match(@text)
|
536
|
-
return false unless m
|
537
|
-
txt = @text.slice!(0, m.end(0))
|
538
|
-
if m[1] == '\''
|
539
|
-
format(txt, :string)
|
540
|
-
return true
|
541
|
-
end
|
542
|
-
# double quoted strings may contain interpolated code
|
543
|
-
until txt.empty?
|
544
|
-
m = RE_STRING_INTERPOL.match(txt)
|
545
|
-
unless m
|
546
|
-
format(txt, :string)
|
547
|
-
break
|
548
|
-
end
|
549
|
-
format(m[1], :string) unless m[1].empty?
|
550
|
-
format(m[2], :string1)
|
551
|
-
txt.slice!(0, m.end(0))
|
552
|
-
end
|
553
|
-
true
|
554
|
-
end
|
555
|
-
|
556
|
-
RE_REGEXP = /\A\/(.*?[^\\])?\//
|
557
|
-
|
558
|
-
RE_IVAR = /\A@[[:alnum:]_]+/
|
559
|
-
|
560
|
-
RE_DOT = /\A\.[[:alnum:]_]*[[:alnum:]_\?\!]/
|
561
|
-
|
562
|
-
RE_IDENTIFIER = /\A(?:[[:alnum:]_]+|\S+)/
|
563
|
-
|
564
|
-
RE_NUMBER = Regexp.new(
|
565
|
-
'\A(?:' + [
|
566
|
-
'0x[_a-fA-F0-9]+',
|
567
|
-
'0b[_01]+',
|
568
|
-
'\d[0-9_]*(?:\.[0-9_]*)?',
|
569
|
-
'\?.'
|
570
|
-
].join('|') +
|
571
|
-
')'
|
572
|
-
)
|
573
|
-
|
574
|
-
PUNCT = ['(', ')'] +
|
575
|
-
%w(=== == =~ => = != !~ !) +
|
576
|
-
%w(<< <=> <= < >= >) +
|
577
|
-
%w({ } [ ]) +
|
578
|
-
%w(:: : ... ..) +
|
579
|
-
%w(+= + -= - ** * / %) +
|
580
|
-
%w(|| | && &) +
|
581
|
-
%w(, ;)
|
582
|
-
RE_PUNCT = Regexp.new(
|
583
|
-
'\A(?:' +
|
584
|
-
PUNCT.map{|txt|Regexp.escape(txt)}.join('|') +
|
585
|
-
')'
|
586
|
-
)
|
587
|
-
|
588
|
-
VAR_GLOBALS = %q(_~*$!@/\\;,.=:<>"-&`'+1234567890).split(//)
|
589
|
-
RE_GVAR = Regexp.new(
|
590
|
-
'\A\$(?:' +
|
591
|
-
VAR_GLOBALS.map{|txt|Regexp.escape(txt)}.join('|') +
|
592
|
-
'|[[:alnum:]_]+' +
|
593
|
-
')'
|
594
|
-
)
|
595
|
-
|
596
|
-
# TODO: deal with multiline literals
|
597
|
-
RE_LITERAL = Regexp.new(
|
598
|
-
'\A%[Qqwrx]?(?:' + [
|
599
|
-
'\(.*?\)', # TODO: must count pairs
|
600
|
-
'\{.*?\}', # TODO: must count pairs
|
601
|
-
'\<.*?\>', # TODO: must count pairs
|
602
|
-
'\[.*?\]', # TODO: must count pairs
|
603
|
-
'([^\(\{\<\[]).*?\1'
|
604
|
-
].join('|') + ')'
|
605
|
-
)
|
606
|
-
|
607
|
-
RE_BEGIN = /\A=begin$\n?\x20*\z/ # eat tailing space
|
608
|
-
def match_comment_begin
|
609
|
-
m = RE_BEGIN.match(@text)
|
610
|
-
return false unless m
|
611
|
-
#puts "comments"
|
612
|
-
@states << State::Comment.new
|
613
|
-
txt = @text.slice!(0, m.end(0))
|
614
|
-
format(txt, :mcomment)
|
615
|
-
format_end(:mcomment_end)
|
616
|
-
true
|
617
|
-
end
|
618
|
-
RE_HEREDOC = /\A<<(-)?('|"|)(\w+)\2/
|
619
|
-
def match_heredoc_begin
|
620
|
-
m = RE_HEREDOC.match(@text)
|
621
|
-
return false unless m
|
622
|
-
ignore_leading_space = (m[1] != nil)
|
623
|
-
interpolate = (m[2] != "'")
|
624
|
-
begin_pattern = m[3]
|
625
|
-
@states << State::Heredoc.new(
|
626
|
-
begin_pattern,
|
627
|
-
ignore_leading_space,
|
628
|
-
interpolate
|
629
|
-
)
|
630
|
-
txt = @text.slice!(0, m.end(0))
|
631
|
-
format(txt, :heredoc)
|
632
|
-
true
|
633
|
-
end
|
634
|
-
RE_END = /\A__END__$\n?\x20*\z/ # eat tailing space
|
635
|
-
def match_endoffile
|
636
|
-
m = RE_END.match(@text)
|
637
|
-
return false unless m
|
638
|
-
#puts "propagate __END__"
|
639
|
-
@states << State::Endoffile.new
|
640
|
-
txt = @text.slice!(0, m.end(0))
|
641
|
-
format(txt, :endoffile)
|
642
|
-
format_end(:endoffile_end)
|
643
|
-
true
|
644
|
-
end
|
645
|
-
def lex_line_normal(text)
|
646
|
-
@text = text
|
647
|
-
return if match_comment_begin
|
648
|
-
return if match_endoffile
|
649
|
-
until @text.empty?
|
650
|
-
if match(RE_COMMENT, :comment)
|
651
|
-
format_end(:comment_end)
|
652
|
-
elsif match(RE_REGEXP, :regexp)
|
653
|
-
elsif match_heredoc_begin
|
654
|
-
elsif match(RE_LITERAL, :literal)
|
655
|
-
elsif match(RE_KEYWORD, :keyword)
|
656
|
-
elsif match(RE_SYMBOL, :symbol)
|
657
|
-
elsif match(RE_PUNCT, :punct)
|
658
|
-
elsif match(RE_GVAR, :gvar)
|
659
|
-
elsif match_string
|
660
|
-
elsif match(RE_NUMBER, :number)
|
661
|
-
elsif match(RE_IVAR, :ivar)
|
662
|
-
elsif match(RE_DOT, :dot)
|
663
|
-
elsif match(RE_IDENTIFIER, :ident)
|
664
|
-
elsif match(RE_TAB, :tab)
|
665
|
-
elsif match(RE_SPACE, :space)
|
666
|
-
else
|
667
|
-
#@text.slice!(0, 1)
|
668
|
-
txt = @text.slice!(0, 1)
|
669
|
-
format(txt, :any)
|
670
|
-
end
|
671
|
-
end
|
672
|
-
end
|
673
|
-
def match_heredoc_end(regexp)
|
674
|
-
m = regexp.match(@text)
|
675
|
-
return false unless m
|
676
|
-
#puts "end of heredoc"
|
677
|
-
@states.shift
|
678
|
-
txt = @text.slice!(0, m.end(0))
|
679
|
-
format(txt, :heredoc)
|
680
|
-
format_end(:heredoc_end2)
|
681
|
-
true
|
682
|
-
end
|
683
|
-
def lex_line_heredoc(text)
|
684
|
-
# TODO: color interpolated code #{code}
|
685
|
-
@text = text
|
686
|
-
format_end(:heredoc_end)
|
687
|
-
hd_end = nil
|
688
|
-
state = @states[0]
|
689
|
-
hd_end = /\A#{state.begin_tag}$\n?\x20*\z/
|
690
|
-
return if match_heredoc_end(hd_end)
|
691
|
-
# continue lexing
|
692
|
-
ign_lead_spc = state.ignore_leading_spaces
|
693
|
-
until @text.empty?
|
694
|
-
if match(RE_TAB, :heredoc_tab)
|
695
|
-
elsif ign_lead_spc and match_heredoc_end(hd_end)
|
696
|
-
else
|
697
|
-
txt = @text.slice!(0, 1)
|
698
|
-
format(txt, :heredoc)
|
699
|
-
end
|
700
|
-
end
|
701
|
-
end
|
702
|
-
def match_comment_end
|
703
|
-
m = /\A\=end\b.*?$\n?\x20*\z/.match(@text)
|
704
|
-
return false unless m
|
705
|
-
#puts "comment end"
|
706
|
-
@states.shift
|
707
|
-
txt = @text.slice!(0, m.end(0))
|
708
|
-
format(txt, :mcomment)
|
709
|
-
true
|
710
|
-
end
|
711
|
-
def lex_line_comment(text)
|
712
|
-
@text = text
|
713
|
-
format_end(:mcomment_end)
|
714
|
-
return if match_comment_end
|
715
|
-
until @text.empty?
|
716
|
-
if match(RE_TAB, :mcomment_tab)
|
717
|
-
else
|
718
|
-
txt = @text.slice!(0, 1)
|
719
|
-
format(txt, :mcomment)
|
720
|
-
end
|
721
|
-
end
|
722
|
-
end
|
723
|
-
def lex_line_endoffile(text)
|
724
|
-
@text = text
|
725
|
-
format_end(:endoffile_end)
|
726
|
-
until @text.empty?
|
727
|
-
if match(RE_TAB, :endoffile_tab)
|
728
|
-
else
|
729
|
-
txt = @text.slice!(0, 1)
|
730
|
-
format(txt, :endoffile)
|
731
|
-
end
|
732
|
-
end
|
733
|
-
end
|
734
|
-
def lex_line(text)
|
735
|
-
if @states.empty?
|
736
|
-
return lex_line_normal(text)
|
737
|
-
end
|
738
|
-
state = @states[0]
|
739
|
-
case state
|
740
|
-
when State::Heredoc: lex_line_heredoc(text)
|
741
|
-
when State::Comment: lex_line_comment(text)
|
742
|
-
when State::Endoffile: lex_line_endoffile(text)
|
743
|
-
else
|
744
|
-
raise "unknown state #{state.class}"
|
745
|
-
end
|
746
|
-
end
|
747
|
-
end
|
748
|
-
|
749
|
-
# TODO: make the new lexer work!
|
750
|
-
Lexer = LexerOld # slow
|
751
|
-
#Lexer = LexerNew # slow
|
752
|
-
#Lexer = Lexer3 # fastest
|
753
|
-
|
754
|
-
end # module LexerRuby
|
755
|
-
|
756
|
-
__END__
|
757
|
-
<html>
|
758
|
-
<head>
|
759
|
-
<title>Oops!</title>
|
760
|
-
<style>
|
761
|
-
.data {
|
762
|
-
border-style: dotted;
|
763
|
-
padding: 4px; }
|
764
|
-
.trace_header {
|
765
|
-
border-style: dotted;
|
766
|
-
border-width: thin;
|
767
|
-
background-color: #CCCCCC;
|
768
|
-
text-align: center; }
|
769
|
-
.normal_trace_entry {
|
770
|
-
border-style: dotted;
|
771
|
-
border-width: thin;
|
772
|
-
text-align: center;
|
773
|
-
padding: 6px; }
|
774
|
-
.stdlib_trace_entry {
|
775
|
-
border-style: dotted;
|
776
|
-
border-width: thin;
|
777
|
-
text-align: right;
|
778
|
-
padding: 6px; }
|
779
|
-
.source {
|
780
|
-
width: 100%;
|
781
|
-
background-color: #F4F4F4;
|
782
|
-
display: none;
|
783
|
-
}
|
784
|
-
span.hl_lineno {
|
785
|
-
font-weight: bold;
|
786
|
-
}
|
787
|
-
pre {
|
788
|
-
width: 80%;
|
789
|
-
padding: 0px;
|
790
|
-
margin: 0px;
|
791
|
-
}
|
792
|
-
div.current_line {
|
793
|
-
color: red;
|
794
|
-
background-color: #F4DADA;
|
795
|
-
}
|
796
|
-
span.hl_keyword {
|
797
|
-
font-weight: bold;
|
798
|
-
}
|
799
|
-
span.hl_punct {
|
800
|
-
font-weight: bold;
|
801
|
-
color: darkblue;
|
802
|
-
}
|
803
|
-
span.hl_ident {
|
804
|
-
}
|
805
|
-
span.hl_command {
|
806
|
-
font-weight: bold;
|
807
|
-
}
|
808
|
-
span.hl_number {
|
809
|
-
color: darkgreen;
|
810
|
-
}
|
811
|
-
span.hl_string {
|
812
|
-
color: darkgreen;
|
813
|
-
}
|
814
|
-
span.hl_comment {
|
815
|
-
color: grey;
|
816
|
-
}
|
817
|
-
span.hl_ivar {
|
818
|
-
font-weight: bold;
|
819
|
-
color: darkred;
|
820
|
-
}
|
821
|
-
span.hl_dot {
|
822
|
-
font-weight: bold;
|
823
|
-
}
|
824
|
-
span.hl_literal {
|
825
|
-
color: green;
|
826
|
-
}
|
827
|
-
span.hl_gvar {
|
828
|
-
font-weight: bold;
|
829
|
-
}
|
830
|
-
span.hl_symbol {
|
831
|
-
color: blue;
|
832
|
-
}
|
833
|
-
span.hl_regexp {
|
834
|
-
color: green;
|
835
|
-
}
|
836
|
-
tr {
|
837
|
-
background-color: white;
|
838
|
-
}
|
839
|
-
</style>
|
840
|
-
<script type="text/javascript" language="javascript">
|
841
|
-
function toggleCode( id ) {
|
842
|
-
if ( document.getElementById )
|
843
|
-
elem = document.getElementById( id );
|
844
|
-
else if ( document.all )
|
845
|
-
elem = eval( "document.all." + id );
|
846
|
-
else
|
847
|
-
return false;
|
848
|
-
|
849
|
-
elemStyle = elem.style;
|
850
|
-
|
851
|
-
if ( elemStyle.display != "block" ) {
|
852
|
-
elemStyle.display = "block"
|
853
|
-
} else {
|
854
|
-
elemStyle.display = "none"
|
855
|
-
}
|
856
|
-
|
857
|
-
return true;
|
858
|
-
}
|
859
|
-
|
860
|
-
var isDOM = (typeof(document.getElementsByTagName) != 'undefined'
|
861
|
-
&& typeof(document.createElement) != 'undefined')
|
862
|
-
? 1 : 0;
|
863
|
-
var isIE4 = (typeof(document.all) != 'undefined'
|
864
|
-
&& parseInt(navigator.appVersion) >= 4)
|
865
|
-
? 1 : 0;
|
866
|
-
var isNS4 = (typeof(document.layers) != 'undefined')
|
867
|
-
? 1 : 0;
|
868
|
-
var capable = (isDOM || isIE4 || isNS4)
|
869
|
-
? 1 : 0;
|
870
|
-
// Uggly fix for Opera and Konqueror 2.2 that are half DOM compliant
|
871
|
-
if (capable) {
|
872
|
-
if (typeof(window.opera) != 'undefined') {
|
873
|
-
var browserName = ' ' + navigator.userAgent.toLowerCase();
|
874
|
-
if ((browserName.indexOf('konqueror 7') == 0)) {
|
875
|
-
capable = 0;
|
876
|
-
}
|
877
|
-
} else if (typeof(navigator.userAgent) != 'undefined') {
|
878
|
-
var browserName = ' ' + navigator.userAgent.toLowerCase();
|
879
|
-
if ((browserName.indexOf('konqueror') > 0) && (browserName.indexOf('konqueror/3') == 0)) {
|
880
|
-
capable = 0;
|
881
|
-
}
|
882
|
-
} // end if... else if...
|
883
|
-
} // end if
|
884
|
-
|
885
|
-
/**
|
886
|
-
* This array is used to remember mark status of rows in browse mode
|
887
|
-
*/
|
888
|
-
var marked_row = new Array;
|
889
|
-
|
890
|
-
|
891
|
-
/**
|
892
|
-
* Sets/unsets the pointer and marker in browse mode
|
893
|
-
*
|
894
|
-
* @param object the table row
|
895
|
-
* @param integer the row number
|
896
|
-
* @param string the action calling this script (over, out or click)
|
897
|
-
* @param string the default background color
|
898
|
-
* @param string the color to use for mouseover
|
899
|
-
* @param string the color to use for marking a row
|
900
|
-
*
|
901
|
-
* @return boolean whether pointer is set or not
|
902
|
-
*/
|
903
|
-
function setPointer(theRow, theRowNum, theAction, theDefaultColor, thePointerColor, theMarkColor)
|
904
|
-
{
|
905
|
-
var theCells = null;
|
906
|
-
|
907
|
-
// 1. Pointer and mark feature are disabled or the browser can't get the
|
908
|
-
// row -> exits
|
909
|
-
if ((thePointerColor == '' && theMarkColor == '')
|
910
|
-
|| typeof(theRow.style) == 'undefined') {
|
911
|
-
return false;
|
912
|
-
}
|
913
|
-
|
914
|
-
// 2. Gets the current row and exits if the browser can't get it
|
915
|
-
if (typeof(document.getElementsByTagName) != 'undefined') {
|
916
|
-
theCells = theRow.getElementsByTagName('td');
|
917
|
-
}
|
918
|
-
else if (typeof(theRow.cells) != 'undefined') {
|
919
|
-
theCells = theRow.cells;
|
920
|
-
}
|
921
|
-
else {
|
922
|
-
return false;
|
923
|
-
}
|
924
|
-
|
925
|
-
// 3. Gets the current color...
|
926
|
-
var rowCellsCnt = theCells.length;
|
927
|
-
var domDetect = null;
|
928
|
-
var currentColor = null;
|
929
|
-
var newColor = null;
|
930
|
-
// 3.1 ... with DOM compatible browsers except Opera that does not return
|
931
|
-
// valid values with "getAttribute"
|
932
|
-
if (typeof(window.opera) == 'undefined'
|
933
|
-
&& typeof(theCells[0].getAttribute) != 'undefined') {
|
934
|
-
currentColor = theCells[0].getAttribute('bgcolor');
|
935
|
-
domDetect = true;
|
936
|
-
}
|
937
|
-
// 3.2 ... with other browsers
|
938
|
-
else {
|
939
|
-
currentColor = theCells[0].style.backgroundColor;
|
940
|
-
domDetect = false;
|
941
|
-
} // end 3
|
942
|
-
|
943
|
-
// 3.3 ... Opera changes colors set via HTML to rgb(r,g,b) format so fix it
|
944
|
-
if (currentColor.indexOf("rgb") >= 0)
|
945
|
-
{
|
946
|
-
var rgbStr = currentColor.slice(currentColor.indexOf('(') + 1,
|
947
|
-
currentColor.indexOf(')'));
|
948
|
-
var rgbValues = rgbStr.split(",");
|
949
|
-
currentColor = "#";
|
950
|
-
var hexChars = "0123456789ABCDEF";
|
951
|
-
for (var i = 0; i < 3; i++)
|
952
|
-
{
|
953
|
-
var v = rgbValues[i].valueOf();
|
954
|
-
currentColor += hexChars.charAt(v/16) + hexChars.charAt(v%16);
|
955
|
-
}
|
956
|
-
}
|
957
|
-
|
958
|
-
// 4. Defines the new color
|
959
|
-
// 4.1 Current color is the default one
|
960
|
-
if (currentColor == ''
|
961
|
-
|| currentColor.toLowerCase() == theDefaultColor.toLowerCase()) {
|
962
|
-
if (theAction == 'over' && thePointerColor != '') {
|
963
|
-
newColor = thePointerColor;
|
964
|
-
}
|
965
|
-
else if (theAction == 'click' && theMarkColor != '') {
|
966
|
-
newColor = theMarkColor;
|
967
|
-
marked_row[theRowNum] = true;
|
968
|
-
// Garvin: deactivated onclick marking of the checkbox because it's also executed
|
969
|
-
// when an action (like edit/delete) on a single item is performed. Then the checkbox
|
970
|
-
// would get deactived, even though we need it activated. Maybe there is a way
|
971
|
-
// to detect if the row was clicked, and not an item therein...
|
972
|
-
// document.getElementById('id_rows_to_delete' + theRowNum).checked = true;
|
973
|
-
}
|
974
|
-
}
|
975
|
-
// 4.1.2 Current color is the pointer one
|
976
|
-
else if (currentColor.toLowerCase() == thePointerColor.toLowerCase()
|
977
|
-
&& (typeof(marked_row[theRowNum]) == 'undefined' || !marked_row[theRowNum])) {
|
978
|
-
if (theAction == 'out') {
|
979
|
-
newColor = theDefaultColor;
|
980
|
-
}
|
981
|
-
else if (theAction == 'click' && theMarkColor != '') {
|
982
|
-
newColor = theMarkColor;
|
983
|
-
marked_row[theRowNum] = true;
|
984
|
-
// document.getElementById('id_rows_to_delete' + theRowNum).checked = true;
|
985
|
-
}
|
986
|
-
}
|
987
|
-
// 4.1.3 Current color is the marker one
|
988
|
-
else if (currentColor.toLowerCase() == theMarkColor.toLowerCase()) {
|
989
|
-
if (theAction == 'click') {
|
990
|
-
newColor = (thePointerColor != '')
|
991
|
-
? thePointerColor
|
992
|
-
: theDefaultColor;
|
993
|
-
marked_row[theRowNum] = (typeof(marked_row[theRowNum]) == 'undefined' || !marked_row[theRowNum])
|
994
|
-
? true
|
995
|
-
: null;
|
996
|
-
// document.getElementById('id_rows_to_delete' + theRowNum).checked = false;
|
997
|
-
}
|
998
|
-
} // end 4
|
999
|
-
|
1000
|
-
// 5. Sets the new color...
|
1001
|
-
if (newColor) {
|
1002
|
-
var c = null;
|
1003
|
-
// 5.1 ... with DOM compatible browsers except Opera
|
1004
|
-
if (domDetect) {
|
1005
|
-
for (c = 0; c < rowCellsCnt; c++) {
|
1006
|
-
theCells[c].setAttribute('bgcolor', newColor, 0);
|
1007
|
-
} // end for
|
1008
|
-
}
|
1009
|
-
// 5.2 ... with other browsers
|
1010
|
-
else {
|
1011
|
-
for (c = 0; c < rowCellsCnt; c++) {
|
1012
|
-
theCells[c].style.backgroundColor = newColor;
|
1013
|
-
}
|
1014
|
-
}
|
1015
|
-
} // end 5
|
1016
|
-
|
1017
|
-
return true;
|
1018
|
-
} // end of the 'setPointer()' function
|
1019
|
-
|
1020
|
-
|
1021
|
-
/**
|
1022
|
-
* getElement
|
1023
|
-
*/
|
1024
|
-
function getElement(e,f){
|
1025
|
-
if(document.layers){
|
1026
|
-
f=(f)?f:self;
|
1027
|
-
if(f.document.layers[e]) {
|
1028
|
-
return f.document.layers[e];
|
1029
|
-
}
|
1030
|
-
for(W=0;i<f.document.layers.length;W++) {
|
1031
|
-
return(getElement(e,fdocument.layers[W]));
|
1032
|
-
}
|
1033
|
-
}
|
1034
|
-
if(document.all) {
|
1035
|
-
return document.all[e];
|
1036
|
-
}
|
1037
|
-
return document.getElementById(e);
|
1038
|
-
}
|
1039
|
-
</script>
|
1040
|
-
</head>
|
1041
|
-
<body bgcolor="white">
|
1042
|
-
<table cellspacing="4" width ="80%" align="center">
|
1043
|
-
<tr>
|
1044
|
-
<td colspan="3" align="left" class="normal_trace_entry">
|
1045
|
-
<p class="data" style="font-size: large; margin: 0px; border-color: red;"><b>Exception raised!</b><br />
|
1046
|
-
<b>%exception%</b>: <b>%message%</b><br />
|
1047
|
-
Time: <b>%time%</b>
|
1048
|
-
</p>
|
1049
|
-
</td>
|
1050
|
-
</tr>
|
1051
|
-
<tr>
|
1052
|
-
<td class="trace_header"><b>File</b></td>
|
1053
|
-
<td class="trace_header"><b>Line</b></td>
|
1054
|
-
<td class="trace_header"><b>Info</b></td>
|
1055
|
-
</tr>
|
1056
|
-
START:backtrace
|
1057
|
-
IFNOT:from_stdlib
|
1058
|
-
<tr bgcolor="white" onmouseover="setPointer(this, %iteration_id%, 'over', 'white', '#CCFFCC', '#FFB2B2');" onmouseout="setPointer(this, %iteration_id%, 'out', 'white', '#CCFFCC', '#FFB2B2');" onmousedown="toggleCode('src%iteration_id%'); setPointer(this, %iteration_id%, 'click', 'white', '#CCFFCC', '#FFB2B2');">
|
1059
|
-
IF:text
|
1060
|
-
<td bgcolor="white" class="normal_trace_entry">%file%</td>
|
1061
|
-
<td bgcolor="white" class="normal_trace_entry">%line%</td>
|
1062
|
-
<td bgcolor="white" class="normal_trace_entry">%text%</td>
|
1063
|
-
ENDIF:text
|
1064
|
-
IFNOT:text
|
1065
|
-
<td bgcolor="white" class="normal_trace_entry">%file%</td>
|
1066
|
-
<td bgcolor="white" class="normal_trace_entry" colspan="2">%line%</td>
|
1067
|
-
ENDIF:text
|
1068
|
-
IF:source
|
1069
|
-
</tr>
|
1070
|
-
<tr>
|
1071
|
-
<td colspan="3"><div id="src%iteration_id%" class="source"><tt><pre>%source%</pre></tt></div></td>
|
1072
|
-
ENDIF:source
|
1073
|
-
ENDIF:from_stdlib
|
1074
|
-
IF:from_stdlib
|
1075
|
-
<tr bgcolor="white" onmouseover="setPointer(this, %iteration_id%, 'over', 'white', '#CCFFCC', '#FFB2B2');" onmouseout="setPointer(this, %iteration_id%, 'out', 'white', '#CCFFCC', '#FFB2B2');" onmousedown="setPointer(this, %iteration_id%, 'click', 'white', '#CCFFCC', '#FFB2B2');">
|
1076
|
-
IF:text
|
1077
|
-
<td bgcolor="white" class="stdlib_trace_entry">%file%</td>
|
1078
|
-
<td bgcolor="white" class="stdlib_trace_entry">%line%</td>
|
1079
|
-
<td bgcolor="white" class="stdlib_trace_entry">%text%</td>
|
1080
|
-
ENDIF:text
|
1081
|
-
IFNOT:text
|
1082
|
-
<td bgcolor="white" class="stdlib_trace_entry">%file%</td>
|
1083
|
-
<td bgcolor="white" class="stdlib_trace_entry" colspan="2">%line%</td>
|
1084
|
-
ENDIF:text
|
1085
|
-
ENDIF:from_stdlib
|
1086
|
-
</tr>
|
1087
|
-
|
1088
|
-
END:backtrace
|
1089
|
-
</table>
|
1090
|
-
</body>
|
1091
|
-
</html>
|