NOTE: Documentation is work -in-progress ...

For any suggestions, drop in a email to me at ram.adhikari@gmail.com with subject line starting "GrammarIntelliForJs: ". I'll more than happy to help you out.

*Alpha-3 with Visual Grammar Editor is now available.

Index

.... todo ...somehow i'm not able to get the table of content markup working.

For the impatient ones - you can:
  1. You can use Visual Grammar Editor to visually design your grammar and generate working html page.
  2. Checkout simple demo 1, demo 2, and demo with UIHelper js
  3. Jump to the Quick Start Tutorial page.
    1. I know the Quick Start is just too quick ... so quick that it has nothing ! You'll be better off going through the examples in this page.
    2. I recommend you read through this page & keep the sample html pages open for reference.
  4. Refer to GIGen Tool page
  5. Refer to UI Helper Script page

Introduction

GrammerIntelliForJS is a JavaScript library to enable intellisense (aka auto-completion) based on a grammar. Normally you would use it in web pages where you expect users to type a query. The query should follow some pattern & you would like to guide the user as s/he types. This library helps you to do that. Since its JavaScript based you cannot expect it to work for complicated grammar.

Before you try this library, I suggest you read this page completely. Also, please do not expect magic ....

As a consumer of this library, there two distinct phase you need to plan:
  1. Create a GIMachine equivalent of your grammar or the set of sentences you plan to expose to end user. This is a one time job per grammar. Also, you need to decide what aspects (part of sentence) of the user input that you want to capture.
  2. Decide how you want to link the dynamic suggestions to your web-page
I'll walk you through this two phase with a specific example in subsequent sections.

For the geeks, please note:
  1. This library defines a client side state machine. It lays down the JavaScript object definition (as per the class diagram shown below) for the state machine. The suggestions and match operations are a simple traversal over the state machine guided by the input string.
  2. This library is NOT a DFA implementation in its true sense
  3. Needless to say that its not optimized either.
  4. This library does not provide any UI, you need to work out how you want to bind the suggestions (against the user input) to textbox or other UI element.
    1. You can use Visual Grammar Editor to visually design your grammar and generate working html page.
    2. With UI Helper Script, we now provide a default suggestion UI library that makes it easy to bind your GIMachine to textbox.
    3. But, if you want to drive the suggestion UI yourself, you don't have to include the GrammarIntelliForJsUIHelper.js script file.

GrammerIntelliForJS Object Model For JavaScript

GrammarIntelliForJSClassDiagram.jpg
  • GIMachine is the state machine having the starting states.
  • GIMachineWordState is state holding the matching criteria against a word. The NextStates are the possible word patterns following a state.
  • In a DFA/Automata implementation you would expect states and transition. In this design, I've merged them into one, the GIMachineWordState has the state information and the criteria to check for a machine to be on this state.
  • Given a partially constructed sentence by end user (and assuming a GIMachine instance of your grammar is ready), your code can call the Suggestions to get the set of possible words that your grammar expects.
  • Given a fully constructed user sentence, you can call ProcessText to get match collection & obtain the values that interests you.

Public Method/APIs

The class model diagram shown above should tell you most of the stories, I missed putting down the constructor in class diagram ... so let me talk about that & few global helper functions.

GIMachine Class

GIMachineWordState Class

GetLiteralPattern Method

How to generate the state machine for your grammar - Tools for you

Now its time to walk you through concrete examples of this library usage. First few examples demonstrate using the library in a "raw" way .... you take control of everything & learn the hard way (hopefully not that hard). Beyond the hard path lies the simpler tools to help you out. You can refer to GIGen Tool, its a .Net tool to allow you generate JavaScript code for your pattern(s).

Creating GIMachine instance for your grammar

