sdl4r 0.9.9 → 0.9.11
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +70 -1
- data/LICENSE +499 -497
- data/Rakefile +38 -28
- data/TODO +194 -45
- data/doc/classes/SDL4R/AbbreviationTimezoneProxy.html +151 -0
- data/doc/classes/SDL4R/ConstantTimezone.html +129 -0
- data/doc/classes/SDL4R/Element.html +148 -0
- data/doc/classes/SDL4R/Reader.html +683 -0
- data/doc/classes/SDL4R/RelativeTimezone.html +187 -0
- data/doc/classes/SDL4R/SdlBinary.html +188 -0
- data/doc/classes/SDL4R/SdlParseError.html +110 -0
- data/doc/classes/SDL4R/SdlTimeSpan.html +651 -0
- data/doc/classes/SDL4R/Serializer.html +557 -0
- data/doc/classes/SDL4R/Serializer/Ref.html +138 -0
- data/doc/classes/SDL4R/TZAbbreviationDB/Record.html +117 -0
- data/doc/classes/SDL4R/Tag.html +1274 -0
- data/doc/classes/SDL4R/Token.html +131 -0
- data/doc/created.rid +1 -0
- data/doc/files/CHANGELOG.html +290 -0
- data/doc/files/LICENSE.html +53 -0
- data/doc/files/README.html +405 -0
- data/doc/files/lib/sdl4r/abbreviation_timezone_proxy_rb.html +63 -0
- data/doc/files/lib/sdl4r/constant_timezone_rb.html +54 -0
- data/doc/files/lib/sdl4r/element_rb.html +54 -0
- data/doc/files/lib/sdl4r/reader_rb.html +68 -0
- data/doc/files/lib/sdl4r/relative_timezone_rb.html +62 -0
- data/doc/files/lib/sdl4r/sdl4r_rb.html +66 -0
- data/doc/files/lib/sdl4r/sdl4r_tzinfo_rb.html +64 -0
- data/doc/files/lib/sdl4r/sdl4r_version_rb.html +54 -0
- data/doc/files/lib/sdl4r/sdl_binary_rb.html +54 -0
- data/doc/files/lib/sdl4r/sdl_parse_error_rb.html +54 -0
- data/doc/files/lib/sdl4r/sdl_time_span_rb.html +54 -0
- data/doc/files/lib/sdl4r/serializer_rb.html +62 -0
- data/doc/files/lib/sdl4r/tag_rb.html +66 -0
- data/doc/files/lib/sdl4r/token_rb.html +54 -0
- data/doc/files/lib/sdl4r/tokenizer_rb.html +64 -0
- data/doc/files/lib/sdl4r/tz_abbreviation_db_rb.html +67 -0
- data/doc/files/lib/sdl4r_rb.html +54 -0
- data/doc/files/lib/sdl4r_tzinfo_rb.html +54 -0
- data/doc/fr_class_index.html +23 -0
- data/doc/fr_file_index.html +40 -0
- data/doc/fr_method_index.html +4711 -0
- data/doc/index.html +15 -0
- data/doc/rdoc-style.css +328 -0
- data/lib/sdl4r.rb +3 -1
- data/lib/sdl4r/abbreviation_timezone_proxy.rb +38 -0
- data/lib/sdl4r/constant_timezone.rb +58 -0
- data/{test/sdl4r/sdl_test.rb → lib/sdl4r/element.rb} +19 -14
- data/lib/sdl4r/reader.rb +772 -0
- data/lib/sdl4r/relative_timezone.rb +156 -0
- data/lib/sdl4r/sdl4r.rb +187 -45
- data/lib/sdl4r/sdl4r_tzinfo.rb +75 -0
- data/lib/sdl4r/sdl4r_version.rb +24 -0
- data/lib/sdl4r/sdl_parse_error.rb +1 -1
- data/lib/sdl4r/sdl_time_span.rb +5 -1
- data/lib/sdl4r/serializer.rb +473 -60
- data/lib/sdl4r/tag.rb +126 -51
- data/lib/sdl4r/token.rb +49 -0
- data/lib/sdl4r/tokenizer.rb +431 -0
- data/lib/sdl4r/tz_abbreviation_db.rb +266 -0
- data/read_jprof.html +16934 -0
- data/read_jprof_pull.html +14451 -0
- data/read_prof.html +4983 -0
- data/read_prof_pull.html +4896 -0
- data/test/sdl4r/parser_test.rb +577 -503
- data/test/sdl4r/read_jprof.rb +58 -0
- data/test/sdl4r/read_prof.rb +70 -0
- data/test/sdl4r/reader_test.rb +173 -0
- data/test/sdl4r/relative_timezone_test.rb +102 -0
- data/test/sdl4r/sdl4r_test.rb +611 -528
- data/test/sdl4r/sdl4r_tzinfo_test.rb +108 -0
- data/test/sdl4r/sdl_test_case.rb +60 -0
- data/test/sdl4r/serializer_test.rb +448 -11
- data/test/sdl4r/tag_test.rb +84 -5
- data/test/sdl4r/tokenizer_test.rb +128 -0
- metadata +69 -11
- data/lib/sdl4r/parser.rb +0 -648
- data/lib/sdl4r/parser/reader.rb +0 -184
- data/lib/sdl4r/parser/time_span_with_zone.rb +0 -57
- data/lib/sdl4r/parser/token.rb +0 -138
- data/lib/sdl4r/parser/tokenizer.rb +0 -507
data/lib/sdl4r/parser/reader.rb
DELETED
@@ -1,184 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby -w
|
2
|
-
# encoding: UTF-8
|
3
|
-
|
4
|
-
#--
|
5
|
-
# Simple Declarative Language (SDL) for Ruby
|
6
|
-
# Copyright 2005 Ikayzo, inc.
|
7
|
-
#
|
8
|
-
# This program is free software. You can distribute or modify it under the
|
9
|
-
# terms of the GNU Lesser General Public License version 2.1 as published by
|
10
|
-
# the Free Software Foundation.
|
11
|
-
#
|
12
|
-
# This program is distributed AS IS and WITHOUT WARRANTY. OF ANY KIND,
|
13
|
-
# INCLUDING MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
14
|
-
# See the GNU Lesser General Public License for more details.
|
15
|
-
#
|
16
|
-
# You should have received a copy of the GNU Lesser General Public License
|
17
|
-
# along with this program; if not, contact the Free Software Foundation, Inc.,
|
18
|
-
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
19
|
-
#++
|
20
|
-
|
21
|
-
module SDL4R
|
22
|
-
|
23
|
-
class Parser
|
24
|
-
|
25
|
-
# Gives access to the characters read to the Parser.
|
26
|
-
# This class was designed to gather the handling of the UTF-8 issues in one place and shield the
|
27
|
-
# Parser class from these problems.
|
28
|
-
class Reader # :nodoc: all
|
29
|
-
|
30
|
-
# +io+ an open IO from which the characters are read.
|
31
|
-
def initialize(io)
|
32
|
-
raise ArgumentError, "io == nil" unless io
|
33
|
-
|
34
|
-
@io = io
|
35
|
-
@line = nil
|
36
|
-
@line_chars = nil
|
37
|
-
@line_no = -1
|
38
|
-
@pos = 0
|
39
|
-
end
|
40
|
-
|
41
|
-
# The current line no (zero-based)
|
42
|
-
attr_reader :line_no
|
43
|
-
|
44
|
-
# The position of the char currently pointed by this reader in the current line.
|
45
|
-
# This is not necessarily the position of the char you just got from a method, it might be
|
46
|
-
# the position of the next char, for instance.
|
47
|
-
attr_reader :pos
|
48
|
-
|
49
|
-
attr_reader :line
|
50
|
-
|
51
|
-
def line_length
|
52
|
-
return @line_chars.nil? ? 0 : @line_chars.length
|
53
|
-
end
|
54
|
-
|
55
|
-
# Reads next line in stream skipping comment lines and blank lines.
|
56
|
-
#
|
57
|
-
# Returns the next line or nil at the end of the file.
|
58
|
-
def read_line
|
59
|
-
@line_chars = nil
|
60
|
-
|
61
|
-
while @line = read_raw_line()
|
62
|
-
# Skip empty and commented lines
|
63
|
-
break unless @line.empty? or @line =~ /^#/
|
64
|
-
end
|
65
|
-
|
66
|
-
return @line
|
67
|
-
end
|
68
|
-
|
69
|
-
# Returns the string that goes from the current position of this Reader to the end of the line
|
70
|
-
# or nil if the current position doesn't allow that.
|
71
|
-
def rest_of_line
|
72
|
-
return @line[@pos..-1]
|
73
|
-
end
|
74
|
-
|
75
|
-
# Indicates whether the end of file has been reached.
|
76
|
-
def eof?
|
77
|
-
return @line.nil?
|
78
|
-
end
|
79
|
-
|
80
|
-
# Indicates whether there are more characters in the current line after the current char
|
81
|
-
def more_chars_in_line?
|
82
|
-
return @pos < line_length - 1
|
83
|
-
end
|
84
|
-
|
85
|
-
# Returns whether the end of the current +line+ as been reached.
|
86
|
-
def eol?
|
87
|
-
return @pos >= line_length
|
88
|
-
end
|
89
|
-
|
90
|
-
# Skips the current line by going just after its end.
|
91
|
-
def skip_to_eol
|
92
|
-
@pos = line_length
|
93
|
-
end
|
94
|
-
|
95
|
-
# Skips the whitespaces that follow the current position.
|
96
|
-
def skip_whitespaces
|
97
|
-
while (@pos + 1) < line_length and
|
98
|
-
(@line[@pos + 1] == ?\s or @line[@pos + 1] == ?\t)
|
99
|
-
@pos += 1
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
# Returns the character at position +pos+ in the current line.
|
104
|
-
# Returns nil if there is no current line or if +pos+ is after the end of the line.
|
105
|
-
def get_line_char(pos)
|
106
|
-
if @line_chars and pos < line_length
|
107
|
-
return @line_chars[pos]
|
108
|
-
else
|
109
|
-
return nil
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
# Returns the character at the current position or nil after end-of-line or end-of -file.
|
114
|
-
def current_char
|
115
|
-
return get_line_char(@pos)
|
116
|
-
end
|
117
|
-
|
118
|
-
# Go to the next character in the line. This method doesn't skip to the next line once the
|
119
|
-
# reached eol.
|
120
|
-
def skip_char
|
121
|
-
@pos += 1 if @pos < line_length
|
122
|
-
end
|
123
|
-
|
124
|
-
# Returns the current char and go to the next.
|
125
|
-
# Returns nil if end-of-line or -file has been reached.
|
126
|
-
def read_char
|
127
|
-
c = current_char
|
128
|
-
skip_char()
|
129
|
-
c
|
130
|
-
end
|
131
|
-
|
132
|
-
# Returns to the previous char if possible.
|
133
|
-
def previous_char
|
134
|
-
@pos -= 1 if @pos >= 1
|
135
|
-
end
|
136
|
-
|
137
|
-
# Returns the next index of the expression (string, regexp, fixnum) in the current line,
|
138
|
-
# starting from after the current position if no position is specified.
|
139
|
-
def find_next_in_line(searched, start_pos = nil)
|
140
|
-
start_pos = @pos + 1 unless start_pos
|
141
|
-
return @line.index(searched, start_pos)
|
142
|
-
end
|
143
|
-
|
144
|
-
# Skips the specified position in the current line.
|
145
|
-
def skip_to(new_pos)
|
146
|
-
@pos = new_pos
|
147
|
-
end
|
148
|
-
|
149
|
-
|
150
|
-
# Returns a subpart of the current line starting from +from+ and stopping at +to+ (excluded).
|
151
|
-
def substring(from, to = -1)
|
152
|
-
return @line[from..to]
|
153
|
-
end
|
154
|
-
|
155
|
-
# Reads and returns a "raw" line including lines with comments and blank lines.
|
156
|
-
#
|
157
|
-
# Returns the next line or nil if at the end of the file.
|
158
|
-
#
|
159
|
-
# This method changes the value of @line, @lineNo and @pos.
|
160
|
-
def read_raw_line
|
161
|
-
@line = @io.gets()
|
162
|
-
|
163
|
-
# Remove a possible \r at the end of line
|
164
|
-
@line.gsub!(/\r+$/, "") if @line
|
165
|
-
|
166
|
-
@pos = 0;
|
167
|
-
@line_chars = nil
|
168
|
-
if @line
|
169
|
-
@line_no += 1
|
170
|
-
@line_chars = @line.scan(/./m)
|
171
|
-
end
|
172
|
-
return @line
|
173
|
-
end
|
174
|
-
|
175
|
-
# Closes this Reader and its underlying +IO+.
|
176
|
-
def close
|
177
|
-
@io.close
|
178
|
-
end
|
179
|
-
|
180
|
-
end
|
181
|
-
|
182
|
-
end
|
183
|
-
|
184
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby -w
|
2
|
-
# encoding: UTF-8
|
3
|
-
|
4
|
-
#--
|
5
|
-
# Simple Declarative Language (SDL) for Ruby
|
6
|
-
# Copyright 2005 Ikayzo, inc.
|
7
|
-
#
|
8
|
-
# This program is free software. You can distribute or modify it under the
|
9
|
-
# terms of the GNU Lesser General Public License version 2.1 as published by
|
10
|
-
# the Free Software Foundation.
|
11
|
-
#
|
12
|
-
# This program is distributed AS IS and WITHOUT WARRANTY. OF ANY KIND,
|
13
|
-
# INCLUDING MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
14
|
-
# See the GNU Lesser General Public License for more details.
|
15
|
-
#
|
16
|
-
# You should have received a copy of the GNU Lesser General Public License
|
17
|
-
# along with this program; if not, contact the Free Software Foundation, Inc.,
|
18
|
-
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
19
|
-
#++
|
20
|
-
|
21
|
-
module SDL4R
|
22
|
-
|
23
|
-
class Parser
|
24
|
-
|
25
|
-
# An intermediate object used to store a timeSpan or the time
|
26
|
-
# component of a date/time instance. The types are disambiguated at a later stage.
|
27
|
-
#
|
28
|
-
# +seconds+ can have a fraction
|
29
|
-
# +time_zone_offset+ is a fraction of a day (equal to nil if not specified)
|
30
|
-
class TimeSpanWithZone # :nodoc: all
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
SECONDS_IN_DAY = 24 * 60 * 60
|
35
|
-
|
36
|
-
public
|
37
|
-
|
38
|
-
def initialize(day, hour, minute, second, time_zone_offset)
|
39
|
-
@day = day
|
40
|
-
@hour = hour
|
41
|
-
@min = minute
|
42
|
-
@sec = second
|
43
|
-
@time_zone_offset = time_zone_offset
|
44
|
-
end
|
45
|
-
|
46
|
-
attr_reader :day, :hour, :min, :sec, :time_zone_offset
|
47
|
-
|
48
|
-
# Returns the UTC offset as a fraction of a day on the current machine
|
49
|
-
def TimeSpanWithZone.default_time_zone_offset
|
50
|
-
return Rational(Time.now.utc_offset, SECONDS_IN_DAY)
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
end
|
56
|
-
|
57
|
-
end
|
data/lib/sdl4r/parser/token.rb
DELETED
@@ -1,138 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby -w
|
2
|
-
# encoding: UTF-8
|
3
|
-
|
4
|
-
#--
|
5
|
-
# Simple Declarative Language (SDL) for Ruby
|
6
|
-
# Copyright 2005 Ikayzo, inc.
|
7
|
-
#
|
8
|
-
# This program is free software. You can distribute or modify it under the
|
9
|
-
# terms of the GNU Lesser General Public License version 2.1 as published by
|
10
|
-
# the Free Software Foundation.
|
11
|
-
#
|
12
|
-
# This program is distributed AS IS and WITHOUT WARRANTY. OF ANY KIND,
|
13
|
-
# INCLUDING MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
14
|
-
# See the GNU Lesser General Public License for more details.
|
15
|
-
#
|
16
|
-
# You should have received a copy of the GNU Lesser General Public License
|
17
|
-
# along with this program; if not, contact the Free Software Foundation, Inc.,
|
18
|
-
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
19
|
-
#++
|
20
|
-
|
21
|
-
module SDL4R
|
22
|
-
|
23
|
-
require File.dirname(__FILE__) + '/time_span_with_zone'
|
24
|
-
|
25
|
-
class Parser
|
26
|
-
|
27
|
-
# An SDL token.
|
28
|
-
#
|
29
|
-
# @author Daniel Leuck, Philippe Vosges
|
30
|
-
#
|
31
|
-
class Token # :nodoc: all
|
32
|
-
|
33
|
-
def initialize(text, line = -1, position = -1)
|
34
|
-
@text = text
|
35
|
-
@line = line
|
36
|
-
@position = position
|
37
|
-
@size = text.length
|
38
|
-
|
39
|
-
begin
|
40
|
-
@type = nil
|
41
|
-
@object = nil
|
42
|
-
|
43
|
-
if text =~ /^["`]/
|
44
|
-
@type = :STRING
|
45
|
-
@object = Parser.parse_string(text)
|
46
|
-
|
47
|
-
elsif text =~ /^'/
|
48
|
-
@type = :CHARACTER
|
49
|
-
@object = text[1...-1]
|
50
|
-
|
51
|
-
elsif text == "null"
|
52
|
-
@type = :NULL
|
53
|
-
@object = nil
|
54
|
-
|
55
|
-
elsif text =~ /^true$|^on$/
|
56
|
-
@type = :BOOLEAN
|
57
|
-
@object = true
|
58
|
-
|
59
|
-
elsif text =~ /^false$|^off$/
|
60
|
-
@type = :BOOLEAN
|
61
|
-
@object = false
|
62
|
-
|
63
|
-
elsif text =~ /^\[/
|
64
|
-
@type=:BINARY
|
65
|
-
@object = Parser.parse_binary(text)
|
66
|
-
|
67
|
-
elsif text =~ /^-?\d+\/\d+\/\d+$/
|
68
|
-
@type = :DATE;
|
69
|
-
@object = Parser.parse_date_time(text)
|
70
|
-
|
71
|
-
elsif text =~ /^-?\d+d?:\d+/
|
72
|
-
@type = :TIME
|
73
|
-
@object = parse_time_span_with_zone(text)
|
74
|
-
|
75
|
-
elsif text =~ /^[\d\-\.]/
|
76
|
-
@type = :NUMBER
|
77
|
-
@object = Parser.parse_number(text)
|
78
|
-
|
79
|
-
else
|
80
|
-
case text[0]
|
81
|
-
when ?{
|
82
|
-
@type = :START_BLOCK
|
83
|
-
when ?}
|
84
|
-
@type = :END_BLOCK
|
85
|
-
when ?=
|
86
|
-
@type = :EQUALS
|
87
|
-
when ?:
|
88
|
-
@type = :COLON
|
89
|
-
when ?;
|
90
|
-
@type = :SEMICOLON
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
rescue ArgumentError
|
95
|
-
raise SdlParseError.new($!.message, @line, @position)
|
96
|
-
end
|
97
|
-
|
98
|
-
@type = :IDENTIFIER if @type.nil? # if all hope is lost, it's an identifier
|
99
|
-
|
100
|
-
@punctuation =
|
101
|
-
@type == :COLON || @type == :SEMICOLON || @type == :EQUALS ||
|
102
|
-
@type == :START_BLOCK || @type == :END_BLOCK
|
103
|
-
@literal = @type != :IDENTIFIER && !@punctuation
|
104
|
-
end
|
105
|
-
|
106
|
-
attr_reader :text, :type, :line, :position
|
107
|
-
|
108
|
-
def literal?
|
109
|
-
@literal
|
110
|
-
end
|
111
|
-
|
112
|
-
# Returns the Ruby object corresponding to this literal (or nil if it is
|
113
|
-
# not a literal).
|
114
|
-
def object_for_literal
|
115
|
-
return @object
|
116
|
-
end
|
117
|
-
|
118
|
-
def to_s
|
119
|
-
@type.to_s + " " + @text + " pos:" + @position.to_s
|
120
|
-
end
|
121
|
-
|
122
|
-
# This special parse method is used only by the Token class for
|
123
|
-
# tokens which are ambiguously either a TimeSpan or the time component
|
124
|
-
# of a date/time type
|
125
|
-
def parse_time_span_with_zone(literal)
|
126
|
-
raise ArgumentError("time span or date literal is nil") if literal.nil?
|
127
|
-
|
128
|
-
days, hours, minutes, seconds, time_zone_offset =
|
129
|
-
Parser.parse_time_span_and_time_zone(literal, true, true)
|
130
|
-
|
131
|
-
return Parser::TimeSpanWithZone.new(days, hours, minutes, seconds, time_zone_offset)
|
132
|
-
end
|
133
|
-
|
134
|
-
end
|
135
|
-
|
136
|
-
end
|
137
|
-
|
138
|
-
end
|
@@ -1,507 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby -w
|
2
|
-
# encoding: UTF-8
|
3
|
-
|
4
|
-
#--
|
5
|
-
# Simple Declarative Language (SDL) for Ruby
|
6
|
-
# Copyright 2005 Ikayzo, inc.
|
7
|
-
#
|
8
|
-
# This program is free software. You can distribute or modify it under the
|
9
|
-
# terms of the GNU Lesser General Public License version 2.1 as published by
|
10
|
-
# the Free Software Foundation.
|
11
|
-
#
|
12
|
-
# This program is distributed AS IS and WITHOUT WARRANTY. OF ANY KIND,
|
13
|
-
# INCLUDING MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
14
|
-
# See the GNU Lesser General Public License for more details.
|
15
|
-
#
|
16
|
-
# You should have received a copy of the GNU Lesser General Public License
|
17
|
-
# along with this program; if not, contact the Free Software Foundation, Inc.,
|
18
|
-
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
19
|
-
#++
|
20
|
-
|
21
|
-
|
22
|
-
module SDL4R
|
23
|
-
|
24
|
-
require File.dirname(__FILE__) + '/reader'
|
25
|
-
require File.dirname(__FILE__) + '/token'
|
26
|
-
|
27
|
-
class Parser
|
28
|
-
|
29
|
-
# Tokenizer of the SDL parser
|
30
|
-
class Tokenizer # :nodoc: all
|
31
|
-
|
32
|
-
TOKEN_TYPES = [
|
33
|
-
:IDENTIFIER,
|
34
|
-
|
35
|
-
# punctuation
|
36
|
-
:COLON, :SEMICOLON, :EQUALS, :START_BLOCK, :END_BLOCK,
|
37
|
-
|
38
|
-
# literals
|
39
|
-
:STRING, :CHARACTER, :BOOLEAN, :NUMBER, :DATE, :TIME, :BINARY, :NULL ]
|
40
|
-
|
41
|
-
|
42
|
-
# Creates an SDL tokenizer on the specified +IO+.
|
43
|
-
def initialize(io)
|
44
|
-
raise ArgumentError, "io == nil" if io.nil?
|
45
|
-
|
46
|
-
@reader = Parser::Reader.new(io)
|
47
|
-
@token_start = 0
|
48
|
-
@starting_escaped_quote_line = false
|
49
|
-
@tokens = nil
|
50
|
-
@token_text = nil
|
51
|
-
end
|
52
|
-
|
53
|
-
# Closes this Tokenizer and its underlying +Reader+.
|
54
|
-
def close
|
55
|
-
@reader.close
|
56
|
-
end
|
57
|
-
|
58
|
-
# Raises a SdlParseError.
|
59
|
-
def parse_error(description, line_no = nil, position = nil)
|
60
|
-
line_no = @reader.line_no unless line_no
|
61
|
-
# @reader.pos points to the *next* position (thence the "pos - 1")
|
62
|
-
position = @reader.pos - 1 unless position and position >= 0
|
63
|
-
|
64
|
-
# We add one because editors typically start with line 1 rather than 0...
|
65
|
-
raise SdlParseError.new(description, line_no + 1, position + 1, @reader.line)
|
66
|
-
end
|
67
|
-
|
68
|
-
# Close the reader and throw a SdlParseError using the format
|
69
|
-
# Was expecting X but got Y.
|
70
|
-
#
|
71
|
-
def expecting_but_got(expecting, got, line, position)
|
72
|
-
parse_error("Was expecting #{expecting} but got #{got}", line, position)
|
73
|
-
end
|
74
|
-
|
75
|
-
# Returns the next line as tokens or nil if the end of the stream has been reached.
|
76
|
-
# This method handles line continuations both within and outside String literals.
|
77
|
-
# The line of tokens is assigned to @tokens.
|
78
|
-
#
|
79
|
-
# Returns a logical line as a list of Tokens.
|
80
|
-
#
|
81
|
-
def read_line_tokens
|
82
|
-
begin
|
83
|
-
read_line_tokens_even_if_empty()
|
84
|
-
end until @tokens.nil? or !@tokens.empty?
|
85
|
-
return @tokens
|
86
|
-
end
|
87
|
-
|
88
|
-
def line_no
|
89
|
-
@reader.line_no
|
90
|
-
end
|
91
|
-
|
92
|
-
def pos
|
93
|
-
@reader.pos
|
94
|
-
end
|
95
|
-
|
96
|
-
def line
|
97
|
-
@reader.line
|
98
|
-
end
|
99
|
-
|
100
|
-
private
|
101
|
-
|
102
|
-
# Returns the next line as tokens or nil if the end of the stream has been reached.
|
103
|
-
# This method handles line continuations both within and outside String literals.
|
104
|
-
# The line of tokens is assigned to @tokens.
|
105
|
-
#
|
106
|
-
# Returns a logical line as a list of Tokens.
|
107
|
-
# Returns an empty array if the line was empty.
|
108
|
-
# Returns +nil+ if the end of the stream has been reached.
|
109
|
-
#
|
110
|
-
def read_line_tokens_even_if_empty
|
111
|
-
# Reset of the token-related fields
|
112
|
-
@tokens = nil
|
113
|
-
@token_text = nil
|
114
|
-
@token_start = nil
|
115
|
-
|
116
|
-
if @reader.eol? and not @reader.read_line()
|
117
|
-
return nil
|
118
|
-
end
|
119
|
-
|
120
|
-
@tokens = []
|
121
|
-
@token_start = @reader.pos
|
122
|
-
|
123
|
-
until @reader.eol?
|
124
|
-
if @token_text
|
125
|
-
@tokens << Token.new(@token_text, @reader.line_no, @token_start)
|
126
|
-
@token_text = nil
|
127
|
-
@token_start = @reader.pos
|
128
|
-
end
|
129
|
-
|
130
|
-
c = @reader.current_char
|
131
|
-
next_c = @reader.get_line_char(@reader.pos + 1)
|
132
|
-
|
133
|
-
case c
|
134
|
-
when "\""
|
135
|
-
# handle "" style strings including line continuations
|
136
|
-
handle_double_quote_string()
|
137
|
-
|
138
|
-
when "'"
|
139
|
-
handle_character_literal()
|
140
|
-
|
141
|
-
when "{", "}", "=", ":", ";"
|
142
|
-
# handle punctuation
|
143
|
-
punctuation_token = Token.new(c, @reader.line_no, @reader.pos)
|
144
|
-
@token_text = nil
|
145
|
-
|
146
|
-
if punctuation_token.type == :SEMICOLON
|
147
|
-
@reader.skip_char()
|
148
|
-
break
|
149
|
-
else
|
150
|
-
@tokens << punctuation_token
|
151
|
-
end
|
152
|
-
|
153
|
-
when "#"
|
154
|
-
# skip : hash comment at end of line
|
155
|
-
@reader.skip_to_eol()
|
156
|
-
|
157
|
-
when "/"
|
158
|
-
# handle // and /**/ style comments
|
159
|
-
if next_c == "/"
|
160
|
-
# skip : // comment
|
161
|
-
@reader.skip_to_eol()
|
162
|
-
else
|
163
|
-
handle_slash_comment()
|
164
|
-
end
|
165
|
-
|
166
|
-
when "`"
|
167
|
-
# handle multiline `` style strings
|
168
|
-
handle_back_quote_string()
|
169
|
-
|
170
|
-
when "["
|
171
|
-
# handle binary literals
|
172
|
-
handle_binary_literal()
|
173
|
-
|
174
|
-
when "\s", "\t"
|
175
|
-
@reader.skip_whitespaces()
|
176
|
-
|
177
|
-
when "\\"
|
178
|
-
# line continuations (outside a string literal)
|
179
|
-
handle_line_continuation()
|
180
|
-
next # otherwise 1st char of the continuation line is skipped by skip_char()
|
181
|
-
|
182
|
-
when /^[0-9\-\.]$/
|
183
|
-
if c == "-" and next_c == "-"
|
184
|
-
# -- comments : ignore
|
185
|
-
@reader.skip_to_eol()
|
186
|
-
else
|
187
|
-
# handle numbers, dates, and time spans
|
188
|
-
handle_number_date_or_time_span()
|
189
|
-
end
|
190
|
-
|
191
|
-
when /^[a-zA-Z\$_]$/
|
192
|
-
# FIXME Here, the Java code specifies isJavaIdentifierStart() but
|
193
|
-
# this is not easily implemented (at least as of Ruby 1.8).
|
194
|
-
# So, we implement a subset of these characters.
|
195
|
-
handle_identifier()
|
196
|
-
|
197
|
-
when "\n", "\r" # end of line
|
198
|
-
@reader.skip_to_eol()
|
199
|
-
|
200
|
-
else
|
201
|
-
parse_error("Unexpected character '#{c}'")
|
202
|
-
end
|
203
|
-
|
204
|
-
@reader.skip_char()
|
205
|
-
end
|
206
|
-
|
207
|
-
if @token_text
|
208
|
-
@tokens << Token.new(@token_text, @reader.line_no, @token_start)
|
209
|
-
end
|
210
|
-
|
211
|
-
return @tokens
|
212
|
-
end
|
213
|
-
|
214
|
-
# Adds the current escaped character (represented by ((|c|))) to @token_text.
|
215
|
-
# This method assumes the previous char was a backslash.
|
216
|
-
#
|
217
|
-
def add_escaped_char_in_string(c)
|
218
|
-
case c
|
219
|
-
when "\\", "\""
|
220
|
-
@token_text << c
|
221
|
-
when "n"
|
222
|
-
@token_text << ?\n
|
223
|
-
when "r"
|
224
|
-
@token_text << ?\r
|
225
|
-
when "t"
|
226
|
-
@token_text << ?\t
|
227
|
-
else
|
228
|
-
parse_error("Illegal escape character in string literal: '#{c}'.")
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
def handle_double_quote_string
|
233
|
-
escaped = false
|
234
|
-
@starting_escaped_quote_line = false
|
235
|
-
|
236
|
-
@token_text = @reader.read_char()
|
237
|
-
|
238
|
-
until @reader.eol?
|
239
|
-
c = @reader.current_char
|
240
|
-
|
241
|
-
if "\s\t".include?(c) and @starting_escaped_quote_line
|
242
|
-
# skip the heading spaces (indentation) of a continued line
|
243
|
-
else
|
244
|
-
@starting_escaped_quote_line = false
|
245
|
-
|
246
|
-
if escaped
|
247
|
-
add_escaped_char_in_string(c)
|
248
|
-
escaped = false
|
249
|
-
|
250
|
-
elsif c == "\\"
|
251
|
-
# check for String broken across lines
|
252
|
-
if @reader.rest_of_line =~ /^\\\s*$/
|
253
|
-
handle_escaped_double_quoted_string()
|
254
|
-
next # as we are at the beginning of a new line
|
255
|
-
else
|
256
|
-
escaped = true
|
257
|
-
end
|
258
|
-
|
259
|
-
else
|
260
|
-
@token_text << c
|
261
|
-
if c == '"'
|
262
|
-
# end of double-quoted string detected
|
263
|
-
@tokens << Token.new(@token_text, @reader.line_no, @token_start)
|
264
|
-
@token_text = nil
|
265
|
-
return
|
266
|
-
end
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
@reader.skip_char()
|
271
|
-
end
|
272
|
-
|
273
|
-
# detection of ill-terminated literals
|
274
|
-
if @token_text =~ /^".*[^"]$/
|
275
|
-
parse_error(
|
276
|
-
"String literal \"#{@token_text}\" not terminated by end quote.",
|
277
|
-
@reader.line_no,
|
278
|
-
@reader.line_length)
|
279
|
-
else#if @token_text == '"'
|
280
|
-
parse_error("Orphan quote (unterminated string)", @reader.line_no, @reader.line_length)
|
281
|
-
end
|
282
|
-
puts "end of chars"
|
283
|
-
end
|
284
|
-
|
285
|
-
def handle_escaped_double_quoted_string
|
286
|
-
# '\' can be followed by whitespaces
|
287
|
-
if @reader.rest_of_line =~ /^\\\s*$/
|
288
|
-
@reader.read_line()
|
289
|
-
parse_error("Escape at end of file.") if @reader.eof?
|
290
|
-
|
291
|
-
@starting_escaped_quote_line = true
|
292
|
-
|
293
|
-
else
|
294
|
-
parse_error(
|
295
|
-
"Malformed string literal - escape followed by whitespace " +
|
296
|
-
"followed by non-whitespace.")
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
def handle_character_literal
|
301
|
-
@reader.skip_char # skip the starting quote
|
302
|
-
|
303
|
-
parse_error("Got ' at end of line") if @reader.eol?
|
304
|
-
|
305
|
-
c2 = @reader.read_char()
|
306
|
-
|
307
|
-
if c2 == "\\"
|
308
|
-
parse_error("Got '\\ at end of line") if @reader.eol?
|
309
|
-
|
310
|
-
c3 = @reader.read_char()
|
311
|
-
|
312
|
-
parse_error("Got '\\#{c3} at end of line") if @reader.eol?
|
313
|
-
|
314
|
-
case c3
|
315
|
-
when "\\"
|
316
|
-
@tokens << Token.new("'\\'", @reader.line_no, @reader.pos)
|
317
|
-
when "'"
|
318
|
-
@tokens << Token.new("'''", @reader.line_no, @reader.pos)
|
319
|
-
when "n"
|
320
|
-
@tokens << Token.new("'\n'", @reader.line_no, @reader.pos)
|
321
|
-
when "r"
|
322
|
-
@tokens << Token.new("'\r'", @reader.line_no, @reader.pos)
|
323
|
-
when "t"
|
324
|
-
@tokens << Token.new("'\t'", @reader.line_no, @reader.pos)
|
325
|
-
else
|
326
|
-
parse_error("Illegal escape character #{@reader.current_char}")
|
327
|
-
end
|
328
|
-
|
329
|
-
if @reader.read_char != "'"
|
330
|
-
expecting_but_got("single quote (')", "\"#{@reader.current_char}\"")
|
331
|
-
end
|
332
|
-
|
333
|
-
else
|
334
|
-
@tokens << Token.new("'#{c2}'", @reader.line_no, @reader.pos)
|
335
|
-
|
336
|
-
parse_error("Got '#{c2} at end of line") if @reader.eol?
|
337
|
-
|
338
|
-
if @reader.read_char != "'"
|
339
|
-
expecting_but_got(
|
340
|
-
"quote (')", "\"#{@reader.current_char}\"", @reader.line_no, @reader.pos)
|
341
|
-
end
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
def handle_slash_comment
|
346
|
-
if not @reader.more_chars_in_line?
|
347
|
-
parse_error("Got slash (/) at end of line.")
|
348
|
-
end
|
349
|
-
|
350
|
-
if @reader.get_line_char(@reader.pos + 1) == "*"
|
351
|
-
end_index = @reader.find_next_in_line("*/")
|
352
|
-
if end_index
|
353
|
-
# handle comment on same line
|
354
|
-
@reader.skip_to(end_index + 1)
|
355
|
-
else
|
356
|
-
# handle multiline comments
|
357
|
-
loop do
|
358
|
-
@reader.read_raw_line()
|
359
|
-
if @reader.eof?
|
360
|
-
parse_error("/* comment not terminated.", @reader.line_no, -2)
|
361
|
-
end
|
362
|
-
|
363
|
-
end_index = @reader.find_next_in_line("*/", 0)
|
364
|
-
|
365
|
-
if end_index
|
366
|
-
@reader.skip_to(end_index + 1)
|
367
|
-
break
|
368
|
-
end
|
369
|
-
end
|
370
|
-
end
|
371
|
-
elsif @reader.get_line_char(@reader.pos + 1) == "/"
|
372
|
-
parse_error("Got slash (/) in unexpected location.")
|
373
|
-
end
|
374
|
-
end
|
375
|
-
|
376
|
-
def handle_back_quote_string
|
377
|
-
end_index = @reader.find_next_in_line("`")
|
378
|
-
|
379
|
-
if end_index
|
380
|
-
# handle end quote on same line
|
381
|
-
@tokens << Token.new(@reader.substring(@reader.pos, end_index), @reader.line_no, @reader.pos)
|
382
|
-
@token_text = nil
|
383
|
-
@reader.skip_to(end_index)
|
384
|
-
|
385
|
-
else
|
386
|
-
@token_text = @reader.rest_of_line
|
387
|
-
@token_start = @reader.pos
|
388
|
-
# handle multiline quotes
|
389
|
-
loop do
|
390
|
-
@reader.read_raw_line()
|
391
|
-
if @reader.eof?
|
392
|
-
parse_error("` quote not terminated.", @reader.line_no, -2)
|
393
|
-
end
|
394
|
-
|
395
|
-
end_index = @reader.find_next_in_line("`", 0)
|
396
|
-
if end_index
|
397
|
-
@token_text << @reader.substring(0, end_index)
|
398
|
-
@reader.skip_to(end_index)
|
399
|
-
break
|
400
|
-
else
|
401
|
-
@token_text << @reader.line
|
402
|
-
end
|
403
|
-
end
|
404
|
-
|
405
|
-
@tokens << Token.new(@token_text, @reader.line_no, @token_start)
|
406
|
-
@token_text = nil
|
407
|
-
end
|
408
|
-
end
|
409
|
-
|
410
|
-
def handle_binary_literal
|
411
|
-
end_index = @reader.find_next_in_line("]")
|
412
|
-
|
413
|
-
if end_index
|
414
|
-
# handle end quote on same line
|
415
|
-
@tokens << Token.new(@reader.substring(@reader.pos, end_index), @reader.line_no, @reader.pos)
|
416
|
-
@token_text = nil
|
417
|
-
@reader.skip_to(end_index)
|
418
|
-
else
|
419
|
-
@token_text = @reader.substring(@reader.pos)
|
420
|
-
@token_start = @reader.pos
|
421
|
-
# handle multiline quotes
|
422
|
-
loop do
|
423
|
-
@reader.read_raw_line()
|
424
|
-
if @reader.eof?
|
425
|
-
parse_error("[base64] binary literal not terminated.", @reader.line_no, -2)
|
426
|
-
end
|
427
|
-
|
428
|
-
end_index = @reader.find_next_in_line("]", 0)
|
429
|
-
if end_index
|
430
|
-
@token_text << @reader.substring(0, end_index)
|
431
|
-
@reader.skip_to(end_index)
|
432
|
-
break
|
433
|
-
else
|
434
|
-
@token_text << @reader.line
|
435
|
-
end
|
436
|
-
end
|
437
|
-
|
438
|
-
@tokens << Token.new(@token_text, @reader.line_no, @token_start)
|
439
|
-
@token_text = nil
|
440
|
-
end
|
441
|
-
end
|
442
|
-
|
443
|
-
# handle a line continuation (not inside a string)
|
444
|
-
def handle_line_continuation
|
445
|
-
# backslash line continuation outside of a String literal
|
446
|
-
# can only occur at the end of a line
|
447
|
-
if not @reader.rest_of_line =~ /^\\\s*$/
|
448
|
-
parse_error("Line continuation (\\) before end of line")
|
449
|
-
else
|
450
|
-
@line = @reader.read_line()
|
451
|
-
unless @line
|
452
|
-
parse_error("Line continuation at end of file.", @reader.line_no, @reader.pos)
|
453
|
-
end
|
454
|
-
end
|
455
|
-
end
|
456
|
-
|
457
|
-
def handle_number_date_or_time_span
|
458
|
-
@token_start = @reader.pos
|
459
|
-
@token_text = ""
|
460
|
-
|
461
|
-
until @reader.eol?
|
462
|
-
c = @reader.current_char
|
463
|
-
|
464
|
-
if c =~ /[\w\.\-+:]/
|
465
|
-
@token_text << c
|
466
|
-
elsif c == "/" and not @reader.get_line_char(@reader.pos + 1) == "*"
|
467
|
-
@token_text << c
|
468
|
-
else
|
469
|
-
@reader.previous_char()
|
470
|
-
break
|
471
|
-
end
|
472
|
-
|
473
|
-
@reader.skip_char()
|
474
|
-
end
|
475
|
-
|
476
|
-
@tokens << Token.new(@token_text, @reader.line_no, @token_start)
|
477
|
-
@token_text = nil
|
478
|
-
end
|
479
|
-
|
480
|
-
def handle_identifier
|
481
|
-
@token_start = @reader.pos
|
482
|
-
@token_text = ""
|
483
|
-
|
484
|
-
until @reader.eol?
|
485
|
-
c = @reader.current_char
|
486
|
-
|
487
|
-
# FIXME here we are stricter than the Java version because there is no
|
488
|
-
# easy way to implement Character.isJavaIdentifierPart() in Ruby :)
|
489
|
-
if c =~ /[\w_$-\.]/
|
490
|
-
@token_text << c
|
491
|
-
else
|
492
|
-
@reader.previous_char()
|
493
|
-
break
|
494
|
-
end
|
495
|
-
|
496
|
-
@reader.skip_char()
|
497
|
-
end
|
498
|
-
|
499
|
-
@tokens << Token.new(@token_text, @reader.line_no, @token_start)
|
500
|
-
@token_text = nil
|
501
|
-
end
|
502
|
-
|
503
|
-
end
|
504
|
-
|
505
|
-
end
|
506
|
-
|
507
|
-
end
|