diff --git a/PseudoCode.js b/PseudoCode.js
index f84fc162c93b2e751ce2f98c375b73b90c55a031..74e397b36e78ba55795e48386786c5fbdb558358 100644
--- a/PseudoCode.js
+++ b/PseudoCode.js
@@ -10,23 +10,27 @@ counterpart. Some details are improved to make it more natural.
 The TeX-style pseudocode language (follows **algoritmic** environment) represented
 in a context-free grammar:
 
-    <algorithmic>   :== \begin{algorithmic} + <block> + \end{algorithmic}
-    <block>         :== <sentence>[0..n]
-    <sentence>      :== <control> | <statement> | <comment>
+    <algorithmic>   :== \begin{algorithmic}
+                        + <condition>
+                        + <block>
+                        + \end{algorithmic}
+
+    <conditions>    :== ( <require> | <ensure> )[0..n]
+    <require>       :== \REQUIRE + <text>
+    <ensure>        :== \ENSURE + <text>
+
+    <block>         :== ( <control> | <statement> | <comment> )[0..n]
 
     <control>       :== <if> | <for> | <while>
     <if>            :== \IF{<cond>} + <block>
                         + ( \ELIF{<cond>} <block> )[0..n]
                         + ( \ELSE <block> )[0..1]
                         + \ENDIF
-
     <for>           :== \FOR{<cond>} + <block> + \ENDFOR
     <while>         :== \WHILE{<cond>} + <block> + \ENDWHILE
 
-    <statement>     :== <state> | <require> | <ensure> | <return> | <print>
+    <statement>     :== <state> |  <return> | <print>
     <state>         :== \STATE + <text>
-    <require>       :== \REQUIRE + <text>
-    <ensure>        :== \ENSURE + <text>
     <return>        :== \RETURN + <text>
     <print>         :== \PRINT + <text>
 
@@ -61,6 +65,33 @@ Tokens
 
 */
 
