--- /dev/null
+
+# coding: utf-8
+import yaml
+import collections
+import random
+
+Match = collections.namedtuple('Match', 'text, rule, bindings')
+
+pronoun_swaps = {
+ 'i': 'you',
+ 'me': 'you',
+ 'my': 'your',
+ 'mine': 'yours',
+ 'am': 'are'
+}
+
+def read_rules(rules_file):
+ with open(rules_file) as f:
+ rules = [{'pattern': r['pattern'].split(),
+ 'responses': [t.split() for t in r['responses']]}
+ for r in yaml.load(f)]
+ return rules
+
+def is_var(word):
+ return word[0] == '?'
+
+
+def splits(item):
+ return [(item[:i], item[i:]) for i in range(len(item)+1)]
+
+def match(text, rule):
+ return all_matches([Match(text, rule, {})])
+
+def all_matches(matches):
+ successes = []
+ while matches:
+ # print(matches, successes)
+ current = matches[0]
+ new_matches = []
+ if successful_match(current):
+ successes += [current.bindings]
+ elif current.rule:
+ new_matches = match_item(current.text, current.rule, current.bindings)
+ matches = matches[1:] + new_matches
+ return successes
+
+def successful_match(match):
+ return match.text == [] and match.rule == []
+
+def match_item(text, rule, bindings):
+ r0 = rule[0]
+ if is_var(r0):
+ if r0 in bindings:
+ # already seen this variable
+ if text[:len(bindings[r0])] == bindings[r0]:
+ return [Match(text[(len(bindings[r0])):], rule[1:], bindings)]
+ else:
+ return []
+ else:
+ # not seen this variable yet
+ matches = []
+ for pre, suf in splits(text):
+ new_bindings = bindings.copy()
+ new_bindings[r0] = pre
+ matches += [Match(suf, rule[1:], new_bindings)]
+ return matches
+ elif text and text[0] == r0:
+ return [Match(text[1:], rule[1:], bindings)]
+ else:
+ return []
+
+def candidate_rules(rules, comment):
+ return [(rule, bindings)
+ for rule in rules
+ for bindings in match(comment, rule['pattern'])]
+
+
+def fill(response, bindings):
+ filled_response = []
+ for w in response:
+ if is_var(w):
+ if w in bindings:
+ filled_response += bindings[w]
+ else:
+ filled_response += ['MISSING']
+ else:
+ filled_response += [w]
+ return filled_response
+
+
+def pronoun_person_swap(bindings):
+ def swapped(words):
+ sw = []
+ for w in words:
+ if w in pronoun_swaps:
+ sw += [pronoun_swaps[w]]
+ else:
+ sw += [w]
+ return sw
+
+ return {var: swapped(bindings[var]) for var in bindings}
+
+def respond(rule, bindings):
+ return fill(random.choice(rule['responses']), bindings)
+
+
+def eliza_loop(rules):
+ print("Hello. I'm Eliza. What seems to be the problem?")
+ while True:
+ c = input("> ")
+ if c.strip() in 'quit halt exit stop'.split(): break
+ comment = c.split()
+ rule, bindings = candidate_rules(rules, comment)[0]
+ swapped_bindings = pronoun_person_swap(bindings)
+ print(' '.join(respond(rule, swapped_bindings)))
+
+
+all_rules = read_rules('rules.yaml')
+eliza_loop(all_rules)
+
+
+
+
+
+