From 07828fc1ad2e3ffce64fe4a088cad3c2b313a69e Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" <tatetian@gmail.com> Date: Tue, 17 Feb 2015 21:10:59 +0800 Subject: [PATCH] Add caption --- PseudoCode.js | 165 +++++++++++++++++++++++++++++++++++++------------- static.html | 11 ++-- 2 files changed, 129 insertions(+), 47 deletions(-) diff --git a/PseudoCode.js b/PseudoCode.js index 74e397b..df18b5a 100644 --- a/PseudoCode.js +++ b/PseudoCode.js @@ -10,12 +10,16 @@ counterpart. Some details are improved to make it more natural. The TeX-style pseudocode language (follows **algoritmic** environment) represented in a context-free grammar: + <pseudo> :== ( <algorithm> | <algorithmic> )[0..n] + + <algorithm> :== \begin{algorithm} + + ( <caption> | <algorithmic> )[0..n] + \end{algorithm} + <caption> :== \caption{ <text> } + <algorithmic> :== \begin{algorithmic} - + <condition> - + <block> + + ( <ensure> | <require> | <block> )[0..n] + \end{algorithmic} - - <conditions> :== ( <require> | <ensure> )[0..n] <require> :== \REQUIRE + <text> <ensure> :== \ENSURE + <text> @@ -270,47 +274,92 @@ var Parser = function(lexer) { Parser.prototype.parse = function() { var root = new ParseNode('root'); - var algNode = this._parseAlgorithmic(); - root.addChild(algNode); + + while (true) { + var envName = this._acceptEnvironment(); + if (envName === null) break; + + var envNode; + if (envName === 'algorithm') + envNode = this._parseAlgorithmInner(); + else if (envName === 'algorithmic') + envNode = this._parseAlgorithmicInner(); + else + throw new ParseError('Unexpected environment ' + envName); + + this._closeEnvironment(envName); + root.addChild(envNode); + } + //TODO: check this is the end of input return root; }; -Parser.prototype._parseAlgorithmic = function() { - var algNode = new ParseNode('algorithmic'); - +Parser.prototype._acceptEnvironment = function() { var lexer = this._lexer; - // \begin{algorithmic} - lexer.expect('func', 'begin'); + // \begin{XXXXX} + if (!lexer.accept('func', 'begin')) return null; + lexer.expect('open'); - lexer.expect('ordinary', 'algorithmic'); + var envName = lexer.expect('ordinary'); lexer.expect('close'); + return envName; +} - // <condition> precondition, postcondition - algNode.addChild(this._parseConditions()); - - // <block> - algNode.addChild(this._parseBlock()); - - // \end{algorithmic} +Parser.prototype._closeEnvironment = function(envName) { + // \close{XXXXX} + var lexer = this._lexer; lexer.expect('func', 'end'); lexer.expect('open'); - lexer.expect('ordinary', 'algorithmic'); + lexer.expect('ordinary', envName); lexer.expect('close'); +} - return algNode; -}; +Parser.prototype._parseAlgorithmInner = function() { + var algNode = new ParseNode('algorithm'); + while (true) { + var envName = this._acceptEnvironment(); + if (envName !== null) { + if (envName !== 'algorithmic') + throw new ParseError('Unexpected environment ' + envName); + var algmicNode = this._parseAlgorithmicInner(); + this._closeEnvironment(); + algNode.addChild(algmicNode); + continue; + } -Parser.prototype._parseConditions = function() { - var conditionsNode = new ParseNode('conditions'); + var captionNode = this._parseCaption(); + if (captionNode) { + algNode.addChild(captionNode); + continue; + } + break; + } + return algNode; +} + +Parser.prototype._parseAlgorithmicInner = function() { + var algmicNode = new ParseNode('algorithmic'); while (true) { - var commandNode = this._parseCommand(['REQUIRE', 'ENSURE']); - if (commandNode) { conditionsNode.addChild(commandNode); continue; } + var node; + if (!(node = this._parseCommand(['ENSURE', 'REQUIRE'])) && + !(node = this._parseBlock())) break; - break; + algmicNode.addChild(node); } + return algmicNode; +}; - return conditionsNode; +Parser.prototype._parseCaption = function() { + var lexer = this._lexer; + if (!lexer.accept('func', 'caption')) return null; + + var captionNode = new ParseNode('caption'); + lexer.expect('open'); + captionNode.addChild(this._parseText()); + lexer.expect('close'); + + return captionNode; } Parser.prototype._parseBlock = function() { @@ -329,7 +378,7 @@ Parser.prototype._parseBlock = function() { break; } - return blockNode; + return blockNode.children.length > 0 ? blockNode : null; }; Parser.prototype._parseControl = function() { @@ -478,6 +527,8 @@ function Builder(parser) { console.log(this._root.toString()); } +Builder.prototype._captionCount = 0; + Builder.prototype.toMarkup = function() { this._body = []; this._buildTree(this._root); @@ -521,28 +572,58 @@ Builder.prototype._typeText = function(text) { this._body.push('<span>' + text + '</span>'); } +Builder.prototype._buildTreeForAllChildren = function(node) { + var children = node.children; + for (var ci = 0; ci < children.length; ci++) + this._buildTree(children[ci]); +} + Builder.prototype._buildTree = function(node) { switch(node.type) { case 'root': this._beginDiv('pseudo'); - this._buildTree(node.children[0]); + this._buildTreeForAllChildren(node); this._endDiv(); break; - case 'algorithmic': - this._buildTree(node.children[0]); - this._buildTree(node.children[1]); + case 'algorithm': + // First, decide the caption if any + var lastCaptionNode; + for (var ci = 0; ci < node.children.length; ci++) { + var child = node.children[ci]; + if (child.type !== 'caption') continue; + lastCaptionNode = child; + this._captionCount++; + } + // Then, build the header for algorithm + var className = 'ps-alg'; + if (lastCaptionNode) className += ' with-caption'; + this._beginDiv(className); + if (lastCaptionNode) this._buildTree(lastCaptionNode); + // Then, build other nodes + for (var ci = 0; ci < node.children.length; ci++) { + var child = node.children[ci]; + if (child.type === 'caption') continue; + this._buildTree(child); + } + + this._endDiv(); break; - case 'conditions': - for (var ci = 0; ci < node.children.length; ci++) - this._buildTree(node.children[ci]); + case 'caption': + this._beginLine(); + this._typeKeyword('Algorithm ' + this._captionCount); + var textNode = node.children[0]; + this._buildTree(textNode); + this._endLine(); + break; + case 'algorithmic': + this._buildTreeForAllChildren(node); break; case 'block': // node: <block> // ==> // HTML: <div class="ps-block"> ... </div> this._beginDiv('ps-block'); - for (var ci = 0; ci < node.children.length; ci++) - this._buildTree(node.children[ci]); + this._buildTreeForAllChildren(node); this._endDiv(); break; case 'if': @@ -660,10 +741,7 @@ Builder.prototype._buildTree = function(node) { // break; case 'cond': case 'text': - for (var ci = 0; ci < node.children.length; ci++) { - var child = node.children[ci]; - this._buildTree(child); - } + this._buildTreeForAllChildren(node); break; case 'ordinary': var text = node.value; @@ -701,7 +779,8 @@ parentModule.PseudoCode = { var parser = new Parser(lexer); var builder = new Builder(parser); var ele = builder.toDOM(); - baseDomEle.appendChild(ele); + if (baseDomEle) baseDomEle.appendChild(ele); + return ele; // } // catch(e) { // console.log(e.message); diff --git a/static.html b/static.html index 8465be3..62526c8 100644 --- a/static.html +++ b/static.html @@ -12,18 +12,18 @@ font-size: 1em; font-weight: 100; -webkit-font-smoothing: antialiased !important; + } + .pseudo .ps-alg { padding-bottom: 0.2em; } - .pseudo.with-caption { + .pseudo .ps-alg.with-caption { border-top: 3px solid black; border-bottom: 2px solid black; } .pseudo .ps-block { margin-left: 1.2em; } - .pseudo .ps-head { - } - .pseudo.with-caption > .ps-line:first-child { + .pseudo .ps-alg.with-caption > .ps-line:first-child { border-bottom: 2px solid black; } .pseudo .ps-line { @@ -114,6 +114,8 @@ </div> </div> <pre id="code" style="display:none"> + \begin{algorithm} + \caption{Sample Algorithm} \begin{algorithmic} \REQUIRE $n \geq 0$ \ENSURE $y = x^n$ @@ -130,6 +132,7 @@ \ENDIF \ENDWHILE \end{algorithmic} + \end{algorithm} </pre> <pre id="test" style="display:none"> \begin{algorithmic} -- GitLab