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

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")