Calling jsRealB from Python

Motivation

The input notation to jsRealB realizer was originally designed to be a valid JavaScript program reflecting the traditional constituent syntax formalism which is quite convenient when the NLG system is written in Javascript. Given the fact that Python has become the lingua franca of NLP, we have developed an alternative JSON input format for jsRealB that can be built using the standard Python JSON API.

This is useful, but for Python applications, it is now much more convenient to use pyrealb, written in Python, which implements the same capabilities as jsRealB.

JSON creation using the standard API

The following table shows how JSON values are mapped to Python values. Using the JSON API, it is relatively easy to create in Python a value that can be serialized into JSON that can be sent to a jsRealB HTTP server using the JSON input format.

JSON Python
object dict
array list
string string
number number
true True
false False
null None

JSON creation using the Constituent notation

Using the classical constituent based notation is quite similar to Python constructors which are called by simply writing their name, usually starting with a capital letter. Similarly to Javascript, the created object can use the dot notation to call its members. So we developed a constituent based notation for creating Python data structures which is similar (in fact the same, except for False, True, None in Python that are coded as false, true, null in JavaScript) as the one used for the jsRealB Javascript version. Of course, in Python this notation is only used for creating a data structure, while in Javascript it can be serialized directly.

The system is based on three main classes: Constituent with two subclasses, Terminal and Phrase. The Phrase is subclassed to define the jsRealB constructors: S, SP, NP,... Terminal is subclassed to define D, N, V, ... See the jsRealB documentation for details.

With this notation, it is now easy to create Python data structures using the usual jsRealB notation. For example

sent = S(                 # Sentence
  Pro("him").c("nom"),    # Pronoun (citation form), nominative case
  VP(V("eat"),            # Verb at present tense by default
     NP(D("a"),           # Noun Phrase, Determiner
        N("apple").n("p") # Noun plural
       ).tag("em")        # add an <em> tag around the NP
    ))

creates a Python data structure that can be serialized as string with the JSON representation with str(sent) to return

'{"phrase":"S","props":{},"elements":[{"terminal":"Pro","props":{"c":"nom"},"lemma":"him"},{"phrase":"VP","props":{},"elements":[{"terminal":"V","props":{},"lemma":"eat"},{"phrase":"NP","props":{"tag":"em"},"elements":[{"terminal":"D","props":{},"lemma":"a"},{"terminal":"N","props":{"n":"p"},"lemma":"apple"}]}]}]}'

A pretty printed JSON string can be printed with print(sent.pp())

{"phrase":"S",
 "elements":[{"terminal":"Pro","lemma":"him","props":{"c":"nom"}}},
             {"phrase":"VP",
              "elements":[{"terminal":"V","lemma":"eat"},
                          {"phrase":"NP","props":{"tag":"em"},
                           "elements":[{"terminal":"D","lemma":"a"},
                                       {"terminal":"N","lemma":"apple","props":{"n":"p"}}}]}]}]}

A pretty-printed constituent notation of the expression can be printed with print(sent.show())

S(Pro("him").c("nom"),
  VP(V("eat"),
     NP(D("a"),
        N("apple").n("p")).tag("em")))

Note that this string is recomputed from the internal data structure so it is not necessarily the one used as input, especially if it has been modified by the program since its creation. Calling show(-1) will create the expression on a single line which is shorter for sending to the web server.

Realizing the expression with a jsRealB server

jsRealB(exp,lang="en") sends the Python expression exp (either in JSON or in jsRealB like fashion produced by show) to an HTTP jsRealB server and returns the realized string. By default, the jsRealB server runs on http://127.0.0.1:8081. lang sets the realization language, English by default.

Modification of data structure

Although the JSON notation is a static description of a structures, the add(self,elements,ix=None) method of the Phrase class can modify an existing structure as it can be in Javascript. ix is the position at which the new element will be added within the current elements. When not specified it is added at the end. For example, after the following definitions:

apple = NP(D("a"),N("apple"))
boy = NP(D("the"),N("boy"))

the call

jsRealB(S(VP().add(V("love")).add(apple)).add(boy.n("p"),0))

creates the following structure

S(NP(D("the"),
     N("boy")).n("p"),
  VP(V("love"),
     NP(D("a"),
        N("apple"))))

which is realized as

'The boys love an apple.'

Lists as Phrase parameter

It can be convenient to generate Phrase parameters systematically using lists with possibly None values that should be ignored when creating the parameters. So Python phrase constructors will insert the values within a list as parameter and ignore None values; it will also flatten any embedded list. For example:

NP(D("the"),[A("nice"),None,A("little")],[N("cat")]) is equivalent to NP(D("the"),A("nice"),A("little"),N("cat"))

Dynamically changing the realization language

Initially, the realization language is set by the server. The option set_lang("en|fr") can set the realization language for an expression and its children. This facility for bilingual realization is only available when JSON is sent to the server.

Usage

To add these functions to a Python 3 program. First copy this file in your directory, then add this to your program

from jsRealBclass import N,A,Pro,D,Adv,V,C,P,DT,NO,Q,  NP,AP,AdvP,VP,CP,PP,S,SP,  Constituent, Terminal, Phrase, jsRealB

Conclusion

We have described the use of the jsRealB text realizer from a Python program by sending a serialized Python data structure to an HTTP server. We have also described how to create this structure from a constituent inspired notation. Of course, it would be simpler if the realization process could also be done in Python, but we leave this as an exercise for the reader!

Guy Lapalme, September 2020