Full location support with partial match smartitty
[ottawa-travel-planner.git] / CommandParser.py
blob205cec50c5df6eb0662dbe1437e1018975fed7e4
2 # vi: set softtabstop=4 shiftwidth=4 tabstop=8 expandtab:
4 """Splits a command into source, destination, and time."""
6 import sre
7 import time
9 import PlanTime
10 import PlanLocation
11 import LandmarkMatcher
13 class Command:
14 def __init__(self):
15 self.start = None
16 self.end = None
17 self.time = None
19 class CommandParseException(Exception):
20 pass
22 class CommandParser:
23 def __init__(self, str):
24 self.cmd = None
25 self.landmarkMatcher = None
27 self.parse(str)
29 def parse(self, str):
30 # Look for start and end at the start of the string.
31 match = _full_rx.match(str)
32 if not match:
33 raise CommandParseException("Failed to extract source and "
34 "destination from command '%s'" % str);
35 self.cmd = Command()
36 self.cmd.start = self.parseLocation(match.group("start"))
37 self.cmd.end = self.parseLocation(match.group("end"))
39 # Now look for time constraints.
40 rule = match.group("rule")
41 timestr = match.group("time")
42 if rule and timestr:
43 ruleMap = {
44 "by": PlanTime.MUST_ARRIVE_BEFORE,
45 "at": PlanTime.MUST_LEAVE_AFTER,
47 self.cmd.time \
48 = PlanTime.PlanTime(self.decodeTime(timestr), ruleMap[rule])
50 def parseLocation(self, str):
51 match = _intersection_rx.match(str)
52 if match:
53 return PlanLocation.IntersectionLocation(match.group(1),
54 match.group(2))
56 match = _stop_rx.match(str)
57 if match:
58 return PlanLocation.StopLocation(match.group(1))
60 match = _address_rx.match(str)
61 if match:
62 return PlanLocation.AddressLocation(str)
64 # Try a landmark.
65 if self.landmarkMatcher is None:
66 self.landmarkMatcher = LandmarkMatcher.LandmarkMatcher()
67 return PlanLocation.LandmarkLocation(self.landmarkMatcher.match(str))
69 def decodeTime(self, str):
70 match = _time_rx.match(str)
71 if not match:
72 raise CommandParseException("Unable to decode timespec '%s'" % str)
73 t = time.localtime()
74 return time.mktime(t)
76 _full_re = ("(?i)^\s*(?P<start>.*?)\s+to\s+(?P<end>.*?)\s*"
77 # at this point the pattern either ends, or there's a timespec
78 # Require the time to have at least one digit so people can still
79 # use "at" for intersections (although "and" is better)
80 "(?:$|(?P<rule>by|at)\s+(?P<time>\d.+?)\s*$)")
81 _full_rx = sre.compile(_full_re)
83 _intersection_re = "(?i)^(.+?)\s+(?:at|and)\s*(.+?)$"
84 _intersection_rx = sre.compile(_intersection_re)
86 # "stop 6037" or just "6037"
87 _stop_re = "(?i)^(?:stop)?\s*(\d{4})$"
88 _stop_rx = sre.compile(_stop_re)
90 # "123 some street"
91 _address_re = '(?i)^\d+\s+\S.*'
92 _address_rx = sre.compile(_address_re)
94 # 11:30
95 # 11:30 pm
96 # 1130 pm
97 # 130 pm
98 # 130
99 _time_re = ("^(?P<hour>\d{1,2}):?(?P<min>\d\d)\s*(?i)(?P<mod>am?|pm?)?$")
100 _time_rx = sre.compile(_time_re)