1. Aim: You want to display a text box in web-page & let user type in a simple query: "show info for customer <Customer-Name>", You want to guide end users while they type this query & when user is done (and maybe presses some "Go" button) you would like to extract the <Customer-Name> from the query. You can try out the working example here. Here's the GIMachine that you want:
SimpleCustomerInfoQuery.jpg
and here's what you need to do in your webpage's JavaScript:
First, make sure you include the GrammarIntelliForJs.js script file.
Now, here's how you create your GIMachine instance:
        var oGIMachine = new GIMachine("SimpleCustomerInfoQuery"), oCustomerCheckState = null;
        
        // Create the last state first, so that we can "attach" it while forming the literal ones
        oCustomerCheckState = new GIMachineWordState("^[a-zA-Z]+$" /* Pattern */,
                                                     false /* This is not a literal text/pattern */,
                                                     3 /*can be any number for tracking*/,
                                                     "CustomerName" /*This name will can be used to capture result*/,
                                                     true /* final state of the query */);
        oGIMachine.States[0] = GetLiteralPattern("show info for", 0, oCustomerCheckState);

Next, in html, say in a textbox you provide a event handler for onkeyup and in handler code you get the text typed so far & call oGIMachine object to suggest. Here's a sample code

            var arrSuggestions = oGIMachine.Suggestions(sText); // sText is the text entered by user so far

            if (arrSuggestions != null && arrSuggestions.length > 0) {
                for (i = 0; i < arrSuggestions.length; i++) {      // In this case we just construct a html & display the suggestion in a div control
                    if (arrSuggestions[i].IsLiteral) {
                        sHTML += "<b>L:</b> &nbsp;" + arrSuggestions[i].Pattern + "<br>";   
                    } else {
                        sHTML += "<b>R:</b> &nbsp;" + arrSuggestions[i].Pattern + "  [" + arrSuggestions[i].Name + "]<br>";
                    }
                }
                oDiv.innerHTML = sHTML;
            } else {
                oDiv.innerHTML = ""; // No suggestions... may be incorrect sentence or end of query
            }


Now, if at the end of typing say "show info for carol", user clicks on a button & you need to parse this user data to find the actual customer name entered. Here's how you would do it:

                var oMatchCollection = oGIMachine.ProcessText(sText); // sText is user entered data
                if (oMatchCollection != null) {
                var sCustomerName = "";
                oTrace.value = oMatchCollection.ToXML();  // In case you want to see what the response looks like ...

                // Now, its time to get the result ... lets capture the "CustomerName" that user entered
                sCustomerName = oMatchCollection.NamedMatches["CustomerName"].Value;
                // At this point you can do whatever you want with the data ... we'r just displaying it off.
                oDiv.innerHTML = "Result: Captured customerName = '<b>" + sCustomerName + "'</b>";
            } else {
                oTrace.value = "No matches...";
            }



