UNPKG

projen

Version:

CDK for software projects

512 lines • 17.1 kB
"use strict"; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; Object.defineProperty(exports, "__esModule", { value: true }); var interfaces_1 = require("./interfaces"); /** * Represents a lexer for XML content in a string. */ var XMLStringLexer = /** @class */ (function () { /** * Initializes a new instance of `XMLStringLexer`. * * @param str - the string to tokenize and lex * @param options - lexer options */ function XMLStringLexer(str, options) { this._options = { skipWhitespaceOnlyText: false }; this.err = { line: -1, col: -1, index: -1, str: "" }; this._str = str; this._index = 0; this._length = str.length; if (options) { this._options.skipWhitespaceOnlyText = options.skipWhitespaceOnlyText || false; } } /** * Returns the next token. */ XMLStringLexer.prototype.nextToken = function () { if (this.eof()) { return { type: interfaces_1.TokenType.EOF }; } var token = (this.skipIfStartsWith('<') ? this.openBracket() : this.text()); if (this._options.skipWhitespaceOnlyText) { if (token.type === interfaces_1.TokenType.Text && XMLStringLexer.isWhiteSpaceToken(token)) { token = this.nextToken(); } } return token; }; /** * Branches from an opening bracket (`<`). */ XMLStringLexer.prototype.openBracket = function () { if (this.skipIfStartsWith('?')) { if (this.skipIfStartsWith('xml')) { if (XMLStringLexer.isSpace(this._str[this._index])) { return this.declaration(); } else { // a processing instruction starting with xml. e.g. <?xml-stylesheet href="doc.xsl" type="text/xsl"?> this.seek(-3); return this.pi(); } } else { return this.pi(); } } else if (this.skipIfStartsWith('!')) { if (this.skipIfStartsWith('--')) { return this.comment(); } else if (this.skipIfStartsWith('[CDATA[')) { return this.cdata(); } else if (this.skipIfStartsWith('DOCTYPE')) { return this.doctype(); } else { this.throwError("Invalid '!' in opening tag."); } } else if (this.skipIfStartsWith('/')) { return this.closeTag(); } else { return this.openTag(); } }; /** * Produces an XML declaration token. */ XMLStringLexer.prototype.declaration = function () { var version = ''; var encoding = ''; var standalone = ''; while (!this.eof()) { this.skipSpace(); if (this.skipIfStartsWith('?>')) { return { type: interfaces_1.TokenType.Declaration, version: version, encoding: encoding, standalone: standalone }; } else { // attribute name var _a = __read(this.attribute(), 2), attName = _a[0], attValue = _a[1]; if (attName === 'version') version = attValue; else if (attName === 'encoding') encoding = attValue; else if (attName === 'standalone') standalone = attValue; else this.throwError('Invalid attribute name: ' + attName); } } this.throwError('Missing declaration end symbol `?>`'); }; /** * Produces a doc type token. */ XMLStringLexer.prototype.doctype = function () { var pubId = ''; var sysId = ''; // name this.skipSpace(); var name = this.takeUntil2('[', '>', true); this.skipSpace(); if (this.skipIfStartsWith('PUBLIC')) { pubId = this.quotedString(); sysId = this.quotedString(); } else if (this.skipIfStartsWith('SYSTEM')) { sysId = this.quotedString(); } // skip internal subset this.skipSpace(); if (this.skipIfStartsWith('[')) { // skip internal subset nodes this.skipUntil(']'); if (!this.skipIfStartsWith(']')) { this.throwError('Missing end bracket of DTD internal subset'); } } this.skipSpace(); if (!this.skipIfStartsWith('>')) { this.throwError('Missing doctype end symbol `>`'); } return { type: interfaces_1.TokenType.DocType, name: name, pubId: pubId, sysId: sysId }; }; /** * Produces a processing instruction token. */ XMLStringLexer.prototype.pi = function () { var target = this.takeUntilStartsWith('?>', true); if (this.eof()) { this.throwError('Missing processing instruction end symbol `?>`'); } this.skipSpace(); if (this.skipIfStartsWith('?>')) { return { type: interfaces_1.TokenType.PI, target: target, data: '' }; } var data = this.takeUntilStartsWith('?>'); if (this.eof()) { this.throwError('Missing processing instruction end symbol `?>`'); } this.seek(2); return { type: interfaces_1.TokenType.PI, target: target, data: data }; }; /** * Produces a text token. * */ XMLStringLexer.prototype.text = function () { var data = this.takeUntil('<'); return { type: interfaces_1.TokenType.Text, data: data }; }; /** * Produces a comment token. * */ XMLStringLexer.prototype.comment = function () { var data = this.takeUntilStartsWith('-->'); if (this.eof()) { this.throwError('Missing comment end symbol `-->`'); } this.seek(3); return { type: interfaces_1.TokenType.Comment, data: data }; }; /** * Produces a CDATA token. * */ XMLStringLexer.prototype.cdata = function () { var data = this.takeUntilStartsWith(']]>'); if (this.eof()) { this.throwError('Missing CDATA end symbol `]>`'); } this.seek(3); return { type: interfaces_1.TokenType.CDATA, data: data }; }; /** * Produces an element token. */ XMLStringLexer.prototype.openTag = function () { // element name this.skipSpace(); var name = this.takeUntil2('>', '/', true); this.skipSpace(); if (this.skipIfStartsWith('>')) { return { type: interfaces_1.TokenType.Element, name: name, attributes: [], selfClosing: false }; } else if (this.skipIfStartsWith('/>')) { return { type: interfaces_1.TokenType.Element, name: name, attributes: [], selfClosing: true }; } // attributes var attributes = []; while (!this.eof()) { // end tag this.skipSpace(); if (this.skipIfStartsWith('>')) { return { type: interfaces_1.TokenType.Element, name: name, attributes: attributes, selfClosing: false }; } else if (this.skipIfStartsWith('/>')) { return { type: interfaces_1.TokenType.Element, name: name, attributes: attributes, selfClosing: true }; } var attr = this.attribute(); attributes.push(attr); } this.throwError('Missing opening element tag end symbol `>`'); }; /** * Produces a closing tag token. * */ XMLStringLexer.prototype.closeTag = function () { this.skipSpace(); var name = this.takeUntil('>', true); this.skipSpace(); if (!this.skipIfStartsWith('>')) { this.throwError('Missing closing element tag end symbol `>`'); } return { type: interfaces_1.TokenType.ClosingTag, name: name }; }; /** * Reads an attribute name, value pair */ XMLStringLexer.prototype.attribute = function () { // attribute name this.skipSpace(); var name = this.takeUntil('=', true); this.skipSpace(); if (!this.skipIfStartsWith('=')) { this.throwError('Missing equals sign before attribute value'); } // attribute value var value = this.quotedString(); return [name, value]; }; /** * Reads a string between double or single quotes. */ XMLStringLexer.prototype.quotedString = function () { this.skipSpace(); var startQuote = this.take(1); if (!XMLStringLexer.isQuote(startQuote)) { this.throwError('Missing start quote character before quoted value'); } var value = this.takeUntil(startQuote); if (!this.skipIfStartsWith(startQuote)) { this.throwError('Missing end quote character after quoted value'); } return value; }; /** * Determines if the current index is at or past the end of input string. */ XMLStringLexer.prototype.eof = function () { return this._index >= this._length; }; /** * Skips the length of the given string if the string from current position * starts with the given string. * * @param str - the string to match */ XMLStringLexer.prototype.skipIfStartsWith = function (str) { var strLength = str.length; if (strLength === 1) { if (this._str[this._index] === str) { this._index++; return true; } else { return false; } } for (var i = 0; i < strLength; i++) { if (this._str[this._index + i] !== str[i]) return false; } this._index += strLength; return true; }; /** * Seeks a number of character codes. * * @param count - number of characters to skip */ XMLStringLexer.prototype.seek = function (count) { this._index += count; if (this._index < 0) this._index = 0; if (this._index > this._length) this._index = this._length; }; /** * Skips space characters. */ XMLStringLexer.prototype.skipSpace = function () { while (!this.eof() && (XMLStringLexer.isSpace(this._str[this._index]))) { this._index++; } }; /** * Takes a given number of characters. * * @param count - character count */ XMLStringLexer.prototype.take = function (count) { if (count === 1) { return this._str[this._index++]; } var startIndex = this._index; this.seek(count); return this._str.slice(startIndex, this._index); }; /** * Takes characters until the next character matches `char`. * * @param char - a character to match * @param space - whether a space character stops iteration */ XMLStringLexer.prototype.takeUntil = function (char, space) { if (space === void 0) { space = false; } var startIndex = this._index; while (this._index < this._length) { var c = this._str[this._index]; if (c !== char && (!space || !XMLStringLexer.isSpace(c))) { this._index++; } else { break; } } return this._str.slice(startIndex, this._index); }; /** * Takes characters until the next character matches `char1` or `char1`. * * @param char1 - a character to match * @param char2 - a character to match * @param space - whether a space character stops iteration */ XMLStringLexer.prototype.takeUntil2 = function (char1, char2, space) { if (space === void 0) { space = false; } var startIndex = this._index; while (this._index < this._length) { var c = this._str[this._index]; if (c !== char1 && c !== char2 && (!space || !XMLStringLexer.isSpace(c))) { this._index++; } else { break; } } return this._str.slice(startIndex, this._index); }; /** * Takes characters until the next characters matches `str`. * * @param str - a string to match * @param space - whether a space character stops iteration */ XMLStringLexer.prototype.takeUntilStartsWith = function (str, space) { if (space === void 0) { space = false; } var startIndex = this._index; var strLength = str.length; while (this._index < this._length) { var match = true; for (var i = 0; i < strLength; i++) { var c = this._str[this._index + i]; var char = str[i]; if (space && XMLStringLexer.isSpace(c)) { return this._str.slice(startIndex, this._index); } else if (c !== char) { this._index++; match = false; break; } } if (match) return this._str.slice(startIndex, this._index); } this._index = this._length; return this._str.slice(startIndex); }; /** * Skips characters until the next character matches `char`. * * @param char - a character to match */ XMLStringLexer.prototype.skipUntil = function (char) { while (this._index < this._length) { var c = this._str[this._index]; if (c !== char) { this._index++; } else { break; } } }; /** * Determines if the given token is entirely whitespace. * * @param token - the token to check */ XMLStringLexer.isWhiteSpaceToken = function (token) { var str = token.data; for (var i = 0; i < str.length; i++) { var c = str[i]; if (c !== ' ' && c !== '\n' && c !== '\r' && c !== '\t' && c !== '\f') return false; } return true; }; /** * Determines if the given character is whitespace. * * @param char - the character to check */ XMLStringLexer.isSpace = function (char) { return char === ' ' || char === '\n' || char === '\r' || char === '\t'; }; /** * Determines if the given character is a quote character. * * @param char - the character to check */ XMLStringLexer.isQuote = function (char) { return (char === '"' || char === '\''); }; /** * Throws a parser error and records the line and column numbers in the parsed * string. * * @param msg - error message */ XMLStringLexer.prototype.throwError = function (msg) { var regexp = /\r\n|\r|\n/g; var match = null; var line = 0; var firstNewLineIndex = 0; var lastNewlineIndex = this._str.length; while ((match = regexp.exec(this._str)) !== null) { if (match === null) break; line++; if (match.index < this._index) firstNewLineIndex = regexp.lastIndex; if (match.index > this._index) { lastNewlineIndex = match.index; break; } } this.err = { line: line, col: this._index - firstNewLineIndex, index: this._index, str: this._str.substring(firstNewLineIndex, lastNewlineIndex) }; throw new Error(msg + "\nIndex: " + this.err.index + "\nLn: " + this.err.line + ", Col: " + this.err.col + "\nInput: " + this.err.str); }; /** * Returns an iterator for the lexer. */ XMLStringLexer.prototype[Symbol.iterator] = function () { this._index = 0; return { next: function () { var token = this.nextToken(); if (token.type === interfaces_1.TokenType.EOF) { return { done: true, value: null }; } else { return { done: false, value: token }; } }.bind(this) }; }; return XMLStringLexer; }()); exports.XMLStringLexer = XMLStringLexer; //# sourceMappingURL=XMLStringLexer.js.map