@airtasker/form-schema-compiler
Version:
a form schema compiler
542 lines (531 loc) • 21 kB
JavaScript
"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
}
}));
});
});
});