UNPKG

psl-parser

Version:

A parser for the Profile Scripting Language

706 lines 26.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tokenizer_1 = require("./tokenizer"); var SyntaxKind; (function (SyntaxKind) { SyntaxKind[SyntaxKind["ASSIGNMENT"] = 0] = "ASSIGNMENT"; SyntaxKind[SyntaxKind["BINARY_OPERATOR"] = 1] = "BINARY_OPERATOR"; SyntaxKind[SyntaxKind["CATCH_STATEMENT"] = 2] = "CATCH_STATEMENT"; SyntaxKind[SyntaxKind["DO_STATEMENT"] = 3] = "DO_STATEMENT"; SyntaxKind[SyntaxKind["FOR_STATEMENT"] = 4] = "FOR_STATEMENT"; SyntaxKind[SyntaxKind["IDENTIFIER"] = 5] = "IDENTIFIER"; SyntaxKind[SyntaxKind["IF_STATEMENT"] = 6] = "IF_STATEMENT"; SyntaxKind[SyntaxKind["NUMERIC_LITERAL"] = 7] = "NUMERIC_LITERAL"; SyntaxKind[SyntaxKind["POST_CONDITION"] = 8] = "POST_CONDITION"; SyntaxKind[SyntaxKind["QUIT_STATEMENT"] = 9] = "QUIT_STATEMENT"; SyntaxKind[SyntaxKind["RETURN_STATEMENT"] = 10] = "RETURN_STATEMENT"; SyntaxKind[SyntaxKind["SET_STATEMENT"] = 11] = "SET_STATEMENT"; SyntaxKind[SyntaxKind["MULTIPLE_VARIABLE_SET"] = 12] = "MULTIPLE_VARIABLE_SET"; SyntaxKind[SyntaxKind["STRING_LITERAL"] = 13] = "STRING_LITERAL"; SyntaxKind[SyntaxKind["WHILE_STATEMENT"] = 14] = "WHILE_STATEMENT"; SyntaxKind[SyntaxKind["TYPE_STATEMENT"] = 15] = "TYPE_STATEMENT"; SyntaxKind[SyntaxKind["VARIABLE_DECLARATION"] = 16] = "VARIABLE_DECLARATION"; SyntaxKind[SyntaxKind["TYPE_IDENTIFIER"] = 17] = "TYPE_IDENTIFIER"; })(SyntaxKind = exports.SyntaxKind || (exports.SyntaxKind = {})); var OPERATOR_VALUE; (function (OPERATOR_VALUE) { OPERATOR_VALUE["AND_LITERAL"] = "and"; OPERATOR_VALUE["APOSTROPHE"] = "'"; OPERATOR_VALUE["AT"] = "@"; OPERATOR_VALUE["BACK_SLASH"] = "\\"; OPERATOR_VALUE["CARROT"] = "^"; OPERATOR_VALUE["COLON"] = ":"; OPERATOR_VALUE["DOLLAR"] = "$"; OPERATOR_VALUE["DOT"] = "."; OPERATOR_VALUE["EQUAL"] = "="; OPERATOR_VALUE["EXCLAMATION"] = "!"; OPERATOR_VALUE["GREATER_THAN"] = ">"; OPERATOR_VALUE["HASH"] = "#"; OPERATOR_VALUE["LEFT_BRACKET"] = "["; OPERATOR_VALUE["LESS_THAN"] = "<"; OPERATOR_VALUE["MINUS"] = "-"; OPERATOR_VALUE["NOT_LITERAL"] = "not"; OPERATOR_VALUE["OR_LITERAL"] = "or"; OPERATOR_VALUE["PLUS"] = "+"; OPERATOR_VALUE["QUESTION_MARK"] = "?"; OPERATOR_VALUE["RIGHT_BRACKET"] = "]"; OPERATOR_VALUE["SLASH"] = "/"; OPERATOR_VALUE["STAR"] = "*"; OPERATOR_VALUE["UNDERSCORE"] = "_"; OPERATOR_VALUE["RET"] = "ret"; })(OPERATOR_VALUE || (OPERATOR_VALUE = {})); var STORAGE_MODIFIERS; (function (STORAGE_MODIFIERS) { STORAGE_MODIFIERS["STATIC"] = "static"; STORAGE_MODIFIERS["NEW"] = "new"; STORAGE_MODIFIERS["LITERAL"] = "literal"; })(STORAGE_MODIFIERS || (STORAGE_MODIFIERS = {})); var ACCESS_MODIFIERS; (function (ACCESS_MODIFIERS) { ACCESS_MODIFIERS["PUBLIC"] = "public"; ACCESS_MODIFIERS["PRIVATE"] = "private"; })(ACCESS_MODIFIERS || (ACCESS_MODIFIERS = {})); var STATEMENT_KEYWORD; (function (STATEMENT_KEYWORD) { STATEMENT_KEYWORD["DO"] = "do"; STATEMENT_KEYWORD["SET"] = "set"; STATEMENT_KEYWORD["IF"] = "if"; STATEMENT_KEYWORD["CATCH"] = "catch"; STATEMENT_KEYWORD["FOR"] = "for"; STATEMENT_KEYWORD["QUIT"] = "quit"; STATEMENT_KEYWORD["RETURN"] = "return"; STATEMENT_KEYWORD["WHILE"] = "while"; STATEMENT_KEYWORD["TYPE"] = "type"; })(STATEMENT_KEYWORD || (STATEMENT_KEYWORD = {})); const UNARY_OPERATORS = [ { value: OPERATOR_VALUE.APOSTROPHE }, { value: OPERATOR_VALUE.AT }, { value: OPERATOR_VALUE.CARROT }, { value: OPERATOR_VALUE.DOLLAR, appendable: true }, { value: OPERATOR_VALUE.DOT }, { value: OPERATOR_VALUE.MINUS }, { value: OPERATOR_VALUE.NOT_LITERAL }, { value: OPERATOR_VALUE.PLUS }, { value: OPERATOR_VALUE.RIGHT_BRACKET }, { value: OPERATOR_VALUE.RET }, ]; const BINARY_OPERATORS = [ { value: OPERATOR_VALUE.AND_LITERAL }, { value: OPERATOR_VALUE.APOSTROPHE, appendable: true }, { value: OPERATOR_VALUE.AT }, { value: OPERATOR_VALUE.BACK_SLASH }, { value: OPERATOR_VALUE.CARROT }, { value: OPERATOR_VALUE.DOT }, { value: OPERATOR_VALUE.EQUAL }, { value: OPERATOR_VALUE.EXCLAMATION }, { value: OPERATOR_VALUE.GREATER_THAN, appendable: true }, { value: OPERATOR_VALUE.HASH }, { value: OPERATOR_VALUE.LEFT_BRACKET }, { value: OPERATOR_VALUE.LESS_THAN, appendable: true }, { value: OPERATOR_VALUE.MINUS }, { value: OPERATOR_VALUE.NOT_LITERAL, appendable: true }, { value: OPERATOR_VALUE.OR_LITERAL }, { value: OPERATOR_VALUE.PLUS }, { value: OPERATOR_VALUE.QUESTION_MARK }, { value: OPERATOR_VALUE.SLASH }, { value: OPERATOR_VALUE.STAR }, { value: OPERATOR_VALUE.UNDERSCORE }, ]; class StatementParser { constructor(arg) { this.tokens = []; if (typeof arg === 'string') { this.tokenizer = tokenizer_1.getTokens(arg); } else if (Array.isArray(arg)) { this.tokenizer = arg[Symbol.iterator](); } else { this.tokenizer = arg; } this.next(); // should I? } parseLine() { if (!this.activeToken) return []; const statements = []; do { if (this.activeToken.isNewLine()) break; if (this.activeToken.isAlphanumeric()) { const statement = this.parseStatement(); if (!statement) { this.next(); continue; } statements.push(statement); } else if (this.activeToken.isWhiteSpace()) this.next(true); else break; } while (this.activeToken); return statements; } parseStatement() { if (!this.activeToken) return; if (!this.activeToken.isAlphanumeric()) return; const action = this.activeToken; let loadFunction; let kind; const loadSingleExpression = () => { if (!this.next(true)) return { action, kind, expressions: [] }; const expression = loadFunction(); const expressions = expression ? [expression] : []; return { kind, action, expressions }; }; const loadCommaSeparatedExpressions = () => { if (!this.next(true)) return { action, kind, expressions: [] }; const expressions = this.loadCommaSeparated(loadFunction); return { kind, action, expressions }; }; switch (action.value) { case STATEMENT_KEYWORD.DO: loadFunction = () => this.parseExpression(); kind = SyntaxKind.DO_STATEMENT; return loadCommaSeparatedExpressions(); case STATEMENT_KEYWORD.SET: loadFunction = () => this.parseSetExpression(); kind = SyntaxKind.SET_STATEMENT; return loadCommaSeparatedExpressions(); case STATEMENT_KEYWORD.IF: loadFunction = () => this.parseExpression(); kind = SyntaxKind.IF_STATEMENT; return loadCommaSeparatedExpressions(); case STATEMENT_KEYWORD.CATCH: loadFunction = () => this.parseExpression(); kind = SyntaxKind.CATCH_STATEMENT; return loadCommaSeparatedExpressions(); case STATEMENT_KEYWORD.FOR: return this.parseForStatement(); case STATEMENT_KEYWORD.QUIT: loadFunction = () => this.parseExpression(); kind = SyntaxKind.QUIT_STATEMENT; return loadCommaSeparatedExpressions(); case STATEMENT_KEYWORD.RETURN: loadFunction = () => this.parseExpression(); kind = SyntaxKind.RETURN_STATEMENT; return loadSingleExpression(); case STATEMENT_KEYWORD.WHILE: loadFunction = () => this.parseExpression(); kind = SyntaxKind.WHILE_STATEMENT; return loadSingleExpression(); case STATEMENT_KEYWORD.TYPE: return this.parseTypeStatement(); default: return; } } parseTypeStatement() { const action = this.activeToken; this.next(true); let staticToken; let newToken; let publicToken; let literalToken; const getKeyWordToken = (token) => { switch (token.value) { case ACCESS_MODIFIERS.PUBLIC: publicToken = token; return true; case STORAGE_MODIFIERS.LITERAL: literalToken = token; return true; case STORAGE_MODIFIERS.NEW: newToken = token; return true; case STORAGE_MODIFIERS.STATIC: staticToken = token; return true; default: return false; } }; const parseTypeAssignments = () => { if (!this.activeToken || !this.activeToken.isAlphanumeric()) { if (literalToken || newToken || publicToken || staticToken) { const emptyDeclaration = { id: undefined, kind: SyntaxKind.VARIABLE_DECLARATION, literalToken: undefined, newToken, publicToken, staticToken, type: undefined, }; return [emptyDeclaration]; } return []; } const type = { id: this.activeToken, kind: SyntaxKind.TYPE_IDENTIFIER }; if (!this.next(true) || staticToken) { const declaration = { id: undefined, kind: SyntaxKind.VARIABLE_DECLARATION, literalToken, newToken, publicToken, staticToken, type, }; return [declaration]; } const assignments = this.loadCommaSeparated(() => { return this.parseAssignment(() => { const variable = this.parseValue(); // why not parseIdentifier return { args: variable.args, id: variable.id, kind: SyntaxKind.VARIABLE_DECLARATION, literalToken, newToken, publicToken, staticToken, type, }; }); }); assignments.forEach(expression => { forEachChild(expression, node => { if (!node) return; if (node.kind === SyntaxKind.VARIABLE_DECLARATION) { const declaration = node; if (declaration.args) { declaration.args = declaration.args.map((arg) => { if (!arg) return; arg.kind = SyntaxKind.TYPE_IDENTIFIER; return arg; }); } } return true; }); }); return assignments; }; while (this.activeToken && getKeyWordToken(this.activeToken)) { if (!this.next(true)) break; } const expressions = parseTypeAssignments(); return { action, expressions, kind: SyntaxKind.TYPE_STATEMENT, }; } parseAssignment(getLeft) { const left = getLeft(); let rootNode = left; if (this.activeToken && this.activeToken.isEqualSign()) { const equalSign = this.activeToken; this.next(true); const expression = this.parseExpression(); rootNode = { operator: [equalSign], kind: SyntaxKind.ASSIGNMENT }; rootNode.left = left; rootNode.right = expression; } return rootNode; } parseForStatement() { if (!this.activeToken) return; const action = this.activeToken; const forStatement = { action, kind: SyntaxKind.FOR_STATEMENT, expressions: [] }; if (!this.next()) return forStatement; // consume for if (!this.next()) return forStatement; // consume first space const spaceOrExpression = this.activeToken; if (spaceOrExpression.isSpace()) { this.next(); return forStatement; // argumentless for } const expression = this.parseExpression(); if (expression) forStatement.expressions.push(expression); return forStatement; } parseSetExpression() { if (!this.activeToken) return; const postCondition = this.parsePostCondition(); const assignment = this.parseAssignment(() => { const setVariables = this.parseSetVariables(); if (this.activeToken && this.activeToken.isWhiteSpace()) this.next(true); if (postCondition && !setVariables) { postCondition.expression = setVariables; return postCondition; } return setVariables; }); if (assignment && postCondition) { postCondition.expression = assignment; return postCondition; } return assignment; } parsePostCondition() { if (!this.activeToken) return; if (this.activeToken.isColon()) { const colon = this.activeToken; const postCondition = { kind: SyntaxKind.POST_CONDITION, colon }; this.next(true); const condition = this.parseExpression(); if (!condition) return postCondition; postCondition.condition = condition; return postCondition; } } parseSetVariables() { if (!this.activeToken) return; if (this.activeToken.isOpenParen()) { this.next(true); const variables = this.loadCommaSeparated(() => this.parseExpression()); if (this.activeToken && this.activeToken.isCloseParen()) this.next(true); return { variables, kind: SyntaxKind.MULTIPLE_VARIABLE_SET }; } else { return this.parseExpression(true); } } parseExpression(ignoreEquals, includeRet) { const postCondition = this.parsePostCondition(); let rootNode = this.parseValue(undefined, includeRet); if (!rootNode) { if (postCondition) return postCondition; else return; } if (this.activeToken && this.activeToken.isEqualSign() && ignoreEquals) { return rootNode; } rootNode = this.parseOperatorSeparatedValues(rootNode, ignoreEquals); if (!rootNode) return; rootNode = this.parseColonSeparatedValues(rootNode); if (postCondition) { postCondition.expression = rootNode; rootNode = postCondition; } return rootNode; } parseColonSeparatedValues(rootNode) { while (this.activeToken && this.activeToken.isColon()) { const colonToken = this.activeToken; this.next(true); const colon = { kind: SyntaxKind.BINARY_OPERATOR, left: rootNode, operator: [colonToken], right: this.parseValue(), }; rootNode = colon; } return rootNode; } parseOperatorSeparatedValues(rootNode, ignoreEquals) { while (this.activeToken && getBinaryOperator(this.activeToken.value)) { if (this.activeToken.isEqualSign() && ignoreEquals) break; const operator = this.parseBinaryOperator(); if (!operator) return; operator.left = rootNode; operator.right = this.parseValue(); rootNode = operator; } return rootNode; } parseBinaryOperator() { if (!this.activeToken) return; const binaryOperator = { kind: SyntaxKind.BINARY_OPERATOR, operator: [this.activeToken], }; if (!this.next(true)) return binaryOperator; let operator; do { operator = getBinaryOperator(this.activeToken.value); if (!operator) break; if (operator) { const not = operator.value === OPERATOR_VALUE.NOT_LITERAL || operator.value === OPERATOR_VALUE.APOSTROPHE; if (not && binaryOperator.operator.length) break; } binaryOperator.operator.push(this.activeToken); this.next(true); } while (operator && operator.appendable); return binaryOperator; } parseUnaryOperator(includeRet) { if (!this.activeToken) return []; const leftOperator = []; let unaryOperator = getUnaryOperator(this.activeToken.value, includeRet); if (unaryOperator) { leftOperator.push(this.activeToken); this.next(true); while (unaryOperator && unaryOperator.appendable) { unaryOperator = getUnaryOperator(this.activeToken.value); if (unaryOperator) { leftOperator.push(this.activeToken); this.next(true); } } } return leftOperator; } parseValue(tree, includeRet) { let value; if (!this.activeToken || this.activeToken.isWhiteSpace()) { if (tree) return tree; else return; } const unaryOperator = this.parseUnaryOperator(includeRet); if (!this.activeToken) { return { id: new tokenizer_1.Token(-1 /* Undefined */, '', { character: 0, line: 0 }), kind: SyntaxKind.IDENTIFIER, unaryOperator, }; } if (this.activeToken.type === 1 /* Alphanumeric */) { value = this.parseIdentifier(); if (!value) return; if (unaryOperator.length) value.unaryOperator = unaryOperator; } else if (this.activeToken.type === 9 /* DoubleQuotes */) { value = this.parseStringLiteral(); if (!value) return; if (unaryOperator.length) value.unaryOperator = unaryOperator; } else if (this.activeToken.type === 2 /* Numeric */) { value = { id: this.activeToken, kind: SyntaxKind.NUMERIC_LITERAL }; if (unaryOperator.length) value.unaryOperator = unaryOperator; this.next(true); } else if (this.activeToken.type === 40 /* OpenParen */) { this.next(true); value = this.parseExpression(); this.next(true); } if (tree && value) { tree.right = value; value = tree; } if (this.activeToken && (this.activeToken.type === 46 /* Period */ || this.activeToken.type === 94 /* Caret */)) { const operator = { kind: SyntaxKind.BINARY_OPERATOR, left: value, operator: [this.activeToken], }; this.next(); return this.parseValue(operator); } if (value && this.activeToken && this.activeToken.isWhiteSpace()) this.next(true); return value; } parseIdentifier() { if (!this.activeToken) return; const id = this.activeToken; if (this.next() && this.activeToken.isOpenParen()) { const openParen = this.activeToken; if (this.next(true)) { const args = this.parseArgs(); if (this.activeToken.isCloseParen()) { const closeParen = this.activeToken; this.next(); return { id, kind: SyntaxKind.IDENTIFIER, args, openParen, closeParen }; } } } return { id, kind: SyntaxKind.IDENTIFIER }; } parseStringLiteral() { if (!this.activeToken) return; const openQuote = this.activeToken; this.next(); const id = this.activeToken; if (!id || !this.next()) return; if (this.activeToken.isDoubleQuotes()) { const closeQuote = this.activeToken; this.next(true); return { id, kind: SyntaxKind.STRING_LITERAL, openQuote, closeQuote }; } } parseArgs() { return this.loadCommaSeparated(() => this.parseExpression(false, true)); } loadCommaSeparated(getArg) { const args = []; do { if (!this.activeToken) break; if (this.activeToken.isWhiteSpace()) { if (!this.next(true)) break; } if (this.activeToken.isComma()) { args.push(undefined); // desired behavior? continue; } const arg = getArg(); if (arg && !Array.isArray(arg)) args.push(arg); else if (arg) args.push(...arg); else if (!arg && args.length > 0) args.push(undefined); else break; if (!this.activeToken) break; } while (this.activeToken.isComma() && this.next(true)); return args; } next(skipSpaceOrTab) { if (this.activeToken) this.previousToken = this.activeToken; do { this.activeToken = this.tokenizer.next().value; if (this.activeToken) this.tokens.push(this.activeToken); } while (skipSpaceOrTab && this.activeToken && (this.activeToken.isSpace() || this.activeToken.isTab())); return this.activeToken !== undefined; } } exports.StatementParser = StatementParser; function getBinaryOperator(tokenValue) { return BINARY_OPERATORS.find(o => o.value === tokenValue); } function getUnaryOperator(tokenValue, includeRet) { const operator = UNARY_OPERATORS.find(o => o.value === tokenValue); if (!operator) return; if (operator.value === OPERATOR_VALUE.RET && !includeRet) return; return operator; } function forEachChild(node, f) { let goDeeper = false; if (!node) return; switch (node.kind) { case SyntaxKind.DO_STATEMENT: case SyntaxKind.IF_STATEMENT: case SyntaxKind.QUIT_STATEMENT: case SyntaxKind.RETURN_STATEMENT: case SyntaxKind.SET_STATEMENT: case SyntaxKind.WHILE_STATEMENT: case SyntaxKind.FOR_STATEMENT: case SyntaxKind.CATCH_STATEMENT: case SyntaxKind.TYPE_STATEMENT: goDeeper = f(node); if (!goDeeper) return; const statement = node; statement.expressions.forEach(expression => { if (!expression) return; forEachChild(expression, f); }); break; case SyntaxKind.ASSIGNMENT: case SyntaxKind.BINARY_OPERATOR: goDeeper = f(node); if (!goDeeper) return; const assignment = node; const left = assignment.left; if (left && left.kind === SyntaxKind.MULTIPLE_VARIABLE_SET) { left.variables.forEach(n => { forEachChild(n, f); }); } else if (left) { forEachChild(left, f); } const right = assignment.right; if (right) { forEachChild(right, f); } break; case SyntaxKind.POST_CONDITION: goDeeper = f(node); if (!goDeeper) return; const postCondition = node; if (postCondition.condition) forEachChild(postCondition.condition, f); if (postCondition.expression) { const expression = postCondition.expression; if (Array.isArray(expression)) { expression.forEach(n => { forEachChild(n, f); }); } else if (expression) { forEachChild(expression, f); } } break; case SyntaxKind.IDENTIFIER: case SyntaxKind.TYPE_IDENTIFIER: goDeeper = f(node); if (!goDeeper) return; const identifier = node; if (identifier.args) identifier.args.forEach(arg => forEachChild(arg, f)); break; case SyntaxKind.VARIABLE_DECLARATION: goDeeper = f(node); if (!goDeeper) return; const declaration = node; if (declaration.args) declaration.args.forEach(arg => forEachChild(arg, f)); f(declaration.type); case SyntaxKind.NUMERIC_LITERAL: case SyntaxKind.STRING_LITERAL: f(node); break; default: break; } } exports.forEachChild = forEachChild; //# sourceMappingURL=statementParser.js.map