UNPKG

@airtasker/form-schema-compiler

Version:
542 lines (531 loc) 21 kB
"use strict"; var _createInputStream = require("../tokenizers/createInputStream"); var _createInputStream2 = _interopRequireDefault(_createInputStream); var _createExpressionTokenStream = require("../tokenizers/createExpressionTokenStream"); var _createExpressionTokenStream2 = _interopRequireDefault(_createExpressionTokenStream); var _parseExpressionTokenStream = require("./parseExpressionTokenStream"); var _parseExpressionTokenStream2 = _interopRequireDefault(_parseExpressionTokenStream); var _const = require("../const"); var _utils = require("../utils"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } var parse = function parse(str) { return (0, _parseExpressionTokenStream2["default"])((0, _createExpressionTokenStream2["default"])((0, _createInputStream2["default"])(str))); }; describe("parseExpressionTokenStream", function () { it("should parse identifier", function () { var ast = parse("foobar"); expect(ast).toEqual((0, _utils.createProgram)((0, _utils.createIdentifier)("foobar"))); }); it("should parse null", function () { var ast = parse("null"); expect(ast).toEqual((0, _utils.createProgram)((0, _utils.createValue)(null))); }); it("should parse string", function () { var ast = parse("'asdfsad\"f'"); expect(ast).toEqual((0, _utils.createProgram)((0, _utils.createValue)('asdfsad"f'))); var ast2 = parse("\"asdfsad'f\""); expect(ast2).toEqual((0, _utils.createProgram)((0, _utils.createValue)("asdfsad'f"))); }); describe("should parse boolean", function () { it("should parse true", function () { var ast = parse("true"); expect(ast).toEqual((0, _utils.createProgram)((0, _utils.createValue)(true))); }); it("should parse false", function () { var ast = parse("false"); expect(ast).toEqual((0, _utils.createProgram)((0, _utils.createValue)(false))); }); }); describe("should parse expression", function () { describe("should parse unary expression", function () { it("should parse not unary expression", function () { var ast = parse("not true"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.UnaryExpression, operator: _const.OPERATORS.Not, argument: (0, _utils.createValue)(true) })); }); it("should parse not unary expression within binary expression", function () { var ast = parse("not true or not (true is not false)"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.BinaryExpression, left: { type: _const.TYPES.UnaryExpression, operator: _const.OPERATORS.Not, argument: (0, _utils.createValue)(true) }, operator: _const.OPERATORS.Or, right: { type: _const.TYPES.UnaryExpression, operator: _const.OPERATORS.Not, argument: { type: _const.TYPES.BinaryExpression, left: (0, _utils.createValue)(true), operator: _const.OPERATORS.EqualTo, right: { type: _const.TYPES.UnaryExpression, operator: _const.OPERATORS.Not, argument: (0, _utils.createValue)(false) } } } })); }); it("should parse not unary expression within call expression", function () { var ast = parse("not hello(not a)"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.UnaryExpression, operator: _const.OPERATORS.Not, argument: { type: _const.TYPES.CallExpression, callee: (0, _utils.createIdentifier)("hello"), arguments: [{ type: _const.TYPES.UnaryExpression, operator: _const.OPERATORS.Not, argument: (0, _utils.createIdentifier)("a") }] } })); }); it("should parse subtract unary expression", function () { var ast = parse("-1"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.UnaryExpression, operator: _const.OPERATORS.Subtract, argument: (0, _utils.createValue)(1) })); }); it("should parse add unary expression", function () { var ast = parse("+foo"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.UnaryExpression, operator: _const.OPERATORS.Add, argument: (0, _utils.createIdentifier)("foo") })); }); it("should parse chained unary expression", function () { var ast = parse("not - + foo"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.UnaryExpression, operator: _const.OPERATORS.Not, argument: { type: _const.TYPES.UnaryExpression, operator: _const.OPERATORS.Subtract, argument: { type: _const.TYPES.UnaryExpression, operator: _const.OPERATORS.Add, argument: (0, _utils.createIdentifier)("foo") } } })); }); }); describe("binary expression", function () { it("should parse assign expression", function () { var ast = parse("a = 1"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.AssignExpression, operator: _const.OPERATORS.Assign, left: (0, _utils.createIdentifier)("a"), right: (0, _utils.createValue)(1) })); }); it("should parse binary expression", function () { var ast = parse("a < 1"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.LessThan, left: (0, _utils.createIdentifier)("a"), right: (0, _utils.createValue)(1) })); }); it("should parse an complex binary expression", function () { var ast = parse("a >= 5 and b <= 2000 or c > 5 or d < 5 "); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Or, left: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Or, left: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.And, left: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.GreaterThanOrEqualTo, left: (0, _utils.createIdentifier)("a"), right: (0, _utils.createValue)(5) }, right: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.LessThanOrEqualTo, left: (0, _utils.createIdentifier)("b"), right: (0, _utils.createValue)(2000) } }, right: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.GreaterThan, left: (0, _utils.createIdentifier)("c"), right: (0, _utils.createValue)(5) } }, right: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.LessThan, left: (0, _utils.createIdentifier)("d"), right: (0, _utils.createValue)(5) } })); }); }); it("should parse chained call expression", function () { var ast = parse("hello(1)(2)(3)"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.CallExpression, callee: { type: _const.TYPES.CallExpression, callee: { type: _const.TYPES.CallExpression, callee: (0, _utils.createIdentifier)("hello"), arguments: [(0, _utils.createValue)(1)] }, arguments: [(0, _utils.createValue)(2)] }, arguments: [(0, _utils.createValue)(3)] })); }); it("should parse call expression", function () { var ast = parse('hello(a, "c")'); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.CallExpression, callee: (0, _utils.createIdentifier)("hello"), arguments: [(0, _utils.createIdentifier)("a"), (0, _utils.createValue)("c")] })); }); it("should parse an equation expression", function () { var ast = parse("1 + 2 * 3 - -4"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Subtract, left: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Add, left: (0, _utils.createValue)(1), right: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Multiply, left: (0, _utils.createValue)(2), right: (0, _utils.createValue)(3) } }, right: { type: _const.TYPES.UnaryExpression, operator: _const.OPERATORS.Subtract, argument: (0, _utils.createValue)(4) } })); }); it("should parse a complex expression", function () { var ast = parse("1 * +2 isnt not 3"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.NotEqualTo, left: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Multiply, left: (0, _utils.createValue)(1), right: { type: _const.TYPES.UnaryExpression, operator: _const.OPERATORS.Add, argument: (0, _utils.createValue)(2) } }, right: { type: _const.TYPES.UnaryExpression, operator: _const.OPERATORS.Not, argument: (0, _utils.createValue)(3) } })); }); it("should work with parenthesis", function () { var ast = parse("1 * (2 + 3) / 4"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Divide, left: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Multiply, left: (0, _utils.createValue)(1), right: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Add, left: (0, _utils.createValue)(2), right: (0, _utils.createValue)(3) } }, right: (0, _utils.createValue)(4) })); }); it("should work with call expression [FE-1398]", function () { var ast = parse("hello(1) / 2"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Divide, left: (0, _utils.createCallExpression)((0, _utils.createIdentifier)("hello"), (0, _utils.createValue)(1)), right: (0, _utils.createValue)(2) })); }); }); describe("should parse object expression", function () { it("should parse empty object expression ", function () { var ast = parse("{}"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.ObjectExpression, properties: [] })); }); it("should parse object expression with one key", function () { var stream = (0, _createExpressionTokenStream2["default"])((0, _createInputStream2["default"])("{a: 1}")); var ast = parse("{a: 1}"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.ObjectExpression, properties: [{ key: (0, _utils.createIdentifier)("a"), value: (0, _utils.createValue)(1) }] })); }); it("should parse object expression with multiple keys", function () { var ast = parse("{a: 1, 'b': 2, 1: x, d: 1 + 1}"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.ObjectExpression, properties: [{ key: (0, _utils.createIdentifier)("a"), value: (0, _utils.createValue)(1) }, { key: (0, _utils.createValue)("b"), value: (0, _utils.createValue)(2) }, { key: (0, _utils.createValue)(1), value: (0, _utils.createIdentifier)("x") }, { key: (0, _utils.createIdentifier)("d"), value: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Add, left: (0, _utils.createValue)(1), right: (0, _utils.createValue)(1) } }] })); }); it("should throw errors on wrong schemas", function () { expect(function () { return parse("{a>1}"); }).toThrow(); expect(function () { return parse('{"a"}'); }).toThrow(); expect(function () { return parse("{1}"); }).toThrow(); expect(function () { return parse("{a}"); }).toThrow(); }); it("should parse nested object expression", function () { var ast = parse("{a:{b:{c:1}, d: []}}"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.ObjectExpression, properties: [{ key: (0, _utils.createIdentifier)("a"), value: { type: _const.TYPES.ObjectExpression, properties: [{ key: (0, _utils.createIdentifier)("b"), value: { type: _const.TYPES.ObjectExpression, properties: [{ key: (0, _utils.createIdentifier)("c"), value: (0, _utils.createValue)(1) }] } }, { key: (0, _utils.createIdentifier)("d"), value: { type: _const.TYPES.ArrayExpression, elements: [] } }] } }] })); }); it("should parse array expression", function () { var ast = parse("['a', 'b', c, 1, [], [1], {}, 1 + 1]"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.ArrayExpression, elements: [(0, _utils.createValue)("a"), (0, _utils.createValue)("b"), (0, _utils.createIdentifier)("c"), (0, _utils.createValue)(1), { type: _const.TYPES.ArrayExpression, elements: [] }, { type: _const.TYPES.ArrayExpression, elements: [(0, _utils.createValue)(1)] }, { type: _const.TYPES.ObjectExpression, properties: [] }, { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Add, left: (0, _utils.createValue)(1), right: (0, _utils.createValue)(1) }] })); }); it("should parse Member Expression", function () { var ast = parse("a[1]()[1+1]"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.MemberExpression, object: (0, _utils.createCallExpression)({ type: _const.TYPES.MemberExpression, object: (0, _utils.createIdentifier)("a"), property: (0, _utils.createValue)(1) }), property: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Add, left: (0, _utils.createValue)(1), right: (0, _utils.createValue)(1) } })); }); }); describe("template literal", function () { it("should parse empty template literal", function () { var ast = parse("``"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.TemplateLiteral, expressions: [], quasis: [(0, _utils.createValue)("")] })); }); it("should parse template literal leading and ending with expression", function () { var ast = parse("`{a}`"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.TemplateLiteral, expressions: [(0, _utils.createIdentifier)("a")], quasis: [(0, _utils.createValue)(""), (0, _utils.createValue)("")] })); }); it("should parse complex template literal", function () { var ast = parse("`foo{`hello{a + b}world`}bar{foo(1)}`"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.TemplateLiteral, expressions: [{ type: _const.TYPES.TemplateLiteral, expressions: [{ type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Add, left: (0, _utils.createIdentifier)("a"), right: (0, _utils.createIdentifier)("b") }], quasis: [(0, _utils.createValue)("hello"), (0, _utils.createValue)("world")] }, (0, _utils.createCallExpression)((0, _utils.createIdentifier)("foo"), (0, _utils.createValue)(1))], quasis: [(0, _utils.createValue)("foo"), (0, _utils.createValue)("bar"), (0, _utils.createValue)("")] })); }); }); it("should parse multiple expression", function () { var ast = parse("foobar; \nhello(); 1*2"); expect(ast).toEqual((0, _utils.createProgram)([(0, _utils.createIdentifier)("foobar"), (0, _utils.createCallExpression)((0, _utils.createIdentifier)("hello")), { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Multiply, left: (0, _utils.createValue)(1), right: (0, _utils.createValue)(2) }])); }); describe("if statement", function () { it("should parse if statement with only else", function () { var ast = parse("if true and true else hello()"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.IfStatement, test: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.And, left: (0, _utils.createValue)(true), right: (0, _utils.createValue)(true) }, consequent: null, alternate: (0, _utils.createCallExpression)((0, _utils.createIdentifier)("hello")) })); }); it("should parse if statement with only then", function () { var ast = parse("if true and true then hello()"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.IfStatement, test: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.And, left: (0, _utils.createValue)(true), right: (0, _utils.createValue)(true) }, alternate: null, consequent: (0, _utils.createCallExpression)((0, _utils.createIdentifier)("hello")) })); }); it("should parse simple if statement", function () { var ast = parse("if true and true then 1+2 else hello()"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.IfStatement, test: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.And, left: (0, _utils.createValue)(true), right: (0, _utils.createValue)(true) }, consequent: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Add, left: (0, _utils.createValue)(1), right: (0, _utils.createValue)(2) }, alternate: (0, _utils.createCallExpression)((0, _utils.createIdentifier)("hello")) })); }); it("should parse if statement with blocks", function () { var ast = parse("if true and true then {1+2;} else {hello(); hello2();}"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.IfStatement, test: { type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.And, left: (0, _utils.createValue)(true), right: (0, _utils.createValue)(true) }, consequent: (0, _utils.createProgram)({ type: _const.TYPES.BinaryExpression, operator: _const.OPERATORS.Add, left: (0, _utils.createValue)(1), right: (0, _utils.createValue)(2) }, false), alternate: (0, _utils.createProgram)([(0, _utils.createCallExpression)((0, _utils.createIdentifier)("hello")), (0, _utils.createCallExpression)((0, _utils.createIdentifier)("hello2"))], false) })); }); it("should parse chained if statements ", function () { var ast = parse("if true then 1 else if false then {2}"); expect(ast).toEqual((0, _utils.createProgram)({ type: _const.TYPES.IfStatement, test: (0, _utils.createValue)(true), consequent: (0, _utils.createValue)(1), alternate: { type: _const.TYPES.IfStatement, test: (0, _utils.createValue)(false), consequent: (0, _utils.createProgram)((0, _utils.createValue)(2), false), alternate: null } })); }); }); });