Example

Now, let’s use Lango to do the example in the beginning which is to to transform the following sentences into the corresponding programmatic commands:

Call me an Uber -> me.call({‘item’: ‘uber’})

Get my mother some flowers -> me.mother.get({‘item’: ‘flowers’})

Order me a pizza with extra cheese -> me.order({‘item’: ‘pizza’, ‘with’: ‘extra cheese’})

Give Sam’s dog a biscuit from Petshop -> sam.dog.give({‘item’: ‘biscuit’, ‘from’: ‘Petshop’})

At first glance, there doesn’t seem to be an obvious pattern with these sentences. Let’s take a look at the parse trees for the above sentences so that we can make generalized match trees for them. Call me an Uber: (S (VP (VB Call) (S (NP (PRP me)) (NP (DT an) (NNP Uber)))) ) Get my mother some flowers (incorrect parse): (S (VP (VB Get) (NP (PRP$ my) (NN mother)) (NP (DT some) (NNS flowers))) ) Order me a pizza with extra cheese: (S (VP (VB Order) (S (NP (PRP me)) (NP (NP (DT a) (NN pizza)) (PP (IN with) (NP (JJ extra) (NN cheese)))))) ) Give Sam’s dog a biscuit from Petshop: (S (VP (VB Give) (S (NP (NP (NNP Sam) (POS 's)) (NN dog)) (NP (NP (DT a) (NN biscuit)) (PP (IN from) (NP (NNP Petshop)))))) ) Engineering Match Trees

From the parse trees, we can see that although the sentences look very different, the syntax is very similar. We can take advantage of this by matching the patterns of the parse trees. If we look at the top parts of all these parse trees, we can see they follow the structure of either:

( S ( VP ( VB ) ( S ( NP ) ( NP ) ) ) or

( S ( VP ( VB ) ( NP ) ( NP ) )

We can go a step further and also add context to these match trees to match the action verb, the subject subtree and the object subtree:

( S ( VP ( VB:action-o ) ( S ( NP:subj_t ) ( NP:obj_t ) ) ) )

( S ( VP ( VB:action-o ) ( NP:subj_t ) ( NP:obj_t ) ) )

Let’s look at the subtrees for the subtree NP:subj_t for the sentences above: ( NP ( PRP$ me ) )

( NP ( PRP$ my ) (NN mother ) )

( NP ( PRP$ me ) )

( NP ( NP ( NNP Sam ) ( POS ‘s ) ) ( NN dog ) ) ) We can use the following rules to match context for subtree NP:subj_t to match the subject noun and the relation to the subject if it exists. ( NP ( PRP$:subject-o=my ) ( NN:relation-o ) )

( NP ( NP ( NNP:subject-o ) ( POS ) ) ( NN:relation-o ) )

( NP:subject-o ) Next, let’s look at the subtrees for NP:obj_t for the sentences above: ( NP ( DT an ) ( NNP Uber ) )

( NP ( DT some ) ( NNS flowers ) )

( NP ( NP ( DT a ) ( NN pizza ) ) ( PP ( IN with ) ( NP ( JJ extra ) ( NN cheese ) ) )

( NP ( NP ( DT a ) ( NN biscuit ) ) ( PP ( IN from ) ( NP ( NNP Petshop ) ) ) ) We can use the following rules to match context for subtree NP:obj_t to match the item and any modifiers/addons for the item. ( NP ( NP:item-o ) ( PP ( IN:item_in-o ) ( NP:item_addon-o ) ) )

( NP:item-o ) Finally, after matching all the trees and subtrees, our matched contexts for each sentence is: Call me an Uber: action: Call subject: me item: Uber Get my mother some flowers (incorrect parse): action: Get subject: my relation: mother item: flowers Order me a pizza with extra cheese: action: Order subject: me item: pizza item_in: with item_addon: extra cheese Give Sam’s dog a biscuit from Petshop: action: Give subject: Sam relation: dog item: biscuit item_in: from item_addon: Petshop

Once we have the context, we can easily transform them into programmatic language: def perform_action(action, item, subject, relation=None, item_addon=None, item_in=None): entity = subject if entity == "my": entity = "me" if relation: entity = '{0}.{1}'.format(entity, relation) item_props = {'item': item} if item_in and item_addon: item_props[item_in] = item_addon return '{0}.{1}({2})'.format(entity, action, item_props)

Full Code

Below is the full working code for the example. As you can see the it is very short and can handle any sentence which matches the syntax structure which may be hundreds of sentences. It is also easy to understand what is being matched compared to the black box of intent based guessing.

from collections import OrderedDict from lango.parser import StanfordLibParser from lango.matcher import match_rules parser = StanfordLibParser() sents = [ 'Call me an Uber.', 'Get my mother some flowers.', 'Order me a pizza with extra cheese.', 'Give Sam\'s dog a biscuit from Petshop.' ] """ me.call({'item': u'uber'}) my.mother.get({'item': u'flowers'}) me.order({'item': u'pizza', u'with': u'extra cheese'}) sam.dog.give({'item': u'biscuit', u'from': u'petshop'}) """ subj_obj_rules = { 'subj': OrderedDict([ # my brother / my mother ('( NP ( PRP$:subject-o=my ) ( NN:relation-o ) )', {}), # Sam's dog ('( NP ( NP ( NNP:subject-o ) ( POS ) ) ( NN:relation-o ) )', {}), # me ('( NP:subject-o )', {}), ]), 'obj': OrderedDict([ # pizza with onions ('( NP ( NP:item-o ) ( PP ( IN:item_in-o ) ( NP:item_addon-o ) ) )', {}), # pizza ('( NP:item-o )', {}), ]) } rules = { # Get me a pizza '( S ( VP ( VB:action-o ) ( S ( NP:subj ) ( NP:obj ) ) ) )': subj_obj_rules, # Get my mother flowers '( S ( VP ( VB:action-o ) ( NP:subj ) ( NP:obj ) ) )': subj_obj_rules, } def perform_action(action, item, subject, relation=None, item_addon=None, item_in=None): entity = subject if entity == "my": entity = "me" if relation: entity = '{0}.{1}'.format(entity, relation) item_props = {'item': item} if item_in and item_addon: item_props[item_in] = item_addon return '{0}.{1}({2})'.format(entity, action, item_props) for sent in sents: tree = parser.parse(sent) print match_rules(tree, rules, perform_action)

Tips and Tricks

From my experience with working with match trees, here are some tips and tricks:

Look at some parse trees of some of the sentences/commands you will be processing and find patterns at the top level of the tree and work your way down.

Make sure your commands/sentences are punctuated (have periods at the end) before parsing through the Stanford Parser

Take advantage of the recursive structure of parse trees to recursively match rules.

More to come: