This parser is a variation of what I originally made for Grendel. I dug back into the Grendel source code to make this, which fixes a few obvious bugs.

This style of parsing, which is what you’d find in most older IF engines, could be massively improved by using a complex library like NLTK, spaCy, or Google’s Parsey McParseface, but simple parsing like this has a kind of beautiful simplicity.

Source

[python]
class Processor:

directobjects = ["through", "into", "in", "out", "on", "off", "with", "to", "for", "from", "at", "around"]

def __init__(self):
self.solved = {
‘verb’: None,
‘object’: None,
‘objectadj’: None,
‘indirectobject’: None,
‘indirectobjectadj’: None
}
self.originalphrase = None
self.errors = []

def OriginalPhrase(self): return self.originalphrase
def Verb(self): return self.solved["verb"]
def Object(self): return self.solved["object"]
def ObjectAdj(self): return self.solved["objectadj"]
def IndirectObject(self): return self.solved["indirectobject"]
def IndirectObjectAdj(self): return self.solved["indirectobjectadj"]
def Errors(self): return self.errors

def RequiresIndirectObject(self, verb, words = ""):
# Certain verbs in certain situations require an indirect object. This helps determine those situations.
if (verb == "throw" or verb == "push") and (ArrayIndex(self.directobjects, words)): return 1
elif verb == "put": return 1
else: return None

# THIS IS WHERE THE MAGIC HAPPENS
def Chew(self, input):
self.originalphrase = input

# Standardise the case
input = input.lower()

# Strip needless words and characters
for i in ["the", "a", "an"]:
input = input.replace(" "+i+" ", " ")
for i in ["?", ".", ",", "!"]:
input = input.replace(i, " ")

# Chop chop chop
words = input.split()

# Let’s just assume that the first word is a verb
try: self.solved["verb"] = words[0]
except: pass

# Do we need an indirect object?
if (self.RequiresIndirectObject(self.Verb(), words) or ArrayIndex(self.directobjects, words)) and len(words) > 3:
index = ArrayIndex(self.directobjects, words)
if(index > 2): # Let’s brute-force this sucker then correct the result later
try:
self.solved["indirectobject"] = words[len(words)-1]
self.solved["indirectobjectadj"] = words[len(words)-2]
self.solved["object"] = words[index-2]
self.solved["objectadj"] = words[index-3]
except: # Whoops, the phrase isn’t that long
self.solved["indirectdobject"] = None
self.solved["object"] = words[len(words)-1]
self.solved["objectadj"] = words[len(words)-2]
# A bit of clean-up, checking for errors with the indirect object
try:
self.directobjects.index(self.IndirectObject())
self.solved["indirectobject"] = None
except: pass
else: # Hmm… we seem to be missing an indirect object
self.solved["object"] = words[len(words)-1]
self.solved["objectadj"] = words[len(words)-2]
if not self.IndirectObject():
self.errors.append("no_indirect_object")
else: # Okay, we don’t need an indirect object, let’s just continue
if len(words) > 1:
self.solved["object"] = words[len(words)-1]
self.solved["objectadj"] = words[len(words)-2]
else:
self.solved["object"] = None
self.solved["indirectdobject"] = None

# Okay, let’s do a bit of clean-up and weed out some of the obvious mistakes
try:
(self.directobjects + [self.Verb(), self.Object(), self.IndirectObject()] + ["up", "down"]).index(self.ObjectAdj())
self.solved["objectadj"] = None
except: pass
try:
(self.directobjects + [self.Verb(), self.Object(), self.IndirectObject()] + ["up", "down"]).index(self.IndirectObjectAdj())
self.solved["indirectobjectadj"] = None
except: pass

# Done! We should have the phrase broken down and identified by this point
return True

def Shout(phrase):
processor = Processor()
processor.Chew(phrase)
print "verb", processor.Verb(), "",
if processor.Object(): print "(the)",
if processor.ObjectAdj():
print "adjective["+processor.ObjectAdj()+"]",
if processor.Object():
print "object", processor.Object(), "",
if processor.IndirectObject():
print "(with)",
if processor.IndirectObjectAdj():
print "adjective["+processor.IndirectObjectAdj()+"]",
print "indirect object", processor.IndirectObject(), "",
print
print

def ArrayIndex(needle, haystack):
# Checks to see if anything in an array exists inside another array.
for i in needle:
try: index = haystack.index(i)+1
except: None
try: return index
except: return None

Shout("eat the apple")
[/python]