UNPKG

abnf-to-railroad

Version:

Command line tool to convert ABNF grammar files to HTML with railroad diagrams

87 lines (75 loc) 2.97 kB
const LayoutElement = require('./element'); const { Direction } = require('./track-builder'); /** * Bypass element (optional element) * @extends LayoutElement */ class BypassElement extends LayoutElement { /** * Create a bypass element for optional elements * @param {LayoutElement} element - The element that can be bypassed */ constructor(element) { super(); /** @type {LayoutElement} */ this.child = element; // Layout will be calculated in layout() method } /** * Calculate layout dimensions based on child * @param {LayoutConfig} layoutConfig - Configuration for layout calculations * @returns {void} */ layout(layoutConfig) { // First layout the child if (!this.child.isLaidOut) { this.child.layout(layoutConfig); } // Calculate layout dimensions with extra width for bypass routing this.width = this.child.width + 4; // Add 4 for routing space this.height = this.child.height + 1; // Extra height for bypass track this.baseline = this.child.baseline + 1; this.isLaidOut = true; // Assert the width invariant: all Expression widths must be even console.assert(this.width % 2 === 0, `BypassExpression violates width invariant: expected even width, got ${this.width}`); } /** * Render the element with a bypass track for optional path * @param {RenderContext} ctx - Rendering context * @returns {void} */ render(ctx) { const childX = (this.width - this.child.width) / 2; const childY = 1; // Child is 1 unit down from top // Render child using RenderContext ctx.renderChild(this.child, childX, childY, 'bypass-child'); // Draw the bypass path (above the child) per specification ctx.trackBuilder .start(0, this.baseline, Direction.EAST) .turnLeft() .forward(this.baseline - 2) .turnRight() .forward(this.width - 4) .turnRight() .forward(this.baseline - 2) .turnLeft() .finish('bypass-path'); // Through path connects entry to child and child to exit ctx.trackBuilder .start(0, this.baseline, Direction.EAST) .forward(childX) .finish('through-left'); ctx.trackBuilder .start(childX + this.child.width, this.baseline, Direction.EAST) .forward(this.width - (childX + this.child.width)) .finish('through-right'); } /** * Convert to debug string representation * @returns {string} Debug string like 'bypass(nonterminal("X"))' */ toString() { return `bypass(${this.child.toString()})`; } } module.exports = BypassElement;