Class: Qreport::TimeParser
- Inherits:
-
Object
- Object
- Qreport::TimeParser
- Defined in:
- lib/qreport/time_parser.rb,
lib/qreport/time_parser/examples.rb,
lib/qreport/time_parser/time_unit.rb,
lib/qreport/time_parser/time_range.rb,
lib/qreport/time_parser/time_interval.rb,
lib/qreport/time_parser/time_relative.rb,
lib/qreport/time_parser/time_with_unit.rb
Defined Under Namespace
Modules: TimeUnit, Token Classes: Error, TimeInterval, TimeRange, TimeRelative, TimeWithUnit
Constant Summary collapse
- EOS =
Object.new
- @@operation_alias =
{ :plus => :+, :minus => :-, :in => :+, }
- @@direction_alias =
{ :ago => -1, :before => -1, :after => 1, :from => 1, :since => 1, :later => 1, }
Instance Attribute Summary collapse
-
#debug ⇒ Object
Returns the value of attribute debug.
-
#input ⇒ Object
Returns the value of attribute input.
-
#now ⇒ Object
Returns the value of attribute now.
-
#result ⇒ Object
Returns the value of attribute result.
-
#start ⇒ Object
Returns the value of attribute start.
-
#unit_for_now ⇒ Object
Returns the value of attribute unit_for_now.
Class Method Summary collapse
Instance Method Summary collapse
- #_wrap_p!(sel, &blk) ⇒ Object
- #describe_current_parse_position ⇒ Object
- #get_unit_for_now(name) ⇒ Object
-
#initialize(start = nil) ⇒ TimeParser
constructor
A new instance of TimeParser.
- #initialize_copy(x) ⇒ Object
- #lex ⇒ Object
- #parse(str, start = nil) ⇒ Object
- #push_token!(token) ⇒ Object
- #restore_tokens_on_failure!(sel) ⇒ Object
- #take_token ⇒ Object
- #token ⇒ Object
Constructor Details
#initialize(start = nil) ⇒ TimeParser
Returns a new instance of TimeParser.
14 15 16 17 18 19 20 |
# File 'lib/qreport/time_parser.rb', line 14 def initialize start = nil @start = start @unit_for_now = { :today => :day, :t => :now } @debug = false # @debug = true initialize_copy nil end |
Instance Attribute Details
#debug ⇒ Object
Returns the value of attribute debug.
12 13 14 |
# File 'lib/qreport/time_parser.rb', line 12 def debug @debug end |
#input ⇒ Object
Returns the value of attribute input.
12 13 14 |
# File 'lib/qreport/time_parser.rb', line 12 def input @input end |
#now ⇒ Object
Returns the value of attribute now.
12 13 14 |
# File 'lib/qreport/time_parser.rb', line 12 def now @now end |
#result ⇒ Object
Returns the value of attribute result.
12 13 14 |
# File 'lib/qreport/time_parser.rb', line 12 def result @result end |
#start ⇒ Object
Returns the value of attribute start.
12 13 14 |
# File 'lib/qreport/time_parser.rb', line 12 def start @start end |
#unit_for_now ⇒ Object
Returns the value of attribute unit_for_now.
12 13 14 |
# File 'lib/qreport/time_parser.rb', line 12 def unit_for_now @unit_for_now end |
Class Method Details
.def_p(name, &blk) ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/qreport/time_parser.rb', line 42 def self.def_p name, &blk sel = :"p_#{name}" _sel = :"_#{sel}" define_method _sel, &blk define_method sel do _wrap_p! sel do restore_tokens_on_failure!(sel) do send(_sel) end end end sel end |
.examples ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/qreport/time_parser/examples.rb', line 5 def self.examples now = ::Time.parse("2011-03-10T15:10:37.981304-06:00") examples = { "now" => "nil 2011-03-10T15:10:37.981304-06:00", now.to_s => "nil 2011-03-10T15:10:37.000000-06:00", "2011-03-10 15:10:37.981304 -0600" => "nil 2011-03-10T15:10:37.981304-06:00", "today" => ":day 2011-03-10T00:00:00.000000-06:00", "tomorrow" => ":day 2011-03-11T00:00:00.000000-06:00", "yesterday" => ":day 2011-03-09T00:00:00.000000-06:00", "9:15am yesterday" => ":min 2011-03-09T09:15:00.000000-06:00", "yesterday 9:15am" => ":day 2011-03-09T00:00:00.000000-06:00", # FIXME "10 days ago" => ":day 2011-02-28T00:00:00.000000-06:00", "10 s ago" => ":sec 2011-03-10T15:10:27.000000-06:00", "day before yesterday" => ":day 2011-03-08T00:00:00.000000-06:00", "hr before tomorrow" => ":hour 2011-03-10T23:00:00.000000-06:00", "3 days before today" => ":day 2011-03-07T00:00:00.000000-06:00", "5 days after today" => ":day 2011-03-15T00:00:00.000000-05:00", "5 days before now" => "nil 2011-03-05T15:10:37.981304-06:00", "3 days before this minute" => ":min 2011-03-07T15:10:00.000000-06:00", "5 days before yesterday" => ":day 2011-03-04T00:00:00.000000-06:00", "2 days before 50 hours after tomorrow" => ":hour 2011-03-11T02:00:00.000000-06:00", "2 centuries after today" => ":day 2211-01-21T00:00:00.000000-06:00", "1pm" => ":hour 2011-03-10T13:00:00.000000-06:00", "12:30pm" => ":min 2011-03-10T12:30:00.000000-06:00", "9:20am tomorrow" => ":min 2011-03-11T09:20:00.000000-06:00", "6am 3 days from yesterday" => ":hour 2011-03-12T06:00:00.000000-06:00", "2001/01" => ":mon 2001-01-01T00:00:00.000000-06:00", "2001-01" => ":mon 2001-01-01T00:00:00.000000-06:00", # "01/2001" => ":mon 2001-01-01T00:00:00.000000-06:00", "01/2001" => "#<Qreport::TimeParser::Error::Syntax: syntax error at position 2: \"01 |^| /2001\">", "2001/02/03 12:23pm" => ":min 2001-02-03T12:23:00.000000-06:00", "12/31 12:59pm" => ":min 2011-12-31T12:59:00.000000-06:00", "12/31 last year" => ":day 2010-12-31T00:00:00.000000-06:00", "12:59:59pm 12/31 next year" => ":sec 2012-12-31T12:59:59.000000-06:00", "1:23:45pm 1/2 in 2 years" => ":sec 2013-01-01T13:23:45.000000-06:00", "2011-03-10T15:10:37-06:00" => "nil 2011-03-10T15:10:37.000000-06:00", "2011-03-10T15:10:37.981304-06:00" => "nil 2011-03-10T15:10:37.981304-06:00", "2011-03-10T15:10:37-06:00 plus 10 sec" => ":sec 2011-03-10T15:10:47.000000-06:00", "2011-03-10T15:10:37.981304-06:00 - 2 weeks" => "nil 2011-02-24T15:10:37.981304-06:00", "now minus 2.5 weeks" => "nil 2011-03-10T15:10:35.481304-06:00", "t - 10 sec" => "nil 2011-03-10T15:10:27.981304-06:00", "123.45 sec ago" => "nil 2011-03-10T15:08:34.531303-06:00", "year 2010" => ":year 2010-01-01T00:00:00.000000-06:00", "between 12:45pm and 1:15pm" => ":min 2011-03-10T12:45:00.000000-06:00 ... :min 2011-03-10T13:15:00.000000-06:00", "before 1:23pm tomorrow" => ":min 2011-03-11T13:22:00.000000-06:00", "this minute" => ":min 2011-03-10T15:10:00.000000-06:00", "last hour" => ":hour 2011-03-10T14:00:00.000000-06:00", "previous hour" => ":hour 2011-03-10T14:00:00.000000-06:00", "last day" => [ ":day 2011-03-09T00:00:00.000000-06:00", ":day 2011-03-09T00:00:00.000000-06:00 ... :day 2011-03-10T00:00:00.000000-06:00" ], "previous day" => ":day 2011-03-09T00:00:00.000000-06:00", " 2001-01 + 1234 ajsdkfsd hours" => "#<Qreport::TimeParser::Error::Syntax: syntax error at position 17: \" 2001-01 + 1234 |^| ajsdkfsd hours\">", "15 sec" => "#<Qreport::TimeParser::Error: Qreport::TimeParser::Error>", "12 minutes" => "#<Qreport::TimeParser::Error: Qreport::TimeParser::Error>", } examples[:now] = now examples end |
Instance Method Details
#_wrap_p!(sel, &blk) ⇒ Object
195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/qreport/time_parser.rb', line 195 def _wrap_p! sel, &blk if @debug $stderr.puts " #{' ' * @p_depth} #{sel} ... | #{token.inspect} #{token.value.inspect}" @p_depth += 1 begin restore_tokens_on_failure!(sel, &blk) ensure @p_depth -= 1 $stderr.puts " #{' ' * @p_depth} #{sel} => #{result.inspect} | #{token.inspect}" end else restore_tokens_on_failure!(sel, &blk) end end |
#describe_current_parse_position ⇒ Object
381 382 383 384 385 |
# File 'lib/qreport/time_parser.rb', line 381 def describe_current_parse_position s = @input_orig.dup s[@pos, 0] = " |^| " s end |
#get_unit_for_now(name) ⇒ Object
410 411 412 413 |
# File 'lib/qreport/time_parser.rb', line 410 def get_unit_for_now name name = name.to_sym @unit_for_now[name] || @unit_for_now[nil] end |
#initialize_copy(x) ⇒ Object
22 23 24 25 26 27 |
# File 'lib/qreport/time_parser.rb', line 22 def initialize_copy x @input = '' @token = nil @token_stack = [ ] @taken_tokens = [ ] end |
#lex ⇒ Object
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 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 |
# File 'lib/qreport/time_parser.rb', line 254 def lex debug = @debug type = value = nil @input.sub!(/\A(\s+)/, '') pre_whitespace = $1 @pos += pre_whitespace.size if pre_whitespace # $stderr.puts " @input = #{@input.inspect[0, 20]}..."; debugger case @input when '' return EOS when /\A(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(-[\d:]+)?)\b/ # iso8601 value = TimeWithUnit.new(Time.parse($1), nil) when /\A(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d+)?(\s+[-+]?[\d]+)?)\b/ # Time#to_s value = TimeWithUnit.new(Time.parse($1), nil) when /\A(year\s+(\d+))/i year = $2 && $2.to_i value = TimeRelative.new value.year = year type = :date_relative when /\A((\d{4})(?:([-\/])(0?[1-9]|1[0-2])(?:\3([0-2][0-9]|3[01]))?))\b/i year = $2 && $2.to_i sep = $3 mon = $4 && $4.to_i day = $5 && $5.to_i value = TimeRelative.new value.year = year value.mon = mon value.day = day type = :date_relative when /\A((0[1-9]|1[0-2]|[1-9])(?:[-\/](0[1-9]|[1-2][0-9]|3[01]|[1-9])))\b/i mon = $2 && $2.to_i day = $3 && $3.to_i value = TimeRelative.new value.mon = mon value.day = day type = :date_relative # debug = true when /\A((0?[1-9]|1[0-2])(?::([0-5][0-9])(?::([0-5][0-9]|60))?)\s*(am?|pm?)?)\b/i hour = $2.to_i min = $3 && $3.to_i sec = $4 && $4.to_i meridian = ($5 || '').downcase hour = 0 if hour == 12 hour += 12 if meridian.index('p') value = TimeRelative.new value.hour = hour value.min = min value.sec = sec type = :time_relative when /\A(\d\d?)([\/-])(\d\d\d\d)\b/i mon = $1 && $1.to_i sep = $2 year = $3 && $3.to_i value = TimeRelative.new value.year = year value.mon = mon type = :date_relative when /\A((0?[1-9]|1[0-2])\s*(am?|pm?))\b/i hour = $2.to_i meridian = ($3 || '').downcase hour = 0 if hour == 12 hour += 12 if meridian.index('p') value = TimeRelative.new value.hour = hour type = :time_relative when /\A([-+]?\d+\.\d*|\.\d+)/ value = $1.to_f type = :number when /\A([-+]?\d+)/ value = $1.to_i type = :number when /\A(\+|\-|plus\b|minus\b|in\b)/i value = $1.downcase.to_sym value = @@operation_alias[value] || value type = :operation when /\A(today|now|t)\b/i case unit = get_unit_for_now($1) when :now value = TimeWithUnit.new(now, nil) else value = TimeWithUnit.new(now, unit) end when /\A(yesterday)\b/i value = TimeWithUnit.new(now, :day) - 1 when /\A(tomorrow)\b/i value = TimeWithUnit.new(now, :day) + 1 when /\A((this)\s+(#{TimeUnit::UNIT_REGEXP}))\b/io value = TimeWithUnit.new(now, $3) when /\A((previous|last|next)\s+(#{TimeUnit::UNIT_REGEXP}))\b/io value = TimeWithUnit.new(now, $3) + TimeInterval.new($2, $3) when /\A(#{TimeUnit::UNIT_REGEXP})\b/io value = TimeInterval.new(1, $1) type = :unit when /\A(ago)\b/i value = $1.downcase.to_sym value = @@direction_alias[value] type = :relative when /\A(before|after|from|since)\b/i value = $1.downcase.to_sym value = @@direction_alias[value] type = :relation when /\A(between)\b/i value = $1.downcase.to_sym type = :range when /\A(and|or)\b/i value = $1.downcase.to_sym type = :logical else desc = describe_current_parse_position err = Error::Syntax.new("syntax error at position #{@pos}: #{desc.inspect}") err.position = @pos err.description = desc raise err end token = $1 pos = @pos @input[0, token.size] = '' @pos += token.size token.extend(Token) token.pos = pos token.pre = pre_whitespace token.value = value token.type = type $stderr.puts " token => #{token.inspect}" if debug token end |
#parse(str, start = nil) ⇒ Object
29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/qreport/time_parser.rb', line 29 def parse str, start = nil start ||= @start $stderr.puts "\n parse #{str.inspect} #{start.inspect}" if @debug @input_orig = str.dup @input = str.dup @pos = 0 @p_depth = 1 @result = send(start || :p_start) @result = @result.value if @result.respond_to?(:value) $stderr.puts " parse #{str.inspect} #{start.inspect} => #{@result.inspect}\n\n" if @debug @result end |
#push_token!(token) ⇒ Object
244 245 246 247 248 249 250 251 252 |
# File 'lib/qreport/time_parser.rb', line 244 def push_token! token if @token @token_stack.unshift @token $stderr.puts "push_token! #{@token.inspect}" if @debug end @token = token $stderr.puts "push_token! #{@token.inspect}" if @debug self end |
#restore_tokens_on_failure!(sel) ⇒ Object
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/qreport/time_parser.rb', line 210 def restore_tokens_on_failure!(sel) restore = true (@taken_tokens_stack ||= [ ]) << @taken_tokens @taken_tokens = [ ] begin result = yield # $stderr.puts " #{sel.inspect} taken_tokens = #{@taken_tokens.inspect}" restore = false if result result ensure if restore && ! @taken_tokens.empty? $stderr.puts " #{sel.inspect} restoring tokens #{@taken_tokens.inspect}" if @debug @taken_tokens.reverse.each do | t | push_token! t end end @taken_tokens = @taken_tokens_stack.pop end end |
#take_token ⇒ Object
237 238 239 240 241 242 |
# File 'lib/qreport/time_parser.rb', line 237 def take_token t = token @taken_tokens << t @token = nil t end |
#token ⇒ Object
232 233 234 235 |
# File 'lib/qreport/time_parser.rb', line 232 def token @token ||= (@token_stack.first ? @token_stack.shift : lex) end |