From b2e7bade04817d63032e24d5bce011f28ab10d98 Mon Sep 17 00:00:00 2001 From: Jeff Avallone Date: Thu, 16 Apr 2015 17:52:50 -0400 Subject: [PATCH] Adding more documentation --- src/js/parser/javascript.js | 31 ++++++++++++++++++++++-- src/js/parser/javascript/parser.js | 12 +++++++++ src/js/parser/javascript/parser_state.js | 10 ++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/js/parser/javascript.js b/src/js/parser/javascript.js index 8d16739..5eb9128 100644 --- a/src/js/parser/javascript.js +++ b/src/js/parser/javascript.js @@ -1,3 +1,9 @@ +// Entry point for the JavaScript-flavor regular expression parsing and +// rendering. Actual parsing code is in +// [parser.js](./javascript/parser.html) and the grammar file. Rendering code +// is contained in the various subclasses of +// [Node](./javascript/node.html) + import Snap from 'snapsvg'; import _ from 'lodash'; @@ -6,6 +12,10 @@ import javascript from './javascript/parser.js'; import ParserState from './javascript/parser_state.js'; export default class Parser { + // - __container__ - DOM node that will contain the rendered expression + // - __options.keepContent__ - Boolean indicating if content of the container + // should be preserved after rendering. Defaults to false (don't keep + // contents) constructor(container, options) { this.options = options || {}; _.defaults(this.options, { @@ -14,9 +24,15 @@ export default class Parser { this.container = container; + // The [ParserState](./javascript/parser_state.html) instance is used to + // communicate between the parser and a running render, and to update the + // progress bar for the running render. this.state = new ParserState(this.container.querySelector('.progress div')); } + // DOM node that will contain the rendered expression. Setting this will add + // the base markup necessary for rendering the expression, and set the + // `svg-container` class set container(cont) { this._container = cont; this._container.innerHTML = [ @@ -30,23 +46,28 @@ export default class Parser { return this._container; } + // Helper method to simplify adding classes to the container. _addClass(className) { this.container.className = _(this.container.className.split(' ')) .union([className]) - .value() .join(' '); } + // Helper method to simplify removing classes from the container. _removeClass(className) { this.container.className = _(this.container.className.split(' ')) .without(className) - .value() .join(' '); } + // Parse a regular expression into a tree of + // [Nodes](./javascript/node.html) that can then be used to render an SVG. + // - __expression__ - Regular expression to parse. parse(expression) { this._addClass('loading'); + // Allow the browser to repaint before parsing so that the loading bar is + // displayed before the (possibly lengthy) parsing begins. return util.tick().then(() => { javascript.Parser.SyntaxNode.state = this.state; @@ -55,10 +76,13 @@ export default class Parser { }); } + // Render the parsed expression to an SVG. render() { var svg = Snap(this.container.querySelector('svg')); return this.parsed.render(svg.group()) + // Once rendering is complete, the rendered expression is positioned and + // the SVG resized to create some padding around the image contents. .then(result => { var box = result.getBBox(); @@ -69,16 +93,19 @@ export default class Parser { height: box.height + 20 }); }) + // Stop and remove loading indicator after render is totally complete. .then(() => { this._removeClass('loading'); this.container.removeChild(this.container.querySelector('.progress')); }); } + // Cancels any currently in-progress render. cancel() { this.state.cancelRender = true; } + // Returns any warnings that may have been set during the rendering process. get warnings() { return this.state.warnings; } diff --git a/src/js/parser/javascript/parser.js b/src/js/parser/javascript/parser.js index f139dc8..1ae217c 100644 --- a/src/js/parser/javascript/parser.js +++ b/src/js/parser/javascript/parser.js @@ -1,3 +1,7 @@ +// Sets up the parser generated by canopy to use the +// [Node](./javascript/node.html) subclasses in the generated tree. This is all +// a bit of a hack that is dependent on how canopy creates nodes in its parse +// tree. import parser from './grammar.peg'; import Node from './node.js'; @@ -18,7 +22,15 @@ import RepeatOptional from './repeat_optional.js'; import RepeatRequired from './repeat_required.js'; import RepeatSpec from './repeat_spec.js'; +// Canopy creates an instance of SyntaxNode for each element in the tree, then +// adds any necessary fields to that instance. In this case, we're replacing +// the default class with the Node class. parser.Parser.SyntaxNode = Node; + +// Once the SyntaxNode instance is created, the specific node type object is +// overlayed onto it. This causes the module attribute on the Node to be set, +// which updates the Node instance into the more specific "subclass" that is +// used for rendering. parser.Parser.Root = { module: Root }; parser.Parser.Regexp = { module: Regexp }; parser.Parser.Match = { module: Match }; diff --git a/src/js/parser/javascript/parser_state.js b/src/js/parser/javascript/parser_state.js index 3904a0c..8ef71e9 100644 --- a/src/js/parser/javascript/parser_state.js +++ b/src/js/parser/javascript/parser_state.js @@ -1,13 +1,23 @@ +// State tracking for an in-progress parse and render. export default class ParserState { + // - __progress__ - DOM node to update to indicate completion progress. constructor(progress) { + // Tracks the number of capture groups in the expression. this.groupCounter = 1; + // Cancels the in-progress render when set to true. this.cancelRender = false; + // Warnings that have been generated while rendering. this.warnings = []; + + // Used to display the progress indicator this._renderCounter = 0; this._maxCounter = 0; this._progress = progress; } + // Counts the number of in-progress rendering steps. As the counter goes up, + // a maximum value is also tracked. The maximum value and current render + // counter are used to calculate the completion process. get renderCounter() { return this._renderCounter; }