JSON use within jsRealB

Motivation

The input notation to jsRealB realizer was originally designed to be a valid JavaScript program that reflects the traditional constituent syntax formalism. An internal structure is built by Constructors that create JavaScript objects which can be modified with property functions. The fact that jsRealB expressions are JavaScript expressions allows the use of the many development tools available for JavaScript to create and debug the realizer. This also allows a seamless integration in the browser environment.

While being compact and linguist-friendly, this notation is not so convenient when it must be created by another system. Producing a syntactically legal JavaScript programmatically can be cumbersome and error-prone. This is why we have defined an alternative input format to jsRealB using JSON. Almost all programming languages provide convenient and efficient APIs for transforming their data structures in JSON. Using this format, the What to say part of a natural language generation system can be defined in any programming language, while being able to use the jsRealB realizer for producing a well-formed and well-formatted natural language utterance. This is similar in spirit to the XML input format for SimpleNLG.

Use cases

This format of JSON input has been used as output from two different programming languages, for which auxiliary functions have been developed to ease the creation of the JSON structures.

Python
documentation
source file
Prolog
it uses the classical JSON representation of SWI-Prolog
source file

Description

This notation closely parallels the usual jsRealB function calls that create constituents as described in the documentation. The JSON input is an object with a key that indicates if it is a phrase or a terminal depending on its key.

Constituents have two optional fields associated with the following keys:

To create the internal representation of the realizer, the JSON data structure is given as parameter to the fromJSON(...) function which traverses the object and calls the appropriate constructors and option functions.

The following table illustrates the parallel between both jsRealB specifications for the sentence: The cat will not eat grey mice.



S(
  NP(
     D("the"),
     N("cat")),  
  VP(
     V("eat").t("f"),
     NP(
        D("a"),
        A("grey"),
        N("mouse").n("p")))
        
 ).typ({"neg":true})


fromJSON(
    {"phrase":"S",
     "elements":[{"phrase":"NP",
                  "elements":[{"terminal":"D","lemma":"the"},
                              {"terminal":"N","lemma":"cat"}]},
                 {"phrase":"VP",
                  "elements":[{"terminal":"V","lemma":"eat","props":{"t":"f"}},
                              {"phrase":"NP",
                               "elements":[{"terminal":"D","lemma":"a"},
                                           {"terminal":"A","lemma":"grey"},
                                           {"terminal":"N","lemma":"mouse",
                                            "props":{"n":"p"}}]}]}],
     "props":{"typ":{"neg":true}},
     "lang":"en"}
)

The JSON specification (on the right) is admittedly much more verbose than the original one (on the left). But like any markup language, the JSON specification is designed to be created programmatically using the JSON API of a host system, not written by a human.

Schema of the JSON input

The following is the json-rnc schema used to validate the JSON input to jsRealB. This schema describes a valid JSON input, but it does not implement all constraints, most of which will be checked when fromJSON(...) interprets the key-value pairs within the props fields. Download the file [here](jsRealB.jsonrnc).


start = Constituent

Constituent = Phrase | Terminal 

Phrase = { 
    phrase  : /S|NP|AP|VP|AdvP|PP|CP|SP/,
    elements: [Constituent],
    props?  : propsContent,
    lang?   : /en|fr/    
}

Terminal = {
    terminal: /N|A|Pro|D|V|Adv|C|P|DT|NO|Q/
    lemma   : string,
    props?  : propsContent,
    lang?   : /en|fr/    
}

propsContent = {
    g?   : /[mfx]/,                      # gender: masculine, feminine or neutral
    n?   : /[sp]/,                       # number: singular, plural
    pe?  : (/123/|1|2|3),                # person either as a number or a string containing a number
    t?   : /p|i|f|ps|c|s|si|ip|pr|pp|b|pc|pq|cp|fa|spa|spq/, # tense
    f?   : /co|su/,                      # comparative or superlative form
    aux? : /av|êt|aê/,                   # French auxiliary
    tn?  : /|refl/,                      # tonic for pronoun
    c?   : /nom|acc|dat|refl|gen/,       # case for pronoun
    ow?  : /s|p/,                        # owner for English pronoun: singular, plural
    pos? : /post|pre/,                   # (post or pre)position of a French adjective
    pro? : //,                           # NP or PP should be pronominalized
    nat? : boolean,                      # NO or DT should be in natural form
                            # formatting options
    cap? : //,                           # first letter of constituent should be capitalized 
    lier?: //,                           # an hyphen should be added with the following word
                            # list options
    b?   : [string],                     # strings to be added before a constituent
    a?   : [string],                     # strings to be added after a constituent
    en?  : [string],                     # strings to be added before and after a constituent
                            # special cases
    tag? : tagContent,                    
    date?: dateContent,
    no?  : noContent,
    typ? : typContent
}

tagContent = [[string|{*:string}]       # list of [tagname,{attributes as key:value pairs}]
  @(minItems=2,maxItems=2)]

dateContent = {                         # selection of fields when realizing a DT
    year?  : boolean,
    month? : boolean,
    date?  : boolean,
    day?   : boolean,
    hour?  : boolean,
    minute?: boolean,
    second?: boolean,
    nat?   : boolean,
    det?   : boolean,
    rtime? : boolean
}

noContent = {                           # format of number
    mprecision?: integer,               # max precision
    raw?       : boolean,               # output as originally entered
    nat?       : boolean,               # output as "number in letters"
    ord?       : boolean                # output as an ordinal number
}

typContent = {                          # modification of the type of sentence
    neg?  : (boolean|string)            # negation (in French, string is used instead of "pas")
    pas?  : boolean,                    # passive
    prog? : boolean,                    # use progressive 
    exc?  : boolean,                    # exclamative form
    perf? : boolean,                    # use perfect (English only)
    contr?: boolean,                    # use contracted form (English only)
    mod?  : (boolean | /poss|perm|nece|obli|will/), # modals
    int?  : (boolean | /yon|wos|wod|wad|woi|whe|why|whn|how|muc/) # questions
}
    

Remarks

Example of use

The JSON specification is useful as input to a Unix-like filter that takes the output of another system. The dist directory shows jsRealB-filter.js, a node.js script that can be used for such purpose. After reading a line from the standard input, it checks if it starts with a left brace. If so it takes for granted that it is a JSON input, it parses the line as JSON and calls fromJSON on the resulting parse and realizes the expression. Here is an example of a simplistic external process for creating a JSON expression:

echo '{"phrase":"NP","elements":[{"terminal":"D","lemma":"a"},'\
     '{"terminal":"N","lemma":"woman","props":{"g":"f","n":"p"}}],"lang":"en"}'| node jsRealB-filter.js

returns women as realization. The determiner "a" is plural because it agrees with the number of the noun within the noun phrase.

If the line does not start with a left brace, then it evaluated as a jsRealB expression, so women is also realized with the following:

echo 'NP(D("a"),N("woman").n("p"))' | node jsRealB-filter.js

JSON output

Although our original motivation was for allowing JSON input, it is also possible to get the JSON expression corresponding to a given jsRealB expression by calling .toJSON() on any Constituent. This call (similar to .toSource()) creates a string that corresponds to the jsRealB expression to which it is applied. In fact, fromJSOM(exp.toJSON()) creates a clone of the original exp.

The expression created by .toJSON() is a single line (jsonl format) which can be hard to decipher. This line can be given to ppJSON(exp) to create an indented string such as the one shown in the right part of the above table.