sdl4r 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,171 @@
1
+ # Simple Declarative Language (SDL) for Ruby
2
+ # Copyright 2005 Ikayzo, inc.
3
+ #
4
+ # This program is free software. You can distribute or modify it under the
5
+ # terms of the GNU Lesser General Public License version 2.1 as published by
6
+ # the Free Software Foundation.
7
+ #
8
+ # This program is distributed AS IS and WITHOUT WARRANTY. OF ANY KIND,
9
+ # INCLUDING MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
10
+ # See the GNU Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public License
13
+ # along with this program; if not, contact the Free Software Foundation, Inc.,
14
+ # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15
+
16
+
17
+ module SDL4R
18
+
19
+ # Gives access to the characters read to the Parser.
20
+ # This class was designed to gather the handling of the UTF-8 issues in one place and shield the
21
+ # Parser class from these problems.
22
+ class Reader
23
+
24
+ RUBY_1_8_OR_LESS = require 'jcode'
25
+
26
+
27
+ # +io+ an open IO from which the characters are read.
28
+ def initialize(io)
29
+ raise ArgumentError, "io == nil" if io.nil?
30
+
31
+ @io = io
32
+ @line = nil
33
+ @line_chars = nil
34
+ @line_no = 0
35
+ @pos = 0
36
+ end
37
+
38
+ attr_reader :line_no, :pos, :line;
39
+
40
+ def line_length
41
+ return @line_chars.nil? ? 0 : @line_chars.length
42
+ end
43
+
44
+ # Reads next line in stream skipping comment lines and blank lines.
45
+ #
46
+ # Returns the next line or nil at the end of the file.
47
+ def read_line
48
+ @line_chars = nil
49
+
50
+ while @line = read_raw_line()
51
+ # Skip empty and commented lines
52
+ break unless @line.empty? or @line =~ /^#/
53
+ end
54
+
55
+ return @line
56
+ end
57
+
58
+ # Returns the string that goes from the current position of this Reader to the end of the line
59
+ # or nil if the current position doesn't allow that.
60
+ def rest_of_line
61
+ return @line[@pos..-1]
62
+ end
63
+
64
+ # Indicates whether the end of file has been reached.
65
+ def end_of_file?
66
+ return @line.nil?
67
+ end
68
+
69
+ # Indicates whether there are more characters in the current line
70
+ def more_chars_in_line?
71
+ return @pos < line_length - 1
72
+ end
73
+
74
+ # Returns whether the end of the current +line+ as been reached.
75
+ def end_of_line?
76
+ return @pos >= line_length
77
+ end
78
+
79
+ # Skips the current line by going just after its end.
80
+ def skip_line
81
+ @pos = line_length
82
+ end
83
+
84
+ # Skips the whitespaces that follow the current position.
85
+ def skip_whitespaces
86
+ while (@pos + 1) < line_length and
87
+ (@line[@pos + 1] == ?\s or @line[@pos + 1] == ?\t)
88
+ @pos += 1
89
+ end
90
+ end
91
+
92
+ # Returns the character at position +pos+ in the current line.
93
+ # Returns nil if there is no current line or if +pos+ is after the end of the line.
94
+ def get_line_char(pos)
95
+ if @line_chars and pos < line_length
96
+ return @line_chars[pos]
97
+ else
98
+ return nil
99
+ end
100
+ end
101
+
102
+ # Returns the character at the current position or nil after end-of-line or end-of -file.
103
+ def current_char
104
+ return get_line_char(@pos)
105
+ end
106
+
107
+ # Go to the next character in the stream.
108
+ def skip_char
109
+ @pos += 1 if @pos < line_length
110
+ end
111
+
112
+ # Go to the next character and returns it (or nil if end-of-line or -file has been reached).
113
+ def read_char
114
+ skip_char()
115
+ return current_char
116
+ end
117
+
118
+ # Returns to the previous char if possible.
119
+ def previous_char
120
+ @pos -= 1 if @pos >= -1
121
+ end
122
+
123
+ # Returns the next index of the expression (string, regexp, fixnum) in the current line, starting
124
+ # from after the current position if no position is specified.
125
+ def find_next_in_line(searched, start_pos = nil)
126
+ start_pos = @pos + 1 unless start_pos
127
+ return @line.index(searched, start_pos)
128
+ end
129
+
130
+ # Skips the specified position in the current line.
131
+ def skip_to(new_pos)
132
+ @pos = new_pos
133
+ end
134
+
135
+
136
+ # Returns a subpart of the current line starting from +from+ and stopping at +to+ (excluded).
137
+ def substring(from, to = -1)
138
+ return @line[from..to]
139
+ end
140
+
141
+ # Reads and returns a "raw" line including lines with comments and blank lines.
142
+ #
143
+ # Returns the next line or nil if at the end of the file.
144
+ #
145
+ # This method changes the value of @line, @lineNo and @pos.
146
+ def read_raw_line
147
+ @line = @io.gets()
148
+
149
+ # We ensure that only \n is used as an end-of-line by replacing \r and \r\n.
150
+ if @line
151
+ if not @line.gsub!(/\r\n/m, "\n")
152
+ @line.gsub!(/\r/m, "\n")
153
+ end
154
+ end
155
+
156
+ @pos = 0;
157
+ @line_chars = nil
158
+ if @line
159
+ @line_no += 1
160
+ @line_chars = @line.scan(/./m)
161
+ end
162
+ return @line
163
+ end
164
+
165
+ # Closes this Reader and its underlying +IO+.
166
+ def close
167
+ @io.close
168
+ end
169
+ end
170
+
171
+ end
@@ -0,0 +1,242 @@
1
+ # Simple Declarative Language (SDL) for Ruby
2
+ # Copyright 2005 Ikayzo, inc.
3
+ #
4
+ # This program is free software. You can distribute or modify it under the
5
+ # terms of the GNU Lesser General Public License version 2.1 as published by
6
+ # the Free Software Foundation.
7
+ #
8
+ # This program is distributed AS IS and WITHOUT WARRANTY. OF ANY KIND,
9
+ # INCLUDING MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
10
+ # See the GNU Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public License
13
+ # along with this program; if not, contact the Free Software Foundation, Inc.,
14
+ # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15
+
16
+
17
+ require 'jcode'
18
+ require 'base64'
19
+ require 'rational'
20
+ require 'date'
21
+
22
+ # Various SDL related utility methods
23
+ #
24
+ module SDL4R
25
+
26
+ MAX_INTEGER_32 = 2**31 - 1
27
+ MIN_INTEGER_32 = -(2**31)
28
+
29
+ MAX_INTEGER_64 = 2**63 - 1
30
+ MIN_INTEGER_64 = -(2**63)
31
+
32
+
33
+ # Returns the milliseonds part of a DateTime (by translating the +sec_fraction+ part) as an
34
+ # integer.
35
+ def self.get_date_milliseconds(date)
36
+ sec_fraction = date.sec_fraction() # in days
37
+ # 86400000 is the number of milliseconds in a day
38
+ return (sec_fraction * 86400000).round()
39
+ end
40
+
41
+ BASE64_WRAP_LINE_LENGTH = 72
42
+
43
+ # Creates an SDL string representation for a given object and returns it.
44
+ #
45
+ # +o+:: the object to format
46
+ # +add_quotes+:: indicates whether quotes will be added to Strings and characters
47
+ # (true by default)
48
+ # +line_prefix+:: the line prefix to use ("" by default)
49
+ # +indent+:: the indent string to use ("\t" by default)
50
+ #
51
+ def self.format(o, add_quotes = true, line_prefix = "", indent = "\t")
52
+ if o.is_a?(String)
53
+ if add_quotes
54
+ o_length = 0
55
+ o.scan(/./m) { o_length += 1 } # counts the number of chars (as opposed of bytes)
56
+ if o_length == 1
57
+ return "'" + escape(o, "'") + "'"
58
+ else
59
+ return '"' + escape(o, '"') + '"'
60
+ end
61
+ else
62
+ return escape(o)
63
+ end
64
+
65
+ elsif o.is_a?(Bignum)
66
+ return o.to_s + "BD"
67
+
68
+ elsif o.is_a?(Integer)
69
+ if MIN_INTEGER_32 <= o and o <= MAX_INTEGER_32
70
+ return o.to_s
71
+ elsif MIN_INTEGER_64 <= o and o <= MAX_INTEGER_64
72
+ return o.to_s + "L"
73
+ else
74
+ return o.to_s + "BD"
75
+ end
76
+
77
+ elsif o.is_a?(Float)
78
+ return o.to_s + "F"
79
+
80
+ elsif o.is_a?(Rational)
81
+ return o.to_f.to_s + "F"
82
+
83
+ elsif defined? Flt::DecNum and o.is_a?(Flt::DecNum)
84
+ return o.to_s + "BD"
85
+
86
+ elsif o.nil?
87
+ return "null"
88
+
89
+ elsif o.is_a?(SdlBinary)
90
+ encoded_o = Base64.encode64(o.bytes)
91
+ encoded_o.gsub!(/[\r\n]/m, "") # Remove the EOL inserted every 60 chars
92
+
93
+ if add_quotes
94
+ if encoded_o.length > BASE64_WRAP_LINE_LENGTH
95
+ # FIXME: we should a constant or some parameter instead of hardcoded spaces
96
+ wrap_lines_in_ascii(encoded_o, BASE64_WRAP_LINE_LENGTH, "#{line_prefix}#{indent}")
97
+ encoded_o.insert(0, "[#{$/}")
98
+ encoded_o << "#{$/}#{line_prefix}]"
99
+ else
100
+ encoded_o.insert(0, "[")
101
+ encoded_o << "]"
102
+ end
103
+ end
104
+
105
+ return encoded_o
106
+
107
+ # Below, we use "#{o.year}" instead of "%Y" because "%Y" always emit 4 chars at least even if
108
+ # the date is before 1000.
109
+ elsif o.is_a?(DateTime) || o.is_a?(Time)
110
+ milliseconds = o.is_a?(DateTime) ? get_date_milliseconds(o) : (o.usec / 10).to_i
111
+
112
+ if milliseconds == 0
113
+ if o.zone
114
+ return o.strftime("#{o.year}/%m/%d %H:%M:%S%Z")
115
+ else
116
+ return o.strftime("#{o.year}/%m/%d %H:%M:%S")
117
+ end
118
+ else
119
+ if o.zone
120
+ return o.strftime("#{o.year}/%m/%d %H:%M:%S." + milliseconds.to_s.ljust(3, '0') + "%Z")
121
+ else
122
+ return o.strftime("#{o.year}/%m/%d %H:%M:%S." + milliseconds.to_s.ljust(3, '0'))
123
+ end
124
+ end
125
+
126
+ elsif o.is_a?(Date)
127
+ return o.strftime("#{o.year}/%m/%d")
128
+
129
+ else
130
+ return o.to_s
131
+ end
132
+ end
133
+
134
+ # Wraps lines in "s" (by modifying it). This method only supports 1-byte character strings.
135
+ #
136
+ def self.wrap_lines_in_ascii(s, line_length, line_prefix = nil)
137
+ # We could use such code if it supported any value for "line_prefix": unfortunately it is capped
138
+ # at 64 in the regular expressions.
139
+ #
140
+ # return "#{line_prefix}" + encoded_o.scan(/.{1,#{line_prefix}}/).join("#{$/}#{line_prefix}")
141
+
142
+ eol_size = "#{$/}".size
143
+
144
+ i = 0
145
+ while i < s.size
146
+ if i > 0
147
+ s.insert(i, $/)
148
+ i += eol_size
149
+ end
150
+
151
+ if line_prefix
152
+ s.insert(i, line_prefix)
153
+ i += line_prefix.size
154
+ end
155
+
156
+ i += line_length
157
+ end
158
+ end
159
+
160
+ ESCAPED_QUOTES = {
161
+ "\"" => "\\\"",
162
+ "'" => "\\'",
163
+ "`" => "\\`",
164
+ }
165
+
166
+ ESCAPED_CHARS = {
167
+ "\\" => "\\\\",
168
+ "\t" => "\\t",
169
+ "\r" => "\\r",
170
+ "\n" => "\\n",
171
+ }
172
+ ESCAPED_CHARS.merge!(ESCAPED_QUOTES)
173
+
174
+ # Returns an escaped version of +s+ (i.e. where characters which need to be
175
+ # escaped, are escaped).
176
+ #
177
+ def self.escape(s, quote_char = nil)
178
+ escaped_s = ""
179
+
180
+ s.each_char { |c|
181
+ escaped_char = ESCAPED_CHARS[c]
182
+ if escaped_char
183
+ if ESCAPED_QUOTES.has_key?(c)
184
+ if quote_char && c == quote_char
185
+ escaped_s << escaped_char
186
+ else
187
+ escaped_s << c
188
+ end
189
+ else
190
+ escaped_s << escaped_char
191
+ end
192
+ else
193
+ escaped_s << c
194
+ end
195
+ }
196
+
197
+ return escaped_s
198
+ end
199
+
200
+ # This method was kept from the Java code but it is not sure if it should have a usefulness yet.
201
+ #
202
+ def self.coerce_or_fail(o)
203
+ return o
204
+ end
205
+
206
+ # Validates an SDL identifier String. SDL Identifiers must start with a
207
+ # Unicode letter or underscore (_) and contain only unicode letters,
208
+ # digits, underscores (_), and dashes(-).
209
+ #
210
+ # @param identifier The identifier to validate
211
+ # @throws IllegalArgumentException if the identifier is not legal
212
+ #
213
+ # TODO: support UTF-8 identifiers
214
+ #
215
+ def self.validate_identifier(identifier)
216
+ if identifier.nil? or identifier.empty?
217
+ raise ArgumentError, "SDL identifiers cannot be null or empty."
218
+ end
219
+
220
+ # in Java, was if(!Character.isJavaIdentifierStart(identifier.charAt(0)))
221
+ unless identifier =~ /^[a-zA-Z_]/
222
+ raise ArgumentError,
223
+ "'" + identifier[0..0] +
224
+ "' is not a legal first character for an SDL identifier. " +
225
+ "SDL Identifiers must start with a unicode letter or " +
226
+ "an underscore (_)."
227
+ end
228
+
229
+ unless identifier.length == 1 or identifier =~ /^[a-zA-Z_][a-zA-Z_0-9\-]*$/
230
+ for i in 1..identifier.length
231
+ unless identifier[i..i] =~ /^[a-zA-Z_0-9\-]$/
232
+ raise ArgumentError,
233
+ "'" + identifier[i..i] +
234
+ "' is not a legal character for an SDL identifier. " +
235
+ "SDL Identifiers must start with a unicode letter or " +
236
+ "underscore (_) followed by 0 or more unicode " +
237
+ "letters, digits, underscores (_), or dashes (-)"
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
@@ -0,0 +1,78 @@
1
+ # Simple Declarative Language (SDL) for Ruby
2
+ # Copyright 2005 Ikayzo, inc.
3
+ #
4
+ # This program is free software. You can distribute or modify it under the
5
+ # terms of the GNU Lesser General Public License version 2.1 as published by
6
+ # the Free Software Foundation.
7
+ #
8
+ # This program is distributed AS IS and WITHOUT WARRANTY. OF ANY KIND,
9
+ # INCLUDING MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
10
+ # See the GNU Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public License
13
+ # along with this program; if not, contact the Free Software Foundation, Inc.,
14
+ # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15
+
16
+
17
+ module SDL4R
18
+
19
+ #
20
+ # Represents a binary value.
21
+ # This class was introduced to avoid the confusion between a Ruby String and a binary literal.
22
+ #
23
+ class SdlBinary
24
+
25
+ attr_accessor :bytes
26
+
27
+ # +value+: a String containing the bytes
28
+ def initialize(bytes)
29
+ @bytes = bytes
30
+ end
31
+
32
+ def ==(o)
33
+ return true if self.equal?(o)
34
+ return false if not o.instance_of?(self.class)
35
+ return self.bytes == o.bytes
36
+ end
37
+
38
+ def eql?(o)
39
+ self == o
40
+ end
41
+
42
+ def hash
43
+ return bytes.hash
44
+ end
45
+
46
+ # Returns the bytes base64-encoded.
47
+ def to_s
48
+ return Base64.encode64(bytes)
49
+ end
50
+
51
+ # Decodes the specified base-64 encoded string and returns a corresponding SdlBinary
52
+ # instance.
53
+ # +s+ might not include the conventional starting and ending square brackets.
54
+ def self.decode64(s)
55
+ s = s.delete("\n\r\t ")
56
+
57
+ binary = Base64.decode64(s)
58
+
59
+ if binary.empty? and not s.empty?
60
+ raise ArgumentError, "bad binary literal"
61
+ end
62
+
63
+ return SdlBinary.new(binary)
64
+ end
65
+ end
66
+
67
+ # Try to coerce 'o' into a SdlBinary.
68
+ # Raise an ArgumentError if it fails.
69
+ def self.SdlBinary(o)
70
+ if o.kind_of? SdlBinary
71
+ return o
72
+ elsif o.kind_of? String
73
+ return SdlBinary.new(o)
74
+ else
75
+ raise ArgumentError, "can't coerce argument"
76
+ end
77
+ end
78
+ end