Mercurial > hip
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 */ |