http://www.mediawiki.org/wiki/Special:Code/MediaWiki/93717
Revision: 93717 Author: brion Date: 2011-08-02 09:40:00 +0000 (Tue, 02 Aug 2011) Log Message: ----------- ParserPlayground -- checking in my airplane work :D Starting on primitive parser environment with extension points for parser functions & tag hooks, start on moving the <ref> support out to a tag hook extension. Doing this one as a 'pure-wiki-tree' extension which expands to additional nodes within the tree which can be rendered by the core renderer. Modified Paths: -------------- trunk/extensions/ParserPlayground/ParserPlayground.php trunk/extensions/ParserPlayground/modules/ext.parserPlayground.js trunk/extensions/ParserPlayground/modules/ext.parserPlayground.renderer.js trunk/extensions/ParserPlayground/modules/pegParser.pegjs.txt trunk/extensions/ParserPlayground/tests/parserTests.js trunk/extensions/ParserPlayground/tests/parserTests.pegjs Added Paths: ----------- trunk/extensions/ParserPlayground/modules/ext.cite.taghook.ref.js trunk/extensions/ParserPlayground/modules/mediawiki.parser.environment.js Modified: trunk/extensions/ParserPlayground/ParserPlayground.php =================================================================== --- trunk/extensions/ParserPlayground/ParserPlayground.php 2011-08-02 09:38:46 UTC (rev 93716) +++ trunk/extensions/ParserPlayground/ParserPlayground.php 2011-08-02 09:40:00 UTC (rev 93717) @@ -61,6 +61,9 @@ $wgResourceModules['ext.parserPlayground'] = array( 'scripts' => array( + 'mediawiki.parser.environment.js', + 'ext.cite.taghook.ref.js', + 'lib.jsdiff.js', 'lib.pegjs.js', 'jquery.nodetree.js', Added: trunk/extensions/ParserPlayground/modules/ext.cite.taghook.ref.js =================================================================== --- trunk/extensions/ParserPlayground/modules/ext.cite.taghook.ref.js (rev 0) +++ trunk/extensions/ParserPlayground/modules/ext.cite.taghook.ref.js 2011-08-02 09:40:00 UTC (rev 93717) @@ -0,0 +1,160 @@ +/** + * The ref / references tags don't do any fancy HTML, so we can actually + * implement this in terms of parse tree manipulations, skipping the need + * for renderer-specific plugins as well. + * + * Pretty neat huh! + */ + +MWRefTagHook = function( env ) { + if (!('cite' in env)) { + env.cite = { + refGroups: {} + }; + } + var refGroups = env.cite.refGroups; + + var getRefGroup = function(group) { + if (!(group in refGroups)) { + var refs = [], + byName = {}; + refGroups[group] = { + refs: refs, + byName: byName, + add: function(node, options) { + var ref; + if (options.name && options.name in byName) { + ref = byName[options.name]; + } else { + var n = refs.length; + var key = n + ''; + if (options.name) { + key = options.name + '-' + key; + } + ref = { + node: node, + index: n, + groupIndex: n, // @fixme + name: options.name, + group: options.group, + key: key, + target: 'cite_note-' + key, + linkbacks: [] + }; + refs[n] = ref; + if (options.name) { + byName[options.name] = ref; + } + } + ref.linkbacks.push( + 'cite_ref-' + ref.key + '-' + ref.linkbacks.length + ); + return ref; + } + } + } + return refGroups[group]; + }; + + this.execute = function( node ) { + var options = $.extend({ + name: null, + group: null + }, node.params); + + var group = getRefGroup(options.group); + var ref = group.add(node, options); + var linkback = ref.linkbacks[ref.linkbacks.length - 1]; + + var bits = [] + if (options.group) { + bits.push(options.group); + } + bits.push(env.formatNum( ref.groupIndex + 1 )); + + return { + type: 'span', + attrs: { + id: linkback, + 'class': 'reference' + }, + content: [ + { + type: 'hashlink', + target: '#' + ref.target, + content: [ + '[' + bits.join(' ') + ']' + ] + }, + ], + origNode: node + }; + }; +}; + +MWReferencesTagHook = function( env ) { + var refGroups = env.cite.refGroups; + + var arrow = '↑'; + var renderLine = function( ref ) { + var out = { + type: 'li', + attrs: { + id: 'cite-note-' + ref.target + }, + content: [] + }; + if (ref.linkbacks.length == 1) { + out.content.push({ + type: 'hashlink', + target: '#' + ref.linkbacks[0], + content: [ + arrow + ] + }) + } else { + out.content.push(arrow) + $.each(ref.linkbacks, function(i, linkback) { + out.contents.push({ + type: 'hashlink', + target: '#' + ref.linkbacks[0], + content: [ + env.formatNum( ref.groupIndex + '.' + i) + ] + }); + }) + } + out.content.push(' '); + out.content.push({ + type: 'placeholder', + content: ref.node.content + }); + return out; + }; + this.execute = function( node ) { + var options = $.extend({ + name: null, + group: null + }, node.params); + if (options.group in refGroups) { + var group = refGroups[options.group]; + return { + type: 'ol', + attrs: { + 'class': 'references' + }, + content: $.each(group, renderLine), + origNode: node + } + } else { + return { + type: 'placeholder', + origNode: node + } + } + } +} + +if (typeof module == "object") { + module.exports.MWRefTagHook = MWRefTagHook; +} Modified: trunk/extensions/ParserPlayground/modules/ext.parserPlayground.js =================================================================== --- trunk/extensions/ParserPlayground/modules/ext.parserPlayground.js 2011-08-02 09:38:46 UTC (rev 93716) +++ trunk/extensions/ParserPlayground/modules/ext.parserPlayground.js 2011-08-02 09:40:00 UTC (rev 93717) @@ -124,12 +124,17 @@ var pp = context.parserPlayground; pp.parser = new parserClass(); // hack + pp.env = new MWParserEnvironment({ + tagHooks: { + 'ref': MWRefTagHook + } + }); if (pp.parser instanceof MediaWikiParser) { pp.serializer = pp.parser; pp.renderer = pp.parser; } else { pp.serializer = new MWTreeSerializer(); - pp.renderer = new MWTreeRenderer(); + pp.renderer = new MWTreeRenderer(pp.env); } context.parserPlayground.fn.initDisplay(); $.cookie('pp-editmode', className, { Modified: trunk/extensions/ParserPlayground/modules/ext.parserPlayground.renderer.js =================================================================== --- trunk/extensions/ParserPlayground/modules/ext.parserPlayground.renderer.js 2011-08-02 09:38:46 UTC (rev 93716) +++ trunk/extensions/ParserPlayground/modules/ext.parserPlayground.renderer.js 2011-08-02 09:40:00 UTC (rev 93717) @@ -1,9 +1,9 @@ /** * @param {ParserContext} context */ -function MWTreeRenderer(context) { +function MWTreeRenderer(env) { // whee - this.context = context || {}; + this.env = env || {}; } /** @@ -45,6 +45,7 @@ // A sequence of block-level elements... var page = $('<div class="parseNode"></div>'); subParseArray(tree.content, page); + /* if (self.context.refs) { // We're at the end; drop all the remaining refs! subParseArray([{ @@ -52,6 +53,7 @@ name: 'references' }], page); } + */ node = page[0]; break; case 'para': @@ -111,7 +113,42 @@ t.append('}}'); node = t[0]; break; + case 'placeholder': + if ('content' in tree) { + var $place = $('<span>'); // hmmmm + subParseArray(tree.content, $place); + node = $place[0]; + } + break; + case 'span': + var $span = $('<span>'); + if ('attrs' in tree) { + $.map(tree.attrs, function(val, key) { + $span.attr(key, val); // @fixme safety! + }); + if ('content' in tree) { + subParseArray(tree.content, $span); + } + } + node = $span[0]; + break; + case 'hashlink': + var $a = $('<a>'); + $a.attr('href', '#' + tree.target); + subParseArray(tree.content, $a); + node = $a[0]; + break; case 'ext': + var hook = this.env.getTagHook(tree.name); + if (!hook) { + console.log('kabooom! no ext ' + tree.name) + } + var transformed = hook.execute(tree); + var $ext = $('<span>'); // hmmmm + subParseArray([transformed], $ext); + node = $ext[0]; + // @fixme + /* if (tree.name == 'ref') { // Save the reference for later! // @fixme names etc? @@ -176,6 +213,7 @@ callback(null, 'Unrecognized extension in parse tree'); return; } + */ break; case 'comment': var h = $('<span class="parseNode comment"></span>').text('<!--' + tree.text + '-->'); Added: trunk/extensions/ParserPlayground/modules/mediawiki.parser.environment.js =================================================================== --- trunk/extensions/ParserPlayground/modules/mediawiki.parser.environment.js (rev 0) +++ trunk/extensions/ParserPlayground/modules/mediawiki.parser.environment.js 2011-08-02 09:40:00 UTC (rev 93717) @@ -0,0 +1,86 @@ +var MWParserEnvironment = function(opts) { + var options = { + tagHooks: {}, + parserFunctions: {} + }; + $.extend(options, opts); + this.tagHooks = options.tagHooks; + this.parserFunctions = options.parserFunctions; +}; + +$.extend(MWParserEnvironment.prototype, { + // Does this need separate UI/content inputs? + formatNum: function( num ) { + return num + ''; + }, + + getVariable: function( varname, options ) { + // + }, + + /** + * @return MWParserFunction + */ + getParserFunction: function( name ) { + if (name in this.parserFunctions) { + return new this.parserFunctions[name]( this ); + } else { + return null; + } + }, + + /** + * @return MWParserTagHook + */ + getTagHook: function( name ) { + if (name in this.tagHooks) { + return new this.tagHooks[name](this); + } else { + return null; + } + } + +}); + + + +/** + * @parm MWParserEnvironment env + * @constructor + */ +MWParserTagHook = function( env ) { + if (!env) { + throw new Error( 'Tag hook requires a parser environment.' ); + } + this.env = env; +}; + +/** + * @param string text (or a parse tree?) + * @param object params map of named parameters (strings or parse frames?) + * @return either a string or a parse frame -- finalize this? + */ +MWParserTagHook.execute = function( text, params ) { + return ''; +}; + + +MWParserFunction = function( env) { + if (!env) { + throw new Error( 'Parser funciton requires a parser environment.'); + } + this.env = env; +}; + +/** + * @param string text (or a parse tree?) + * @param object params map of named parameters (strings or parse frames?) + * @return either a string or a parse frame -- finalize this? + */ +MWParserFunction.execute = function( text, params ) { + return ''; +}; + +if (typeof module == "object") { + module.exports.MWParserEnvironment = MWParserEnvironment; +} Modified: trunk/extensions/ParserPlayground/modules/pegParser.pegjs.txt =================================================================== --- trunk/extensions/ParserPlayground/modules/pegParser.pegjs.txt 2011-08-02 09:38:46 UTC (rev 93716) +++ trunk/extensions/ParserPlayground/modules/pegParser.pegjs.txt 2011-08-02 09:40:00 UTC (rev 93717) @@ -239,8 +239,9 @@ ref = ref_full / ref_empty +/* Can we do backreferences to genericize this? */ ref_full - = start:ref_start ">" content:ref_content+ close:ref_end { + = start:ref_start ">" content:ref_content* close:ref_end { return { type: 'ext', name: 'ref', Modified: trunk/extensions/ParserPlayground/tests/parserTests.js =================================================================== --- trunk/extensions/ParserPlayground/tests/parserTests.js 2011-08-02 09:38:46 UTC (rev 93716) +++ trunk/extensions/ParserPlayground/tests/parserTests.js 2011-08-02 09:40:00 UTC (rev 93717) @@ -39,6 +39,9 @@ global.PEG = _require('lib.pegjs.js'); // Our code... +_import('mediawiki.parser.environment.js', ['MWParserEnvironment']); +_import('ext.cite.taghook.ref.js', ['MWRefTagHook']); + _import('ext.parserPlayground.serializer.js', ['MWTreeSerializer']); _import('ext.parserPlayground.renderer.js', ['MWTreeRenderer']); _import('ext.parserPlayground.pegParser.js', ['PegParser']); @@ -47,10 +50,16 @@ PegParser.src = fs.readFileSync(path.join(basePath, 'pegParser.pegjs.txt'), 'utf8'); var parser = new PegParser(); -var renderer = new MWTreeRenderer(); +var testFileName = '../../../tests/parser/parserTests.txt'; // default +if (process.argv.length > 2) { + // hack :D + testFileName = process.argv[2]; + console.log(testFileName); +} + var testParser = PEG.buildParser(fs.readFileSync('parserTests.pegjs', 'utf8')); -var testFile = fs.readFileSync('../../../tests/parser/parserTests.txt', 'utf8'); +var testFile = fs.readFileSync(testFileName, 'utf8'); try { @@ -108,6 +117,12 @@ if (err) { console.log('PARSE FAIL', err); } else { + var environment = new MWParserEnvironment({ + tagHooks: { + 'ref': MWRefTagHook + } + }); + var renderer = new MWTreeRenderer(environment); renderer.treeToHtml(tree, function(node, err) { if (err) { console.log('RENDER FAIL', err); Modified: trunk/extensions/ParserPlayground/tests/parserTests.pegjs =================================================================== --- trunk/extensions/ParserPlayground/tests/parserTests.pegjs 2011-08-02 09:38:46 UTC (rev 93716) +++ trunk/extensions/ParserPlayground/tests/parserTests.pegjs 2011-08-02 09:40:00 UTC (rev 93717) @@ -33,7 +33,8 @@ comment / article / test / - line + line / + hooks @@ -112,3 +113,18 @@ end_test = "!!" ws? "end" ws? eol + +hooks = + start_hooks text:text end_hooks +{ + return { + type: 'hooks', + text: text + } +} + +start_hooks = + "!!" ws? "hooks" ":"? ws? eol + +end_hooks = + "!!" ws? "endhooks" ws? eol _______________________________________________ MediaWiki-CVS mailing list MediaWiki-CVS@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs