PageSourceSearch

https://helpgidget.com/lib/ideagarden/programming-antipatterns.js

js helpgidget.com collected 2026-09-30 09:25:23 UTC 17,769 bytes, 534 lines download raw bytes

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.