Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Added support for custom tag literal syntax. Opt-in. Should not inter…

…fere with parser if unused.
  • Loading branch information...
commit a4706422306d3b083968f47e45799ffa7b31500f 1 parent 5da4ae1
authored March 26, 2012

Showing 2 changed files with 170 additions and 64 deletions. Show diff stats Hide diff stats

  1. 196  Tokenizer.js
  2. 38  ZeParser.js
196  Tokenizer.js
@@ -20,12 +20,12 @@ function Tokenizer(inp){
20 20
 	this.line = 0;
21 21
 	this.column = 0;
22 22
 	this.cache = {};
23  
-	
  23
+
24 24
 	this.errorStack = [];
25  
-	
  25
+
26 26
 	this.wtree = [];
27 27
 	this.btree = [];
28  
-	
  28
+
29 29
 //	this.regexWhiteSpace = Tokenizer.regexWhiteSpace;
30 30
 	this.regexLineTerminator = Tokenizer.regexLineTerminator; // used in fallback
31 31
 	this.regexAsciiIdentifier = Tokenizer.regexAsciiIdentifier;
@@ -38,20 +38,23 @@ function Tokenizer(inp){
38 38
 //	this.regexPunctuators = Tokenizer.regexPunctuators;
39 39
 	this.regexNumber = Tokenizer.regexNumber;
40 40
 	this.regexNewline = Tokenizer.regexNewline;
41  
-	
  41
+
42 42
 	this.regexBig = Tokenizer.regexBig;
43 43
 	this.regexBigAlt = Tokenizer.regexBigAlt;
44  
-	
  44
+
45 45
 	this.tokenCount = 0;
46 46
 	this.tokenCountNoWhite = 0;
47  
-	
  47
+
48 48
 	this.Unicode = window.Unicode;
49  
-	
  49
+
50 50
 	// if the Parser throws an error. it will set this property to the next match
51 51
 	// at the time of the error (which was not what it was expecting at that point) 
52 52
 	// and pass on an "error" match. the error should be scooped on the stack and 
53 53
 	// this property should be returned, without looking at the input...
54 54
 	this.errorEscape = null;
  55
+
  56
+	// support tag literals
  57
+	this.tagLiterals = false;
55 58
 };
56 59
 
57 60
 Tokenizer.prototype = {
@@ -62,10 +65,10 @@ Tokenizer.prototype = {
62 65
 	column:null,
63 66
 	cache:null,
64 67
 	errorStack:null,
65  
-	
  68
+
66 69
 	wtree: null, // contains whitespace (spaces, comments, newlines)
67 70
 	btree: null, // does not contain any whitespace tokens.
68  
-	
  71
+
69 72
 	regexLineTerminator:null,
70 73
 	regexAsciiIdentifier:null,
71 74
 	hashAsciiIdentifier:null,
@@ -79,9 +82,9 @@ Tokenizer.prototype = {
79 82
 	regexBigAlt:null,
80 83
 	tokenCount:null,
81 84
 	tokenCountNoWhite:null,
82  
-	
  85
+
83 86
 	Unicode:null,
84  
-	
  87
+
85 88
 	// storeCurrentAndFetchNextToken(bool, false, false true) to get just one token
86 89
 	storeCurrentAndFetchNextToken: function(noRegex, returnValue, stack, _dontStore){
87 90
 		var regex = !noRegex; // TOFIX :)
@@ -101,16 +104,16 @@ Tokenizer.prototype = {
101 104
 				}
102 105
 			}
103 106
 			_dontStore = false;
104  
-		
  107
+
105 108
 			if (pos >= inp.length) {
106 109
 				returnValue = {start:inp.length,stop:inp.length,name:12/*EOF*/};
107  
-				break; 
  110
+				break;
108 111
 			}
109 112
 			var returnValue = null;
110  
-		
  113
+
111 114
 			var start = pos;
112 115
 			var chr = inp[pos];
113  
-	
  116
+
114 117
 			//							1 ws							2 lt				   3 scmt 4 mcmt 5/6 str 7 nr     8 rx  9 punc
115 118
 			//if (true) {
116 119
 				// substring method (I think this is faster..)
@@ -122,7 +125,7 @@ Tokenizer.prototype = {
122 125
 			//	this.regexBigAlt.lastIndex = pos;
123 126
 			//	var part = this.regexBigAlt.exec(inp);
124 127
 			//}
125  
-			
  128
+
126 129
 			if (part[1]) { //this.regexWhiteSpace.test(chr)) { // SP, TAB, VT, FF, NBSP, BOM (, TOFIX: USP)
127 130
 				++pos;
128 131
 				returnValue = {start:start,stop:pos,name:9/*WHITE_SPACE*/,line:this.line,col:this.column,isWhite:true};
@@ -153,7 +156,7 @@ Tokenizer.prototype = {
153 156
 				} else {
154 157
 					pos = newpos+2;
155 158
 					returnValue = {start:start,stop:pos,name:8/*COMMENT_MULTI*/,value:inp.substring(start, pos),line:this.line,col:this.column,isComment:true,isWhite:true};
156  
-	
  159
+
157 160
 					// multi line comments are also reason for asi, but only if they contain at least one newline (use shadow input, because all line terminators would be valid...)
158 161
 					var shadowValue = shadowInp.substring(start, pos);
159 162
 					var i = 0, hasNewline = 0;
@@ -172,7 +175,7 @@ Tokenizer.prototype = {
172 175
 			} else if (part[5]) { //chr == "'") {
173 176
 				// old method
174 177
 				//console.log("old method");
175  
-				
  178
+
176 179
 				var hasNewline = 0;
177 180
 				do {
178 181
 					// process escaped characters
@@ -200,7 +203,7 @@ Tokenizer.prototype = {
200 203
 					} else {
201 204
 						this.column += (pos-start);
202 205
 					}
203  
-				}				
  206
+				}
204 207
 			} else if (part[6]) { //chr == '"') {
205 208
 				var hasNewline = 0;
206 209
 				// TODO: something like this: var regexmatch = /([^\']|$)+/.match();
@@ -249,7 +252,7 @@ Tokenizer.prototype = {
249 252
 				} else {
250 253
 					throw 'unexpected parser errror... regex fail :(';
251 254
 				}
252  
-				
  255
+
253 256
 				if (value.length < 300) {
254 257
 					pos += value.length;
255 258
 				} else {
@@ -278,8 +281,8 @@ Tokenizer.prototype = {
278 281
 						nonLethalError = Tokenizer.Error.NothingToRepeat;
279 282
 					} else if (chr == '^') {
280 283
 						if (
281  
-							inp[pos-1] != '/' && 
282  
-							inp[pos-1] != '|' && 
  284
+							inp[pos-1] != '/' &&
  285
+							inp[pos-1] != '|' &&
283 286
 							inp[pos-1] != '(' &&
284 287
 							!(inp[pos-3] == '(' && inp[pos-2] == '?' && (inp[pos-1] == ':' || inp[pos-1] == '!' || inp[pos-1] == '='))
285 288
 						) {
@@ -313,7 +316,7 @@ Tokenizer.prototype = {
313 316
 									if (shadowInp[pos+1] == '\n') break;
314 317
 									else ++pos; // skip next char. (mainly prohibits ] to be picked up as closing the group...)
315 318
 								}
316  
-							} 
  319
+							}
317 320
 							if (inp[pos] != ']') {
318 321
 								returnValue = {start:start,stop:pos,name:14/*error*/,tokenError:true,error:Tokenizer.Error.ClosingClassRangeNotFound};
319 322
 								this.errorStack.push(returnValue);
@@ -327,9 +330,9 @@ Tokenizer.prototype = {
327 330
 							// is ok anywhere in the regex (match next char literally, regardless of its otherwise special meaning)
328 331
 							++pos;
329 332
 						}
330  
-						
  333
+
331 334
 						// now process repeaters (+, ? and *)
332  
-						
  335
+
333 336
 						// non-collecting group (?:...) and positive (?=...) or negative (?!...) lookahead
334 337
 						if (chr == '(') {
335 338
 							if (inp[pos+1] == '?' && (inp[pos+2] == ':' || inp[pos+2] == '=' || inp[pos+2] == '!')) {
@@ -377,8 +380,8 @@ Tokenizer.prototype = {
377 380
 				} else {
378 381
 					// this is the identifier scanner, for now
379 382
 					do ++pos;
380  
-					while (pos < inp.length && this.hashAsciiIdentifier[inp[pos]]); /*this.regexAsciiIdentifier.test(inp[pos])*/ 
381  
-	
  383
+					while (pos < inp.length && this.hashAsciiIdentifier[inp[pos]]); /*this.regexAsciiIdentifier.test(inp[pos])*/
  384
+
382 385
 					if (parens.length) {
383 386
 						// nope, this is still an error, there was at least one paren that did not have a matching twin
384 387
 						if (parens.length > 0) returnValue = {start:start,stop:pos,name:14/*error*/,tokenError:true,error:Tokenizer.Error.RegexOpenGroup};
@@ -388,16 +391,114 @@ Tokenizer.prototype = {
388 391
 						this.errorStack.push(returnValue);
389 392
 					} else {
390 393
 						returnValue = {start:start,stop:pos,name:1/*REG_EX*/,isPrimitive:true};
391  
-					}				
  394
+					}
392 395
 				}
393 396
 				returnValue.twinfo = twinfo;
  397
+      } else if (regex && part[9]) { // this.tagLiterals
  398
+				function tagOpen(node){
  399
+					var r = /[^\S]*([a-zA-Z][a-zA-Z0-9-]*)/g;
  400
+					r.lastIndex = pos+1;
  401
+					var tag = r.exec(inp);
  402
+					if (tag) {
  403
+						pos = r.lastIndex;
  404
+						node.name = tag[1];
  405
+						node.attributes = {};
  406
+
  407
+						// var r = /[^\S]+([a-zA-Z0-9-]+)(?:=(?:(?:"([^"]*?)")|(?:'([^']*?)')))?/g; r.lastIndex = 11; r.exec(' aabc="bef" c=\'d\' hidden'); r.lastIndex
  408
+		        var s = /[^\S]+([a-zA-Z0-9-]+)(?:=(?:(?:"([^"]*?)")|(?:'([^']*?)')))?/g;
  409
+						var attr = '';
  410
+						var lastIndex = pos = s.lastIndex = r.lastIndex;
  411
+						attr = s.exec(inp);
  412
+						while (attr && attr.index == pos) {
  413
+							if (typeof attr[2] == 'undefined') node.attributes[attr[1]] = attr[3];
  414
+							else node.attributes[attr[1]] = attr[2];
  415
+							pos = lastIndex = s.lastIndex;
  416
+							attr = s.exec(inp);
  417
+						}
  418
+
  419
+						// it was a unary tag
  420
+						var t = /[^\S]*\/[^\S]*>/g;
  421
+						t.lastIndex = lastIndex;
  422
+						var x = t.exec(inp);
  423
+	          node.unary = !!x && x.index == pos;
  424
+						if (node.unary) {
  425
+							pos = t.lastIndex;
  426
+							return true;
  427
+						}
  428
+						// it was a binary tag
  429
+						var u = /[^\S]*?>/g;
  430
+						u.lastIndex = lastIndex;
  431
+						var x = u.exec(inp);
  432
+						if (x && x.index == pos) {
  433
+	            node.children = [];
  434
+	            // now parse strings and other tags until you find a closing tag on the same level...
  435
+							pos = u.lastIndex;
  436
+              return true;
  437
+						}
  438
+            // i dont know what that was
  439
+						throw console.warn("Error parsing tag");
  440
+						return false;
  441
+					}
  442
+				}
  443
+
  444
+        function tagBody(node){
  445
+					do {
  446
+            var start = pos;
  447
+
  448
+	          var r = /([^<]*)/g;
  449
+						r.lastIndex = pos;
  450
+						var text = r.exec(inp);
  451
+						if (text && text[1]) {
  452
+							node.children.push(text[1]);
  453
+							pos = r.lastIndex;
  454
+						}
  455
+						if (inp[pos] == '<') {
  456
+							var s = /<[^\S]*[\/>]*\//g;
  457
+							s.lastIndex = pos;
  458
+							var x = s.exec(inp);
  459
+							if (x && x.index == pos) {
  460
+								return node; // end of body
  461
+							}
  462
+							node.children.push(tag({}));
  463
+						}
  464
+					} while (start != pos);
  465
+        }
  466
+
  467
+				function tagClose(node){
  468
+					var r = /<[^\S]*\/[^\S]*([a-zA-Z0-9-]+)[^\S]*>/g;
  469
+					r.lastIndex = pos;
  470
+					var ctag = r.exec(inp);
  471
+					if (ctag) {
  472
+						pos = r.lastIndex;
  473
+						if (node.name == ctag[1]) return true;
  474
+						return false; // was not expecting to close this tag
  475
+					}
  476
+
  477
+          // tagClose should only be called if the next chars are starting a closing tag...
  478
+          return false;
  479
+				}
  480
+
  481
+				function tag(node){
  482
+	        if (!tagOpen(node)) {
  483
+						return node;
  484
+					}
  485
+	        if (!node.unary) {
  486
+	          tagBody(node);
  487
+	          tagClose(node);
  488
+	        }
  489
+					return node;
  490
+				}
  491
+
  492
+        var root = tag({});
  493
+
  494
+				returnValue = {start:start,stop:pos,name:15/*TAG*/,isPrimitive:true,root:root};
394 495
 			} else {
395 496
 				// note: operators need to be ordered from longest to smallest. regex will take care of the rest.
396 497
 				// no need to worry about div vs regex. if looking for regex, earlier if will have eaten it
397 498
 				//var result = this.regexPunctuators.exec(inp.substring(pos,pos+4));
398  
-				
399  
-				// note: due to the regex, the single forward slash might be caught by an earlier part of the regex. so check for that.
400  
-				var result = part[8] || part[9];
  499
+
  500
+				// note: due to the regex, the single / or < might be caught by an earlier part of the regex. so check for that.
  501
+				var result = part[8] || part[9] || part[10];
401 502
 				if (result) {
402 503
 					//result = result[1];
403 504
 					returnValue = {start:pos,stop:pos+=result.length,name:11/*PUNCTUATOR*/,value:result};
@@ -406,7 +507,7 @@ Tokenizer.prototype = {
406 507
 					// identifiers cannot start with a number. but if the leading string would be a number, another if would have eaten it already for numeric literal :)
407 508
 					while (pos < inp.length) {
408 509
 						var c = inp[pos];
409  
-	
  510
+
410 511
 						if (this.hashAsciiIdentifier[c]) ++pos; //if (this.regexAsciiIdentifier.test(c)) ++pos;
411 512
 						else if (c == '\\' && this.regexUnicodeEscape.test(inp.substring(pos,pos+6))) pos += 6; // this is like a \uxxxx
412 513
 						// ok, now test unicode ranges...
@@ -419,17 +520,17 @@ Tokenizer.prototype = {
419 520
 							var Unicode = this.Unicode; // cache
420 521
 							if (!(
421 522
 									// these may all occur in an identifier... (pure a specification compliance thing :)
422  
-									Unicode.Lu.test(c) || Unicode.Ll.test(c) || Unicode.Lt.test(c) || Unicode.Lm.test(c) || 
  523
+									Unicode.Lu.test(c) || Unicode.Ll.test(c) || Unicode.Lt.test(c) || Unicode.Lm.test(c) ||
423 524
 									Unicode.Lo.test(c) || Unicode.Nl.test(c) || Unicode.Mn.test(c) || Unicode.Mc.test(c) ||
424 525
 									Unicode.Nd.test(c) || Unicode.Pc.test(c) || Unicode.sp.test(c)
425 526
 							)) break; // end of match.
426 527
 							// passed, next char
427 528
 							++pos;
428 529
 						} else break; // end of match.
429  
-			
  530
+
430 531
 						found = true;
431 532
 					}
432  
-		
  533
+
433 534
 					if (found) {
434 535
 						returnValue = {start:start,stop:pos,name:2/*IDENTIFIER*/,value:inp.substring(start,pos)};
435 536
 						if (returnValue.value == 'undefined' || returnValue.value == 'null' || returnValue.value == 'true' || returnValue.value == 'false') returnValue.isPrimitive = true;
@@ -454,7 +555,7 @@ Tokenizer.prototype = {
454 555
 					}
455 556
 				}
456 557
 			}
457  
-			
  558
+
458 559
 			if (returnValue) {
459 560
 				// note that ASI's are slipstreamed in here from the parser since the tokenizer cant determine that
460 561
 				// if this part ever changes, make sure you change that too :)
@@ -463,15 +564,15 @@ Tokenizer.prototype = {
463 564
 				if (!returnValue.isWhite) {
464 565
 					returnValue.tokposb = this.btree.length;
465 566
 					this.btree.push(returnValue);
466  
-				} 
  567
+				}
467 568
 			}
468  
-			
469  
-			
  569
+
  570
+
470 571
 		} while (stack && returnValue && returnValue.isWhite); // WHITE_SPACE LINETERMINATOR COMMENT_SINGLE COMMENT_MULTI
471 572
 		++this.tokenCountNoWhite;
472  
-		
  573
+
473 574
 		this.pos = pos;
474  
-	
  575
+
475 576
 		if (matchedNewline) returnValue.newline = true;
476 577
 		return returnValue;
477 578
 	},
@@ -584,7 +685,7 @@ Tokenizer.testSuite = function(arr){
584 685
 		var outputLen = test[1].length ? test[1][0] : test[1];
585 686
 		var regexHints = test[3] ? test[2] : null; // if flags, then len=4
586 687
 		var desc = test[3] || test[2];
587  
-		
  688
+
588 689
 		var result = new Tokenizer(input).tokens(regexHints); // regexHints can be null, that's ok
589 690
 		if (result.length == outputLen) {
590 691
 			debug('<span class="green">Test '+i+' ok:</span>',desc);
@@ -607,7 +708,7 @@ Tokenizer.hashAsciiIdentifier = {_:1,$:1,a:1,b:1,c:1,d:1,e:1,f:1,g:1,h:1,i:1,j:1
607 708
 Tokenizer.regexHex = /[0-9A-Fa-f]/;
608 709
 Tokenizer.hashHex = {0:1,1:1,2:1,3:1,4:1,5:1,6:1,7:1,8:1,9:1,a:1,b:1,c:1,d:1,e:1,f:1,A:1,B:1,C:1,D:1,E:1,F:1};
609 710
 Tokenizer.regexUnicodeEscape = /u[0-9A-Fa-f]{4}/; // the \ is already checked at usage...
610  
-Tokenizer.regexIdentifierStop = /[\>\=\!\|\<\+\-\&\*\%\^\/\{\}\(\)\[\]\.\;\,\~\?\:\ \t\n\\\'\"]/; 
  711
+Tokenizer.regexIdentifierStop = /[\>\=\!\|\<\+\-\&\*\%\^\/\{\}\(\)\[\]\.\;\,\~\?\:\ \t\n\\\'\"]/;
611 712
 Tokenizer.hashIdentifierStop = {'>':1,'=':1,'!':1,'|':1,'<':1,'+':1,'-':1,'&':1,'*':1,'%':1,'^':1,'/':1,'{':1,'}':1,'(':1,')':1,'[':1,']':1,'.':1,';':1,',':1,'~':1,'?':1,':':1,'\\':1,'\'':1,'"':1,' ':1,'\t':1,'\n':1};
612 713
 Tokenizer.regexNewline = /\n/g;
613 714
 //Tokenizer.regexPunctuators = /^(>>>=|===|!==|>>>|<<=|>>=|<=|>=|==|!=|\+\+|--|<<|>>|\&\&|\|\||\+=|-=|\*=|%=|\&=|\|=|\^=|\/=|\{|\}|\(|\)|\[|\]|\.|;|,|<|>|\+|-|\*|%|\||\&|\||\^|!|~|\?|:|=|\/)/;
@@ -615,8 +716,13 @@ Tokenizer.Unidocde = window.Unicode;
615 716
 Tokenizer.regexNumber = /^(?:(0[xX][0-9A-Fa-f]+)|((?:(?:(?:(?:[0-9]+)(?:\.[0-9]*)?))|(?:\.[0-9]+))(?:[eE][-+]?[0-9]{1,})?))/;
616 717
 Tokenizer.regexNormalizeNewlines = /(\u000D[^\u000A])|[\u2028\u2029]/;
617 718
 
618  
-//							1 ws							2 lt				   3 scmt 4 mcmt 5/6 str 7 nr     8 rx  9 punc
619  
-Tokenizer.regexBig = /^([ \t\u000B\u000C\u00A0\uFFFF])?([\u000A\u000D\u2028\u2029])?(\/\/)?(\/\*)?(')?(")?(\.?[0-9])?(?:(\/)[^=])?(>>>=|===|!==|>>>|<<=|>>=|<=|>=|==|!=|\+\+|--|<<|>>|\&\&|\|\||\+=|-=|\*=|%=|\&=|\|=|\^=|\/=|\{|\}|\(|\)|\[|\]|\.|;|,|<|>|\+|-|\*|%|\||\&|\||\^|!|~|\?|:|=|\/)?/;
  719
+// Tokenizer.regexUnaryTag = /<\s*(\S)(?:\s+)?/>/;
  720
+// /<\s*(\S+)\/>/.exec('< foo/>')
  721
+// /[^\S]+([a-zA-Z0-9-]+)(?:="([^"]*)")?/g.exec(' a-b="b"')
  722
+
  723
+
  724
+//                      1 ws                            2 lt                        3 scmt 4 mcmt 5/6 str 7 nr       8 rx         9 dom        10 punc
  725
+Tokenizer.regexBig = /^([ \t\u000B\u000C\u00A0\uFFFF])?([\u000A\u000D\u2028\u2029])?(\/\/)?(\/\*)?(')?(")?(\.?[0-9])?(?:(\/)[^=])?(?:(<)[^<=])?(>>>=|===|!==|>>>|<<=|>>=|<=|>=|==|!=|\+\+|--|<<|>>|\&\&|\|\||\+=|-=|\*=|%=|\&=|\|=|\^=|\/=|\{|\}|\(|\)|\[|\]|\.|;|,|<|>|\+|-|\*|%|\||\&|\||\^|!|~|\?|:|=|\/)?/;
620 726
 Tokenizer.regexBigAlt = /([ \t\u000B\u000C\u00A0\uFFFF])?([\u000A\u000D\u2028\u2029])?(\/\/)?(\/\*)?(')?(")?(\.?[0-9])?(?:(\/)[^=])?(>>>=|===|!==|>>>|<<=|>>=|<=|>=|==|!=|\+\+|--|<<|>>|\&\&|\|\||\+=|-=|\*=|%=|\&=|\|=|\^=|\/=|\{|\}|\(|\)|\[|\]|\.|;|,|<|>|\+|-|\*|%|\||\&|\||\^|!|~|\?|:|=|\/)?/g;
621 727
 
622 728
 Tokenizer.Error = {
38  ZeParser.js
@@ -183,7 +183,7 @@ ZeParser.prototype = {
183 183
 			// TOFIX: can probably get the regex out somehow...
184 184
 			if (!first) {
185 185
 				match = this.tokenizer.storeCurrentAndFetchNextToken(false, match, stack);
186  
-				if (!(/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value))) match = this.failsafe('ExpectedAnotherExpressionComma', match);
  186
+				if (!(/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value))) match = this.failsafe('ExpectedAnotherExpressionComma', match);
187 187
 			}
188 188
 
189 189
 			if (this.ast) { //#ifdef FULL_AST
@@ -234,8 +234,8 @@ ZeParser.prototype = {
234 234
 					} //#endif
235 235
 
236 236
 					match = this.tokenizer.storeCurrentAndFetchNextToken(false, match, stack);
237  
-					// ensure that it is in fact a valid lhs-start
238  
-					if (!(/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value))) match = this.failsafe('ExpectedAnotherExpressionRhs', match);
  237
+					// ensure that it is in fact a valid lhs-start. TAG is a custom extension for optional tag literal syntax support.
  238
+					if (!(/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value))) match = this.failsafe('ExpectedAnotherExpressionRhs', match);
239 239
 					// not allowed to parse assignment
240 240
 					parsedUnaryOperator = true;
241 241
 				};
@@ -263,7 +263,7 @@ ZeParser.prototype = {
263 263
 						match.isGroupStart = true;
264 264
 					} //#endif
265 265
 					match = this.tokenizer.storeCurrentAndFetchNextToken(false, match, stack);
266  
-					if (!(/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value))) match = this.failsafe('GroupingShouldStartWithExpression', match);
  266
+					if (!(/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value))) match = this.failsafe('GroupingShouldStartWithExpression', match);
267 267
 					// keep parsing expressions as long as they are followed by a comma
268 268
 					match = this.eatExpressions(false, match, stack);
269 269
 
@@ -311,7 +311,7 @@ ZeParser.prototype = {
311 311
 					while (foundAtLeastOneComma && match.value != ']') {
312 312
 						foundAtLeastOneComma = false;
313 313
 
314  
-						if (!(/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value)) && match.name != 14/*error*/) match = this.failsafe('ArrayShouldStartWithExpression', match);
  314
+						if (!(/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value)) && match.name != 14/*error*/) match = this.failsafe('ArrayShouldStartWithExpression', match);
315 315
 						match = this.eatExpressions(false, match, stack, true);
316 316
 
317 317
 						while (match.value == ',') {
@@ -546,7 +546,7 @@ ZeParser.prototype = {
546 546
 						stack = oldstack;
547 547
 						this.scope = oldscope;
548 548
 					} //#endif
549  
-				} else if (match.name <= 6) { // IDENTIFIER STRING_SINGLE STRING_DOUBLE NUMERIC_HEX NUMERIC_DEC REG_EX
  549
+				} else if (match.name <= 6 || match.name == 15/*TAG*/) { // IDENTIFIER STRING_SINGLE STRING_DOUBLE NUMERIC_HEX NUMERIC_DEC REG_EX or TAG
550 550
 					// save it in case it turns out to be a label.
551 551
 					var possibleLabel = match;
552 552
 
@@ -674,7 +674,7 @@ ZeParser.prototype = {
674 674
 						} //#endif
675 675
 						// property access, read expression list. allow assignments
676 676
 						match = this.tokenizer.storeCurrentAndFetchNextToken(false, match, stack);
677  
-						if (!(/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value))) {
  677
+						if (!(/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value))) {
678 678
 							if (match.value == ']') match = this.failsafe('SquareBracketsMayNotBeEmpty', match);
679 679
 							else match = this.failsafe('SquareBracketExpectsExpression', match);
680 680
 						}
@@ -703,7 +703,7 @@ ZeParser.prototype = {
703 703
 						} //#endif
704 704
 						// call expression, eat optional expression list, disallow assignments
705 705
 						match = this.tokenizer.storeCurrentAndFetchNextToken(false, match, stack);
706  
-						if (/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value)) match = this.eatExpressions(false, match, stack); // arguments are optional
  706
+						if (/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value)) match = this.eatExpressions(false, match, stack); // arguments are optional
707 707
 						if (match.value != ')') match = this.failsafe('UnclosedCallParens', match);
708 708
 						if (this.ast) { //#ifdef FULL_AST
709 709
 							match.twin = lhp;
@@ -778,7 +778,7 @@ ZeParser.prototype = {
778 778
 						if (ternary) {
779 779
 							// LogicalORExpression "?" AssignmentExpression ":" AssignmentExpression
780 780
 							// so that means just one expression center and right.
781  
-							if (!(/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value))) this.failignore('InvalidCenterTernaryExpression', match, stack);
  781
+							if (!(/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value))) this.failignore('InvalidCenterTernaryExpression', match, stack);
782 782
 							match = this.eatExpressions(false, match, stack, true, forHeader); // only one expression allowed inside ternary center/right
783 783
 
784 784
 							if (match.value != ':') {
@@ -809,7 +809,7 @@ ZeParser.prototype = {
809 809
 				} while (ternary); // if we just parsed a ternary expression, we need to check _again_ whether the next token is a binary operator.
810 810
 
811 811
 				// start over. match is the rhs for the lhs we just parsed, but lhs for the next expression
812  
-				if (parseAnotherExpression && !(/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value))) {
  812
+				if (parseAnotherExpression && !(/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value))) {
813 813
 					// no idea what to do now. lets just ignore and see where it ends. TOFIX: maybe just break the loop or return?
814 814
 					this.failignore('InvalidRhsExpression', match, stack);
815 815
 				}
@@ -1073,7 +1073,7 @@ ZeParser.prototype = {
1073 1073
 					stack = singleDecStack;
1074 1074
 				} //#endif
1075 1075
 
1076  
-				if (!(/*is left hand side start?*/ match.name <= 6 || match.name == 14/*error*/ || this.regexLhsStart.test(match.value))) match = this.failsafe('VarInitialiserExpressionExpected', match);
  1076
+				if (!(/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || match.name == 14/*error*/ || this.regexLhsStart.test(match.value))) match = this.failsafe('VarInitialiserExpressionExpected', match);
1077 1077
 				match = this.eatExpressions(false, match, stack, true, forHeader); // only one expression 
1078 1078
 				// var statement: comma or semi now
1079 1079
 				// for statement: semi, comma or 'in'
@@ -1115,7 +1115,7 @@ ZeParser.prototype = {
1115 1115
 			match.statementHeaderStart = true;
1116 1116
 		} //#endif
1117 1117
 		match = this.tokenizer.storeCurrentAndFetchNextToken(false, match, stack);
1118  
-		if (!(/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value))) match = this.failsafe('StatementHeaderIsNotOptional', match);
  1118
+		if (!(/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value))) match = this.failsafe('StatementHeaderIsNotOptional', match);
1119 1119
 		match = this.eatExpressions(false, match, stack);
1120 1120
 		if (match.value != ')') match = this.failsafe('ExpectedStatementHeaderClose', match);
1121 1121
 		if (this.ast) { //#ifdef FULL_AST
@@ -1183,7 +1183,7 @@ ZeParser.prototype = {
1183 1183
 			match.statementHeaderStart = true;
1184 1184
 		} //#endif
1185 1185
 		match = this.tokenizer.storeCurrentAndFetchNextToken(false, match, stack);
1186  
-		if (!(/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value))) match = this.failsafe('StatementHeaderIsNotOptional', match);
  1186
+		if (!(/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value))) match = this.failsafe('StatementHeaderIsNotOptional', match);
1187 1187
 		match = this.eatExpressions(false, match, stack);
1188 1188
 		if (match.value != ')') match = this.failsafe('ExpectedStatementHeaderClose', match);
1189 1189
 		if (this.ast) { //#ifdef FULL_AST
@@ -1223,7 +1223,7 @@ ZeParser.prototype = {
1223 1223
 			match.statementHeaderStart = true;
1224 1224
 		} //#endif
1225 1225
 		match = this.tokenizer.storeCurrentAndFetchNextToken(false, match, stack);
1226  
-		if (!(/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value))) match = this.failsafe('StatementHeaderIsNotOptional', match);
  1226
+		if (!(/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value))) match = this.failsafe('StatementHeaderIsNotOptional', match);
1227 1227
 		match = this.eatExpressions(false, match, stack);
1228 1228
 		if (match.value != ')') match = this.failsafe('ExpectedStatementHeaderClose', match);
1229 1229
 		if (this.ast) { //#ifdef FULL_AST
@@ -1270,7 +1270,7 @@ ZeParser.prototype = {
1270 1270
 		if (match.value == 'var') {
1271 1271
 			match = this.eatVarDecl(match, stack, true);
1272 1272
 		} else if (match.value != ';') { // expressions are optional in for-each
1273  
-			if (!(/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value))) {
  1273
+			if (!(/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value))) {
1274 1274
 				this.failignore('StatementHeaderIsNotOptional', match, stack);
1275 1275
 			}
1276 1276
 			match = this.eatExpressions(false, match, stack, false, true); // can parse multiple expressions, in is not ok here
@@ -1301,13 +1301,13 @@ ZeParser.prototype = {
1301 1301
 			} //#endif
1302 1302
 			// parse another optional no-in expression, another semi and then one more optional no-in expression
1303 1303
 			match = this.tokenizer.storeCurrentAndFetchNextToken(false, match, stack);
1304  
-			if (/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value)) match = this.eatExpressions(false, match, stack); // in is ok here
  1304
+			if (/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value)) match = this.eatExpressions(false, match, stack); // in is ok here
1305 1305
 			if (match.value != ';') match = this.failsafe('ExpectedSecondSemiOfForHeader', match);
1306 1306
 			if (this.ast) { //#ifdef FULL_AST
1307 1307
 				match.forEachHeaderStop = true;
1308 1308
 			} //#endif
1309 1309
 			match = this.tokenizer.storeCurrentAndFetchNextToken(false, match, stack);
1310  
-			if (/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value)) match = this.eatExpressions(false, match, stack); // in is ok here
  1310
+			if (/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value)) match = this.eatExpressions(false, match, stack); // in is ok here
1311 1311
 		}
1312 1312
 
1313 1313
 		if (match.value != ')') match = this.failsafe('ExpectedStatementHeaderClose', match);
@@ -1447,7 +1447,7 @@ ZeParser.prototype = {
1447 1447
 			match.statementHeaderStart = true;
1448 1448
 		} //#endif
1449 1449
 		match = this.tokenizer.storeCurrentAndFetchNextToken(false, match, stack);
1450  
-		if (!(/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value))) {
  1450
+		if (!(/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value))) {
1451 1451
 			this.failignore('StatementHeaderIsNotOptional', match, stack);
1452 1452
 		}
1453 1453
 		match = this.eatExpressions(false, match, stack);
@@ -1799,7 +1799,7 @@ ZeParser.prototype = {
1799 1799
 			match.statementHeaderStart = true;
1800 1800
 		} //#endif
1801 1801
 		match = this.tokenizer.storeCurrentAndFetchNextToken(false, match, stack);
1802  
-		if (!(/*is left hand side start?*/ match.name <= 6 || this.regexLhsStart.test(match.value))) match = this.failsafe('StatementHeaderIsNotOptional', match);
  1802
+		if (!(/*is left hand side start?*/ match.name <= 6 || match.name == 15/*TAG*/ || this.regexLhsStart.test(match.value))) match = this.failsafe('StatementHeaderIsNotOptional', match);
1803 1803
 		match = this.eatExpressions(false, match, stack);
1804 1804
 		if (match.value != ')') match = this.failsafe('ExpectedStatementHeaderClose', match);
1805 1805
 		if (this.ast) { //#ifdef FULL_AST

0 notes on commit a470642

Please sign in to comment.
Something went wrong with that request. Please try again.