UNPKG

swift-pattern-compiler

Version:

A compiler which transforms SWIFT patterns to an object representation with additional information's.

294 lines 10.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const interfaces_1 = require("./interfaces"); /** * The parser takes an array of tokens and compute into an AST. * * ``` * const tokens = [ * { type: "num", value: "35" }, * { type: "char", value: "a" } * ]; * const ast = parser(tokens); * { * type: "SwiftPattern", * body: [ * { * type: "line", * nodes: [ * { * type: "field", * length: { min: 0, max: 35 }, * char: "a" * }, * ] * } * ] * } * ``` * * @param tokens the SWIFT field pattern string * @returns the tokens array */ exports.parser = (tokens) => { tokens = tokens || []; const lines = exports.splitLines(tokens); // tslint:disable-next-line:max-line-length const body = lines.map(({ tokens: t, optional: o }) => exports.parse(t, o)); const ast = { type: "SwiftPattern", body, }; return ast; }; /** * The parse function takes an array of tokens and compute them into an * array of nodes. * * ``` * const tokens = [ * { type: ETokenType.num, value: "35" }, * { type: ETokenType.char, value: "a" }, * { type: ETokenType.bracket, value: "[" }, * { type: ETokenType.num, value: "3" }, * { type: ETokenType.quant, value: "-" }, * { type: ETokenType.num, value: "20" }, * { type: ETokenType.char, value: "c" }, * { type: ETokenType.bracket, value: "]" }, * ]; * const ast = parser(tokens); * { * type: "line", * nodes: [ * { * type: "field", * length: { min: 0, max: 35 }, * char: "a", * }, * { * type: "optionalField", * nodes: [ * { * type: "field", * length: { min: 3, max: 20 }, * char: "c", * }, * ], * }, * ], * }, * ``` * * @param tokens */ exports.parse = (tokens, optional) => { let current = 0; const walk = () => { let token = exports.getToken(tokens[current]); if (token.type === interfaces_1.ETokenType.char) { throw new Error(`Missing number token for character token "${token.value}"`); } if (token.type === interfaces_1.ETokenType.num) { const num = token.value; const nextToken = exports.getToken(tokens[++current]); switch (nextToken.type) { case interfaces_1.ETokenType.quant: // min/max quantifier e.g. "3-5x" consists of // min (3), quant (-), max (5) and char (x) // so we need to increment to get max and then the char if (nextToken.value === "-") { return { type: interfaces_1.ENodeType.field, length: exports.getLengthObj(num, nextToken.value, exports.getToken(tokens[++current]).value), char: exports.getToken(tokens[++current]).value, }; } // quantifer e.g. "3!x" wich consits just of // max (3), quant (!) and char (x) // so we need to just increment once to get the char else { return { type: interfaces_1.ENodeType.field, length: exports.getLengthObj(num, nextToken.value), char: exports.getToken(tokens[++current]).value, }; } case interfaces_1.ETokenType.char: return { type: interfaces_1.ENodeType.field, length: exports.getLengthObj(num), char: nextToken.value, }; default: throw new Error(`Missing character or quantifier token after number token "${num}"`); } } // optional Field e.g."[35a3c]" which consists of optional field 35a // and field 3c if (token.type === interfaces_1.ETokenType.bracket && token.value === "[") { const optionalNodes = []; token = exports.getToken(tokens[++current]); while (token.type !== interfaces_1.ETokenType.bracket || token.type === interfaces_1.ETokenType.bracket && token.value !== "]") { optionalNodes.push(walk()); token = exports.getToken(tokens[++current]); } return { type: interfaces_1.ENodeType.optional, nodes: optionalNodes }; } // block Field e.g."(35a3c)" which consists of field 35a // and field 3c if (token.type === interfaces_1.ETokenType.paren && token.value === "(") { const blockNodes = []; token = exports.getToken(tokens[++current]); while (token.type !== interfaces_1.ETokenType.paren || token.type === interfaces_1.ETokenType.paren && token.value !== ")") { blockNodes.push(walk()); token = exports.getToken(tokens[++current]); } return { type: interfaces_1.ENodeType.block, nodes: blockNodes }; } if (token.type === interfaces_1.ETokenType.const) { const char = token.value; const length = char.length; return { type: interfaces_1.ENodeType.field, length: { min: length, max: length }, char, }; } if (token.type === interfaces_1.ETokenType.sign) { return { type: interfaces_1.ENodeType.sign, char: token.value, }; } throw new Error(`Unknown token type "${token.type}" with value "${token.value}"`); }; const nodes = []; while (current < tokens.length) { nodes.push(walk()); current++; } const line = { type: interfaces_1.ENodeType.line, nodes }; if (optional) { line.optional = optional; } return line; }; /** * The splitLines function takes an array of tokens and compute into an * array of lines with the corresponding tokens. * * ``` * const tokens = [ * { type: ETokenType.num, value: "35" }, * { type: ETokenType.char, value: "a" }, * { type: ETokenType.bracket, value: "[" }, * { type: ETokenType.num, value: "3" }, * { type: ETokenType.quant, value: "-" }, * { type: ETokenType.num, value: "20" }, * { type: ETokenType.char, value: "c" }, * { type: ETokenType.bracket, value: "]" }, * { type: ETokenType.num, value: "2" }, * { type: ETokenType.multilineQuant, value: "*" }, * { type: ETokenType.num, value: "35" }, * { type: ETokenType.char, value: "a" }, * ]; * const ast = parser(tokens); * [ * { * type: "line", * tokens: [ * { type: "num", value: "35" }, * { type: "char", value: "a" }, * { type: "bracket", value: "[" }, * { type: "num", value: "3" }, * { type: "quant", value: "-" }, * { type: "num", value: "20" }, * { type: "char", value: "c" }, * { type: "bracket", value: "]" }, * ] * }, * { * type: "line", * tokens: [ * { type: "num", value: "35" }, * { type: "char", value: "a" }, * ] * }, * { * type: "line", * tokens: [ * { type: "num", value: "35" }, * { type: "char", value: "a" }, * ] * } * ] * ``` * * @param tokens */ exports.splitLines = (tokens) => { const lines = []; let current = 0; while (current < tokens.length) { const token = tokens[current]; const nextToken = exports.getToken(tokens[++current]); if (nextToken.type === interfaces_1.ETokenType.multilineQuant) { current++; const num = +token.value; for (let i = 0; i < num; i++) { const multilineTokens = tokens.slice(current); lines.push({ type: "line", optional: true, tokens: multilineTokens, }); } break; } if (token.type === interfaces_1.ETokenType.newline) { lines.push({ type: "line", tokens: [], }); continue; } let line = lines[lines.length - 1]; if (!line) { lines.push({ type: "line", tokens: [], }); } line = lines[lines.length - 1]; line.tokens.push(token); } return lines; }; /** * Returns the token when it is defined or an undefined token. * @param token */ exports.getToken = (token) => { return token || { type: interfaces_1.ETokenType.undefined, value: "" }; }; /** * Returns an length object based on the provided quantifier. * @param num1 * @param quant */ exports.getLengthObj = (num1, quant, num2) => { switch (quant) { case "!": return { min: +num1, max: +num1 }; case "-": if (!num2) { throw new Error("Missing secound number for min/max quantifier!"); } return { min: +num1, max: +num2 }; default: return { min: 0, max: +num1 }; } }; //# sourceMappingURL=parser.js.map