@airtasker/form-schema-compiler
Advanced tools
Comparing version 0.1.1 to 0.1.2
@@ -6,2 +6,4 @@ "use strict"; | ||
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); | ||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
@@ -153,3 +155,3 @@ | ||
if (error.kind === 'ErrorPathTrace') { | ||
if (error.kind === "ErrorPathTrace") { | ||
_this.path = key + "." + error.path; | ||
@@ -161,5 +163,4 @@ _this.originMessage = error.originMessage; | ||
} | ||
_this.kind = 'ErrorPathTrace'; | ||
_this.kind = "ErrorPathTrace"; | ||
_this.message = "Found an error in " + _this.path + ". Error details: " + _this.originMessage; | ||
return _this; | ||
@@ -171,3 +172,47 @@ } | ||
var tripeOutFromProgramInPropertyBinding = function tripeOutFromProgramInPropertyBinding(ast) { | ||
if (ast.type !== _const.TYPES.PropertyBinding || ast.value.type !== _const.TYPES.Program) { | ||
return ast; | ||
} | ||
return _extends({}, ast, { | ||
value: ast.value.body[0] | ||
}); | ||
}; | ||
/** | ||
* compile PropertyBindingValue to ast expression | ||
* @param {*} options | ||
* @param {*} value | ||
*/ | ||
var compilePropertyBindingValue = function compilePropertyBindingValue(options, value) { | ||
if (Array.isArray(value)) { | ||
return { | ||
type: _const.TYPES.ArrayExpression, | ||
elements: value.map(function (v) { | ||
return tripeOutFromProgramInPropertyBinding(compilePropertyBindingValue(options, v)); | ||
}) | ||
}; | ||
} else if ((typeof value === "undefined" ? "undefined" : _typeof(value)) === "object") { | ||
return { | ||
type: _const.TYPES.ObjectExpression, | ||
properties: Object.entries(compileProps(value, options)).map(function (_ref) { | ||
var _ref2 = _slicedToArray(_ref, 2), | ||
key = _ref2[0], | ||
value = _ref2[1]; | ||
return { | ||
key: toValueObject(key), | ||
value: tripeOutFromProgramInPropertyBinding(value) | ||
}; | ||
}) | ||
}; | ||
} else if (typeof value === "string") { | ||
return (0, _parsers.parseExpressionString)(value); | ||
} | ||
throw new Error("Unsupported type for property binding " + (typeof value === "undefined" ? "undefined" : _typeof(value))); | ||
}; | ||
/** | ||
* compile value to ast expression, | ||
@@ -179,4 +224,2 @@ * examples see expression parsers and tests | ||
*/ | ||
var compileValue = (0, _curry2.default)(function (options, value, key) { | ||
@@ -188,16 +231,11 @@ try { | ||
type: _const.ANNOTATION_TYPES.EventBinding, | ||
value: (0, _parsers.parseExpressionString)(value) | ||
value: (0, _parsers.parseBlockExpressionString)(value) | ||
}; | ||
case _const.ANNOTATION_TYPES.PropertyBinding: | ||
if ((typeof value === "undefined" ? "undefined" : _typeof(value)) === "object") { | ||
return { | ||
type: _const.ANNOTATION_TYPES.PropertyBinding, | ||
nested: true, | ||
value: compileProps(value, options) | ||
}; | ||
} | ||
return { | ||
type: _const.ANNOTATION_TYPES.PropertyBinding, | ||
nested: false, | ||
value: (0, _parsers.parseExpressionString)(value) | ||
value: { | ||
type: _const.TYPES.Program, | ||
body: [compilePropertyBindingValue(options, value, key)] | ||
} | ||
}; | ||
@@ -255,7 +293,7 @@ case _const.ANNOTATION_TYPES.TwoWayBinding: | ||
*/ | ||
var compileComponent = function compileComponent(_ref) { | ||
var compileComponent = function compileComponent(_ref3) { | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var type = _ref.type, | ||
props = _objectWithoutProperties(_ref, ["type"]); | ||
var type = _ref3.type, | ||
props = _objectWithoutProperties(_ref3, ["type"]); | ||
@@ -291,5 +329,5 @@ if (!type) { | ||
var compile = function compile(_ref2, options) { | ||
var schemaVersion = _ref2.schemaVersion, | ||
rest = _objectWithoutProperties(_ref2, ["schemaVersion"]); | ||
var compile = function compile(_ref4, options) { | ||
var schemaVersion = _ref4.schemaVersion, | ||
rest = _objectWithoutProperties(_ref4, ["schemaVersion"]); | ||
@@ -296,0 +334,0 @@ if (!schemaVersion || !(0, _utils.isVersionCompatible)(schemaVersion)) { |
@@ -12,55 +12,37 @@ "use strict"; | ||
var TYPES = exports.TYPES = { | ||
Numeric: "Numeric", | ||
String: "String", | ||
Boolean: "Boolean", | ||
Object: "Object", | ||
Array: "Array", | ||
Null: "Null", | ||
RegExp: "RegExp", | ||
Identifier: "Identifier", | ||
Keyword: 'Keyword', | ||
ArrayExpression: "ArrayExpression", | ||
AssignExpression: "AssignExpression", | ||
ObjectExpression: "ObjectExpression", | ||
ObjectProperty: "ObjectProperty", | ||
ArrayExpression: "ArrayExpression", | ||
BinaryExpression: "BinaryExpression", | ||
UnaryExpression: "UnaryExpression", | ||
BlockStatement: "BlockStatement", | ||
Boolean: "Boolean", | ||
CallExpression: "CallExpression", | ||
TemplateLiteral: "TemplateLiteral", | ||
Components: "Components", | ||
ConditionalExpression: "ConditionalExpression", | ||
EventBinding: "EventBinding", | ||
Identifier: "Identifier", | ||
IfStatement: "IfStatement", | ||
Keyword: "Keyword", | ||
MemberExpression: "MemberExpression", | ||
IfStatement: "IfStatement", | ||
Components: "Components", | ||
Null: "Null", | ||
Numeric: "Numeric", | ||
Object: "Object", | ||
ObjectExpression: "ObjectExpression", | ||
Operator: "Operator", | ||
Program: "Program", | ||
PropertyBinding: "PropertyBinding", | ||
Punctuation: "Punctuation", | ||
Raw: "Raw", | ||
Program: 'Program', | ||
BlockStatement: 'BlockStatement', | ||
PropertyBinding: 'PropertyBinding', | ||
EventBinding: 'EventBinding' | ||
RegExp: "RegExp", | ||
String: "String", | ||
TemplateLiteral: "TemplateLiteral", | ||
UnaryExpression: "UnaryExpression" | ||
}; | ||
/** | ||
those const not used in the app, leave here for reference | ||
var PRIMITIVES = exports.PRIMITIVES = [TYPES.Numeric, TYPES.String, TYPES.Boolean, TYPES.Null]; | ||
export const PRIMITIVES = [ | ||
TYPES.Numeric, | ||
TYPES.String, | ||
TYPES.Boolean, | ||
TYPES.Null | ||
]; | ||
var OBJECTS = exports.OBJECTS = [TYPES.RegExp, TYPES.Identifier, TYPES.Component]; | ||
export const OBJECTS = [TYPES.RegExp, TYPES.Identifier, TYPES.Component]; | ||
var EXPRESSIONS = exports.EXPRESSIONS = [TYPES.ObjectExpression, TYPES.ArrayExpression, TYPES.BinaryExpression, TYPES.AssignExpression, TYPES.CallExpression, TYPES.UnaryExpression, TYPES.MemberExpression, TYPES.ConditionalExpression, TYPES.TemplateLiteral]; | ||
export const EXPRESSIONS = [ | ||
TYPES.ObjectExpression, | ||
TYPES.ArrayExpression, | ||
TYPES.BinaryExpression, | ||
TYPES.AssignExpression, | ||
TYPES.CallExpression, | ||
TYPES.UnaryExpression, | ||
TYPES.TemplateLiteral, | ||
]; | ||
*/ | ||
var OPERATORS = exports.OPERATORS = { | ||
@@ -89,5 +71,5 @@ Assign: "=", | ||
var IF_KEYWORDS = exports.IF_KEYWORDS = { | ||
If: 'if', | ||
Else: 'else', | ||
Then: 'then' | ||
If: "if", | ||
Else: "else", | ||
Then: "then" | ||
}; | ||
@@ -94,0 +76,0 @@ var KEYWORDS = exports.KEYWORDS = [].concat(BOOLEANS, _toConsumableArray(Object.values(IF_KEYWORDS))); |
@@ -5,3 +5,3 @@ "use strict"; | ||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
@@ -12,14 +12,6 @@ var _TypeHandlers; | ||
var _evaluateIdentifier = require("./evaluateIdentifier"); | ||
var _evaluateArrayExpression = require("./evaluateArrayExpression"); | ||
var _evaluateIdentifier2 = _interopRequireDefault(_evaluateIdentifier); | ||
var _evaluateArrayExpression2 = _interopRequireDefault(_evaluateArrayExpression); | ||
var _evaluateProgram = require("./evaluateProgram"); | ||
var _evaluateProgram2 = _interopRequireDefault(_evaluateProgram); | ||
var _evaluateUnaryExpression = require("./evaluateUnaryExpression"); | ||
var _evaluateUnaryExpression2 = _interopRequireDefault(_evaluateUnaryExpression); | ||
var _evaluateAssignExpression = require("./evaluateAssignExpression"); | ||
@@ -37,2 +29,18 @@ | ||
var _evaluateConditionalExpression = require("./evaluateConditionalExpression"); | ||
var _evaluateConditionalExpression2 = _interopRequireDefault(_evaluateConditionalExpression); | ||
var _evaluateEventBindingExpression = require("./evaluateEventBindingExpression"); | ||
var _evaluateEventBindingExpression2 = _interopRequireDefault(_evaluateEventBindingExpression); | ||
var _evaluateIdentifier = require("./evaluateIdentifier"); | ||
var _evaluateIdentifier2 = _interopRequireDefault(_evaluateIdentifier); | ||
var _evaluateIfStatement = require("./evaluateIfStatement"); | ||
var _evaluateIfStatement2 = _interopRequireDefault(_evaluateIfStatement); | ||
var _evaluateMemberObjectExpression = require("./evaluateMemberObjectExpression"); | ||
@@ -46,6 +54,10 @@ | ||
var _evaluateArrayExpression = require("./evaluateArrayExpression"); | ||
var _evaluateProgram = require("./evaluateProgram"); | ||
var _evaluateArrayExpression2 = _interopRequireDefault(_evaluateArrayExpression); | ||
var _evaluateProgram2 = _interopRequireDefault(_evaluateProgram); | ||
var _evaluatePropertyBindingExpression = require("./evaluatePropertyBindingExpression"); | ||
var _evaluatePropertyBindingExpression2 = _interopRequireDefault(_evaluatePropertyBindingExpression); | ||
var _evaluateTemplateLiteral = require("./evaluateTemplateLiteral"); | ||
@@ -55,14 +67,6 @@ | ||
var _evaluateIfStatement = require("./evaluateIfStatement"); | ||
var _evaluateUnaryExpression = require("./evaluateUnaryExpression"); | ||
var _evaluateIfStatement2 = _interopRequireDefault(_evaluateIfStatement); | ||
var _evaluateUnaryExpression2 = _interopRequireDefault(_evaluateUnaryExpression); | ||
var _evaluateEventBindingExpression = require("./evaluateEventBindingExpression"); | ||
var _evaluateEventBindingExpression2 = _interopRequireDefault(_evaluateEventBindingExpression); | ||
var _evaluatePropertyBindingExpression = require("./evaluatePropertyBindingExpression"); | ||
var _evaluatePropertyBindingExpression2 = _interopRequireDefault(_evaluatePropertyBindingExpression); | ||
var _utils = require("../utils"); | ||
@@ -84,12 +88,16 @@ | ||
}; | ||
var toRegExp = function toRegExp(_ref2) { | ||
var pattern = _ref2.pattern, | ||
flags = _ref2.flags; | ||
return new RegExp(pattern, flags); | ||
var getStringifyValue = function getStringifyValue(_ref2) { | ||
var value = _ref2.value; | ||
return JSON.stringify(value); | ||
}; | ||
var evaluateComponents = function evaluateComponents(expression, env) { | ||
return env.evaluateComponents(expression); | ||
var toRegExp = function toRegExp(_ref3) { | ||
var pattern = _ref3.pattern, | ||
flags = _ref3.flags; | ||
return "new RegExp(" + JSON.stringify(pattern) + ", " + JSON.stringify(flags) + ")"; | ||
}; | ||
var evaluateComponents = function evaluateComponents(expression) { | ||
return "return $$env.evaluateComponents(" + JSON.stringify(expression) + ")"; | ||
}; | ||
var TypeHandlers = (_TypeHandlers = {}, _defineProperty(_TypeHandlers, _const.TYPES.Numeric, getValue), _defineProperty(_TypeHandlers, _const.TYPES.String, getValue), _defineProperty(_TypeHandlers, _const.TYPES.Boolean, getValue), _defineProperty(_TypeHandlers, _const.TYPES.Null, getValue), _defineProperty(_TypeHandlers, _const.TYPES.Raw, getValue), _defineProperty(_TypeHandlers, _const.TYPES.RegExp, toRegExp), _defineProperty(_TypeHandlers, _const.TYPES.Identifier, _evaluateIdentifier2.default), _defineProperty(_TypeHandlers, _const.TYPES.UnaryExpression, _evaluateUnaryExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.AssignExpression, _evaluateAssignExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.BinaryExpression, _evaluateBinaryExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.CallExpression, _evaluateCallExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.Components, evaluateComponents), _defineProperty(_TypeHandlers, _const.TYPES.MemberExpression, _evaluateMemberObjectExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.ObjectExpression, _evaluateObjectExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.ArrayExpression, _evaluateArrayExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.TemplateLiteral, _evaluateTemplateLiteral2.default), _defineProperty(_TypeHandlers, _const.TYPES.Program, _evaluateProgram2.default), _defineProperty(_TypeHandlers, _const.TYPES.BlockStatement, _evaluateProgram2.default), _defineProperty(_TypeHandlers, _const.TYPES.IfStatement, _evaluateIfStatement2.default), _defineProperty(_TypeHandlers, _const.TYPES.EventBinding, _evaluateEventBindingExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.PropertyBinding, _evaluatePropertyBindingExpression2.default), _TypeHandlers); | ||
var TypeHandlers = (_TypeHandlers = {}, _defineProperty(_TypeHandlers, _const.TYPES.ArrayExpression, _evaluateArrayExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.AssignExpression, _evaluateAssignExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.BinaryExpression, _evaluateBinaryExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.BlockStatement, _evaluateProgram2.default), _defineProperty(_TypeHandlers, _const.TYPES.Boolean, getValue), _defineProperty(_TypeHandlers, _const.TYPES.CallExpression, _evaluateCallExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.Components, evaluateComponents), _defineProperty(_TypeHandlers, _const.TYPES.ConditionalExpression, _evaluateConditionalExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.EventBinding, _evaluateEventBindingExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.Identifier, _evaluateIdentifier2.default), _defineProperty(_TypeHandlers, _const.TYPES.IfStatement, _evaluateIfStatement2.default), _defineProperty(_TypeHandlers, _const.TYPES.MemberExpression, _evaluateMemberObjectExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.Null, getValue), _defineProperty(_TypeHandlers, _const.TYPES.Numeric, getValue), _defineProperty(_TypeHandlers, _const.TYPES.ObjectExpression, _evaluateObjectExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.Program, _evaluateProgram2.default), _defineProperty(_TypeHandlers, _const.TYPES.PropertyBinding, _evaluatePropertyBindingExpression2.default), _defineProperty(_TypeHandlers, _const.TYPES.Raw, getStringifyValue), _defineProperty(_TypeHandlers, _const.TYPES.RegExp, toRegExp), _defineProperty(_TypeHandlers, _const.TYPES.String, getStringifyValue), _defineProperty(_TypeHandlers, _const.TYPES.TemplateLiteral, _evaluateTemplateLiteral2.default), _defineProperty(_TypeHandlers, _const.TYPES.UnaryExpression, _evaluateUnaryExpression2.default), _TypeHandlers); | ||
@@ -99,15 +107,64 @@ /** | ||
* @param {*} ast | ||
* @param {Environment} environment | ||
* @returns {*} | ||
*/ | ||
var evaluate = function evaluate(_ref3, environment) { | ||
var type = _ref3.type, | ||
ast = _objectWithoutProperties(_ref3, ["type"]); | ||
var evaluate = function evaluate(ast) { | ||
var type = ast.type, | ||
rest = _objectWithoutProperties(ast, ["type"]); | ||
if ((0, _utils.hasKey)(TypeHandlers, type)) { | ||
return TypeHandlers[type](ast, environment, evaluate); | ||
return TypeHandlers[type](rest, evaluate); | ||
} | ||
throw new Error("Wrong type " + type + ", " + JSON.stringify(_extends({ type: type }, ast))); | ||
throw new Error("Wrong type " + type + ", " + JSON.stringify(ast)); | ||
}; | ||
var match = function match(left, right) { | ||
var regex = right instanceof RegExp ? right : new RegExp(right); | ||
if (left == null) { | ||
return regex.test(""); | ||
} | ||
return regex.test(left); | ||
}; | ||
var get = function get(obj, key) { | ||
if (obj == null) { | ||
return null; | ||
} | ||
var value = obj[key]; | ||
if (value == null) { | ||
return null; | ||
} | ||
return value; | ||
}; | ||
var toString = function toString(obj) { | ||
switch (typeof obj === "undefined" ? "undefined" : _typeof(obj)) { | ||
case "string": | ||
return obj; | ||
case "number": | ||
if (Number.isNaN(obj)) { | ||
return ""; | ||
} | ||
return obj.toString(); | ||
default: | ||
// all other types will be convert to '' for convenience | ||
return ""; | ||
} | ||
}; | ||
var cache = new WeakMap(); | ||
var getOrCreate = function getOrCreate(ast) { | ||
var _ref4; | ||
var cachedFn = cache.get(ast); | ||
if (cachedFn) { | ||
return cachedFn; | ||
} | ||
var context = [match, get, toString]; | ||
var newFn = (_ref4 = new Function("$$match", "$$get", "$$toString", "$$env", evaluate(ast))).bind.apply(_ref4, [null].concat(context)); | ||
cache.set(ast, newFn); | ||
return newFn; | ||
}; | ||
/** | ||
@@ -123,5 +180,6 @@ * evaluateWithEnvironmentCheck | ||
} | ||
return evaluate(ast, environment); | ||
return getOrCreate(ast)(environment); | ||
}; | ||
exports.default = evaluateWithEnvironmentCheck; |
"use strict"; | ||
exports.__esModule = true; | ||
var evaluateArrayExpression = function evaluateArrayExpression(_ref, env, evaluate) { | ||
var _path = require("path"); | ||
var evaluateArrayExpression = function evaluateArrayExpression(_ref, evaluate) { | ||
var elements = _ref.elements; | ||
return elements.map(function (element) { | ||
return evaluate(element, env); | ||
}); | ||
return "[" + elements.map(function (element) { | ||
return evaluate(element); | ||
}).join(",") + "]"; | ||
}; | ||
exports.default = evaluateArrayExpression; |
"use strict"; | ||
exports.__esModule = true; | ||
var evaluateAssignExpression = function evaluateAssignExpression(_ref, env, evaluate) { | ||
var left = _ref.left, | ||
var evaluateAssignExpression = function evaluateAssignExpression(_ref, evaluate) { | ||
var name = _ref.left.name, | ||
right = _ref.right; | ||
return env.set(left.name, evaluate(right, env)); | ||
return "$$env.set(\"" + name + "\", " + evaluate(right) + ")"; | ||
}; | ||
exports.default = evaluateAssignExpression; |
"use strict"; | ||
exports.__esModule = true; | ||
exports.evaluateNonBranchableBinaryExpression = undefined; | ||
var _OpMapping; | ||
var _const = require("../const"); | ||
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 handleRegex = function handleRegex(left, operator, right) { | ||
@@ -16,3 +19,5 @@ var regex = right instanceof RegExp ? right : new RegExp(right); | ||
var evaluateNonBranchableBinaryExpression = exports.evaluateNonBranchableBinaryExpression = function evaluateNonBranchableBinaryExpression(_ref) { | ||
var OpMapping = (_OpMapping = {}, _defineProperty(_OpMapping, _const.OPERATORS.Or, "||"), _defineProperty(_OpMapping, _const.OPERATORS.And, "&&"), _defineProperty(_OpMapping, _const.OPERATORS.LessThan, "<"), _defineProperty(_OpMapping, _const.OPERATORS.LessThanOrEqualTo, "<="), _defineProperty(_OpMapping, _const.OPERATORS.GreaterThan, ">"), _defineProperty(_OpMapping, _const.OPERATORS.GreaterThanOrEqualTo, ">="), _defineProperty(_OpMapping, _const.OPERATORS.EqualTo, "==="), _defineProperty(_OpMapping, _const.OPERATORS.NotEqualTo, "!=="), _defineProperty(_OpMapping, _const.OPERATORS.Add, "+"), _defineProperty(_OpMapping, _const.OPERATORS.Subtract, "-"), _defineProperty(_OpMapping, _const.OPERATORS.Multiply, "*"), _defineProperty(_OpMapping, _const.OPERATORS.Divide, "/"), _defineProperty(_OpMapping, _const.OPERATORS.Remainder, "%"), _OpMapping); | ||
var evaluateBinaryExpression = function evaluateBinaryExpression(_ref, evaluate) { | ||
var operator = _ref.operator, | ||
@@ -22,54 +27,15 @@ left = _ref.left, | ||
switch (operator) { | ||
case _const.OPERATORS.LessThan: | ||
return left < right; | ||
case _const.OPERATORS.LessThanOrEqualTo: | ||
return left <= right; | ||
case _const.OPERATORS.GreaterThan: | ||
return left > right; | ||
case _const.OPERATORS.GreaterThanOrEqualTo: | ||
return left >= right; | ||
case _const.OPERATORS.EqualTo: | ||
return left === right; | ||
case _const.OPERATORS.NotEqualTo: | ||
return left !== right; | ||
case _const.OPERATORS.Match: | ||
return handleRegex(left, operator, right); | ||
case _const.OPERATORS.Add: | ||
return left + right; | ||
case _const.OPERATORS.Subtract: | ||
return left - right; | ||
case _const.OPERATORS.Multiply: | ||
return left * right; | ||
case _const.OPERATORS.Divide: | ||
return left / right; | ||
case _const.OPERATORS.Remainder: | ||
return left % right; | ||
default: | ||
throw new Error("unknown binary expression: " + { operator: operator, left: left, right: right }); | ||
var evaluatedLeft = evaluate(left); | ||
var evaluatedRight = evaluate(right); | ||
if (operator in OpMapping) { | ||
return "(" + evaluatedLeft + OpMapping[operator] + evaluatedRight + ")"; | ||
} | ||
}; | ||
if (operator === _const.OPERATORS.Match) { | ||
return "$$match(" + evaluatedLeft + "," + evaluatedRight + ")"; | ||
} | ||
var evaluateBinaryExpression = function evaluateBinaryExpression(_ref2, env, evaluate) { | ||
var operator = _ref2.operator, | ||
left = _ref2.left, | ||
right = _ref2.right; | ||
switch (operator) { | ||
// specially handling for OR and AND | ||
case _const.OPERATORS.Or: | ||
// if left is true will ignore right | ||
return evaluate(left, env) || evaluate(right, env); | ||
case _const.OPERATORS.And: | ||
// if left is false will ignore right | ||
return evaluate(left, env) && evaluate(right, env); | ||
default: | ||
return evaluateNonBranchableBinaryExpression({ | ||
operator: operator, | ||
left: evaluate(left, env), | ||
right: evaluate(right, env) | ||
}); | ||
} | ||
throw new Error("unknown binary expression: " + { operator: operator, left: left, right: right }); | ||
}; | ||
exports.default = evaluateBinaryExpression; |
"use strict"; | ||
exports.__esModule = true; | ||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } | ||
var evaluateCallExpression = function evaluateCallExpression(ast, env, evaluate) { | ||
var callee = evaluate(ast.callee, env); | ||
if (typeof callee !== "function") { | ||
throw new Error("Wrong call expression, callee have to be function " + JSON.stringify(ast)); | ||
} | ||
var evaluateCallExpression = function evaluateCallExpression(ast, evaluate) { | ||
var callee = evaluate(ast.callee); | ||
var args = ast.arguments.map(function (arg) { | ||
return evaluate(arg, env); | ||
return evaluate(arg); | ||
}); | ||
return callee.apply(undefined, _toConsumableArray(args)); | ||
return callee + "(" + args.join(",") + ")"; | ||
}; | ||
exports.default = evaluateCallExpression; |
"use strict"; | ||
exports.__esModule = true; | ||
var evaluateEventBindingExpression = function evaluateEventBindingExpression(_ref, env, evaluate) { | ||
var evaluateEventBindingExpression = function evaluateEventBindingExpression(_ref, evaluate) { | ||
var value = _ref.value; | ||
return evaluate(value, env); | ||
return evaluate(value); | ||
}; | ||
exports.default = evaluateEventBindingExpression; |
"use strict"; | ||
exports.__esModule = true; | ||
var evaluateIdentifier = function evaluateIdentifier(_ref, env) { | ||
var evaluateIdentifier = function evaluateIdentifier(_ref) { | ||
var name = _ref.name; | ||
return env.get(name); | ||
return "$$env.get(\"" + name + "\")"; | ||
}; | ||
exports.default = evaluateIdentifier; |
"use strict"; | ||
exports.__esModule = true; | ||
var evaluateIfStatement = function evaluateIfStatement(_ref, env, evaluate) { | ||
var _const = require("../const"); | ||
var evaluateIfStatement = function evaluateIfStatement(_ref, evaluate) { | ||
var test = _ref.test, | ||
@@ -9,7 +12,8 @@ consequent = _ref.consequent, | ||
if (evaluate(test, env)) { | ||
return consequent ? evaluate(consequent, env) : null; | ||
} | ||
return alternate ? evaluate(alternate, env) : null; | ||
var evaluatedTest = evaluate(test); | ||
var evaluatedConsequent = consequent ? evaluate(consequent) : ""; | ||
var evaluatedAlternate = alternate ? evaluate(alternate) : ""; | ||
return "if(" + evaluatedTest + "){" + evaluatedConsequent + ";}else{" + evaluatedAlternate + ";}"; | ||
}; | ||
exports.default = evaluateIfStatement; |
"use strict"; | ||
exports.__esModule = true; | ||
var evaluateMemberObjectExpression = function evaluateMemberObjectExpression(ast, env, evaluate) { | ||
var object = evaluate(ast.object, env); | ||
var property = evaluate(ast.property, env); | ||
var evaluateMemberObjectExpression = function evaluateMemberObjectExpression(ast, evaluate) { | ||
var evaluatedObject = evaluate(ast.object); | ||
var evaluatedProperty = evaluate(ast.property); | ||
if (object == null) { | ||
return null; | ||
} | ||
return object[property]; | ||
return "$$get(" + evaluatedObject + "," + evaluatedProperty + ")"; | ||
}; | ||
exports.default = evaluateMemberObjectExpression; |
@@ -1,15 +0,14 @@ | ||
"use strict"; | ||
'use strict'; | ||
exports.__esModule = true; | ||
var evaluateObjectExpression = function evaluateObjectExpression(_ref, env, evaluate) { | ||
var evaluateObjectExpression = function evaluateObjectExpression(_ref, evaluate) { | ||
var properties = _ref.properties; | ||
return properties.reduce(function (result, _ref2) { | ||
return '({' + properties.map(function (_ref2) { | ||
var key = _ref2.key, | ||
value = _ref2.value; | ||
result[evaluate(key, env)] = evaluate(value, env); | ||
return result; | ||
}, {}); | ||
return '[' + evaluate(key) + ']:' + evaluate(value); | ||
}).join(',') + '})'; | ||
}; | ||
exports.default = evaluateObjectExpression; |
"use strict"; | ||
exports.__esModule = true; | ||
var _const = require("../const"); | ||
// evaluate both block and program statement | ||
var evaluateProgram = function evaluateProgram(_ref, env, evaluate) { | ||
var evaluateProgram = function evaluateProgram(_ref, evaluate) { | ||
var body = _ref.body; | ||
if (body.length === 0) { | ||
return null; | ||
return "return null;"; | ||
} | ||
var lstIndex = body.length - 1; | ||
var evaluatedBody = body.map(function (ast) { | ||
return evaluate(ast); | ||
}); | ||
var js = ""; | ||
for (var i = 0; i < lstIndex; i++) { | ||
evaluate(body[i], env); | ||
js += evaluatedBody[i] + ";"; | ||
} | ||
return evaluate(body[lstIndex], env); // always return the last value | ||
if (body[lstIndex].type === _const.TYPES.IfStatement) { | ||
js += "" + evaluatedBody[lstIndex]; | ||
} else { | ||
js += "return " + evaluatedBody[lstIndex] + ";"; | ||
} | ||
return js; | ||
}; | ||
exports.default = evaluateProgram; |
@@ -1,6 +0,6 @@ | ||
'use strict'; | ||
"use strict"; | ||
exports.__esModule = true; | ||
var _mapValues = require('lodash/mapValues'); | ||
var _mapValues = require("lodash/mapValues"); | ||
@@ -11,13 +11,8 @@ var _mapValues2 = _interopRequireDefault(_mapValues); | ||
var evaluatePropertyBindingExpression = function evaluatePropertyBindingExpression(_ref, env, evaluate) { | ||
var evaluatePropertyBindingExpression = function evaluatePropertyBindingExpression(_ref, evaluate) { | ||
var value = _ref.value; | ||
if ('type' in value) { | ||
return evaluate(value, env); | ||
} | ||
return (0, _mapValues2.default)(value, function (ast) { | ||
return evaluate(ast, env); | ||
}); | ||
return evaluate(value); | ||
}; | ||
exports.default = evaluatePropertyBindingExpression; |
"use strict"; | ||
exports.__esModule = true; | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
var toString = function toString(obj) { | ||
switch (typeof obj === "undefined" ? "undefined" : _typeof(obj)) { | ||
case "string": | ||
return obj; | ||
case "number": | ||
if (Number.isNaN(obj)) { | ||
return ""; | ||
} | ||
return obj.toString(); | ||
default: | ||
// all other types will be convert to '' for convenience | ||
return ""; | ||
} | ||
}; | ||
var evaluateTemplateLiteral = function evaluateTemplateLiteral(_ref, env, evaluate) { | ||
var evaluateTemplateLiteral = function evaluateTemplateLiteral(_ref, evaluate) { | ||
var expressions = _ref.expressions, | ||
quasis = _ref.quasis; | ||
return quasis.reduce(function (retVal, _ref2, i) { | ||
var value = _ref2.value; | ||
return retVal + value + toString(i < expressions.length && evaluate(expressions[i], env)); | ||
}, ""); | ||
var evaluatedExpr = expressions.map(function (expr) { | ||
return evaluate(expr); | ||
}); | ||
return quasis.reduce(function (retVal, quasi, i) { | ||
return i < expressions.length ? retVal + "+(" + evaluate(quasi) + ")+$$toString(" + evaluatedExpr[i] + ")" : retVal + "+(" + evaluate(quasi) + ")"; | ||
}, "''"); | ||
}; | ||
exports.default = evaluateTemplateLiteral; |
@@ -7,13 +7,14 @@ "use strict"; | ||
var evaluateUnaryExpression = function evaluateUnaryExpression(_ref, env, evaluate) { | ||
var evaluateUnaryExpression = function evaluateUnaryExpression(_ref, evaluate) { | ||
var operator = _ref.operator, | ||
argument = _ref.argument; | ||
var evaluated = evaluate(argument); | ||
switch (operator) { | ||
case _const.OPERATORS.Not: | ||
return !evaluate(argument, env); | ||
return "(!(" + evaluated + "))"; | ||
case _const.OPERATORS.Add: | ||
return +evaluate(argument, env); | ||
return "(+(" + evaluated + "))"; | ||
case _const.OPERATORS.Subtract: | ||
return -evaluate(argument, env); | ||
return "(-(" + evaluated + "))"; | ||
default: | ||
@@ -20,0 +21,0 @@ throw new Error("wrong UnaryExpression " + JSON.stringify({ operator: operator, argument: argument })); |
"use strict"; | ||
exports.__esModule = true; | ||
exports.parseTwoWayBindingString = exports.parseExpressionString = exports.parseTemplateString = undefined; | ||
exports.parseTemplateString = exports.parseExpressionString = exports.parseBlockExpressionString = undefined; | ||
var _flowRight = require("lodash/flowRight"); | ||
var _flowRight2 = _interopRequireDefault(_flowRight); | ||
var _curryRight = require("lodash/curryRight"); | ||
var _curryRight2 = _interopRequireDefault(_curryRight); | ||
var _parseExpressionTokenStream = require("./parseExpressionTokenStream"); | ||
@@ -12,6 +20,2 @@ | ||
var _flowRight = require("lodash/flowRight"); | ||
var _flowRight2 = _interopRequireDefault(_flowRight); | ||
var _const = require("../const"); | ||
@@ -21,11 +25,7 @@ | ||
var parse = (0, _flowRight2.default)(_parseExpressionTokenStream2.default, _tokenizers.createExpressionTokenStream, _tokenizers.createInputStream); | ||
/** | ||
* parse template string | ||
* @param templateString:string | ||
* parse a block of expression string | ||
* @param expressionString:string | ||
*/ | ||
var parseTemplateString = exports.parseTemplateString = function parseTemplateString(templateString) { | ||
return parse("`" + templateString + "`"); | ||
}; | ||
var parseBlockExpressionString = exports.parseBlockExpressionString = (0, _flowRight2.default)(_parseExpressionTokenStream2.default, _tokenizers.createExpressionTokenStream, _tokenizers.createInputStream); | ||
@@ -36,17 +36,10 @@ /** | ||
*/ | ||
var parseExpressionString = exports.parseExpressionString = parse; | ||
var parseExpressionString = exports.parseExpressionString = (0, _flowRight2.default)((0, _curryRight2.default)(_parseExpressionTokenStream2.default, 2)(false), _tokenizers.createExpressionTokenStream, _tokenizers.createInputStream); | ||
/** | ||
* parse two way binding string | ||
* Will throw error if TwoWayBinding is not a identifier. | ||
* @param expressionString | ||
* parse template string | ||
* @param templateString:string | ||
*/ | ||
var parseTwoWayBindingString = exports.parseTwoWayBindingString = function parseTwoWayBindingString(expressionString) { | ||
var ast = parse(expressionString); | ||
var id = ast.body[0]; | ||
if (id && id.type !== _const.TYPES.Identifier) { | ||
throw new Error("data binding type have to be Identifier instead of " + ast.type + "(" + expressionString + ")"); | ||
} | ||
return ast; | ||
var parseTemplateString = exports.parseTemplateString = function parseTemplateString(templateString) { | ||
return parseBlockExpressionString("`" + templateString + "`"); | ||
}; |
@@ -32,4 +32,7 @@ "use strict"; | ||
* @param tokenStream | ||
* @param isBlock default to true, pass as block of code if true | ||
*/ | ||
var parseExpressionTokenStream = function parseExpressionTokenStream(tokenStream) { | ||
var isBlock = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; | ||
var isKeyword = function isKeyword(keyword) { | ||
@@ -132,13 +135,22 @@ return utils.isKeyword(tokenStream.peek(), keyword); | ||
var token = isUnary(); | ||
if (token) { | ||
tokenStream.next(); | ||
return { | ||
type: _const.TYPES.UnaryExpression, | ||
operator: token.value, | ||
argument: maybeUnary(expr) | ||
}; | ||
if (!token) { | ||
return expr(); | ||
} | ||
return expr(); | ||
tokenStream.next(); | ||
var argument = maybeUnary(expr); | ||
if (!isSimpleExpr(argument)) { | ||
unexpected(token); | ||
} | ||
return { | ||
type: _const.TYPES.UnaryExpression, | ||
operator: token.value, | ||
argument: argument | ||
}; | ||
} | ||
function isSimpleExpr(_ref) { | ||
var type = _ref.type; | ||
return _const.EXPRESSIONS.includes(type) || _const.PRIMITIVES.includes(type) || _const.OBJECTS.includes(type); | ||
} | ||
/** | ||
@@ -154,21 +166,24 @@ * return binary expression if next token is an operator | ||
var token = isOperator(); | ||
if (token) { | ||
var rightOpPrec = _const.PRECEDENCE[token.value]; | ||
var isAssign = token.value === _const.OPERATORS.Assign; | ||
if (rightOpPrec > leftOpPrec) { | ||
if (isAssign && left.type !== _const.TYPES.Identifier) { | ||
tokenStream.croak("You can only assign to an identifier \"" + JSON.stringify(left) + "\""); | ||
} | ||
tokenStream.next(); | ||
var right = maybeBinary(parseAtom(), rightOpPrec); | ||
var binary = { | ||
type: isAssign ? _const.TYPES.AssignExpression : _const.TYPES.BinaryExpression, | ||
operator: token.value, | ||
left: left, | ||
right: right | ||
}; | ||
return maybeBinary(binary, leftOpPrec); | ||
} | ||
if (!isSimpleExpr(left) || !token) { | ||
return left; | ||
} | ||
return left; | ||
var rightOpPrec = _const.PRECEDENCE[token.value]; | ||
var isAssign = token.value === _const.OPERATORS.Assign; | ||
if (rightOpPrec <= leftOpPrec) { | ||
return left; | ||
} | ||
if (isAssign && left.type !== _const.TYPES.Identifier) { | ||
tokenStream.croak("You can only assign to an identifier \"" + JSON.stringify(left) + "\""); | ||
} | ||
tokenStream.next(); | ||
var right = maybeBinary(parseAtom(), rightOpPrec); | ||
var binary = { | ||
type: isAssign ? _const.TYPES.AssignExpression : _const.TYPES.BinaryExpression, | ||
operator: token.value, | ||
left: left, | ||
right: right | ||
}; | ||
return maybeBinary(binary, leftOpPrec); | ||
} | ||
@@ -284,6 +299,9 @@ | ||
* parse next expression | ||
* @param {Boolean} allowBlock | ||
* @returns {Expression} | ||
*/ | ||
function parseExpression() { | ||
return maybeBinary(parseAtom(), 0); | ||
var allowBlock = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; | ||
return maybeBinary(parseAtom(allowBlock), 0); | ||
} | ||
@@ -293,9 +311,10 @@ | ||
* parse single expression, could be a call expression, an unary expression, an identifier or an expression inside a parentheses | ||
* @param {Boolean} allowBlock | ||
* @returns {Expression} | ||
*/ | ||
function parseAtom() { | ||
var skipUnaryCheck = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; | ||
var allowBlock = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; | ||
return maybeUnary(function () { | ||
return maybeCallOrMember(parseSimpleAtom()); | ||
return maybeCallOrMember(parseSimpleAtom(allowBlock)); | ||
}); | ||
@@ -307,3 +326,3 @@ } | ||
while (!tokenStream.eof()) { | ||
body.push(parseExpression()); | ||
body.push(parseExpression(true)); | ||
if (tokenStream.eof()) { | ||
@@ -327,7 +346,13 @@ // skip semi colon check if tokenStream is end; | ||
_const.PUNCTUATIONS.SemiColon, // ; | ||
parseExpression) | ||
function () { | ||
return parseExpression(true); | ||
}) | ||
}; | ||
} | ||
function parseIfStatement() { | ||
/** | ||
* @param {Boolean} allowBlock | ||
*/ | ||
function parseIfStatement(allowBlock) { | ||
debugger; | ||
skipKeyword(_const.IF_KEYWORDS.If); | ||
@@ -338,9 +363,13 @@ var test = parseExpression(); | ||
var alternate = null; | ||
var isConditionalExpression = false; | ||
if (isKeyword(_const.IF_KEYWORDS.Then)) { | ||
skipKeyword(_const.IF_KEYWORDS.Then); | ||
if (isPunctuation(_const.PUNCTUATIONS.Braces[0])) { | ||
consequent = parseExpression(); | ||
isConditionalExpression = true; | ||
} else { | ||
if (allowBlock) { | ||
consequent = parseBlockStatement(); | ||
} else { | ||
consequent = parseExpression(); | ||
unexpected(); | ||
} | ||
@@ -351,6 +380,12 @@ } | ||
skipKeyword(_const.IF_KEYWORDS.Else); | ||
if (isPunctuation(_const.PUNCTUATIONS.Braces[0])) { | ||
alternate = parseBlockStatement(); | ||
if (isKeyword(_const.IF_KEYWORDS.If)) { | ||
alternate = parseExpression(allowBlock); | ||
} else if (isConditionalExpression) { | ||
alternate = parseExpression(); | ||
} else { | ||
alternate = parseExpression(); | ||
if (allowBlock) { | ||
alternate = parseBlockStatement(); | ||
} else { | ||
unexpected(); | ||
} | ||
} | ||
@@ -360,3 +395,3 @@ } | ||
return { | ||
type: _const.TYPES.IfStatement, | ||
type: isConditionalExpression ? _const.TYPES.ConditionalExpression : _const.TYPES.IfStatement, | ||
test: test, | ||
@@ -370,5 +405,8 @@ consequent: consequent, | ||
* parse a simple atom, e.g identifier, number, string, object, array, boolean, etc | ||
* @param {Boolean} allowBlock | ||
* @returns {Expression} | ||
*/ | ||
function parseSimpleAtom() { | ||
var allowBlock = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; | ||
if (isPunctuation(_const.PUNCTUATIONS.Parentheses[0])) { | ||
@@ -398,3 +436,3 @@ // if it reads parentheses, then will parse the expression inside the parentheses | ||
if (isKeyword(_const.IF_KEYWORDS.If)) { | ||
return parseIfStatement(); | ||
return parseIfStatement(allowBlock); | ||
} | ||
@@ -416,4 +454,4 @@ | ||
return parseProgram(); | ||
return isBlock ? parseProgram() : parseExpression(); | ||
}; /* eslint-disable no-use-before-define */ | ||
exports.default = parseExpressionTokenStream; |
{ | ||
"name": "@airtasker/form-schema-compiler", | ||
"version": "0.1.1", | ||
"version": "0.1.2", | ||
"description": "a form schema compiler", | ||
@@ -5,0 +5,0 @@ "main": "./lib/index.js", |
@@ -16,4 +16,4 @@ import curryRight from "lodash/curryRight"; | ||
parseExpressionString, | ||
parseTemplateString, | ||
parseTwoWayBindingString | ||
parseBlockExpressionString, | ||
parseTemplateString | ||
} from "./parsers"; | ||
@@ -113,3 +113,3 @@ import createTypeCompiler from "./typeCompiler"; | ||
super(); | ||
if (error.kind === 'ErrorPathTrace') { | ||
if (error.kind === "ErrorPathTrace") { | ||
this.path = `${key}.${error.path}`; | ||
@@ -121,9 +121,53 @@ this.originMessage = error.originMessage; | ||
} | ||
this.kind = 'ErrorPathTrace'; | ||
this.message = `Found an error in ${this.path}. Error details: ${this.originMessage}`; | ||
this.kind = "ErrorPathTrace"; | ||
this.message = `Found an error in ${this.path}. Error details: ${ | ||
this.originMessage | ||
}`; | ||
} | ||
} | ||
const tripeOutFromProgramInPropertyBinding = ast => { | ||
if (ast.type !== TYPES.PropertyBinding || ast.value.type !== TYPES.Program) { | ||
return ast; | ||
} | ||
return { | ||
...ast, | ||
value: ast.value.body[0] | ||
}; | ||
}; | ||
/** | ||
* compile PropertyBindingValue to ast expression | ||
* @param {*} options | ||
* @param {*} value | ||
*/ | ||
const compilePropertyBindingValue = (options, value) => { | ||
if (Array.isArray(value)) { | ||
return { | ||
type: TYPES.ArrayExpression, | ||
elements: value.map(v => | ||
tripeOutFromProgramInPropertyBinding( | ||
compilePropertyBindingValue(options, v) | ||
) | ||
) | ||
}; | ||
} else if (typeof value === "object") { | ||
return { | ||
type: TYPES.ObjectExpression, | ||
properties: Object.entries(compileProps(value, options)).map( | ||
([key, value]) => ({ | ||
key: toValueObject(key), | ||
value: tripeOutFromProgramInPropertyBinding(value) | ||
}) | ||
) | ||
}; | ||
} else if (typeof value === "string") { | ||
return parseExpressionString(value); | ||
} | ||
throw new Error(`Unsupported type for property binding ${typeof value}`); | ||
}; | ||
/** | ||
* compile value to ast expression, | ||
@@ -141,16 +185,11 @@ * examples see expression parsers and tests | ||
type: ANNOTATION_TYPES.EventBinding, | ||
value: parseExpressionString(value) | ||
value: parseBlockExpressionString(value) | ||
}; | ||
case ANNOTATION_TYPES.PropertyBinding: | ||
if (typeof value === "object") { | ||
return { | ||
type: ANNOTATION_TYPES.PropertyBinding, | ||
nested: true, | ||
value: compileProps(value, options) | ||
}; | ||
} | ||
return { | ||
type: ANNOTATION_TYPES.PropertyBinding, | ||
nested: false, | ||
value: parseExpressionString(value) | ||
value: { | ||
type: TYPES.Program, | ||
body: [compilePropertyBindingValue(options, value, key)] | ||
} | ||
}; | ||
@@ -157,0 +196,0 @@ case ANNOTATION_TYPES.TwoWayBinding: |
@@ -115,2 +115,8 @@ import { compileComponents, compileProps } from "./compileComponents"; | ||
}, | ||
"{nestedArray}": [ | ||
"n", | ||
{ | ||
"{nestedNestedProp}": "n" | ||
} | ||
], | ||
"(event)": "c", | ||
@@ -122,30 +128,62 @@ "#template#": "a+d" | ||
type: ANNOTATION_TYPES.PropertyBinding, | ||
value: createProgram(createIdentifier("b")), | ||
nested: false | ||
value: createProgram(createIdentifier("b")) | ||
}, | ||
nestedObject: { | ||
type: ANNOTATION_TYPES.PropertyBinding, | ||
nested: true, | ||
value: { | ||
nestedProp: { | ||
type: ANNOTATION_TYPES.PropertyBinding, | ||
nested: true, | ||
value: { | ||
nestedNestedProp: { | ||
value: createProgram({ | ||
type: TYPES.ObjectExpression, | ||
properties: [ | ||
{ | ||
key: createValue("nestedProp"), | ||
value: { | ||
type: ANNOTATION_TYPES.PropertyBinding, | ||
value: createProgram(createIdentifier("n")), | ||
nested: false | ||
value: { | ||
type: TYPES.ObjectExpression, | ||
properties: [ | ||
{ | ||
key: createValue("nestedNestedProp"), | ||
value: { | ||
type: ANNOTATION_TYPES.PropertyBinding, | ||
value: createIdentifier("n") | ||
} | ||
} | ||
] | ||
} | ||
} | ||
}, | ||
{ | ||
key: createValue("nestedComponent"), | ||
value: { | ||
type: TYPES.Components, | ||
components: [ | ||
{ | ||
type: "Component" | ||
} | ||
] | ||
} | ||
} | ||
}, | ||
nestedComponent: { | ||
type: TYPES.Components, | ||
components: [ | ||
{ | ||
type: "Component" | ||
} | ||
] | ||
} | ||
} | ||
] | ||
}) | ||
}, | ||
nestedArray: { | ||
type: ANNOTATION_TYPES.PropertyBinding, | ||
value: createProgram({ | ||
type: TYPES.ArrayExpression, | ||
elements: [ | ||
createIdentifier("n"), | ||
{ | ||
type: TYPES.ObjectExpression, | ||
properties: [ | ||
{ | ||
key: createValue("nestedNestedProp"), | ||
value: { | ||
type: ANNOTATION_TYPES.PropertyBinding, | ||
value: createIdentifier("n") | ||
} | ||
} | ||
] | ||
} | ||
] | ||
}) | ||
}, | ||
onEvent: { | ||
@@ -152,0 +190,0 @@ type: ANNOTATION_TYPES.EventBinding, |
export const TYPES = { | ||
Numeric: "Numeric", | ||
String: "String", | ||
Boolean: "Boolean", | ||
Object: "Object", | ||
Array: "Array", | ||
Null: "Null", | ||
RegExp: "RegExp", | ||
Identifier: "Identifier", | ||
Keyword: 'Keyword', | ||
ArrayExpression: "ArrayExpression", | ||
AssignExpression: "AssignExpression", | ||
ObjectExpression: "ObjectExpression", | ||
ObjectProperty: "ObjectProperty", | ||
ArrayExpression: "ArrayExpression", | ||
BinaryExpression: "BinaryExpression", | ||
UnaryExpression: "UnaryExpression", | ||
BlockStatement: "BlockStatement", | ||
Boolean: "Boolean", | ||
CallExpression: "CallExpression", | ||
TemplateLiteral: "TemplateLiteral", | ||
Components: "Components", | ||
ConditionalExpression: "ConditionalExpression", | ||
EventBinding: "EventBinding", | ||
Identifier: "Identifier", | ||
IfStatement: "IfStatement", | ||
Keyword: "Keyword", | ||
MemberExpression: "MemberExpression", | ||
IfStatement: "IfStatement", | ||
Components: "Components", | ||
Null: "Null", | ||
Numeric: "Numeric", | ||
Object: "Object", | ||
ObjectExpression: "ObjectExpression", | ||
Operator: "Operator", | ||
Program: "Program", | ||
PropertyBinding: "PropertyBinding", | ||
Punctuation: "Punctuation", | ||
Raw: "Raw", | ||
Program: 'Program', | ||
BlockStatement: 'BlockStatement', | ||
PropertyBinding: 'PropertyBinding', | ||
EventBinding: 'EventBinding', | ||
RegExp: "RegExp", | ||
String: "String", | ||
TemplateLiteral: "TemplateLiteral", | ||
UnaryExpression: "UnaryExpression" | ||
}; | ||
/** | ||
those const not used in the app, leave here for reference | ||
export const PRIMITIVES = [ | ||
@@ -50,6 +47,7 @@ TYPES.Numeric, | ||
TYPES.UnaryExpression, | ||
TYPES.TemplateLiteral, | ||
TYPES.MemberExpression, | ||
TYPES.ConditionalExpression, | ||
TYPES.TemplateLiteral, // special expression | ||
]; | ||
*/ | ||
@@ -95,6 +93,6 @@ export const OPERATORS = { | ||
export const IF_KEYWORDS = { | ||
If: 'if', | ||
Else: 'else', | ||
Then: 'then' | ||
} | ||
If: "if", | ||
Else: "else", | ||
Then: "then" | ||
}; | ||
export const KEYWORDS = [...BOOLEANS, ...Object.values(IF_KEYWORDS)]; | ||
@@ -109,3 +107,3 @@ | ||
BackQuote: "`", | ||
SemiColon: ";", | ||
SemiColon: ";" | ||
}; | ||
@@ -112,0 +110,0 @@ |
import { TYPES } from "../const"; | ||
import evaluateIdentifier from "./evaluateIdentifier"; | ||
import evaluateProgram from "./evaluateProgram"; | ||
import evaluateUnaryExpression from "./evaluateUnaryExpression"; | ||
import evaluateArrayExpression from "./evaluateArrayExpression"; | ||
import evaluateAssignExpression from "./evaluateAssignExpression"; | ||
import evaluateBinaryExpression from "./evaluateBinaryExpression"; | ||
import evaluateCallExpression from "./evaluateCallExpression"; | ||
import evaluateConditionalExpression from "./evaluateConditionalExpression"; | ||
import evaluateEventBindingExpression from "./evaluateEventBindingExpression"; | ||
import evaluateIdentifier from "./evaluateIdentifier"; | ||
import evaluateIfStatement from "./evaluateIfStatement"; | ||
import evaluateMemberObjectExpression from "./evaluateMemberObjectExpression"; | ||
import evaluateObjectExpression from "./evaluateObjectExpression"; | ||
import evaluateArrayExpression from "./evaluateArrayExpression"; | ||
import evaluateProgram from "./evaluateProgram"; | ||
import evaluatePropertyBindingExpression from "./evaluatePropertyBindingExpression"; | ||
import evaluateTemplateLiteral from "./evaluateTemplateLiteral"; | ||
import evaluateIfStatement from "./evaluateIfStatement"; | ||
import evaluateEventBindingExpression from "./evaluateEventBindingExpression"; | ||
import evaluatePropertyBindingExpression from "./evaluatePropertyBindingExpression"; | ||
import evaluateUnaryExpression from "./evaluateUnaryExpression"; | ||
import { hasKey } from "../utils"; | ||
@@ -19,28 +20,31 @@ import Environment from "./Environment"; | ||
const getValue = ({ value }) => value; | ||
const toRegExp = ({ pattern, flags }) => new RegExp(pattern, flags); | ||
const evaluateComponents = (expression, env) => | ||
env.evaluateComponents(expression); | ||
const getStringifyValue = ({ value }) => JSON.stringify(value); | ||
const toRegExp = ({ pattern, flags }) => | ||
`new RegExp(${JSON.stringify(pattern)}, ${JSON.stringify(flags)})`; | ||
const evaluateComponents = expression => | ||
`return $$env.evaluateComponents(${JSON.stringify(expression)})`; | ||
const TypeHandlers = { | ||
[TYPES.Numeric]: getValue, | ||
[TYPES.String]: getValue, | ||
[TYPES.Boolean]: getValue, | ||
[TYPES.Null]: getValue, | ||
[TYPES.Raw]: getValue, | ||
[TYPES.RegExp]: toRegExp, | ||
[TYPES.Identifier]: evaluateIdentifier, | ||
[TYPES.UnaryExpression]: evaluateUnaryExpression, | ||
[TYPES.ArrayExpression]: evaluateArrayExpression, | ||
[TYPES.AssignExpression]: evaluateAssignExpression, | ||
[TYPES.BinaryExpression]: evaluateBinaryExpression, | ||
[TYPES.BlockStatement]: evaluateProgram, | ||
[TYPES.Boolean]: getValue, | ||
[TYPES.CallExpression]: evaluateCallExpression, | ||
[TYPES.Components]: evaluateComponents, | ||
[TYPES.ConditionalExpression]: evaluateConditionalExpression, | ||
[TYPES.EventBinding]: evaluateEventBindingExpression, | ||
[TYPES.Identifier]: evaluateIdentifier, | ||
[TYPES.IfStatement]: evaluateIfStatement, | ||
[TYPES.MemberExpression]: evaluateMemberObjectExpression, | ||
[TYPES.Null]: getValue, | ||
[TYPES.Numeric]: getValue, | ||
[TYPES.ObjectExpression]: evaluateObjectExpression, | ||
[TYPES.ArrayExpression]: evaluateArrayExpression, | ||
[TYPES.TemplateLiteral]: evaluateTemplateLiteral, | ||
[TYPES.Program]: evaluateProgram, | ||
[TYPES.BlockStatement]: evaluateProgram, | ||
[TYPES.IfStatement]: evaluateIfStatement, | ||
[TYPES.EventBinding]: evaluateEventBindingExpression, | ||
[TYPES.PropertyBinding]: evaluatePropertyBindingExpression, | ||
[TYPES.Raw]: getStringifyValue, | ||
[TYPES.RegExp]: toRegExp, | ||
[TYPES.String]: getStringifyValue, | ||
[TYPES.TemplateLiteral]: evaluateTemplateLiteral, | ||
[TYPES.UnaryExpression]: evaluateUnaryExpression, | ||
}; | ||
@@ -51,12 +55,67 @@ | ||
* @param {*} ast | ||
* @param {Environment} environment | ||
* @returns {*} | ||
*/ | ||
const evaluate = ({ type, ...ast }, environment) => { | ||
const evaluate = ast => { | ||
const { type, ...rest } = ast; | ||
if (hasKey(TypeHandlers, type)) { | ||
return TypeHandlers[type](ast, environment, evaluate); | ||
return TypeHandlers[type](rest, evaluate); | ||
} | ||
throw new Error(`Wrong type ${type}, ${JSON.stringify({ type, ...ast })}`); | ||
throw new Error(`Wrong type ${type}, ${JSON.stringify(ast)}`); | ||
}; | ||
const match = (left, right) => { | ||
const regex = right instanceof RegExp ? right : new RegExp(right); | ||
if (left == null) { | ||
return regex.test(""); | ||
} | ||
return regex.test(left); | ||
}; | ||
const get = (obj, key) => { | ||
if (obj == null) { | ||
return null; | ||
} | ||
const value = obj[key]; | ||
if (value == null) { | ||
return null; | ||
} | ||
return value; | ||
}; | ||
const toString = obj => { | ||
switch (typeof obj) { | ||
case "string": | ||
return obj; | ||
case "number": | ||
if (Number.isNaN(obj)) { | ||
return ""; | ||
} | ||
return obj.toString(); | ||
default: | ||
// all other types will be convert to '' for convenience | ||
return ""; | ||
} | ||
}; | ||
const cache = new WeakMap(); | ||
const getOrCreate = ast => { | ||
const cachedFn = cache.get(ast); | ||
if (cachedFn) { | ||
return cachedFn; | ||
} | ||
const context = [match, get, toString]; | ||
const newFn = new Function( | ||
"$$match", | ||
"$$get", | ||
"$$toString", | ||
"$$env", | ||
evaluate(ast) | ||
).bind(null, ...context); | ||
cache.set(ast, newFn); | ||
return newFn; | ||
}; | ||
/** | ||
@@ -72,5 +131,6 @@ * evaluateWithEnvironmentCheck | ||
} | ||
return evaluate(ast, environment); | ||
return getOrCreate(ast)(environment); | ||
}; | ||
export default evaluateWithEnvironmentCheck; |
import { TYPES } from "../const"; | ||
import evaluate from "./evaluate"; | ||
import { parseExpressionString } from "../parsers"; | ||
import { parseBlockExpressionString } from "../parsers"; | ||
import {compileProps} from '../compileComponents'; | ||
import Environment from "./Environment"; | ||
const evaluateWithStringExpression = (str, env) => { | ||
const ast = parseExpressionString(str); | ||
const ast = parseBlockExpressionString(str); | ||
return evaluate(ast, env); | ||
@@ -206,7 +207,7 @@ }; | ||
env.set("test", jest.fn().mockReturnValue(false)); | ||
env.set("test2", jest.fn().mockReturnValue(false)); | ||
env.set("test2", jest.fn().mockReturnValue(true)); | ||
env.set("consequent", jest.fn()); | ||
env.set("alternate", jest.fn().mockReturnValue('foobar')); | ||
env.set("consequent2", jest.fn().mockReturnValue('foobar')); | ||
expect( | ||
evaluateWithStringExpression('if test() then consequent() else if test2() else alternate()', env) | ||
evaluateWithStringExpression('if test() then consequent() else if test2() then consequent2()', env) | ||
).toBe('foobar'); | ||
@@ -216,3 +217,3 @@ expect(env.get("test")).toHaveBeenCalled(); | ||
expect(env.get("consequent")).not.toHaveBeenCalled(); | ||
expect(env.get("alternate")).toHaveBeenCalled(); | ||
expect(env.get("consequent2")).toHaveBeenCalled(); | ||
}); | ||
@@ -225,3 +226,3 @@ | ||
expect( | ||
evaluateWithStringExpression('if test() then {call1(); call2();\n 123+110}', env) | ||
evaluateWithStringExpression('if test() {call1(); call2();\n 123+110}', env) | ||
).toBe(233); | ||
@@ -233,2 +234,20 @@ expect(env.get("test")).toHaveBeenCalled(); | ||
}); | ||
it('should support property binding', () => { | ||
const ast = compileProps({ | ||
'{obj}': { | ||
'{prop1}': 'prop1' | ||
}, | ||
'{array}': [ | ||
"prop2", { | ||
'{prop1}': 'prop2' | ||
}], | ||
}); | ||
env.set("prop1", '1'); | ||
env.set("prop2", '2'); | ||
debugger; | ||
expect(evaluate(ast.obj, env)).toEqual({prop1: '1'}); | ||
expect(evaluate(ast.array, env)).toEqual(['2', {prop1: '2'}]); | ||
}); | ||
}); |
@@ -1,4 +0,6 @@ | ||
const evaluateArrayExpression = ({ elements }, env, evaluate) => | ||
elements.map(element => evaluate(element, env)); | ||
import { join } from "path"; | ||
const evaluateArrayExpression = ({ elements }, evaluate) => | ||
`[${elements.map(element => evaluate(element)).join(",")}]`; | ||
export default evaluateArrayExpression; |
@@ -1,3 +0,4 @@ | ||
const evaluateAssignExpression = ({ left, right }, env, evaluate) => | ||
env.set(left.name, evaluate(right, env)); | ||
const evaluateAssignExpression = ({ left: { name }, right }, evaluate) => | ||
`$$env.set("${name}", ${evaluate(right)})`; | ||
export default evaluateAssignExpression; |
@@ -11,53 +11,32 @@ import { OPERATORS } from "../const"; | ||
export const evaluateNonBranchableBinaryExpression = ({ operator, left, right }) => { | ||
switch (operator) { | ||
case OPERATORS.LessThan: | ||
return left < right; | ||
case OPERATORS.LessThanOrEqualTo: | ||
return left <= right; | ||
case OPERATORS.GreaterThan: | ||
return left > right; | ||
case OPERATORS.GreaterThanOrEqualTo: | ||
return left >= right; | ||
case OPERATORS.EqualTo: | ||
return left === right; | ||
case OPERATORS.NotEqualTo: | ||
return left !== right; | ||
case OPERATORS.Match: | ||
return handleRegex(left, operator, right); | ||
case OPERATORS.Add: | ||
return left + right; | ||
case OPERATORS.Subtract: | ||
return left - right; | ||
case OPERATORS.Multiply: | ||
return left * right; | ||
case OPERATORS.Divide: | ||
return left / right; | ||
case OPERATORS.Remainder: | ||
return left % right; | ||
default: | ||
throw new Error( | ||
`unknown binary expression: ${{ operator, left, right }}` | ||
); | ||
} | ||
const OpMapping = { | ||
[OPERATORS.Or]: "||", | ||
[OPERATORS.And]: "&&", | ||
[OPERATORS.LessThan]: "<", | ||
[OPERATORS.LessThanOrEqualTo]: "<=", | ||
[OPERATORS.GreaterThan]: ">", | ||
[OPERATORS.GreaterThanOrEqualTo]: ">=", | ||
[OPERATORS.EqualTo]: "===", | ||
[OPERATORS.NotEqualTo]: "!==", | ||
[OPERATORS.Add]: "+", | ||
[OPERATORS.Subtract]: "-", | ||
[OPERATORS.Multiply]: "*", | ||
[OPERATORS.Divide]: "/", | ||
[OPERATORS.Remainder]: "%" | ||
}; | ||
const evaluateBinaryExpression = ({ operator, left, right }, env, evaluate) => { | ||
switch (operator) { | ||
// specially handling for OR and AND | ||
case OPERATORS.Or: | ||
// if left is true will ignore right | ||
return evaluate(left, env) || evaluate(right, env); | ||
case OPERATORS.And: | ||
// if left is false will ignore right | ||
return evaluate(left, env) && evaluate(right, env); | ||
default: | ||
return evaluateNonBranchableBinaryExpression({ | ||
operator, | ||
left: evaluate(left, env), | ||
right: evaluate(right, env) | ||
}); | ||
const evaluateBinaryExpression = ({ operator, left, right }, evaluate) => { | ||
const evaluatedLeft = evaluate(left); | ||
const evaluatedRight = evaluate(right); | ||
if (operator in OpMapping) { | ||
return `(${evaluatedLeft}${OpMapping[operator]}${evaluatedRight})`; | ||
} | ||
if (operator === OPERATORS.Match) { | ||
return `$$match(${evaluatedLeft},${evaluatedRight})`; | ||
} | ||
throw new Error(`unknown binary expression: ${{ operator, left, right }}`); | ||
}; | ||
export default evaluateBinaryExpression; |
@@ -1,16 +0,8 @@ | ||
const evaluateCallExpression = (ast, env, evaluate) => { | ||
const callee = evaluate(ast.callee, env); | ||
if (typeof callee !== "function") { | ||
throw new Error( | ||
`Wrong call expression, callee have to be function ${JSON.stringify( | ||
ast | ||
)}` | ||
); | ||
} | ||
const evaluateCallExpression = (ast, evaluate) => { | ||
const callee = evaluate(ast.callee); | ||
const args = ast.arguments.map(arg => evaluate(arg)); | ||
const args = ast.arguments.map(arg => evaluate(arg, env)); | ||
return callee(...args); | ||
return `${callee}(${args.join(",")})`; | ||
}; | ||
export default evaluateCallExpression; |
@@ -1,4 +0,6 @@ | ||
const evaluateEventBindingExpression = ({ value }, env, evaluate) => | ||
evaluate(value, env); | ||
const evaluateEventBindingExpression = ({ value }, evaluate) => { | ||
return evaluate(value); | ||
} | ||
export default evaluateEventBindingExpression; |
@@ -1,2 +0,2 @@ | ||
const evaluateIdentifier = ({ name }, env) => env.get(name); | ||
const evaluateIdentifier = ({ name }) => `$$env.get("${name}")`; | ||
export default evaluateIdentifier; |
@@ -1,11 +0,10 @@ | ||
const evaluateIfStatement = ( | ||
{ test, consequent, alternate }, | ||
env, | ||
evaluate | ||
) => { | ||
if (evaluate(test, env)) { | ||
return consequent ? evaluate(consequent, env) : null; | ||
} | ||
return alternate ? evaluate(alternate, env) : null; | ||
import { TYPES } from "../const"; | ||
const evaluateIfStatement = ({ test, consequent, alternate }, evaluate) => { | ||
const evaluatedTest = evaluate(test); | ||
const evaluatedConsequent = consequent ? evaluate(consequent) : ""; | ||
const evaluatedAlternate = alternate ? evaluate(alternate) : ""; | ||
return `if(${evaluatedTest}){${evaluatedConsequent};}else{${evaluatedAlternate};}`; | ||
}; | ||
export default evaluateIfStatement; |
@@ -1,12 +0,8 @@ | ||
const evaluateMemberObjectExpression = (ast, env, evaluate) => { | ||
const object = evaluate(ast.object, env); | ||
const property = evaluate(ast.property, env); | ||
const evaluateMemberObjectExpression = (ast, evaluate) => { | ||
const evaluatedObject = evaluate(ast.object); | ||
const evaluatedProperty = evaluate(ast.property); | ||
if (object == null) { | ||
return null; | ||
} | ||
return object[property]; | ||
return `$$get(${evaluatedObject},${evaluatedProperty})`; | ||
}; | ||
export default evaluateMemberObjectExpression; |
@@ -1,7 +0,7 @@ | ||
const evaluateObjectExpression = ({ properties }, env, evaluate) => | ||
properties.reduce((result, { key, value }) => { | ||
result[evaluate(key, env)] = evaluate(value, env); | ||
return result; | ||
}, {}); | ||
const evaluateObjectExpression = ({ properties }, evaluate) => { | ||
return `({${properties.map( | ||
({ key, value }) => `[${evaluate(key)}]:${evaluate(value)}` | ||
).join(',')}})`; | ||
}; | ||
export default evaluateObjectExpression; |
@@ -0,13 +1,26 @@ | ||
import { TYPES } from "../const"; | ||
// evaluate both block and program statement | ||
const evaluateProgram = ({ body }, env, evaluate) => { | ||
const evaluateProgram = ({ body }, evaluate) => { | ||
if (body.length === 0) { | ||
return null; | ||
return "return null;"; | ||
} | ||
const lstIndex = body.length - 1; | ||
const evaluatedBody = body.map(ast => evaluate(ast)); | ||
let js = ""; | ||
for (let i = 0; i < lstIndex; i++) { | ||
evaluate(body[i], env); | ||
js += `${evaluatedBody[i]};`; | ||
} | ||
return evaluate(body[lstIndex], env); // always return the last value | ||
if (body[lstIndex].type === TYPES.IfStatement) { | ||
js += `${evaluatedBody[lstIndex]}`; | ||
} else { | ||
js += `return ${evaluatedBody[lstIndex]};`; | ||
} | ||
return js; | ||
}; | ||
export default evaluateProgram; |
@@ -1,10 +0,7 @@ | ||
import mapValues from 'lodash/mapValues'; | ||
import mapValues from "lodash/mapValues"; | ||
const evaluatePropertyBindingExpression = ({ value }, env, evaluate) => { | ||
if ('type' in value) { | ||
return evaluate(value, env); | ||
} | ||
return mapValues(value, (ast) => evaluate(ast, env)); | ||
} | ||
const evaluatePropertyBindingExpression = ({ value }, evaluate) => { | ||
return evaluate(value); | ||
}; | ||
export default evaluatePropertyBindingExpression; |
@@ -1,25 +0,11 @@ | ||
const toString = obj => { | ||
switch (typeof obj) { | ||
case "string": | ||
return obj; | ||
case "number": | ||
if (Number.isNaN(obj)) { | ||
return ""; | ||
} | ||
return obj.toString(); | ||
default: | ||
// all other types will be convert to '' for convenience | ||
return ""; | ||
} | ||
const evaluateTemplateLiteral = ({ expressions, quasis }, evaluate) => { | ||
const evaluatedExpr = expressions.map(expr => evaluate(expr)); | ||
return quasis.reduce( | ||
(retVal, quasi, i) => | ||
i < expressions.length | ||
? `${retVal}+(${evaluate(quasi)})+$$toString(${evaluatedExpr[i]})` | ||
: `${retVal}+(${evaluate(quasi)})`, | ||
"''" | ||
); | ||
}; | ||
const evaluateTemplateLiteral = ({ expressions, quasis }, env, evaluate) => | ||
quasis.reduce( | ||
(retVal, { value }, i) => | ||
retVal + | ||
value + | ||
toString(i < expressions.length && evaluate(expressions[i], env)), | ||
"" | ||
); | ||
export default evaluateTemplateLiteral; |
import { OPERATORS } from "../const"; | ||
const evaluateUnaryExpression = ({ operator, argument }, env, evaluate) => { | ||
const evaluateUnaryExpression = ({ operator, argument }, evaluate) => { | ||
const evaluated = evaluate(argument); | ||
switch (operator) { | ||
case OPERATORS.Not: | ||
return !evaluate(argument, env); | ||
return `(!(${evaluated}))`; | ||
case OPERATORS.Add: | ||
return +evaluate(argument, env); | ||
return `(+(${evaluated}))`; | ||
case OPERATORS.Subtract: | ||
return -evaluate(argument, env); | ||
return `(-(${evaluated}))`; | ||
default: | ||
@@ -12,0 +13,0 @@ throw new Error( |
@@ -0,7 +1,13 @@ | ||
import flowRight from "lodash/flowRight"; | ||
import curryRight from "lodash/curryRight"; | ||
import parseExpressionTokenStream from "./parseExpressionTokenStream"; | ||
import { createExpressionTokenStream, createInputStream } from "../tokenizers"; | ||
import flowRight from "lodash/flowRight"; | ||
import { TYPES } from "../const"; | ||
const parse = flowRight( | ||
/** | ||
* parse a block of expression string | ||
* @param expressionString:string | ||
*/ | ||
export const parseBlockExpressionString = flowRight( | ||
parseExpressionTokenStream, | ||
@@ -13,32 +19,18 @@ createExpressionTokenStream, | ||
/** | ||
* parse template string | ||
* @param templateString:string | ||
*/ | ||
export const parseTemplateString = templateString => { | ||
return parse(`\`${templateString}\``); | ||
} | ||
/** | ||
* parse expression string | ||
* @param expressionString:string | ||
*/ | ||
export const parseExpressionString = parse; | ||
export const parseExpressionString = flowRight( | ||
curryRight(parseExpressionTokenStream, 2)(false), | ||
createExpressionTokenStream, | ||
createInputStream | ||
); | ||
/** | ||
* parse two way binding string | ||
* Will throw error if TwoWayBinding is not a identifier. | ||
* @param expressionString | ||
* parse template string | ||
* @param templateString:string | ||
*/ | ||
export const parseTwoWayBindingString = expressionString => { | ||
const ast = parse(expressionString); | ||
const id = ast.body[0]; | ||
if (id && id.type !== TYPES.Identifier) { | ||
throw new Error( | ||
`data binding type have to be Identifier instead of ${ | ||
ast.type | ||
}(${expressionString})` | ||
); | ||
} | ||
export const parseTemplateString = templateString => { | ||
return parseBlockExpressionString(`\`${templateString}\``); | ||
} | ||
return ast; | ||
}; |
@@ -8,3 +8,6 @@ /* eslint-disable no-use-before-define */ | ||
TYPES, | ||
IF_KEYWORDS | ||
IF_KEYWORDS, | ||
PRIMITIVES, | ||
OBJECTS, | ||
EXPRESSIONS | ||
} from "../const"; | ||
@@ -26,4 +29,5 @@ import * as utils from "./utils"; | ||
* @param tokenStream | ||
* @param isBlock default to true, pass as block of code if true | ||
*/ | ||
const parseExpressionTokenStream = tokenStream => { | ||
const parseExpressionTokenStream = (tokenStream, isBlock = true) => { | ||
const isKeyword = keyword => utils.isKeyword(tokenStream.peek(), keyword); | ||
@@ -131,13 +135,20 @@ const isPunctuation = paren => utils.isPunctuation(tokenStream.peek(), paren); | ||
const token = isUnary(); | ||
if (token) { | ||
tokenStream.next(); | ||
return { | ||
type: TYPES.UnaryExpression, | ||
operator: token.value, | ||
argument: maybeUnary(expr) | ||
}; | ||
if (!token) { | ||
return expr(); | ||
} | ||
return expr(); | ||
tokenStream.next(); | ||
const argument = maybeUnary(expr); | ||
if (!isSimpleExpr(argument)) { | ||
unexpected(token); | ||
} | ||
return { | ||
type: TYPES.UnaryExpression, | ||
operator: token.value, | ||
argument | ||
}; | ||
} | ||
function isSimpleExpr({type}) { | ||
return EXPRESSIONS.includes(type) || PRIMITIVES.includes(type) || OBJECTS.includes(type); | ||
} | ||
/** | ||
@@ -151,23 +162,26 @@ * return binary expression if next token is an operator | ||
const token = isOperator(); | ||
if (token) { | ||
const rightOpPrec = PRECEDENCE[token.value]; | ||
const isAssign = token.value === OPERATORS.Assign; | ||
if (rightOpPrec > leftOpPrec) { | ||
if (isAssign && left.type !== TYPES.Identifier) { | ||
tokenStream.croak( | ||
`You can only assign to an identifier "${JSON.stringify(left)}"` | ||
); | ||
} | ||
tokenStream.next(); | ||
const right = maybeBinary(parseAtom(), rightOpPrec); | ||
const binary = { | ||
type: isAssign ? TYPES.AssignExpression : TYPES.BinaryExpression, | ||
operator: token.value, | ||
left, | ||
right | ||
}; | ||
return maybeBinary(binary, leftOpPrec); | ||
} | ||
if (!isSimpleExpr(left) || !token) { | ||
return left; | ||
} | ||
return left; | ||
const rightOpPrec = PRECEDENCE[token.value]; | ||
const isAssign = token.value === OPERATORS.Assign; | ||
if (rightOpPrec <= leftOpPrec) { | ||
return left; | ||
} | ||
if (isAssign && left.type !== TYPES.Identifier) { | ||
tokenStream.croak( | ||
`You can only assign to an identifier "${JSON.stringify(left)}"` | ||
); | ||
} | ||
tokenStream.next(); | ||
const right = maybeBinary(parseAtom(), rightOpPrec); | ||
const binary = { | ||
type: isAssign ? TYPES.AssignExpression : TYPES.BinaryExpression, | ||
operator: token.value, | ||
left, | ||
right | ||
}; | ||
return maybeBinary(binary, leftOpPrec); | ||
} | ||
@@ -291,6 +305,7 @@ | ||
* parse next expression | ||
* @param {Boolean} allowBlock | ||
* @returns {Expression} | ||
*/ | ||
function parseExpression() { | ||
return maybeBinary(parseAtom(), 0); | ||
function parseExpression(allowBlock = false) { | ||
return maybeBinary(parseAtom(allowBlock), 0); | ||
} | ||
@@ -300,6 +315,7 @@ | ||
* parse single expression, could be a call expression, an unary expression, an identifier or an expression inside a parentheses | ||
* @param {Boolean} allowBlock | ||
* @returns {Expression} | ||
*/ | ||
function parseAtom(skipUnaryCheck = false) { | ||
return maybeUnary(() => maybeCallOrMember(parseSimpleAtom())); | ||
function parseAtom(allowBlock = false) { | ||
return maybeUnary(() => maybeCallOrMember(parseSimpleAtom(allowBlock))); | ||
} | ||
@@ -310,3 +326,3 @@ | ||
while (!tokenStream.eof()) { | ||
body.push(parseExpression()); | ||
body.push(parseExpression(true)); | ||
if (tokenStream.eof()) { | ||
@@ -331,3 +347,3 @@ // skip semi colon check if tokenStream is end; | ||
PUNCTUATIONS.SemiColon, // ; | ||
parseExpression | ||
() => parseExpression(true) | ||
) | ||
@@ -337,3 +353,7 @@ }; | ||
function parseIfStatement() { | ||
/** | ||
* @param {Boolean} allowBlock | ||
*/ | ||
function parseIfStatement(allowBlock) { | ||
debugger; | ||
skipKeyword(IF_KEYWORDS.If); | ||
@@ -344,9 +364,13 @@ const test = parseExpression(); | ||
let alternate = null; | ||
let isConditionalExpression = false; | ||
if (isKeyword(IF_KEYWORDS.Then)) { | ||
skipKeyword(IF_KEYWORDS.Then); | ||
if (isPunctuation(PUNCTUATIONS.Braces[0])) { | ||
consequent = parseExpression(); | ||
isConditionalExpression = true; | ||
} else { | ||
if (allowBlock) { | ||
consequent = parseBlockStatement(); | ||
} else { | ||
consequent = parseExpression(); | ||
unexpected(); | ||
} | ||
@@ -357,6 +381,12 @@ } | ||
skipKeyword(IF_KEYWORDS.Else); | ||
if (isPunctuation(PUNCTUATIONS.Braces[0])) { | ||
alternate = parseBlockStatement(); | ||
if (isKeyword(IF_KEYWORDS.If)) { | ||
alternate = parseExpression(allowBlock); | ||
} else if (isConditionalExpression) { | ||
alternate = parseExpression(); | ||
} else { | ||
alternate = parseExpression(); | ||
if (allowBlock) { | ||
alternate = parseBlockStatement(); | ||
} else { | ||
unexpected(); | ||
} | ||
} | ||
@@ -366,3 +396,3 @@ } | ||
return { | ||
type: TYPES.IfStatement, | ||
type: isConditionalExpression ? TYPES.ConditionalExpression : TYPES.IfStatement, | ||
test, | ||
@@ -376,5 +406,6 @@ consequent, | ||
* parse a simple atom, e.g identifier, number, string, object, array, boolean, etc | ||
* @param {Boolean} allowBlock | ||
* @returns {Expression} | ||
*/ | ||
function parseSimpleAtom() { | ||
function parseSimpleAtom(allowBlock = false) { | ||
if (isPunctuation(PUNCTUATIONS.Parentheses[0])) { | ||
@@ -404,3 +435,3 @@ // if it reads parentheses, then will parse the expression inside the parentheses | ||
if (isKeyword(IF_KEYWORDS.If)) { | ||
return parseIfStatement(); | ||
return parseIfStatement(allowBlock); | ||
} | ||
@@ -422,5 +453,5 @@ | ||
return parseProgram(); | ||
return isBlock ? parseProgram() : parseExpression(); | ||
}; | ||
export default parseExpressionTokenStream; |
@@ -562,8 +562,8 @@ import createInputStream from "../tokenizers/createInputStream"; | ||
describe("if statement", () => { | ||
it("should parse if statement with only else", () => { | ||
const ast = parse("if true and true else hello()"); | ||
describe("if statement & conditional expression", () => { | ||
it("should parse conditional expression with only then", () => { | ||
const ast = parse("if true and true then hello()"); | ||
expect(ast).toEqual( | ||
createProgram({ | ||
type: TYPES.IfStatement, | ||
type: TYPES.ConditionalExpression, | ||
test: { | ||
@@ -575,4 +575,4 @@ type: TYPES.BinaryExpression, | ||
}, | ||
consequent: null, | ||
alternate: createCallExpression(createIdentifier("hello")) | ||
alternate: null, | ||
consequent: createCallExpression(createIdentifier("hello")) | ||
}) | ||
@@ -582,7 +582,7 @@ ); | ||
it("should parse if statement with only then", () => { | ||
const ast = parse("if true and true then hello()"); | ||
it("should parse simple conditional expression", () => { | ||
const ast = parse("if true and true then 1+2 else hello()"); | ||
expect(ast).toEqual( | ||
createProgram({ | ||
type: TYPES.IfStatement, | ||
type: TYPES.ConditionalExpression, | ||
test: { | ||
@@ -594,4 +594,9 @@ type: TYPES.BinaryExpression, | ||
}, | ||
alternate: null, | ||
consequent: createCallExpression(createIdentifier("hello")) | ||
consequent: { | ||
type: TYPES.BinaryExpression, | ||
operator: OPERATORS.Add, | ||
left: createValue(1), | ||
right: createValue(2) | ||
}, | ||
alternate: createCallExpression(createIdentifier("hello")) | ||
}) | ||
@@ -601,7 +606,7 @@ ); | ||
it("should parse simple if statement", () => { | ||
const ast = parse("if true and true then 1+2 else hello()"); | ||
it("should parse chained conditional expression", () => { | ||
const ast = parse("if true and true then 1+2 else if false then hello()"); | ||
expect(ast).toEqual( | ||
createProgram({ | ||
type: TYPES.IfStatement, | ||
type: TYPES.ConditionalExpression, | ||
test: { | ||
@@ -619,4 +624,8 @@ type: TYPES.BinaryExpression, | ||
}, | ||
alternate: createCallExpression(createIdentifier("hello")) | ||
alternate: { | ||
type: TYPES.ConditionalExpression, | ||
test: createValue(false), | ||
consequent: createCallExpression(createIdentifier("hello")), | ||
alternate: null | ||
} | ||
}) | ||
@@ -626,5 +635,6 @@ ); | ||
it("should parse if statement with blocks", () => { | ||
const ast = parse( | ||
"if true and true then {1+2;} else {hello(); hello2();}" | ||
"if true and true {1+2;} else {hello(); hello2();}" | ||
); | ||
@@ -660,14 +670,22 @@ expect(ast).toEqual( | ||
it("should parse chained if statements ", () => { | ||
const ast = parse("if true then 1 else if false then {2}"); | ||
it("should parse chained if statement with blocks", () => { | ||
const ast = parse(` | ||
if 1 { | ||
2; | ||
} else if 3 { | ||
4; | ||
} else { | ||
5; | ||
} | ||
`); | ||
expect(ast).toEqual( | ||
createProgram({ | ||
type: TYPES.IfStatement, | ||
test: createValue(true), | ||
consequent: createValue(1), | ||
test: createValue(1), | ||
consequent: createProgram([createValue(2)], false), | ||
alternate: { | ||
type: TYPES.IfStatement, | ||
test: createValue(false), | ||
consequent: createProgram(createValue(2), false), | ||
alternate: null | ||
test: createValue(3), | ||
consequent: createProgram([createValue(4)], false), | ||
alternate: createProgram([createValue(5)], false) | ||
} | ||
@@ -674,0 +692,0 @@ }) |
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
179240
66
4885
2