UNPKG

adaptive-expressions

Version:
287 lines 13.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ExpressionParser = void 0; /** * @module adaptive-expressions */ /** * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ const antlr4ts_1 = require("antlr4ts"); const tree_1 = require("antlr4ts/tree"); const constant_1 = require("../constant"); const expression_1 = require("../expression"); const expressionType_1 = require("../expressionType"); const generated_1 = require("./generated"); const ep = __importStar(require("./generated/ExpressionAntlrParser")); const parseErrorListener_1 = require("./parseErrorListener"); const functionUtils_1 = require("../functionUtils"); /** * Parser to turn strings into Expression */ class ExpressionParser { /** * Initializes a new instance of the [ExpressionParser](xref:adaptive-expressions.ExpressionParser) class. * * @param lookup [EvaluatorLookup](xref:adaptive-expressions.EvaluatorLookup) for information from type string. */ constructor(lookup) { this.ExpressionTransformer = class extends tree_1.AbstractParseTreeVisitor { constructor(lookup) { super(); this.escapeRegex = new RegExp(/\\[^\r\n]?/g); this._lookupFunction = undefined; this.transform = (context) => this.visit(context); this.visitParenthesisExp = (context) => this.visit(context.expression()); this.defaultResult = () => new constant_1.Constant(''); this.makeExpression = (functionType, ...children) => { if (!this._lookupFunction(functionType)) { throw new Error(`${functionType} does not have an evaluator, it's not a built-in function or a custom function.`); } return expression_1.Expression.makeExpression(functionType, this._lookupFunction(functionType), ...children); }; this._lookupFunction = lookup; } visitUnaryOpExp(context) { const unaryOperationName = context.getChild(0).text; const operand = this.visit(context.expression()); if (unaryOperationName === expressionType_1.ExpressionType.Subtract || unaryOperationName === expressionType_1.ExpressionType.Add) { return this.makeExpression(unaryOperationName, new constant_1.Constant(0), operand); } return this.makeExpression(unaryOperationName, operand); } visitBinaryOpExp(context) { const binaryOperationName = context.getChild(1).text; const left = this.visit(context.expression(0)); const right = this.visit(context.expression(1)); return this.makeExpression(binaryOperationName, left, right); } visitTripleOpExp(context) { const conditionalExpression = this.visit(context.expression(0)); const left = this.visit(context.expression(1)); const right = this.visit(context.expression(2)); return this.makeExpression(expressionType_1.ExpressionType.If, conditionalExpression, left, right); } visitFuncInvokeExp(context) { const parameters = this.processArgsList(context.argsList()); // Remove the check to check primaryExpression is just an IDENTIFIER to support "." in template name let functionName = context.primaryExpression().text; if (context.NON() !== undefined) { functionName += context.NON().text; } return this.makeExpression(functionName, ...parameters); } visitIdAtom(context) { let result; const symbol = context.text; if (symbol === 'false') { result = new constant_1.Constant(false); } else if (symbol === 'true') { result = new constant_1.Constant(true); } else if (symbol === 'null') { result = new constant_1.Constant(null); } else if (symbol === 'undefined') { result = new constant_1.Constant(undefined); } else { result = this.makeExpression(expressionType_1.ExpressionType.Accessor, new constant_1.Constant(symbol)); } return result; } visitIndexAccessExp(context) { const property = this.visit(context.expression()); const instance = this.visit(context.primaryExpression()); return this.makeExpression(expressionType_1.ExpressionType.Element, instance, property); } visitMemberAccessExp(context) { const property = context.IDENTIFIER().text; const instance = this.visit(context.primaryExpression()); return this.makeExpression(expressionType_1.ExpressionType.Accessor, new constant_1.Constant(property), instance); } visitNumericAtom(context) { const numberValue = parseFloat(context.text); if (functionUtils_1.FunctionUtils.isNumber(numberValue)) { return new constant_1.Constant(numberValue); } throw new Error(`${context.text} is not a number.`); } visitArrayCreationExp(context) { const parameters = this.processArgsList(context.argsList()); return this.makeExpression(expressionType_1.ExpressionType.CreateArray, ...parameters); } visitStringAtom(context) { let text = context.text; if (text.startsWith("'") && text.endsWith("'")) { text = text.substr(1, text.length - 2).replace(/\\'/g, "'"); } else if (text.startsWith('"') && text.endsWith('"')) { // start with "" text = text.substr(1, text.length - 2).replace(/\\"/g, '"'); } else { throw new Error(`Invalid string ${text}`); } return new constant_1.Constant(this.evalEscape(text)); } visitJsonCreationExp(context) { let expr = this.makeExpression(expressionType_1.ExpressionType.Json, new constant_1.Constant('{}')); if (context.keyValuePairList()) { for (const kvPair of context.keyValuePairList().keyValuePair()) { let key = ''; const keyNode = kvPair.key().children[0]; if (keyNode instanceof tree_1.TerminalNode) { if (keyNode.symbol.type === ep.ExpressionAntlrParser.IDENTIFIER) { key = keyNode.text; } else { key = keyNode.text.substring(1, keyNode.text.length - 1); } } expr = this.makeExpression(expressionType_1.ExpressionType.SetProperty, expr, new constant_1.Constant(key), this.visit(kvPair.expression())); } } return expr; } visitStringInterpolationAtom(context) { const children = [new constant_1.Constant('')]; for (const node of context.stringInterpolation().children) { if (node instanceof tree_1.TerminalNode) { switch (node.symbol.type) { case ep.ExpressionAntlrParser.TEMPLATE: { const expressionString = this.trimExpression(node.text); children.push(expression_1.Expression.parse(expressionString, this._lookupFunction)); break; } case ep.ExpressionAntlrParser.ESCAPE_CHARACTER: { children.push(new constant_1.Constant(node.text.replace(/\\`/g, '`').replace(/\\\$/g, '$'))); break; } default: break; } } else { children.push(new constant_1.Constant(node.text)); } } return this.makeExpression(expressionType_1.ExpressionType.Concat, ...children); } processArgsList(context) { const result = []; if (!context) { return result; } for (const child of context.children) { if (child instanceof ep.LambdaContext) { const evalParam = this.makeExpression(expressionType_1.ExpressionType.Accessor, new constant_1.Constant(child.IDENTIFIER().text)); const evalFun = this.visit(child.expression()); result.push(evalParam); result.push(evalFun); } else if (child instanceof ep.ExpressionContext) { result.push(this.visit(child)); } } return result; } trimExpression(expression) { let result = expression.trim(); if (result.startsWith('$')) { result = result.substr(1); } result = result.trim(); if (result.startsWith('{') && result.endsWith('}')) { result = result.substr(1, result.length - 2); } return result.trim(); } evalEscape(text) { const validCharactersDict = { '\\r': '\r', '\\n': '\n', '\\t': '\t', '\\\\': '\\', }; return text.replace(this.escapeRegex, (sub) => { if (sub in validCharactersDict) { return validCharactersDict[sub]; } else { return sub; } }); } }; this.EvaluatorLookup = lookup || expression_1.Expression.lookup; } /** * @protected * Parse the expression to ANTLR lexer and parser. * @param expression The input string expression. * @returns A ParseTree. */ static antlrParse(expression) { if (ExpressionParser.expressionDict.has({ key: expression })) { return ExpressionParser.expressionDict.get({ key: expression }); } const inputStream = new antlr4ts_1.ANTLRInputStream(expression); const lexer = new generated_1.ExpressionAntlrLexer(inputStream); lexer.removeErrorListeners(); const tokenStream = new antlr4ts_1.CommonTokenStream(lexer); const parser = new generated_1.ExpressionAntlrParser(tokenStream); parser.removeErrorListeners(); parser.addErrorListener(parseErrorListener_1.ParseErrorListener.Instance); parser.buildParseTree = true; let expressionContext; const file = parser.file(); if (file !== undefined) { expressionContext = file.expression(); } ExpressionParser.expressionDict.set({ key: expression }, expressionContext); return expressionContext; } /** * Parse the input into an expression. * * @param expression Expression to parse. * @returns Expression tree. */ parse(expression) { if (expression == null || expression === '') { return new constant_1.Constant(''); } else { return new this.ExpressionTransformer(this.EvaluatorLookup).transform(ExpressionParser.antlrParse(expression)); } } } exports.ExpressionParser = ExpressionParser; ExpressionParser.expressionDict = new WeakMap(); //# sourceMappingURL=expressionParser.js.map