1var ProgrammingAntiPattern = (function() { 2 var exports = {}; 3 4 exports.DEBUG = false; 5 exports.state = { 6 isBarrier: false, 7 tokenIndex: [], 8 type: 'none', 9 actions: [], 10 toString: function () { 11 return "No barrier detected!"; 12 } 13 }; 14 15 exports.notUsingIteratorMiniAntiPattern = function (tokens) { 16 17 /** 18 * Converts a plural query to a singleton query. 19 * 20 * @param pluralQuery String 21 * @returns String representing the singleton query 22 */ 23 function getNonPlural(pluralQuery) { 24 var singleton = pluralQuery; 25 if (pluralQuery.slice(-2) == '/s') { 26 singleton = pluralQuery.slice(0, -1); 27 } 28 return singleton; 29 } 30 31 // The loop identifier is the loop variable name, as in "x" in "for x in /puppy/s" 32 function loopIdentifier(token, bstate) { 33 return (bstate.inForLoop && (typeof bstate.identifier === 'undefined' && token.kind === 'id')); 34 } 35 36 function loopList(token, bstate) { 37 return (bstate.inForLoop && (typeof bstate.list === 'undefined' && token.kind === 'query')); 38 } 39 40 function loopBody(bstate) { 41 return (bstate.inForLoop && (typeof bstate.identifier !== 'undefined' && typeof bstate.list !== 'undefined')); 42 } 43 44 function usedSingletonInBody(token, bstate) { 45 return (token.kind === 'query' && (token.text === bstate.singletonQuery || token.text === bstate.list)); 46 } 47 48 function usedIdentifierInBody(token, bstate) { 49 return (token.kind === 'id' && token.text === bstate.identifier); 50 } 51 52 function analyzeIdentifierUsage(token, bstate) { 53 // We're in the body of the code now, presumably, though I need to check for edge cases when we have syntax errors, etc. 54 bstate.singletonQuery = getNonPlural(bstate.list); 55 56 if (token.kind === 'keyword') { 57 } else if (usedSingletonInBody(token, bstate)) { 58 bstate.ownQuery = token.text; 59 bstate.isBarrier = true; 60 bstate.triedUsingIdentifier = true; 61 bstate.tokenForHelp = token.index; 62 return bstate; 63 } else if (usedIdentifierInBody(token, bstate)) { 64 bstate.triedUsingIdentifier = true; 65 } 66 67 return bstate; 68 } 69 70 function identifierNotUsedInBody(bstate) { 71 return (!bstate.isBarrier && typeof bstate.list !== 'undefined' && !bstate.triedUsingIdentifier); 72 } 73 74 // Keep a clean version of the state variable. 75 var state = Object.create(exports.state); 76 state.type = 'notUsingIterator'; 77 state.subtree = []; 78 state.triedUsingIdentifier = false;
79 state.toString = function() { 80 if (this.isBarrier === true) { 81 if (this.triedUsingIdentifier) { 82 return "You're trying to do " + this.actions[0] + " on " + this.ownQuery + 83 " but maybe you should try using " + this.identifier + " since it refers to every " + 84 this.singletonQuery + " in " + this.list + "."; 85 } else { 86 return "You've used a for loop but you didn't use " + this.identifier + 87 ". Maybe you should try using " + this.identifier + " since it refers to every " + 88 this.singletonQuery + " in " + this.list + "."; 89 } 90 } else { 91 return "No barrier detected!"; 92 } 93 }; 94 95 /** 96 * Processes source code to identify barriers in for loops. 97 * 98 * @param tokens 99 * @returns {state|*} 100 */ 101 function processForLoop(tokens) { 102 var barrierState = Object.create(state); 103 barrierState.tokenIndex = []; 104 barrierState.isBarrier = false; 105 barrierState.subtree = []; 106 barrierState.inForLoop = true; 107 var outdentToken; 108 109 if (ProgrammingAntiPattern.DEBUG) console.log("**No-Iterator: Enter new for loop: " + tokens.index); 110 111 var outdent = false; 112 while (tokens.hasMore() && !outdent) { 113 var token = tokens.eat(); 114 if (ProgrammingAntiPattern.DEBUG) console.log("code: " + token.kind + " " + token.text); 115 116 if (token.kind === 'outdent') {
117 outdent = true; 118 outdentToken = token.index; 119 } else if (token.text === 'for') { 120 barrierState.subtree.push(processForLoop(tokens)); // Recursively process a new loop. 121 } else if (loopIdentifier(token, barrierState)) { 122 barrierState.identifier = token.text; 123 } else if (loopList(token, barrierState)) { 124 barrierState.list = token.text; 125 // indexAfterForStatement = token.index; 126 } else if (loopBody(barrierState)) { 127 barrierState = analyzeIdentifierUsage(token, barrierState); 128 if (barrierState.isBarrier) { 129 break; 130 } 131 } else if (token.isNewLine()) { 132 break; 133 } 134 } 135 136 if (identifierNotUsedInBody(barrierState) && outdent) { 137 barrierState.isBarrier = true; 138 barrierState.tokenForHelp = outdentToken - 2; 139 140 if (ProgrammingAntiPattern.DEBUG) console.log("No-Iterator: **Not Used In Body!** at token " + barrierState.tokenForHelp + "\nThis is the iterator: " + barrierState.identifier); 141 } 142 143 // Consume the rest of the tokens until an outdent 144 while (tokens.hasMore() && !outdent) { 145 var token2 = tokens.eat(); 146 if (token2.kind === 'outdent') {
147 outdent = true; 148 } 149 } 150 if (barrierState.subtree.tokenIndex !== 'undefined') { 151 barrierState.isBarrier = barrierState.subtree.isBarrier || barrierState.isBarrier; 152 barrierState.tokenIndex.push(barrierState.subtree.tokenIndex); 153 } 154 return barrierState; 155 } 156 157 //--- Main function starts here --- 158 var barriers = []; 159 var i = 1; 160 161 tokens.reset(); 162 while (tokens.hasMore()) { 163 var token = tokens.eat(); 164 165 if (ProgrammingAntiPattern.DEBUG) console.log("No-Iterator: " + i + ". " + token); 166 i = i + 1; 167 168 if (token.text === 'for') { 169 barriers.push(processForLoop(tokens)); 170 } 171 } 172 173 if (ProgrammingAntiPattern.DEBUG) console.log("About to select..."); 174 if (ProgrammingAntiPattern.DEBUG) console.log(barriers); 175 176 var returnBarrier = Object.create(state); 177 returnBarrier.tokenIndex = []; 178 $.each(barriers, function(index, barrier) { 179 returnBarrier.isBarrier = true; 180 if (barrier.tokenForHelp !== 'undefined') { 181 returnBarrier.tokenIndex.push(barrier.tokenForHelp); 182 } 183 }); 184 185 return returnBarrier; 186 }; 187 188 exports.usingPluralInsteadOfSingletonMiniAntiPattern = function (tokens) { 189 var state = Object.create(exports.state); 190 state.tokenIndex = []; 191 state.type = 'pluralInsteadOfSingleton'; 192 193 state.toString = function () { 194 if (this.isBarrier) { 195 return "All at once barrier: you used a list in place of an object"; 196 } 197 else { 198 return "No barrier detected!"; 199 } 200 }; 201 202 tokens.reset(); 203 while (tokens.hasMore()) { 204 var token = tokens.eat(); 205 if (ProgrammingAntiPattern.DEBUG) console.log("**our token**: " + token.text); 206 if ($.trim(token.text) === 'goto' || $.trim(token.text) === 'grab' || $.trim(token.text) === 'drop' || $.trim(token.text) === 'remove') { 207 var nextToken=tokens.peek(); 208 if (ProgrammingAntiPattern.DEBUG) console.log("char at 0: " + nextToken.charAt(0)); 209 if (ProgrammingAntiPattern.DEBUG) console.log("char at len-2: " + nextToken.charAt(nextToken.length-2)); 210 if (ProgrammingAntiPattern.DEBUG) console.log("char at len-1: " + nextToken.charAt(nextToken.length-1)); 211 212 if (nextToken.charAt(0) === '/' && 213 nextToken.charAt(nextToken.length-2) === '/' && 214 nextToken.charAt(nextToken.length-1) === 's') { 215 // they tried goto /kitten/s or similar, now check for goto /kitten/s[0] or similar 216 if (ProgrammingAntiPattern.DEBUG) console.log("token: " + token + ", nextToken: " + nextToken + ", then..."); 217 token = tokens.eat(); 218 nextToken = tokens.eat(); 219 if (ProgrammingAntiPattern.DEBUG) console.log("token: " + token + ", nextToken: " + nextToken.text + "," + nextToken.kind); 220 if ($.trim(nextToken.kind) === 'eol') { 221 222 state.isBarrier = true; 223 if (!($.inArray(token.index, state.tokenIndex) > - 1)) { 224 state.tokenIndex.push(token.index); 225 } 226 } 227 } 228 } 229 } 230 231 tokens.reset(); 232 return state; 233 }; 234 235 exports.usingEnsure = function (tokens) { 236 var state = Object.create(exports.state); 237 state.tokenIndex = []; 238 state.type = 'usingEnsure'; 239 240 state.toString = function () { 241 if (this.isBarrier) { 242 return "You are using ensure in the code pane."; 243 } 244 else { 245 return "No barrier detected!"; 246 } 247 }; 248 249 tokens.reset(); 250 endOfBarrierDetected = false; 251 while (tokens.hasMore()) { 252 currentIndex = tokens.index; 253 var token = tokens.eat(); 254 if (ProgrammingAntiPattern.DEBUG) console.log("token " + currentIndex + ":" + token.text); 255 if ($.trim(token.text) === 'ensure') { 256 // ensure is being used so a barrier is hit. process longer to find where the unstuckarator should go 257 state.isBarrier = true; 258 while (!token.isNewLine() && tokens.hasMore()) { // find the end of the line to place the unstuckarator, or find no more tokens 259 token = tokens.eat(); 260 } 261 if (!($.inArray(token.index - 1, state.tokenIndex) > - 1)) { 262 state.tokenIndex.push(token.index - 1); 263 if (ProgrammingAntiPattern.DEBUG) console.log("token index for IG character: " + state.tokenIndex); 264 } 265 } 266 } 267 endOfBarrierDetected = false; 268 tokens.reset(); 269 return state; 270 } 271 272 exports.createObjectWithoutDefinition = function (tokens) { 273 var state = Object.create(exports.state); 274 state.tokenIndex = []; 275 state.type = 'createObjectWithoutDefinition'; 276 277 state.toString = function () { 278 if (this.isBarrier) { 279 return "You did not write an object definition.";
280 } 281 else { 282 return "No barrier detected!"; 283 } 284 }; 285 286 var definedObjects = { 287 names: [], 288 tokens: [] 289 }; 290 var createdObjects = { 291 names: [], 292 tokens: [] 293 }; 294 // A lazy way to not show the unstuckarator for the levels where a spelling mistake 295 // in object creation is part of the challenge in the level 296 var levelObjectsToAvoid = []; 297 var tempFuncName, tempFuncIndex; 298 tokens.reset(); 299 while (tokens.hasMore()) { 300 // If the current token begins an object definition, store its name and token for comparison later 301 if (tokens.nextIs('object')) { 302 tokens.eat(); 303 if (tokens.nextKindIs('id')) { 304 tempFuncName = tokens.eat(); 305 if (tokens.nextIs('(')) { 306 tokens.eat(); 307 while (!tokens.nextIs(')') && !tokens.eol()) { 308 tokens.eat(); 309 } 310 if (tokens.nextIs(')')) { 311 tempFuncIndex = tokens.index; 312 tokens.eat(); 313 definedObjects.names.push(tempFuncName.text); 314 definedObjects.tokens.push(tempFuncIndex); 315 } 316 } 317 } 318 } else if (tokens.nextIs('create')) { 319 tokens.eat(); 320 if (tokens.nextKindIs('id')) { 321 tempFuncName = tokens.eat(); 322 if (tokens.nextIs('(')) { 323 tokens.eat(); 324 while (!tokens.nextIs(')') && !tokens.eol()) { 325 tokens.eat(); 326 } 327 if (tokens.nextIs(')')) { 328 tempFuncIndex = tokens.index; 329 tokens.eat(); 330 createdObjects.names.push(tempFuncName.text); 331 createdObjects.tokens.push(tempFuncIndex); 332 } 333 } 334 } 335 } else tokens.eat(); 336 } 337 // Check to see if any of the created objects were not defined. If so, return its index 338 for (var x = 0; x < createdObjects.names.length; x++) { 339 if (($.inArray(createdObjects.names[x], definedObjects.names) === -1) && ($.inArray(createdObjects.names[x], levelObjectsToAvoid) === -1)) { 340 state.isBarrier = true; 341 state.tokenIndex.push(createdObjects.tokens[x]); 342 if (ProgrammingAntiPattern.DEBUG) console.log("call not def'ed: " + createdObjects.names[x]); 343 if (ProgrammingAntiPattern.DEBUG) console.log(definedObjects.names); 344 } 345 } 346 tokens.reset(); 347 return state; 348 } 349 350 exports.functionMissingDefOrCall = function (tokens) { 351 var state = Object.create(exports.state); 352 state.tokenIndex = []; 353 state.type = 'functionMissingDefOrCall'; 354 355 state.toString = function () { 356 if (this.isBarrier) { 357 return "You either failed to define the function or failed to call it."; 358 } 359 else { 360 return "No barrier detected!"; 361 } 362 }; 363 364 var definedFunctions = { 365 names: [], 366 tokens: [] 367 }; 368 var calledFunctions = { 369 names: [], 370 tokens: [] 371 }; 372 var definedObjects = { 373 names: [] 374 }; 375 // A lazy way to not show the unstuckarator for the levels where a spelling mistake 376 // in a function call part of the challenge in the level 377 var levelFunctionsToAvoid = []; 378 var tempFuncName, tempFuncIndex; 379 tokens.reset(); 380 while (tokens.hasMore()) { 381 // If the current token begins an function definition, store its name and token for comparison later 382 if (tokens.nextIs('function')) { 383 tokens.eat(); 384 if (tokens.nextKindIs('id')) { 385 tempFuncName = tokens.eat(); 386 if (tokens.nextIs('(')) { 387 tokens.eat(); 388 while (!tokens.nextIs(')') && !tokens.eol()) { 389 tokens.eat(); 390 } 391 if (tokens.nextIs(')')) { 392 tempFuncIndex = tokens.index; 393 tokens.eat(); 394 definedFunctions.names.push(tempFuncName.text); 395 definedFunctions.tokens.push(tempFuncIndex); 396 } 397 } 398 } 399 } else if (!tokens.nextIs('object') && !tokens.nextIs('create') && !tokens.nextIs(':')) { 400 tokens.eat(); 401 if (tokens.nextKindIs('id')) { 402 tempFuncName = tokens.eat(); 403 if (tokens.nextIs('(')) { 404 tokens.eat(); 405 while (!tokens.nextIs(')') && !tokens.eol()) { 406 tokens.eat(); 407 } 408 if (tokens.nextIs(')')) { 409 tempFuncIndex = tokens.index; 410 tokens.eat(); 411 calledFunctions.names.push(tempFuncName.text); 412 calledFunctions.tokens.push(tempFuncIndex); 413 } 414 } 415 } 416 } else if (tokens.nextIs('object')) { 417 tokens.eat(); 418 if (tokens.nextKindIs('id')) { 419 tempFuncName = tokens.eat(); 420 if (tokens.nextIs('(')) { 421 tokens.eat(); 422 while (!tokens.nextIs(')') && !tokens.eol()) { 423 tokens.eat(); 424 } 425 if (tokens.nextIs(')')) { 426 tempFuncIndex = tokens.index; 427 tokens.eat(); 428 definedObjects.names.push(tempFuncName.text); 429 } 430 } 431 } 432 } else tokens.eat(); 433 } 434 // Check to see if any of the defined functions were not called. If so, return its index 435 for (var x = 0; x < definedFunctions.names.length; x++) { 436 if (($.inArray(definedFunctions.names[x], calledFunctions.names) === -1) && 437 ($.inArray(definedFunctions.names[x], levelFunctionsToAvoid) === -1)) { 438 state.isBarrier = true; 439 state.tokenIndex.push(definedFunctions.tokens[x]); 440 if (ProgrammingAntiPattern.DEBUG) console.log("def not called: " + definedFunctions.names[x]); 441 if (ProgrammingAntiPattern.DEBUG) console.log(calledFunctions.names); 442 } 443 } 444 // Check to see if any of the called functions were not defined. If so, return its index 445 // Also make sure that the called function doesn't have the same name as a defined object 446 for (var x = 0; x < calledFunctions.names.length; x++) { 447 if (($.inArray(calledFunctions.names[x], definedFunctions.names) === -1) && 448 ($.inArray(calledFunctions.names[x], levelFunctionsToAvoid) === -1) && 449 ($.inArray(calledFunctions.names[x], definedObjects.names) === -1)) { 450 state.isBarrier = true; 451 state.tokenIndex.push(calledFunctions.tokens[x]); 452 if (ProgrammingAntiPattern.DEBUG) console.log("call not def'ed: " + calledFunctions.names[x]); 453 if (ProgrammingAntiPattern.DEBUG) console.log(definedFunctions.names); 454 } 455 } 456 tokens.reset(); 457 return state; 458 } 459 460 exports.detectFromTokens = function(tokens) { 461 if (ProgrammingAntiPattern.DEBUG) console.log("----------------------------\n" + tokens + "\n---\n"); 462 463 var barriers = []; 464 var stateA = ProgrammingAntiPattern.notUsingIteratorMiniAntiPattern(tokens); 465 if (ProgrammingAntiPattern.DEBUG) console.log("+ the barrier is: " + stateA.type + " " + stateA.isBarrier); 466 467 var stateB = ProgrammingAntiPattern.usingPluralInsteadOfSingletonMiniAntiPattern(tokens); 468 if (ProgrammingAntiPattern.DEBUG) console.log("+ the barrier is: " + stateB.type + " " + stateB.isBarrier); 469 470 var stateC = ProgrammingAntiPattern.usingEnsure(tokens); 471 if (ProgrammingAntiPattern.DEBUG) console.log("+ the barrier is: " + stateC.type + " " + stateC.isBarrier); 472 473 var stateD = ProgrammingAntiPattern.createObjectWithoutDefinition(tokens); 474 if (ProgrammingAntiPattern.DEBUG) console.log("+ the barrier is: " + stateD.type + " " + stateD.isBarrier); 475 476 var stateE = ProgrammingAntiPattern.functionMissingDefOrCall(tokens); 477 if (ProgrammingAntiPattern.DEBUG) console.log("+ the barrier is: " + stateD.type + " " + stateD.isBarrier); 478 479 if (stateA.isBarrier) { 480 if (ProgrammingAntiPattern.DEBUG) console.log("Not using iterator detected."); 481 if (ProgrammingAntiPattern.DEBUG) console.log("token indexes: " + stateA.tokenIndex); 482 barriers.push(stateA); 483 } 484 485 if (stateB.isBarrier) { 486 if (ProgrammingAntiPattern.DEBUG) console.log("Plural instead of Singleton detected."); 487 if (ProgrammingAntiPattern.DEBUG) console.log("token indexes: " + stateB.tokenIndex); 488 barriers.push(stateB); 489 } 490 491 if (stateC.isBarrier) { 492 if (ProgrammingAntiPattern.DEBUG) console.log("Using ensure detected."); 493 if (ProgrammingAntiPattern.DEBUG) console.log("token indexes: " + stateC.tokenIndex); 494 barriers.push(stateC); 495 } 496 497 if (stateD.isBarrier) { 498 if (ProgrammingAntiPattern.DEBUG) console.log("Object missing definition detected."); 499 if (ProgrammingAntiPattern.DEBUG) console.log("token indexes: " + stateD.tokenIndex); 500 barriers.push(stateD); 501 } 502 503 if (stateE.isBarrier) { 504 if (ProgrammingAntiPattern.DEBUG) console.log("Function without definition/call detected."); 505 if (ProgrammingAntiPattern.DEBUG) console.log("token indexes: " + stateE.tokenIndex); 506 barriers.push(stateE); 507 } 508 509 510 if (barriers.length === 0) { 511 barriers.push({ 512 isBarrier: false, 513 tokenIndex: [], 514 toString: function () { 515 return "No barrier detected!"; 516 } 517 }); 518 } 519 520 return barriers; 521 }; 522 523 /** 524 * Parses the Gidget code and returns the most related help message related to that code. 525 * @param text 526 * @returns {*} 527 */ 528 exports.detect = function(text) { 529 var tokens = new GIDGET.Parser.TokenStream(text); 530 return exports.detectFromTokens(tokens); 531 }; 532 533 return exports; 534})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.