UNPKG

@airtasker/form-schema-compiler

Version:
342 lines (272 loc) 9.94 kB
"use strict"; exports.__esModule = true; var _find = require("lodash/find"); var _find2 = _interopRequireDefault(_find); var _const = require("./../const"); var _utils = require("./utils"); var utils = _interopRequireWildcard(_utils); var _const2 = require("../const"); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj["default"] = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var defaultShouldEnd = function defaultShouldEnd(inputStream) { return inputStream.eof(); }; /** * transpiling expression string input to an expression token stream * e.g "a" to {type: identifier, name: 'a'} * more example see tests * @param {{next, peek, croak}}: inputStream * @param {Function}: shouldEnd * @param {{eof}}: shouldEnd * @returns {{next: (function(): Object), peek: (function(): Object), eof: (function(): boolean), croak: (function(*)), position: (function(): {pos: number, line: number, col: number})}} */ var createExpressionTokenStream = function createExpressionTokenStream(inputStream) { var shouldEnd = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultShouldEnd; var errors = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : { eof: "Not expect input ended at time" }; var isStringTemplateMode = false; var readNumber = function readNumber() { var hasDot = false; var number = utils.readWhile(inputStream, function (ch) { if (ch === ".") { if (hasDot) { inputStream.croak("Unexpected token:. , double dot in a number"); } hasDot = true; return true; } return utils.isDigit(ch); }); return { type: _const2.TYPES.Numeric, value: Number(number) }; }; /** * readIdentifier * @returns {*} */ function readIdentifier() { // read until char is not matching identifier rule var value = utils.readWhile(inputStream, utils.isId); if (utils.isBoolean(value)) { // if identifier is 'true' or 'false' then it's a boolean return { type: _const2.TYPES.Boolean, value: value === _const2.BOOLEANS[1] }; } if (utils.isOperatorString(value)) { // if identifier is 'is' 'isnt' 'not' 'match' then it's an operator return { type: _const2.TYPES.Operator, value: value }; } if (utils.isKeyword(value)) { return { type: _const2.TYPES.Keyword, value: value }; } if (utils.isNull(value)) { // if identifier is 'null' then it's a null return { type: _const2.TYPES.Null, value: null }; } return { type: _const2.TYPES.Identifier, name: value }; } var validFlagChars = "gimuy"; var readRegexp = function readRegexp() { inputStream.next(); // read regexp until see tne regexp ending char (/) var pattern = utils.readWhileWithEscaped(inputStream, function (ch) { return ch === "/"; }); var flags = ""; while (!inputStream.eof()) { var ch = inputStream.peek(); if (validFlagChars.includes(ch) && !flags.includes(ch)) { // only add valid flags and not allow duplication flags += ch; inputStream.next(); } else if (utils.isWhitespace(ch) || utils.isPunctuation(ch)) { // end of regexp break; } else { inputStream.croak("Uncaught SyntaxError: Invalid regular expression flags: \"".concat(flags).concat(ch, "\"")); } } return { type: _const2.TYPES.RegExp, pattern: pattern, flags: flags }; }; var readString = function readString(ch) { inputStream.next(); var ast = { type: _const2.TYPES.String, value: utils.readEscaped(inputStream, function (c) { return c === ch; }) }; inputStream.next(); return ast; }; var stringTemplateExpressionSteam = null; var initStringTemplateMode = function initStringTemplateMode() { stringTemplateExpressionSteam = null; isStringTemplateMode = true; }; var readTemplateLiteral = function readTemplateLiteral() { var ch = inputStream.peek(); if (inputStream.eof()) { inputStream.croak("String template does not end"); } if (stringTemplateExpressionSteam) { if (stringTemplateExpressionSteam.eof()) { stringTemplateExpressionSteam = null; return { type: _const2.TYPES.Punctuation, value: inputStream.next() }; } return stringTemplateExpressionSteam.next(); } if (utils.isBackQuote(ch)) { isStringTemplateMode = false; return { type: _const2.TYPES.Punctuation, value: inputStream.next() }; } if (utils.isBraceStart(ch)) { var ast = { type: _const2.TYPES.Punctuation, value: inputStream.next() }; stringTemplateExpressionSteam = createExpressionTokenStream(inputStream, function (stream, c) { return utils.isBraceEnd(c); }, { eof: 'String Template expression missing a "}"' }); if (stringTemplateExpressionSteam.eof()) { inputStream.croak("Unexpected empty expression in string template e.g {}"); } return ast; } // must be string return { type: _const2.TYPES.String, value: utils.readEscaped(inputStream, function (c) { return utils.isBraceStart(c) || utils.isBackQuote(c); }) }; }; var readOperator = function readOperator() { var value = inputStream.next(); if (inputStream.peek() === "=") { value += inputStream.next(); } return { type: _const2.TYPES.Operator, value: value }; }; var skipComment = function skipComment() { inputStream.next(); utils.readWhile(inputStream, function (ch) { return ch !== "\n" && ch !== "#"; }); inputStream.next(); }; // eslint-disable-next-line no-use-before-define var _utils$createPeekAndN = utils.createPeekAndNext(readNext, 1), peek = _utils$createPeekAndN.peek, next = _utils$createPeekAndN.next, queue = _utils$createPeekAndN.queue; var regexpLeadingTokens = [{ type: _const2.TYPES.Punctuation, value: _const.PUNCTUATIONS.Parentheses[0] }, { type: _const2.TYPES.Punctuation, value: _const.PUNCTUATIONS.Separator }, { type: _const2.TYPES.Operator, value: _const.OPERATORS.Match }]; // check previous token if read / // if previous token is number or string or identifier it's must be division instead of a start of regex var canBeRegexp = function canBeRegexp() { return queue.length === 0 || (0, _find2["default"])(regexpLeadingTokens, queue[0]) !== undefined; }; // eslint-disable-next-line consistent-return function readNext() { if (isStringTemplateMode) { return readTemplateLiteral(); } // skip the white space if it is not in template literal utils.readWhile(inputStream, utils.isWhitespace); var ch = inputStream.peek(); if (shouldEnd(inputStream, ch)) { return null; } if (ch === "#") { skipComment(); return readNext(); } if (utils.isStringStart(ch)) { return readString(ch); } if (utils.isDigit(ch)) { return readNumber(); } if (utils.isIdStart(ch)) { return readIdentifier(); } if (utils.isRegexpStart(ch) && canBeRegexp()) { return readRegexp(); } if (utils.isOperatorStart(ch)) { // when read a char is > or < means this possibly a >= or <= return readOperator(); } if (utils.isBackQuote(ch)) { initStringTemplateMode(); return { type: _const2.TYPES.Punctuation, value: inputStream.next() }; } if (utils.isPunctuation(ch)) { return { type: _const2.TYPES.Punctuation, value: inputStream.next() }; } if (utils.isOperatorChar(ch)) { return { type: _const2.TYPES.Operator, value: inputStream.next() }; } if (inputStream.eof()) { inputStream.croak(errors.eof); } else { inputStream.croak("Can't handle character: \"".concat(ch, "\"")); } } var eof = function eof() { return peek() === null; }; return _objectSpread({}, inputStream, { next: next, peek: peek, eof: eof }); }; exports["default"] = createExpressionTokenStream;