2. Aim: You want to display a text box in web-page & let user type in a simple query: "display invoice from <StartingDate> to <EndDate>", You want to guide end users while they type this query & when user is done (and maybe presses some "Go" button) you would like to extract the <StartDate> and <EndDate> from the query. This time lets try more than one way to start the sentence, lets add synonyms to display. Try out the working demo . Here's the GIMachine that you want:
SInvQueryGrammar.jpg
so, the phase-1 (defining your GIMachine) is shown below

        var sDatePattern ="^\\d\\d/\\d\\d/\\d\\d\\d\\d$", oGIMachine = new GIMachine("SimpleInvoiceQuery"), oStartDate = null, oEndDate = null, oInvoice = null, oFrom = null, oTo = null;
        
        // EndDate node
        oEndDate = new GIMachineWordState(sDatePattern /* Pattern */,
                                                     false /* This is not a literal text/pattern */,
                                                     7 /*can be any number for tracking*/,
                                                     "EndDate" /*This name will can be used to capture result*/,
                                                     true /* final state of the query */);
        // StartDate
        oStartDate = new GIMachineWordState(sDatePattern /* Pattern */,
                                                     false /* This is not a literal text/pattern */,
                                                     5 /*can be any number for tracking*/,
                                                     "StartDate" /*This name will can be used to capture result*/,
                                                     false /* not a final state of the query */);




        oGIMachine.States[0] = new GIMachineWordState("display" /* Pattern */,
                                                     true /* This is a literal text/pattern */,
                                                     0 /*can be any number for tracking*/,
                                                     "" /*Literal shouldn't carry a name*/,
                                                     false /* final state of the query */);
                                                     
        oGIMachine.States[1] = new GIMachineWordState("show" /* Pattern */,
                                                     true /* This is a literal text/pattern */,
                                                     1 /*can be any number for tracking*/,
                                                     "" /*Literal shouldn't carry a name*/,
                                                     false /* final state of the query */);
                                                     
        oGIMachine.States[2] = new GIMachineWordState("list" /* Pattern */,
                                                     true /* This is a literal text/pattern */,
                                                     2 /*can be any number for tracking*/,
                                                     "" /*Literal shouldn't carry a name*/,
                                                     false /* final state of the query */);

        oInvoice = new GIMachineWordState("invoice" /* Pattern */,
                                                     true /* This is a literal text/pattern */,
                                                     3 /*can be any number for tracking*/,
                                                     "" /*Literal shouldn't carry a name*/,
                                                     false /* final state of the query */);

        oFrom = new GIMachineWordState("from" /* Pattern */,
                                                     true /* This is a literal text/pattern */,
                                                     4 /*can be any number for tracking*/,
                                                     "" /*Literal shouldn't carry a name*/,
                                                     false /* final state of the query */);

        oTo = new GIMachineWordState("to" /* Pattern */,
                                                     true /* This is a literal text/pattern */,
                                                     6 /*can be any number for tracking*/,
                                                     "" /*Literal shouldn't carry a name*/,
                                                     false /* final state of the query */);                                            
                                                     

        oGIMachine.States[0].NextStates[0] = oInvoice; // linking display -> invoice
        oGIMachine.States[1].NextStates[0] = oInvoice; // linking show -> invoice
        oGIMachine.States[2].NextStates[0] = oInvoice; // linking list -> invoice

        oInvoice.NextStates[0] = oFrom; // linking invoice -> from

        oFrom.NextStates[0] = oStartDate;

        oStartDate.NextStates[0] = oTo;

        oTo.NextStates[0] = oEndDate;

phase 2 - getting to know suggestions as user types query, like in previous example, this code will be part of the onkeyup handler:

            arrSuggestions = oGIMachine.Suggestions(sText); // sText is the text entered by user so far

            if (arrSuggestions != null && arrSuggestions.length > 0) {
                
                for (i = 0; i < arrSuggestions.length; i++) {
                    if (arrSuggestions[i].IsLiteral) {
                        sHTML += "<b>L:</b> &nbsp;" + arrSuggestions[i].Pattern + "<br>";
                    } else {
                        sHTML += "<b>R:</b> &nbsp;" + arrSuggestions[i].Pattern + "  [" + arrSuggestions[i].Name + "]<br>";
                    }
                }
                oDiv.innerHTML = sHTML;
            } else {
                oTrace.value = "0 suggestions for '" + sText + "'";
                oDiv.innerHTML = "";
            }


As earlier, if you wanted to know the StartDate and EndDate for a given user entry, here's how you would do:

            oMatchCollection = oGIMachine.ProcessText(sText);

            if (oMatchCollection != null) {
                var sStartDate = "", sEndDate = "";
                oTrace.value = oMatchCollection.ToXML();

                // Now, its time to get the result ... lets capture the "CustomerName" that user entered
                sStartDate = oMatchCollection.NamedMatches["StartDate"].Value;
                sEndDate = oMatchCollection.NamedMatches["EndDate"].Value;
                // At this point you can do whatever you want with the data ... we'r just displaying it off.
                oDiv.innerHTML = "Result: Captured StartDate = '<b>" + sStartDate + "'</b>, EndDate = '<b>" + sEndDate + "'</b>";
            } else {
                oTrace.value = "No matches...";
            }

Examples - Putting it together

Pure JavaScript based examples

Sentence -> Greeting | Command
Greeting -> "hello" Name
Name -> string
Command -> "execute " commandName
commandName -> "test" | "build" | "verify"
Then users will be guided with suggestions as they type

Revisiting Examples with GIMachine code generator tool

Current Limitations

Last edited Jun 24, 2011 at 2:47 PM by rb_adhikari, version 95

Comments

No comments yet.