Index: trunk/extensions/VisualEditor/modules/parser/pegTokenizer.pegjs.txt |
— | — | @@ -271,20 +271,58 @@ |
272 | 272 | * productions can just be unrolled for all combinations of environments |
273 | 273 | * at the cost of a much larger grammar. |
274 | 274 | */ |
275 | | - var syntaxFlags = {}; |
276 | | - var setFlag = function(flag) { |
277 | | - if (syntaxFlags[flag] !== undefined) { |
278 | | - syntaxFlags[flag]++; |
| 275 | + function SyntaxStops () { |
| 276 | + this.counters = {}; |
| 277 | + this.stacks = {}; |
| 278 | + } |
| 279 | + SyntaxStops.prototype.inc = function(flag) { |
| 280 | + if (this.counters[flag] !== undefined) { |
| 281 | + this.counters[flag]++; |
279 | 282 | } else { |
280 | | - syntaxFlags[flag] = 1; |
| 283 | + this.counters[flag] = 1; |
281 | 284 | } |
282 | 285 | return true; |
283 | 286 | }; |
284 | | - var clearFlag = function(flag) { |
285 | | - syntaxFlags[flag]--; |
| 287 | + SyntaxStops.prototype.dec = function(flag) { |
| 288 | + this.counters[flag]--; |
286 | 289 | return false; |
287 | 290 | }; |
| 291 | + SyntaxStops.prototype.onCount = function ( name ) { |
| 292 | + return this.counters[name]; |
| 293 | + }; |
288 | 294 | |
| 295 | + /** |
| 296 | + * A stack for nested, but not cumulative syntactic stops. |
| 297 | + * Example: '=' is allowed in values of template arguments, even if those |
| 298 | + * are nested in attribute names. |
| 299 | + */ |
| 300 | + SyntaxStops.prototype.push = function ( name, value ) { |
| 301 | + if( this.stacks[name] === undefined ) { |
| 302 | + this.stacks[name] = [value]; |
| 303 | + } else { |
| 304 | + this.stacks[name].push( value ); |
| 305 | + } |
| 306 | + return true; |
| 307 | + }; |
| 308 | + SyntaxStops.prototype.pop = function ( name ) { |
| 309 | + if( this.stacks[name] !== undefined ) { |
| 310 | + this.stacks[name].pop(); |
| 311 | + } else { |
| 312 | + throw "SyntaxStops.pop: unknown stop for " + name; |
| 313 | + } |
| 314 | + return false; |
| 315 | + }; |
| 316 | + SyntaxStops.prototype.onStack = function ( name ) { |
| 317 | + var stack = this.stacks[name]; |
| 318 | + if ( stack === undefined || stack.length === 0 ) { |
| 319 | + return false; |
| 320 | + } else { |
| 321 | + return stack[stack.length - 1]; |
| 322 | + } |
| 323 | + }; |
| 324 | + |
| 325 | + var stops = new SyntaxStops(); |
| 326 | + |
289 | 327 | // Start position of top-level block |
290 | 328 | // Could also provide positions for lower-level blocks using a stack. |
291 | 329 | var blockStart = 0; |
— | — | @@ -472,7 +510,7 @@ |
473 | 511 | // cache key does not take into account flag states! |
474 | 512 | cacheKey = ''; |
475 | 513 | //console.warn('ilbf: ' + input.substr(pos, 5) ); |
476 | | - return null !== __parseArgs[3].inline_breaks( input, pos, syntaxFlags ) |
| 514 | + return null !== __parseArgs[3].inline_breaks( input, pos, stops ) |
477 | 515 | } |
478 | 516 | |
479 | 517 | inline |
— | — | @@ -504,13 +542,13 @@ |
505 | 543 | // XXX: Also check to end to avoid inline parsing? |
506 | 544 | r:( |
507 | 545 | s:'='+ // moved in here to make s accessible to inner action |
508 | | - & { return setFlag('h'); } |
| 546 | + & { return stops.inc('h'); } |
509 | 547 | c:inlineline |
510 | 548 | e:'='+ |
511 | 549 | spc:(sp:space+ { return sp.join('') } / comment)* |
512 | 550 | &eolf |
513 | 551 | { |
514 | | - clearFlag('h'); |
| 552 | + stops.dec('h'); |
515 | 553 | var level = Math.min(s.length, e.length); |
516 | 554 | // convert surplus equals into text |
517 | 555 | if(s.length > level) { |
— | — | @@ -534,7 +572,7 @@ |
535 | 573 | return [new TagTk( 'h' + level )] |
536 | 574 | .concat(c, [new EndTagTk( 'h' + level ), spc]); |
537 | 575 | } |
538 | | - / & { /* dp('nomatch exit h'); */ clearFlag('h'); return false } { return null } |
| 576 | + / & { /* dp('nomatch exit h'); */ stops.dec('h'); return false } { return null } |
539 | 577 | ) { return r } |
540 | 578 | |
541 | 579 | comment |
— | — | @@ -554,22 +592,22 @@ |
555 | 593 | **************************************************************/ |
556 | 594 | |
557 | 595 | urllink |
558 | | - = ! { return syntaxFlags['extlink'] } |
| 596 | + = ! { return stops.onCount('extlink') } |
559 | 597 | target:url { |
560 | 598 | return [ new TagTk( 'urllink', [new KV('href', target)] ) ]; |
561 | 599 | } |
562 | 600 | |
563 | 601 | extlink |
564 | | - = ! { return syntaxFlags['extlink'] } // extlink cannot be nested |
| 602 | + = ! { return stops.onCount('extlink') } // extlink cannot be nested |
565 | 603 | ( |
566 | 604 | "[" |
567 | | - & { return setFlag('extlink'); } |
| 605 | + & { return stops.inc('extlink'); } |
568 | 606 | //target:urllink |
569 | 607 | target:extlink_preprocessor_text |
570 | 608 | text:(( space / [\u00A0\u1680\u180E\u2000-\u200A\u202F\u205F\u3000] )* |
571 | 609 | t:inlineline { return t } )? |
572 | 610 | "]" { |
573 | | - clearFlag('extlink'); |
| 611 | + stops.dec('extlink'); |
574 | 612 | if ( text === '' ) { |
575 | 613 | // XXX: Link numbering should be implemented in post-processor. |
576 | 614 | text = [ "[" + linkCount + "]" ]; |
— | — | @@ -583,7 +621,7 @@ |
584 | 622 | ] ) |
585 | 623 | ]; |
586 | 624 | } |
587 | | - / "[" & { clearFlag('extlink'); return false; } |
| 625 | + / "[" & { stops.dec('extlink'); return false; } |
588 | 626 | ) |
589 | 627 | |
590 | 628 | /* Defaul URL protocols in MediaWiki (see DefaultSettings). Normally these can |
— | — | @@ -706,7 +744,7 @@ |
707 | 745 | s0:space* |
708 | 746 | eq:"="? |
709 | 747 | s1:space* |
710 | | - value:template_param_text? |
| 748 | + value:template_param_value? |
711 | 749 | |
712 | 750 | { |
713 | 751 | //console.warn( 'named template_param matched' + pp([name, value ]) ); |
— | — | @@ -724,25 +762,35 @@ |
725 | 763 | |
726 | 764 | // FIXME: handle template args and templates in key! (or even parser functions?) |
727 | 765 | template_param_name |
728 | | - = & { return setFlag( 'equalTemplate' ) } |
| 766 | + = & { return stops.push( 'equal', true ) } |
729 | 767 | tpt:template_param_text |
730 | 768 | { |
731 | | - clearFlag( 'equalTemplate' ); |
| 769 | + stops.pop( 'equal' ); |
732 | 770 | //console.warn( 'template param name matched: ' + pp( tpt ) ); |
733 | 771 | return tpt; |
734 | 772 | } |
735 | 773 | |
736 | | - / & { return clearFlag( 'equalTemplate' ) } |
| 774 | + / & { return stops.pop( 'equal' ) } |
737 | 775 | //= h:( !"}}" x:([^=|\n]) { return x } )* { return h.join(''); } |
738 | 776 | |
| 777 | +template_param_value |
| 778 | + = & { return stops.push( 'equal', false ) } |
| 779 | + tpt:template_param_text |
| 780 | + { |
| 781 | + stops.pop( 'equal' ); |
| 782 | + //console.warn( 'template param value matched: ' + pp( tpt ) ); |
| 783 | + return tpt; |
| 784 | + } |
| 785 | + / & { return stops.pop( 'equal' ) } |
| 786 | + |
739 | 787 | template_param_text |
740 | | - = & { return setFlag('template') } |
| 788 | + = & { return stops.inc('template') } |
741 | 789 | il:inline { |
742 | | - clearFlag('template'); |
| 790 | + stops.dec('template'); |
743 | 791 | //console.warn( 'tpt match: ' + pp (il)); |
744 | 792 | return il; |
745 | 793 | } |
746 | | - / & { return clearFlag('template'); } |
| 794 | + / & { return stops.dec('template'); } |
747 | 795 | |
748 | 796 | |
749 | 797 | // TODO: handle link prefixes as in al[[Razi]] |
— | — | @@ -790,39 +838,39 @@ |
791 | 839 | / ! { return posStack.pop( 'wikilink', pos ); } |
792 | 840 | |
793 | 841 | link_text |
794 | | - = & { return setFlag('linkdesc'); } |
| 842 | + = & { return stops.inc('linkdesc'); } |
795 | 843 | h:inline |
796 | 844 | // 'equal' syntaxFlag is set for links in template parameters. Consume the |
797 | 845 | // '=' here. |
798 | 846 | hs:( '=' inline)? |
799 | 847 | { |
800 | 848 | //console.warn('link_text' + pp(h) + pp(hs)); |
801 | | - clearFlag('linkdesc'); |
| 849 | + stops.dec('linkdesc'); |
802 | 850 | if( hs !== '' ) { |
803 | 851 | return h.concat(hs); |
804 | 852 | } else { |
805 | 853 | return h; |
806 | 854 | } |
807 | 855 | } |
808 | | - / & { return clearFlag('linkdesc'); } |
| 856 | + / & { return stops.dec('linkdesc'); } |
809 | 857 | |
810 | 858 | link_option |
811 | | - = & { setFlag('pipe'); return setFlag('linkdesc'); } |
| 859 | + = & { stops.inc('pipe'); return stops.inc('linkdesc'); } |
812 | 860 | h:inline |
813 | 861 | // 'equal' syntaxFlag is set for links in template parameters. Consume the |
814 | 862 | // '=' here. |
815 | 863 | hs:( '=' inline)? |
816 | 864 | { |
817 | 865 | //console.warn('link_text' + pp(h) + pp(hs)); |
818 | | - clearFlag('pipe'); |
819 | | - clearFlag('linkdesc'); |
| 866 | + stops.dec('pipe'); |
| 867 | + stops.dec('linkdesc'); |
820 | 868 | if( hs !== '' ) { |
821 | 869 | return h.concat(hs); |
822 | 870 | } else { |
823 | 871 | return h; |
824 | 872 | } |
825 | 873 | } |
826 | | - / & { clearFlag('pipe'); return clearFlag('linkdesc'); } |
| 874 | + / & { stops.dec('pipe'); return stops.dec('linkdesc'); } |
827 | 875 | |
828 | 876 | link_end = "]]" |
829 | 877 | |
— | — | @@ -845,9 +893,9 @@ |
846 | 894 | * transformer, and only for images. |
847 | 895 | */ |
848 | 896 | img_options = |
849 | | - & { return setFlag( 'pipe' ); } |
| 897 | + & { return stops.inc( 'pipe' ); } |
850 | 898 | os:img_option* { |
851 | | - clearFlag( 'pipe' ); |
| 899 | + stops.dec( 'pipe' ); |
852 | 900 | var options = {}; |
853 | 901 | os = flatten( os ); |
854 | 902 | for ( var i = 0, l = os.length; i < l; i++ ) { |
— | — | @@ -857,7 +905,7 @@ |
858 | 906 | options._options = os; |
859 | 907 | return options; |
860 | 908 | } |
861 | | -/ & { return clearFlag( 'pipe' ); } |
| 909 | +/ & { return stops.dec( 'pipe' ); } |
862 | 910 | |
863 | 911 | img_option |
864 | 912 | = "|" space* |
— | — | @@ -909,10 +957,10 @@ |
910 | 958 | = 'link=' space* |
911 | 959 | u:( |
912 | 960 | t:url { |
913 | | - clearFlag( 'pipe' ); |
| 961 | + stops.dec( 'pipe' ); |
914 | 962 | return t; |
915 | 963 | } |
916 | | - / & { return clearFlag( 'pipe' ); } |
| 964 | + / & { return stops.dec( 'pipe' ); } |
917 | 965 | ) |
918 | 966 | { |
919 | 967 | return new KV( 'link', u ); |
— | — | @@ -946,16 +994,16 @@ |
947 | 995 | "<pre" |
948 | 996 | attribs:generic_attribute* |
949 | 997 | ">" |
950 | | - & { return setFlag('pre'); } |
| 998 | + & { return stops.inc('pre'); } |
951 | 999 | l:inlineline |
952 | 1000 | ls:(sol pre_indent_line)* |
953 | 1001 | "</pre>" |
954 | 1002 | { |
955 | | - clearFlag('pre'); |
| 1003 | + stops.dec('pre'); |
956 | 1004 | return [ new TagTk( 'pre', attribs ) ] |
957 | 1005 | .concat( l, flatten( ls ), [ new EndTagTk( 'pre' ) ] ); |
958 | 1006 | } |
959 | | - / & { return clearFlag('pre'); } |
| 1007 | + / & { return stops.dec('pre'); } |
960 | 1008 | |
961 | 1009 | pre_indent_line = space l:inlineline { |
962 | 1010 | return [ '\n' ].concat(l); |
— | — | @@ -1140,15 +1188,15 @@ |
1141 | 1189 | // } |
1142 | 1190 | |
1143 | 1191 | generic_attribute_name |
1144 | | - = & { return setFlag( 'equalAttrib' ) } |
| 1192 | + = & { return stops.push( 'equal', true ) } |
1145 | 1193 | ! '/>' |
1146 | 1194 | name:attribute_preprocessor_text_line |
1147 | 1195 | { |
1148 | | - clearFlag( 'equalAttrib' ); |
| 1196 | + stops.pop( 'equal' ); |
1149 | 1197 | //console.warn( 'generic attribute name: ' + pp( name ) ); |
1150 | 1198 | return name; |
1151 | 1199 | } |
1152 | | - / & { return clearFlag( 'equalAttrib' ) } |
| 1200 | + / & { return stops.pop( 'equal' ) } |
1153 | 1201 | |
1154 | 1202 | // A generic attribute, possibly spanning multiple lines. |
1155 | 1203 | generic_attribute_newline_value |
— | — | @@ -1226,12 +1274,12 @@ |
1227 | 1275 | dtdd |
1228 | 1276 | = bullets:(!(";" !list_char) list_char)* |
1229 | 1277 | ";" |
1230 | | - & {return setFlag('colon');} |
| 1278 | + & {return stops.inc('colon');} |
1231 | 1279 | c:inlineline |
1232 | 1280 | ":" |
1233 | 1281 | // Fortunately dtdds cannot be nested, so we can simply set the flag |
1234 | 1282 | // back to 0 to disable it. |
1235 | | - & {syntaxFlags['colon'] = 0; return true;} |
| 1283 | + & { stops.counters['colon'] = 0; return true;} |
1236 | 1284 | d:inlineline |
1237 | 1285 | &eolf { |
1238 | 1286 | // Convert trailing space into |
— | — | @@ -1251,7 +1299,7 @@ |
1252 | 1300 | return [ li ].concat( c, [ li2 ], d ); |
1253 | 1301 | } |
1254 | 1302 | // Fall-back case to clear the colon flag |
1255 | | - / & { return true; } { syntaxFlags['colon'] = 0; return null; } |
| 1303 | + / & { return true; } { stops.counters['colon'] = 0; return null; } |
1256 | 1304 | |
1257 | 1305 | |
1258 | 1306 | list_char = [*#:;] |
— | — | @@ -1272,14 +1320,14 @@ |
1273 | 1321 | *********************************************************************/ |
1274 | 1322 | |
1275 | 1323 | table_lines |
1276 | | - = & { return setFlag('table'); } |
| 1324 | + = & { return stops.inc('table'); } |
1277 | 1325 | tl:table_line |
1278 | 1326 | tls:( s:sol tl2:table_line { return s.concat(tl2); } )* { |
1279 | | - clearFlag('table'); |
| 1327 | + stops.dec('table'); |
1280 | 1328 | //console.warn('table_lines: ' + pp(tl.concat(tls))); |
1281 | 1329 | return tl.concat( tls ); |
1282 | 1330 | } |
1283 | | - / & { return clearFlag('table'); } |
| 1331 | + / & { return stops.dec('table'); } |
1284 | 1332 | |
1285 | 1333 | // This production assumes start-of-line position! |
1286 | 1334 | table_line |
— | — | @@ -1380,12 +1428,12 @@ |
1381 | 1429 | } |
1382 | 1430 | |
1383 | 1431 | table_cell_args |
1384 | | - = & { return setFlag('tableCellArg'); } |
| 1432 | + = & { return stops.inc('tableCellArg'); } |
1385 | 1433 | as:generic_attribute* space* "|" !"|" { |
1386 | | - clearFlag('tableCellArg'); |
| 1434 | + stops.dec('tableCellArg'); |
1387 | 1435 | return as; |
1388 | 1436 | } |
1389 | | - / & { return clearFlag('tableCellArg'); } |
| 1437 | + / & { return stops.dec('tableCellArg'); } |
1390 | 1438 | |
1391 | 1439 | |
1392 | 1440 | |
— | — | @@ -1417,13 +1465,13 @@ |
1418 | 1466 | table_start |
1419 | 1467 | = "{" pipe |
1420 | 1468 | res:( |
1421 | | - & { setFlag('table'); return true; } |
| 1469 | + & { stops.inc('table'); return true; } |
1422 | 1470 | ta:generic_attribute* |
1423 | 1471 | { |
1424 | 1472 | //dp("table_start " + pp(ta) + ", pos:" + pos); |
1425 | 1473 | return ta; |
1426 | 1474 | } |
1427 | | - / & { clearFlag('table'); return false; } { return null; } |
| 1475 | + / & { stops.dec('table'); return false; } { return null; } |
1428 | 1476 | ) { return res } |
1429 | 1477 | |
1430 | 1478 | table_caption |
— | — | @@ -1502,7 +1550,7 @@ |
1503 | 1551 | |
1504 | 1552 | table_end |
1505 | 1553 | = nt:newlineToken? ( pipe "}" / eof ) { |
1506 | | - clearFlag('table'); |
| 1554 | + stops.dec('table'); |
1507 | 1555 | if(nt) |
1508 | 1556 | return nt; |
1509 | 1557 | else |
Index: trunk/extensions/VisualEditor/modules/parser/mediawiki.tokenizer.peg.js |
— | — | @@ -103,48 +103,46 @@ |
104 | 104 | * Those inner productions are then exited, so that the outer production can |
105 | 105 | * handle the end marker. |
106 | 106 | */ |
107 | | -PegTokenizer.prototype.inline_breaks = function (input, pos, syntaxFlags ) { |
| 107 | +PegTokenizer.prototype.inline_breaks = function (input, pos, stops ) { |
| 108 | + var counters = stops.counters; |
108 | 109 | switch( input[pos] ) { |
109 | 110 | case '=': |
110 | | - return ( syntaxFlags.equalAttrib && |
111 | | - (syntaxFlags.equalTemplate || ! syntaxFlags.template ) ) || |
112 | | - (syntaxFlags.equalTemplate && |
113 | | - (syntaxFlags.equalAttrib || syntaxFlags.template)) || |
114 | | - ( syntaxFlags.h && |
| 111 | + return stops.onStack( 'equal' ) || |
| 112 | + ( counters.h && |
115 | 113 | input.substr( pos + 1, 200) |
116 | 114 | .match(/[ \t]*[\r\n]/) !== null ) || null; |
117 | 115 | case '|': |
118 | | - return syntaxFlags.pipe || |
119 | | - syntaxFlags.template || |
120 | | - ( syntaxFlags.table && |
| 116 | + return counters.pipe || |
| 117 | + counters.template || |
| 118 | + ( counters.table && |
121 | 119 | ( input[pos + 1].match(/[|}]/) !== null || |
122 | | - syntaxFlags.tableCellArg |
| 120 | + counters.tableCellArg |
123 | 121 | ) |
124 | 122 | ) || null; |
125 | 123 | case "!": |
126 | | - return syntaxFlags.table && input[pos + 1] === "!" || |
| 124 | + return counters.table && input[pos + 1] === "!" || |
127 | 125 | null; |
128 | 126 | case "}": |
129 | | - return syntaxFlags.template && input[pos + 1] === "}" || null; |
| 127 | + return counters.template && input[pos + 1] === "}" || null; |
130 | 128 | case ":": |
131 | | - return syntaxFlags.colon && |
132 | | - ! syntaxFlags.extlink && |
133 | | - ! syntaxFlags.linkdesc || null; |
| 129 | + return counters.colon && |
| 130 | + ! counters.extlink && |
| 131 | + ! counters.linkdesc || null; |
134 | 132 | case "\r": |
135 | | - return syntaxFlags.table && |
| 133 | + return counters.table && |
136 | 134 | input.substr(pos, 4).match(/\r\n?[!|]/) !== null || |
137 | 135 | null; |
138 | 136 | case "\n": |
139 | | - return syntaxFlags.table && |
| 137 | + return counters.table && |
140 | 138 | input[pos + 1] === '!' || |
141 | 139 | input[pos + 1] === '|' || |
142 | 140 | null; |
143 | 141 | case "]": |
144 | | - return syntaxFlags.extlink || |
145 | | - ( syntaxFlags.linkdesc && input[pos + 1] === ']' ) || |
| 142 | + return counters.extlink || |
| 143 | + ( counters.linkdesc && input[pos + 1] === ']' ) || |
146 | 144 | null; |
147 | 145 | case "<": |
148 | | - return syntaxFlags.pre && input.substr( pos, 6 ) === '</pre>' || null; |
| 146 | + return counters.pre && input.substr( pos, 6 ) === '</pre>' || null; |
149 | 147 | default: |
150 | 148 | return null; |
151 | 149 | } |