sdl4r 0.9.1

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