@airtasker/form-schema-compiler
Version:
a form schema compiler
342 lines (272 loc) • 9.94 kB
JavaScript
;
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;