Class: LiteralParser
- Inherits:
-
Object
- Object
- LiteralParser
- Includes:
- Expressions
- Defined in:
- lib/literal_parser.rb,
lib/literal_parser/version.rb
Overview
Limitations
-
LiteralParser does not support ruby 1.9’s ‘value` syntax.
-
LiteralParser does not currently support all of rubys escape sequences in strings and symbols, e.g. “C-…” type sequences don’t work.
-
Trailing commas in Array and Hash are not supported.
BigDecimals
You can instruct LiteralParser to parse “12.5” as a bigdecimal and use “12.5e” to have it parsed as float (short for “12.5e0”, equivalent to “1.25e1”)
Date & Time
LiteralParser supports a subset of ISO-8601 for Date and Time which are not actual valid ruby literals. The form YYYY-MM-DD (e.g. 2012-05-20) is translated to a Date object, and YYYY-MM-DD“T”HH:MM:SS (e.g. 2012-05-20T18:29:52) is translated to a Time object.
LiteralParser
Parse Strings containing ruby literals.
LiteralParser recognizes constants and the following literals:
nil # nil
true # true
false # false
-123 # Fixnum/Bignum (decimal)
0b1011 # Fixnum/Bignum (binary)
0755 # Fixnum/Bignum (octal)
0xff # Fixnum/Bignum (hexadecimal)
120.30 # Float (optional: BigDecimal)
1e0 # Float
"foo" # String, no interpolation, but \t etc. work
'foo' # String, only \\ and \' are escaped
/foo/ # Regexp
:foo # Symbol
:"foo" # Symbol
2012-05-20 # Date
2012-05-20T18:29:52 # DateTime
[Any, Literals, Here] # Array
{Any => Literals} # Hash
Defined Under Namespace
Modules: Expressions Classes: SyntaxError
Constant Summary collapse
- Version =
The version of the LiteralParser library
Gem::Version.new("1.0.1")
Constants included from Expressions
Expressions::DStringEscapes, Expressions::RArrayBegin, Expressions::RArrayEnd, Expressions::RArraySeparator, Expressions::RArrayVoid, Expressions::RBigDecimal, Expressions::RBinaryInteger, Expressions::RConstant, Expressions::RDString, Expressions::RDate, Expressions::RDateTime, Expressions::RFalse, Expressions::RFloat, Expressions::RHashArrow, Expressions::RHashBegin, Expressions::RHashEnd, Expressions::RHashKeySymbol, Expressions::RHashSeparator, Expressions::RHashVoid, Expressions::RHexInteger, Expressions::RInteger, Expressions::RNil, Expressions::ROctalInteger, Expressions::RRegexp, Expressions::RSString, Expressions::RSymbol, Expressions::RTime, Expressions::RTimeZone, Expressions::RTrue
Instance Attribute Summary collapse
-
#constant_base ⇒ Module?
readonly
Where to lookup constants.
-
#use_big_decimal ⇒ Boolean
readonly
True if “1.25” should be parsed into a big-decimal, false if it should be parsed as Float.
Class Method Summary collapse
-
.parse(string, options = nil) ⇒ Object
Parse a String, returning the object which it contains.
Instance Method Summary collapse
-
#end_of_string? ⇒ Boolean
Whether the scanner reached the end of the string.
-
#initialize(string, options = nil) ⇒ LiteralParser
constructor
Parse a String, returning the object which it contains.
-
#position ⇒ Integer
The position of the scanner in the string.
-
#position=(value) ⇒ Object
Moves the scanners position to the given character-index.
-
#rest ⇒ String
The currently unprocessed rest of the string.
-
#scan_value ⇒ Object
Scans the string for a single value and advances the parsers position.
Constructor Details
#initialize(string, options = nil) ⇒ LiteralParser
Parse a String, returning the object which it contains.
202 203 204 205 206 207 208 |
# File 'lib/literal_parser.rb', line 202 def initialize(string, =nil) = ? .dup : {} @constant_base = [:constant_base] # nil means toplevel @use_big_decimal = .delete(:use_big_decimal) { false } @string = string @scanner = StringScanner.new(string) end |
Instance Attribute Details
#constant_base ⇒ Module? (readonly)
Returns Where to lookup constants. Nil is toplevel (equivalent to Object).
181 182 183 |
# File 'lib/literal_parser.rb', line 181 def constant_base @constant_base end |
#use_big_decimal ⇒ Boolean (readonly)
Returns True if “1.25” should be parsed into a big-decimal, false if it should be parsed as Float.
186 187 188 |
# File 'lib/literal_parser.rb', line 186 def use_big_decimal @use_big_decimal end |
Class Method Details
.parse(string, options = nil) ⇒ Object
Parse a String, returning the object which it contains.
171 172 173 174 175 176 177 |
# File 'lib/literal_parser.rb', line 171 def self.parse(string, =nil) parser = new(string, ) value = parser.scan_value raise SyntaxError, "Unexpected superfluous data: #{parser.rest.inspect}" unless parser.end_of_string? value end |
Instance Method Details
#end_of_string? ⇒ Boolean
Returns Whether the scanner reached the end of the string.
224 225 226 |
# File 'lib/literal_parser.rb', line 224 def end_of_string? @scanner.eos? end |
#position ⇒ Integer
Returns The position of the scanner in the string.
211 212 213 |
# File 'lib/literal_parser.rb', line 211 def position @scanner.pos end |
#position=(value) ⇒ Object
Moves the scanners position to the given character-index.
219 220 221 |
# File 'lib/literal_parser.rb', line 219 def position=(value) @scanner.pos = value end |
#rest ⇒ String
Returns The currently unprocessed rest of the string.
229 230 231 |
# File 'lib/literal_parser.rb', line 229 def rest @scanner.rest end |
#scan_value ⇒ Object
Scans the string for a single value and advances the parsers position
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
# File 'lib/literal_parser.rb', line 240 def scan_value case when @scanner.scan(RArrayBegin) then value = [] @scanner.scan(RArrayVoid) if @scanner.scan(RArrayEnd) value else value << scan_value while @scanner.scan(RArraySeparator) value << scan_value end raise SyntaxError, "Expected ]" unless @scanner.scan(RArrayVoid) && @scanner.scan(RArrayEnd) value end when @scanner.scan(RHashBegin) then value = {} @scanner.scan(RHashVoid) if @scanner.scan(RHashEnd) value else if @scanner.scan(RHashKeySymbol) key = @scanner[1].to_sym @scanner.scan(RHashVoid) else key = scan_value raise SyntaxError, "Expected =>" unless @scanner.scan(RHashArrow) end val = scan_value value[key] = val while @scanner.scan(RHashSeparator) if @scanner.scan(RHashKeySymbol) key = @scanner[1].to_sym @scanner.scan(RHashVoid) else key = scan_value raise SyntaxError, "Expected =>" unless @scanner.scan(RHashArrow) end val = scan_value value[key] = val end raise SyntaxError, "Expected }" unless @scanner.scan(RHashVoid) && @scanner.scan(RHashEnd) value end when @scanner.scan(RConstant) then eval("#{@constant_base}::#{@scanner[0]}") # yes, I know it's evil, but it's sane due to the regex, also it's less annoying than deep_const_get when @scanner.scan(RNil) then nil when @scanner.scan(RTrue) then true when @scanner.scan(RFalse) then false when @scanner.scan(RDateTime) then Time.mktime(@scanner[1], @scanner[2], @scanner[3], @scanner[4], @scanner[5], @scanner[6]) when @scanner.scan(RDate) then date = @scanner[1].to_i, @scanner[2].to_i, @scanner[3].to_i Date.civil(*date) when @scanner.scan(RTime) then now = Time.now Time.mktime(now.year, now.month, now.day, @scanner[1].to_i, @scanner[2].to_i, @scanner[3].to_i) when @scanner.scan(RFloat) then Float(@scanner.matched.delete('^0-9.e-')) when @scanner.scan(RBigDecimal) then data = @scanner.matched.delete('^0-9.-') @use_big_decimal ? BigDecimal(data) : Float(data) when @scanner.scan(ROctalInteger) then Integer(@scanner.matched.delete('^0-9-')) when @scanner.scan(RHexInteger) then Integer(@scanner.matched.delete('^xX0-9A-Fa-f-')) when @scanner.scan(RBinaryInteger) then Integer(@scanner.matched.delete('^bB01-')) when @scanner.scan(RInteger) then @scanner.matched.delete('^0-9-').to_i when @scanner.scan(RRegexp) then source = @scanner[1] flags = 0 lang = nil if @scanner[2] then flags |= Regexp::IGNORECASE if @scanner[2].include?('i') flags |= Regexp::EXTENDED if @scanner[2].include?('m') flags |= Regexp::MULTILINE if @scanner[2].include?('x') lang = @scanner[2].delete('^nNeEsSuU')[-1,1] end Regexp.new(source, flags, lang) when @scanner.scan(RSymbol) then case @scanner.matched[1,1] when '"' @scanner.matched[2..-2].gsub(/\\(?:[0-3]?\d\d?|x[A-Fa-f\d]{2}|.)/) { |m| DStringEscapes[m] }.to_sym when "'" @scanner.matched[2..-2].gsub(/\\'/, "'").gsub(/\\\\/, "\\").to_sym else @scanner.matched[1..-1].to_sym end when @scanner.scan(RSString) then @scanner.matched[1..-2].gsub(/\\'/, "'").gsub(/\\\\/, "\\") when @scanner.scan(RDString) then @scanner.matched[1..-2].gsub(/\\(?:[0-3]?\d\d?|x[A-Fa-f\d]{2}|.)/) { |m| DStringEscapes[m] } else raise SyntaxError, "Unrecognized pattern: #{@scanner.rest.inspect}" end end |