+(function(parentModule, katex) { // rely on KaTex to process TeX math
+
+// ===========================================================================
+//  Utility functions
+// ===========================================================================
+
+function isString(str) {
+    return (typeof str === 'string') || (str instanceof String);
+}
+
+function isObject(obj) {
+    return (typeof obj === 'object' && (obj instanceof Object));
+}
+
+function toString(obj) {
+    if (!isObject(obj)) return obj + '';
+
+    var parts = [];
+    for (var member in obj)
+        parts.push(member + ': ' + toString(obj[member]));
+    return parts.join(', ');
+}
+
+// ===========================================================================
+//  Error handling
+// ===========================================================================
+
 function ParseError(message, pos, input) {
     var error = 'Error: ' + message;
     // If we have the input and a position, make the error a bit fancier
@@ -81,6 +112,10 @@ function ParseError(message, pos, input) {
 ParseError.prototype = Object.create(Error.prototype);
 ParseError.prototype.constructor = ParseError;
 
+// ===========================================================================
+//  Lexer
+// ===========================================================================
+
 /* Math pattern
     Math environtment like $ $ or \( \) cannot be matched using regular
     expression. This object simulates a regular expression*/
@@ -183,9 +218,7 @@ Lexer.prototype.next = function() {
             this._pos, this._input);
 };
 
-function isString(str) {
-    return (typeof str === 'string') || (str instanceof String);
-}
+
 
 /* Check whether the text of the next symbol matches */
 Lexer.prototype._matchText = function(text) {
@@ -198,6 +231,9 @@ Lexer.prototype._matchText = function(text) {
         return text.indexOf(this._symbol.text) >= 0;
 };
 
+// ===========================================================================
+//  Parser
+// ===========================================================================
 
 var ParseNode = function(type, val) {
     this.type = type;
@@ -212,7 +248,7 @@ ParseNode.prototype.toString = function(level) {
     for (var i = 0; i < level; i++) indent += '  ';
 
     var res = indent + '<' + this.type + '>';
-    if (this.value) res += ' (' + this.value + ')';
+    if (this.value) res += ' (' + toString(this.value) + ')';
     res += '\n';
 
     for (var ci = 0; ci < this.children.length; ci++) {
@@ -249,6 +285,9 @@ Parser.prototype._parseAlgorithmic = function() {
     lexer.expect('ordinary', 'algorithmic');
     lexer.expect('close');
 
+    // <condition> precondition, postcondition
+    algNode.addChild(this._parseConditions());
+
     // <block>
     algNode.addChild(this._parseBlock());
 
@@ -261,6 +300,19 @@ Parser.prototype._parseAlgorithmic = function() {
     return algNode;
 };
 
+Parser.prototype._parseConditions = function() {
+    var conditionsNode = new ParseNode('conditions');
+
+    while (true) {
+        var commandNode = this._parseCommand(['REQUIRE', 'ENSURE']);
+        if (commandNode) { conditionsNode.addChild(commandNode); continue; }
+
+        break;
+    }
+
+    return conditionsNode;
+}
+
 Parser.prototype._parseBlock = function() {
     var blockNode = new ParseNode('block');
 
@@ -268,7 +320,7 @@ Parser.prototype._parseBlock = function() {
         var controlNode = this._parseControl();
         if (controlNode) { blockNode.addChild(controlNode); continue; }
 
-        var commandNode = this._parseCommand();
+        var commandNode = this._parseCommand(['STATE', 'PRINT', 'RETURN']);
         if (commandNode) { blockNode.addChild(commandNode); continue; }
 
         var commentNode = this._parseComment();
@@ -339,13 +391,12 @@ Parser.prototype._parseLoop = function() {
     return loopNode;
 };
 
-Parser.prototype._parseCommand = function() {
-    if (!this._lexer.accept('func',
-        ['STATE', 'REQUIRE', 'ENSURE', 'RETURN', 'PRINT']))
+Parser.prototype._parseCommand = function(acceptCommands) {
+    if (!this._lexer.accept('func', acceptCommands))
         return null;
 
     var cmdName = this._lexer.text();
-    var cmdNode = new ParseNode(cmdName);
+    var cmdNode = new ParseNode('command', cmdName);
     cmdNode.addChild(this._parseText());
     return cmdNode;
 };
@@ -363,7 +414,8 @@ Parser.prototype._parseComment = function() {
     return commentNode;
 };
 
-Parser.prototype._parseCond = Parser.prototype._parseText = function() {
+Parser.prototype._parseCond =
+Parser.prototype._parseText = function() {
     var textNode = new ParseNode('text');
 
     var symbolNode;
@@ -417,16 +469,244 @@ Parser.prototype._parseSymbol = function() {
     return null;
 }
 
-var PseudoCode = {};
-PseudoCode.renderToString = function(input) {
-    var res;
-    // try {
-        var parser = new Parser(new Lexer(input));
-        var tree = parser.parse();
-        console.log(tree.toString());
-    // }
-    // catch(e) {
-    //     console.log(e.message);
-    // }
-    return res;
+// ===========================================================================
+//  Builder
+// ===========================================================================
+
+function Builder(parser) {
+    this._root = parser.parse();
+    console.log(this._root.toString());
+}
+
+Builder.prototype.toMarkup = function() {
+    this._body = [];
+    this._buildTree(this._root);
+    var html = this._body.join('\n');
+    delete this._body;
+    return html;
+}
+
+Builder.prototype.toDOM = function() {
+    var html = this.toMarkup();
+    var div = document.createElement('div');
+    div.innerHTML = html;
+    return div.firstChild;
+}
+
+Builder.prototype._beginDiv = function(className) {
+    this._body.push('<div class="' + className + '">');
+}
+
+Builder.prototype._endDiv = function() {
+    this._body.push('</div>');
+}
+
+Builder.prototype._beginLine = function() {
+    this._beginP('ps-line');
+}
+
+Builder.prototype._beginP = function(className) {
+    this._body.push('<p class="' + className + '">');
+}
+
+Builder.prototype._endP = Builder.prototype._endLine = function() {
+    this._body.push('</p>');
+}
+
+Builder.prototype._typeKeyword = function(keyword) {
+    this._body.push('<span class="ps-keyword">' + keyword + '</span>');
+}
+
+Builder.prototype._typeText = function(text) {
+    this._body.push('<span>' + text + '</span>');
+}
+
+Builder.prototype._buildTree = function(node) {
+    switch(node.type) {
+    case 'root':
+        this._beginDiv('pseudo');
+        this._buildTree(node.children[0]);
+        this._endDiv();
+        break;
+    case 'algorithmic':
+        this._buildTree(node.children[0]);
+        this._buildTree(node.children[1]);
+        break;
+    case 'conditions':
+        for (var ci = 0; ci < node.children.length; ci++)
+            this._buildTree(node.children[ci]);
+        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._endDiv();
+        break;
+    case 'if':
+        // \IF { <cond> }
+        // ==>
+        // <p class="ps-line">
+        //      <span class="ps-keyword">if</span>
+        //      ...
+        //      <span class="ps-keyword">then</span>
+        // </p>
+        this._beginLine();
+        this._typeKeyword('if');
+        var cond = node.children[0];
+        this._buildTree(cond);
+        this._typeKeyword('then');
+        this._endLine();
+        // <block>
+        var ifBlock = node.children[1];
+        this._buildTree(ifBlock);
+
+        // ( \ELIF {<cond>} <block> )[0..n]
+        var numElif = node.value.numElif;
+        for (var ei = 0 ; ei < numElif; ei++) {
+            // \ELIF {<cond>}
+            // ==>
+            // <p class="ps-line">
+            //      <span class="ps-keyword">elif</span>
+            //      ...
+            //      <span class="ps-keyword">then</span>
+            // </p>
+            this._beginLine();
+            this._typeKeyword('if');
+            var elifCond = node.children[2 + 2 * ei];
+            this._buildTree(elifCond);
+            this._typeKeyword('then');
+            this._endLine();
+
+            // <block>
+            var elifBlock = node.children[2 + 2 * ei + 1];
+            this._buildTree(elifBlock);
+        }
+
+        // ( \ELSE <block> )[0..1]
+        var hasElse = node.value.hasElse;
+        if (hasElse) {
+            // \ELSE
+            // ==>
+            // <p class="ps-line">
+            //      <span class="ps-keyword">else</span>
+            // </p>
+            this._beginLine();
+            this._typeKeyword('else');
+            this._endLine();
+
+            // <block>
+            var elseBlock = node.children[node.children.length - 1];
+            this._buildTree(elseBlock);
+        }
+
+        // ENDIF
+        this._beginLine();
+        this._typeKeyword('end if');
+        this._endLine();
+
+        break;
+    case 'loop':
+        // \FOR{<cond>} or \WHILE{<cond>}
+        // ==>
+        // <p class="ps-line">
+        //      <span class="ps-keyword">for</span>
+        //      ...
+        //      <span class="ps-keyword">do</span>
+        // </p>
+        var loopName = node.value.toLowerCase();
+        this._beginLine();
+        this._typeKeyword(loopName);
+        var cond = node.children[0];
+        this._buildTree(cond);
+        this._typeKeyword('do');
+        this._endLine();
+
+        // <block>
+        var block = node.children[1];
+        this._buildTree(block);
+
+        // \ENDFOR or \ENDWHILE
+        // ==>
+        // <p class="ps-line">
+        //      <span class="ps-keyword">end for</span>
+        // </p>
+        this._beginLine();
+        this._typeKeyword('end ' + loopName);
+        this._endLine();
+
+        break;
+    case 'command':
+        // commands: \STATE, \ENSURE, \PRINT, \RETURN, etc.
+        var cmdName = node.value;
+        var displayName = {
+            'STATE': '',
+            'ENSURE': 'Ensure:',
+            'REQUIRE': 'Require:',
+            'PRINT': 'print',
+            'RETURN': 'return'
+        }[cmdName];
+
+        this._beginLine();
+        if (displayName) this._typeKeyword(displayName);
+        var text = node.children[0];
+        this._buildTree(text);
+        this._endLine();
+
+        break;
+    // 'comment':
+    //     break;
+    case 'cond':
+    case 'text':
+        for (var ci = 0; ci < node.children.length; ci++) {
+            var child = node.children[ci];
+            this._buildTree(child);
+        }
+        break;
+    case 'ordinary':
+        var text = node.value;
+        this._typeText(text);
+        break;
+    case 'math':
+        var math = node.value;
+        var mathHTML = katex.renderToString(math);
+        this._body.push(mathHTML);
+        break;
+    default:
+        throw new ParseError('Unexpected ParseNode of type ' + node.type);
+    }
+}
+
+// ===========================================================================
+//  Entry points
+// ===========================================================================
+
+parentModule.PseudoCode = {
+    renderToString: function(input) {
+        // try {
+            var lexer = new Lexer(input);
+            var parser = new Parser(lexer);
+            var builder = new Builder(parser);
+            return builder.toMarkup();
+        // }
+        // catch(e) {
+        //     console.log(e.message);
+        // }
+    },
+    render: function(input, baseDomEle) {
+        // try {
+            var lexer = new Lexer(input);
+            var parser = new Parser(lexer);
+            var builder = new Builder(parser);
+            var ele = builder.toDOM();
+            baseDomEle.appendChild(ele);
+        // }
+        // catch(e) {
+        //     console.log(e.message);
+        // }
+    }
 };
+
+})(window, katex);
diff --git a/static.html b/static.html
index f4c473377d84028de0e080b20471847473f87abd..8465be39b983600535893224d4c86864cddcdc24 100644
--- a/static.html
+++ b/static.html
@@ -12,16 +12,18 @@
         font-size: 1em;
         font-weight: 100;
         -webkit-font-smoothing: antialiased !important;
+        padding-bottom: 0.2em;
+    }
+    .pseudo.with-caption {
         border-top: 3px solid black;
         border-bottom: 2px solid black;
-        padding-bottom: 0.2em;
     }
     .pseudo .ps-block {
         margin-left: 1.2em;
     }
     .pseudo .ps-head {
     }
-    .pseudo > .ps-line:first-child {
+    .pseudo.with-caption > .ps-line:first-child {
         border-bottom: 2px solid black;
     }
     .pseudo .ps-line {
@@ -41,7 +43,7 @@
     </style>
 </head>
 <body>
-    <div class="pseudo ps-captioned">
+    <div class="pseudo ps-captioned" style="display:none;">
         <p class="ps-line">
             <span class="ps-keyword">Algorithm 1</span>
             <span>Sample algorithm</span>
@@ -145,8 +147,9 @@
     </pre>
     <script type="text/javascript">
         var code = document.getElementById("code").textContent;
-        var html = PseudoCode.renderToString(code);
-        console.log(html);
+        // var html = PseudoCode.renderToString(code);
+        // console.log(html);
+        PseudoCode.render(code, document.body);
     </script>
 </body>
 </html>