comparison hip.js @ 6:98935af12b04

Debugged the recursive part of the parsing algorithm, so it now recognizes multiple prepositions in any order. Still some bugs.
author jonathandicarlo@jonathan-dicarlos-macbook-pro.local
date Wed, 14 May 2008 13:25:17 -0700
parents a049cb93db46
children aab0d14248f5
comparison
equal deleted inserted replaced
5:a049cb93db46 6:98935af12b04
1 function dictDeepCopy( dict ) {
2 var newDict = {};
3 for (var i in dict ) {
4 newDict[i] = dict[i];
5 }
6 return newDict;
7 };
8
9 function dictKeys( dict ) {
10 return [ key for ( key in dict ) ];
11 }
1 12
2 function NounType( name, expectedWords ) { 13 function NounType( name, expectedWords ) {
3 this._init( name, expectedWords ); 14 this._init( name, expectedWords );
4 } 15 }
5 NounType.prototype = { 16 NounType.prototype = {
42 53
43 var person = new NounType( "person", ["atul@mozilla.com", "aza@mozilla.com", "thunder@mozilla.com", "chris@mozilla.com", "myk@mozilla.com" ] ); 54 var person = new NounType( "person", ["atul@mozilla.com", "aza@mozilla.com", "thunder@mozilla.com", "chris@mozilla.com", "myk@mozilla.com" ] );
44 55
45 var anyWord = { 56 var anyWord = {
46 // a singleton object which can be used in place of a NounType. 57 // a singleton object which can be used in place of a NounType.
58 _name: "text",
47 match: function( fragment ) { 59 match: function( fragment ) {
48 return true; 60 return true;
49 }, 61 },
50 suggest: function( fragment ) { 62 suggest: function( fragment ) {
51 return [ fragment ]; 63 return [ fragment ];
62 this._DO = DO; 74 this._DO = DO;
63 this._modifiers = modifiers; 75 this._modifiers = modifiers;
64 }, 76 },
65 77
66 getCompletionText: function() { 78 getCompletionText: function() {
67 // returns completed and canonicalized sentence 79 // return plain text that we should set the input box to if user hits
80 // space bar on this sentence.
81 var sentence = this._verb._name;
82 if ( this._DO ) {
83 sentence = sentence + " " + this._DO;
84 }
85 for ( var x in this._modifiers ) {
86 sentence = sentence + " " + x + " " + this._modifiers[x];
87 }
88 return sentence;
89 },
90
91 getDisplayText: function() {
92 // returns html formatted sentence for display in suggestion list
68 var sentence = this._verb._name; 93 var sentence = this._verb._name;
69 if ( this._verb._DOType ) { 94 if ( this._verb._DOType ) {
70 if ( this._DO ) { 95 if ( this._DO ) {
71 sentence = sentence + " " + this._DO; 96 sentence = sentence + " " + this._DO;
72 } else { 97 } else {
73 sentence = sentence + " <span class=\"needarg\">(" + this._verb._DOLabel + ")</span>"; 98 sentence = sentence + " <span class=\"needarg\">(" + this._verb._DOLabel + ")</span>";
74 } 99 }
75 } 100 }
76 101
77 for ( var x in this._verb._modifiers ) { 102 for ( var x in this._verb._modifiers ) { // was this._verb._modifiers
78 if ( this._modifiers[ x ] ) { 103 if ( this._modifiers[ x ] ) {
79 sentence = sentence + " " + x + " " + this._modifiers[x]; 104 sentence = sentence + " <b>" + x + " " + this._modifiers[x] + "</b>";
80 } else { 105 } else {
81 sentence = sentence + " <span class=\"needarg\">(" + this._verb._modifiers[x]._name + ")</span>"; 106 sentence = sentence + " <span class=\"needarg\">(" + x + " " + this._verb._modifiers[x]._name + ")</span>";
82 } 107 }
83 } 108 }
84 return sentence; 109 return sentence;
85 }, 110 },
86 111
87 getDescription: function() { 112 getDescription: function() {
88 // returns a string describing what the sentence will do if executed 113 // returns a string describing what the sentence will do if executed
114 return this._verb.getDescription( this._DO, this._modifiers );
89 } 115 }
90 116
91 }; 117 };
92 118
93 119
104 // keys are prepositions 130 // keys are prepositions
105 // values are NounTypes. 131 // values are NounTypes.
106 // example: { "from" : City, "to" : City, "on" : Day } 132 // example: { "from" : City, "to" : City, "on" : Day }
107 }, 133 },
108 134
135 getDescription: function() {
136 // returns a string describing what the sentence will do if executed
137 return this._verb.getDescription( this._DO, this._modifiers );
138 }
139
109 recursiveParse: function( unusedWords, filledMods, unfilledMods ) { 140 recursiveParse: function( unusedWords, filledMods, unfilledMods ) {
110 var x; 141 var x;
111 var suggestions = []; 142 var suggestions = [];
112 var completions = []; 143 var completions = [];
113 var directObject = ""; 144 var directObject = "";
114 if ( [key for (key in unfilledMods)].length == 0 ) { 145 if ( dictKeys( unfilledMods ).length == 0 ) {
115 // Done with modifiers, try to parse direct object. 146 // Done with modifiers, try to parse direct object.
116 if ( unusedWords.length == 0 ) { 147 if ( unusedWords.length == 0 ) {
117 // No words left, no direct object. Try parsing sentence 148 // No words left, no direct object. Try parsing sentence
118 // without them. 149 // without them.
119 return [ new ParsedSentence( this, "", filledMods ) ]; 150 return [ new ParsedSentence( this, "", filledMods ) ];
139 } else { 170 } else {
140 // word is invalid direct object. Fail! 171 // word is invalid direct object. Fail!
141 return []; 172 return [];
142 } 173 }
143 } 174 }
144 } 175 } else {
145 },
146
147 /*
148 else {
149 // "pop" a preposition off of the properties of unfilledMods 176 // "pop" a preposition off of the properties of unfilledMods
150 var preposition = [key for (key in unfilledMods)][0]; 177 var preposition = dictKeys( unfilledMods )[0];
151 var newUnfilledMods = unfilledMods.splice(); 178 // newUnfilledMods is the same as unfilledMods without preposition
179 var newUnfilledMods = dictDeepCopy( unfilledMods );
152 delete newUnfilledMods[preposition]; 180 delete newUnfilledMods[preposition];
153 181
154 // Look for a match for this preposition 182 // Look for a match for this preposition
155 var nounType = unfilledMods[ preposition ]; 183 var nounType = unfilledMods[ preposition ];
156 var matchIndices = []; 184 var matchIndices = [];
157 for ( var x = 0; x < unusedWords.length - 1; x++ ) { 185 for ( var x = 0; x < unusedWords.length - 1; x++ ) {
158 if ( preposition.indexOf( unusedWords[x] ) == 0 ) { 186 if ( preposition.indexOf( unusedWords[x] ) == 0 ) {
159 if ( nounType.match( unusedWords[ x + 1 ] ) == 0 ) { 187 if ( nounType.match( unusedWords[ x + 1 ] ) ) {
160 // Match for the preposition at index x followed by 188 // Match for the preposition at index x followed by
161 // an appropriate noun at index x+1 189 // an appropriate noun at index x+1
162 matchIndices.push( x ); 190 matchIndices.push( x );
163 } 191 }
164 } 192 }
165 } 193 }
166 if ( matchIndices.length == 0 ) { 194 if ( matchIndices.length == 0 ) {
167 // no match for this preposition. 195 // no match for this preposition.
168 // Leave it blank and try to parse the rest: 196 // Leave it blank and try to parse the rest:
169 filledMods[preposition] = ""; 197 filledMods[preposition] = "";
170 return recursiveParse( unusedWords, filledMods, newUnfilledMods ); 198 var directObject = unusedWords.join( " " );
199 return [ new ParsedSentence( this, directObject, filledMods ) ];
200 //return this.recursiveParse( unusedWords, filledMods, newUnfilledMods );
171 } else { 201 } else {
202 // this is placeholder, destroy it.
172 for ( x in matchIndices ) { 203 for ( x in matchIndices ) {
173 // remove the words that matched the preposition and following
174 // noun:
175 var noun = unusedWords[ matchIndices[x]+1 ]; 204 var noun = unusedWords[ matchIndices[x]+1 ];
176 var newUnusedWords = unusedWords.slice(); 205 var newUnusedWords = unusedWords.slice();
177 newUnusedWords.splice( possibilities[x], 2 ); 206 newUnusedWords.splice( matchIndices[x], 2 );
178 // add every noun completion from every possibility... 207 var directObject = newUnusedWords.join( " " );
208
179 suggestions = nounType.suggest( noun ); 209 suggestions = nounType.suggest( noun );
180 for ( var y in suggestions ) { 210 for ( var y in suggestions ) {
181 var newFilledMods = filledMods.slice(); 211 var newFilledMods = dictDeepCopy( filledMods );
182 newFilledMods[ preposition ] = suggestions[y]; 212 newFilledMods[ preposition ] = suggestions[y];
183 var newCompletions = recursiveParse( newUnusedWords, 213 var newCompletions = this.recursiveParse( newUnusedWords,
184 newFilledMods, 214 newFilledMods,
185 newUnfilledMods ); 215 newUnfilledMods );
186 completions = completions.concat( newCompletions ); 216 completions = completions.concat( newCompletions );
187 } 217 }
188 } 218 }
189 return completions; 219 return completions;
190 } 220 }
191 } 221 }
192 }, */ 222 },
193 223
194 parse: function( words ) { 224 getCompletions: function( words ) {
195 /* returns a list of ParsedSentences. */ 225 /* returns a list of ParsedSentences. */
196 /* words is an array of words that were space-separated. 226 /* words is an array of words that were space-separated.
197 The first word, which matched this verb, has already been removed. 227 The first word, which matched this verb, has already been removed.
198 Everything after that is either: 228 Everything after that is either:
199 1. my direct object 229 1. my direct object
200 2. a preposition 230 2. a preposition
201 3. a noun following a preposition. 231 3. a noun following a preposition.
202 */ 232 */
203 return this.recursiveParse( words, {}, this._modifiers ); 233 return this.recursiveParse( words, {}, this._modifiers );
204 },
205
206 getCompletions: function( words ) {
207 var sentences = this.parse( words );
208 return [ sentences[x].getCompletionText() for ( x in sentences ) ];
209 }, 234 },
210 235
211 match: function( sentence ) { 236 match: function( sentence ) {
212 // returns a float from 0 to 1 telling how good of a match the input 237 // returns a float from 0 to 1 telling how good of a match the input
213 // is to this verb. 238 // is to this verb.
252 } 277 }
253 QuerySource.prototype = { 278 QuerySource.prototype = {
254 _init: function( ) { 279 _init: function( ) {
255 this._lockedInSentence = null; 280 this._lockedInSentence = null;
256 this._hilitedSuggestion = 0; 281 this._hilitedSuggestion = 0;
257 this._suggestionList = []; 282 this._suggestionList = []; // a list of ParsedSentences.
258 }, 283 },
259 284
260 updateSuggestionList: function( query ) { 285 updateSuggestionList: function( query ) {
261 this._suggestionList = []; 286 this._suggestionList = [];
262 var completions = []; 287 var completions = [];
265 var verb = verbs[x]; 290 var verb = verbs[x];
266 if ( verb.match( words[0] ) ) { 291 if ( verb.match( words[0] ) ) {
267 completions = verb.getCompletions( words.slice(1) ); 292 completions = verb.getCompletions( words.slice(1) );
268 this._suggestionList = this._suggestionList.concat( completions ); 293 this._suggestionList = this._suggestionList.concat( completions );
269 } 294 }
270 } // TODO sort in order of match quality 295 }
296 // TODO sort in order of match quality
271 this._hilitedSuggestion = 0; 297 this._hilitedSuggestion = 0;
272 }, 298 },
273 299
274 getSuggestions : function() { 300 getSuggestionsAsHtml : function() {
275 return this._suggestionList; 301 return [ this._suggestionList[x].getDisplayText() for ( x in this._suggestionList ) ];
276 }, 302 },
277 303
278 indicationDown: function( ) { 304 indicationDown: function( ) {
279 this._hilitedSuggestion ++; 305 this._hilitedSuggestion ++;
280 if ( this._hilitedSuggestion > this._suggestionList.length ) { 306 if ( this._hilitedSuggestion > this._suggestionList.length ) {
295 }, 321 },
296 322
297 autocomplete: function( query ) { 323 autocomplete: function( query ) {
298 var hilited = this.getHilitedSuggestion(); 324 var hilited = this.getHilitedSuggestion();
299 if ( hilited > -1 ) { 325 if ( hilited > -1 ) {
300 var newText = this._suggestionList[ hilited ] + " "; 326 var newText = this._suggestionList[ hilited ].getCompletionText() + " ";
301 } else { 327 } else {
302 newText = query; 328 newText = query;
303 } 329 }
304 return newText; 330 return newText;
305 }, 331 },
328 } 354 }
329 return result; 355 return result;
330 } 356 }
331 357
332 function updateDisplay( ) { 358 function updateDisplay( ) {
333 var suggestions = gQs.getSuggestions(); 359 var suggestions = gQs.getSuggestionsAsHtml();
334 var hilitedSuggestion = gQs.getHilitedSuggestion(); 360 var hilitedSuggestion = gQs.getHilitedSuggestion();
335 var ac = $("#autocomplete-popup"); 361 var ac = $("#autocomplete-popup");
336 ac.html( makeSuggestionHtml( "div", suggestions, hilitedSuggestion ) ); 362 ac.html( makeSuggestionHtml( "div", suggestions, hilitedSuggestion ) );
337 ac.show(); 363 ac.show();
338 } 364 }
375 updateDisplay(); 401 updateDisplay();
376 402
377 } 403 }
378 404
379 $(document).ready( function() { 405 $(document).ready( function() {
406 $("#status-line").html( "Welcome to Ubiquity." );
380 $("#search-box").focus(); 407 $("#search-box").focus();
381 $("#search-box").keyup( searchBoxQuery ); 408 $("#search-box").keyup( searchBoxQuery );
382 $("#autocomplete-popup").css( 409 $("#autocomplete-popup").css(
383 "width", 410 "width",
384 $("#search-box").css("width") 411 $("#search-box").css("width")
385 ); 412 );
386 }); 413 });
387 414
388 /* Minor problems: 415 /* Minor problems:
389 1. verbs with indirect objects are returning empty completion lists
390 2. multiple word direct objects are truncated to single word 416 2. multiple word direct objects are truncated to single word
417 3. prepositional phrases past the first don't get matched?
418 4. sentences need to have descriptions
391 */ 419 */