/test/ng/parseSpec.js
JavaScript | 3510 lines | 3484 code | 22 blank | 4 comment | 7 complexity | a8c0895bf1a8fd4a62fad8e255d17ada MD5 | raw file
Possible License(s): JSON
Large files files are truncated, but you can click here to view the full file
- 'use strict';
- describe('parser', function() {
- beforeEach(function() {
- /* global getterFnCacheDefault: true */
- /* global getterFnCacheExpensive: true */
- // clear caches
- getterFnCacheDefault = createMap();
- getterFnCacheExpensive = createMap();
- });
- describe('lexer', function() {
- var lex;
- beforeEach(function() {
- /* global Lexer: false */
- lex = function() {
- var lexer = new Lexer({csp: false});
- return lexer.lex.apply(lexer, arguments);
- };
- });
- it('should only match number chars with isNumber', function() {
- expect(Lexer.prototype.isNumber('0')).toBe(true);
- expect(Lexer.prototype.isNumber('')).toBeFalsy();
- expect(Lexer.prototype.isNumber(' ')).toBeFalsy();
- expect(Lexer.prototype.isNumber(0)).toBeFalsy();
- expect(Lexer.prototype.isNumber(false)).toBeFalsy();
- expect(Lexer.prototype.isNumber(true)).toBeFalsy();
- expect(Lexer.prototype.isNumber(undefined)).toBeFalsy();
- expect(Lexer.prototype.isNumber(null)).toBeFalsy();
- });
- it('should tokenize a string', function() {
- var tokens = lex("a.bc[22]+1.3|f:'a\\\'c':\"d\\\"e\"");
- var i = 0;
- expect(tokens[i].index).toEqual(0);
- expect(tokens[i].text).toEqual('a');
- i++;
- expect(tokens[i].index).toEqual(1);
- expect(tokens[i].text).toEqual('.');
- i++;
- expect(tokens[i].index).toEqual(2);
- expect(tokens[i].text).toEqual('bc');
- i++;
- expect(tokens[i].index).toEqual(4);
- expect(tokens[i].text).toEqual('[');
- i++;
- expect(tokens[i].index).toEqual(5);
- expect(tokens[i].text).toEqual('22');
- expect(tokens[i].value).toEqual(22);
- expect(tokens[i].constant).toEqual(true);
- i++;
- expect(tokens[i].index).toEqual(7);
- expect(tokens[i].text).toEqual(']');
- i++;
- expect(tokens[i].index).toEqual(8);
- expect(tokens[i].text).toEqual('+');
- i++;
- expect(tokens[i].index).toEqual(9);
- expect(tokens[i].text).toEqual('1.3');
- expect(tokens[i].value).toEqual(1.3);
- expect(tokens[i].constant).toEqual(true);
- i++;
- expect(tokens[i].index).toEqual(12);
- expect(tokens[i].text).toEqual('|');
- i++;
- expect(tokens[i].index).toEqual(13);
- expect(tokens[i].text).toEqual('f');
- i++;
- expect(tokens[i].index).toEqual(14);
- expect(tokens[i].text).toEqual(':');
- i++;
- expect(tokens[i].index).toEqual(15);
- expect(tokens[i].value).toEqual("a'c");
- i++;
- expect(tokens[i].index).toEqual(21);
- expect(tokens[i].text).toEqual(':');
- i++;
- expect(tokens[i].index).toEqual(22);
- expect(tokens[i].value).toEqual('d"e');
- });
- it('should tokenize identifiers with spaces around dots the same as without spaces', function() {
- function getText(t) { return t.text; }
- var spaces = lex('foo. bar . baz').map(getText);
- var noSpaces = lex('foo.bar.baz').map(getText);
- expect(spaces).toEqual(noSpaces);
- });
- it('should tokenize undefined', function() {
- var tokens = lex("undefined");
- var i = 0;
- expect(tokens[i].index).toEqual(0);
- expect(tokens[i].text).toEqual('undefined');
- });
- it('should tokenize quoted string', function() {
- var str = "['\\'', \"\\\"\"]";
- var tokens = lex(str);
- expect(tokens[1].index).toEqual(1);
- expect(tokens[1].value).toEqual("'");
- expect(tokens[3].index).toEqual(7);
- expect(tokens[3].value).toEqual('"');
- });
- it('should tokenize escaped quoted string', function() {
- var str = '"\\"\\n\\f\\r\\t\\v\\u00A0"';
- var tokens = lex(str);
- expect(tokens[0].value).toEqual('"\n\f\r\t\v\u00A0');
- });
- it('should tokenize unicode', function() {
- var tokens = lex('"\\u00A0"');
- expect(tokens.length).toEqual(1);
- expect(tokens[0].value).toEqual('\u00a0');
- });
- it('should ignore whitespace', function() {
- var tokens = lex("a \t \n \r b");
- expect(tokens[0].text).toEqual('a');
- expect(tokens[1].text).toEqual('b');
- });
- it('should tokenize relation and equality', function() {
- var tokens = lex("! == != < > <= >= === !==");
- expect(tokens[0].text).toEqual('!');
- expect(tokens[1].text).toEqual('==');
- expect(tokens[2].text).toEqual('!=');
- expect(tokens[3].text).toEqual('<');
- expect(tokens[4].text).toEqual('>');
- expect(tokens[5].text).toEqual('<=');
- expect(tokens[6].text).toEqual('>=');
- expect(tokens[7].text).toEqual('===');
- expect(tokens[8].text).toEqual('!==');
- });
- it('should tokenize logical and ternary', function() {
- var tokens = lex("&& || ? :");
- expect(tokens[0].text).toEqual('&&');
- expect(tokens[1].text).toEqual('||');
- expect(tokens[2].text).toEqual('?');
- expect(tokens[3].text).toEqual(':');
- });
- it('should tokenize statements', function() {
- var tokens = lex("a;b;");
- expect(tokens[0].text).toEqual('a');
- expect(tokens[1].text).toEqual(';');
- expect(tokens[2].text).toEqual('b');
- expect(tokens[3].text).toEqual(';');
- });
- it('should tokenize function invocation', function() {
- var tokens = lex("a()");
- expect(tokens.map(function(t) { return t.text;})).toEqual(['a', '(', ')']);
- });
- it('should tokenize method invocation', function() {
- var tokens = lex("a.b.c (d) - e.f()");
- expect(tokens.map(function(t) { return t.text;})).
- toEqual(['a', '.', 'b', '.', 'c', '(', 'd', ')', '-', 'e', '.', 'f', '(', ')']);
- });
- it('should tokenize number', function() {
- var tokens = lex("0.5");
- expect(tokens[0].value).toEqual(0.5);
- });
- it('should tokenize negative number', inject(function($rootScope) {
- var value = $rootScope.$eval("-0.5");
- expect(value).toEqual(-0.5);
- value = $rootScope.$eval("{a:-0.5}");
- expect(value).toEqual({a:-0.5});
- }));
- it('should tokenize number with exponent', inject(function($rootScope) {
- var tokens = lex("0.5E-10");
- expect(tokens[0].value).toEqual(0.5E-10);
- expect($rootScope.$eval("0.5E-10")).toEqual(0.5E-10);
- tokens = lex("0.5E+10");
- expect(tokens[0].value).toEqual(0.5E+10);
- }));
- it('should throws exception for invalid exponent', function() {
- expect(function() {
- lex("0.5E-");
- }).toThrowMinErr('$parse', 'lexerr', 'Lexer Error: Invalid exponent at column 4 in expression [0.5E-].');
- expect(function() {
- lex("0.5E-A");
- }).toThrowMinErr('$parse', 'lexerr', 'Lexer Error: Invalid exponent at column 4 in expression [0.5E-A].');
- });
- it('should tokenize number starting with a dot', function() {
- var tokens = lex(".5");
- expect(tokens[0].value).toEqual(0.5);
- });
- it('should throw error on invalid unicode', function() {
- expect(function() {
- lex("'\\u1''bla'");
- }).toThrowMinErr("$parse", "lexerr", "Lexer Error: Invalid unicode escape [\\u1''b] at column 2 in expression ['\\u1''bla'].");
- });
- });
- describe('ast', function() {
- var createAst;
- beforeEach(function() {
- /* global AST: false */
- createAst = function() {
- var lexer = new Lexer({csp: false});
- var ast = new AST(lexer, {csp: false});
- return ast.ast.apply(ast, arguments);
- };
- });
- it('should handle an empty list of tokens', function() {
- expect(createAst('')).toEqual({type: 'Program', body: []});
- });
- it('should understand identifiers', function() {
- expect(createAst('foo')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: { type: 'Identifier', name: 'foo' }
- }
- ]
- }
- );
- });
- it('should understand non-computed member expressions', function() {
- expect(createAst('foo.bar')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'MemberExpression',
- object: { type: 'Identifier', name: 'foo'},
- property: {type: 'Identifier', name: 'bar'},
- computed: false
- }
- }
- ]
- }
- );
- });
- it('should associate non-computed member expressions left-to-right', function() {
- expect(createAst('foo.bar.baz')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'MemberExpression',
- object: {
- type: 'MemberExpression',
- object: { type: 'Identifier', name: 'foo'},
- property: { type: 'Identifier', name: 'bar' },
- computed: false
- },
- property: {type: 'Identifier', name: 'baz'},
- computed: false
- }
- }
- ]
- }
- );
- });
- it('should understand computed member expressions', function() {
- expect(createAst('foo[bar]')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'MemberExpression',
- object: { type: 'Identifier', name: 'foo'},
- property: {type: 'Identifier', name: 'bar'},
- computed: true
- }
- }
- ]
- }
- );
- });
- it('should associate computed member expressions left-to-right', function() {
- expect(createAst('foo[bar][baz]')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'MemberExpression',
- object: {
- type: 'MemberExpression',
- object: { type: 'Identifier', name: 'foo' },
- property: { type: 'Identifier', name: 'bar' },
- computed: true
- },
- property: { type: 'Identifier', name: 'baz' },
- computed: true
- }
- }
- ]
- }
- );
- });
- it('should understand call expressions', function() {
- expect(createAst('foo()')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'CallExpression',
- callee: { type: 'Identifier', name: 'foo'},
- arguments: []
- }
- }
- ]
- }
- );
- });
- it('should parse call expression arguments', function() {
- expect(createAst('foo(bar, baz)')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'CallExpression',
- callee: { type: 'Identifier', name: 'foo'},
- arguments: [
- { type: 'Identifier', name: 'bar' },
- { type: 'Identifier', name: 'baz' }
- ]
- }
- }
- ]
- }
- );
- });
- it('should parse call expression left-to-right', function() {
- expect(createAst('foo(bar, baz)(man, shell)')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'CallExpression',
- callee: {
- type: 'CallExpression',
- callee: { type: 'Identifier', name: 'foo' },
- arguments: [
- { type: 'Identifier', name: 'bar' },
- { type: 'Identifier', name: 'baz' }
- ]
- },
- arguments: [
- { type: 'Identifier', name: 'man' },
- { type: 'Identifier', name: 'shell' }
- ]
- }
- }
- ]
- }
- );
- });
- it('should keep the context when having superfluous parenthesis', function() {
- expect(createAst('(foo)(bar, baz)')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'CallExpression',
- callee: { type: 'Identifier', name: 'foo'},
- arguments: [
- { type: 'Identifier', name: 'bar' },
- { type: 'Identifier', name: 'baz' }
- ]
- }
- }
- ]
- }
- );
- });
- it('should treat member expressions and call expression with the same precedence', function() {
- expect(createAst('foo.bar[baz]()')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'CallExpression',
- callee: {
- type: 'MemberExpression',
- object: {
- type: 'MemberExpression',
- object: { type: 'Identifier', name: 'foo' },
- property: { type: 'Identifier', name: 'bar' },
- computed: false
- },
- property: { type: 'Identifier', name: 'baz' },
- computed: true
- },
- arguments: []
- }
- }
- ]
- }
- );
- expect(createAst('foo[bar]().baz')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'MemberExpression',
- object: {
- type: 'CallExpression',
- callee: {
- type: 'MemberExpression',
- object: { type: 'Identifier', name: 'foo' },
- property: { type: 'Identifier', name: 'bar' },
- computed: true
- },
- arguments: []
- },
- property: { type: 'Identifier', name: 'baz' },
- computed: false
- }
- }
- ]
- }
- );
- expect(createAst('foo().bar[baz]')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'MemberExpression',
- object: {
- type: 'MemberExpression',
- object: {
- type: 'CallExpression',
- callee: { type: 'Identifier', name: 'foo' },
- arguments: [] },
- property: { type: 'Identifier', name: 'bar' },
- computed: false
- },
- property: { type: 'Identifier', name: 'baz' },
- computed: true
- }
- }
- ]
- }
- );
- });
- it('should understand literals', function() {
- // In a strict sense, `undefined` is not a literal but an identifier
- forEach({'123': 123, '"123"': '123', 'true': true, 'false': false, 'null': null, 'undefined': undefined}, function(value, expression) {
- expect(createAst(expression)).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: { type: 'Literal', value: value }
- }
- ]
- }
- );
- });
- });
- it('should understand the `this` expression', function() {
- expect(createAst('this')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: { type: 'ThisExpression' }
- }
- ]
- }
- );
- });
- it('should not confuse `this`, `undefined`, `true`, `false`, `null` when used as identfiers', function() {
- forEach(['this', 'undefined', 'true', 'false', 'null'], function(identifier) {
- expect(createAst('foo.' + identifier)).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'MemberExpression',
- object: { type: 'Identifier', name: 'foo' },
- property: { type: 'Identifier', name: identifier },
- computed: false
- }
- }
- ]
- }
- );
- });
- });
- it('should throw when trying to use non-identifiers as identifiers', function() {
- expect(function() { createAst('foo.)'); }).toThrowMinErr('$parse', 'syntax',
- "Syntax Error: Token ')' is not a valid identifier at column 5 of the expression [foo.)");
- });
- it('should throw when all tokens are not consumed', function() {
- expect(function() { createAst('foo bar'); }).toThrowMinErr('$parse', 'syntax',
- "Syntax Error: Token 'bar' is an unexpected token at column 5 of the expression [foo bar] starting at [bar]");
- });
- it('should understand the unary operators `-`, `+` and `!`', function() {
- forEach(['-', '+', '!'], function(operator) {
- expect(createAst(operator + 'foo')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'UnaryExpression',
- operator: operator,
- prefix: true,
- argument: { type: 'Identifier', name: 'foo' }
- }
- }
- ]
- }
- );
- });
- });
- it('should handle all unary operators with the same precedence', function() {
- forEach([['+', '-', '!'], ['-', '!', '+'], ['!', '+', '-']], function(operators) {
- expect(createAst(operators.join('') + 'foo')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'UnaryExpression',
- operator: operators[0],
- prefix: true,
- argument: {
- type: 'UnaryExpression',
- operator: operators[1],
- prefix: true,
- argument: {
- type: 'UnaryExpression',
- operator: operators[2],
- prefix: true,
- argument: { type: 'Identifier', name: 'foo' }
- }
- }
- }
- }
- ]
- }
- );
- });
- });
- it('should be able to understand binary operators', function() {
- forEach(['*', '/', '%', '+', '-', '<', '>', '<=', '>=', '==','!=','===','!=='], function(operator) {
- expect(createAst('foo' + operator + 'bar')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'BinaryExpression',
- operator: operator,
- left: { type: 'Identifier', name: 'foo' },
- right: { type: 'Identifier', name: 'bar' }
- }
- }
- ]
- }
- );
- });
- });
- it('should associate binary operators with the same precendence left-to-right', function() {
- var operatorsByPrecedence = [['*', '/', '%'], ['+', '-'], ['<', '>', '<=', '>='], ['==','!=','===','!==']];
- forEach(operatorsByPrecedence, function(operators) {
- forEach(operators, function(op1) {
- forEach(operators, function(op2) {
- expect(createAst('foo' + op1 + 'bar' + op2 + 'baz')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'BinaryExpression',
- operator: op2,
- left: {
- type: 'BinaryExpression',
- operator: op1,
- left: { type: 'Identifier', name: 'foo' },
- right: { type: 'Identifier', name: 'bar' }
- },
- right: { type: 'Identifier', name: 'baz' }
- }
- }
- ]
- }
- );
- });
- });
- });
- });
- it('should give higher prcedence to member calls than to unary expressions', function() {
- forEach(['!', '+', '-'], function(operator) {
- expect(createAst(operator + 'foo()')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'UnaryExpression',
- operator: operator,
- prefix: true,
- argument: {
- type: 'CallExpression',
- callee: { type: 'Identifier', name: 'foo' },
- arguments: []
- }
- }
- }
- ]
- }
- );
- expect(createAst(operator + 'foo.bar')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'UnaryExpression',
- operator: operator,
- prefix: true,
- argument: {
- type: 'MemberExpression',
- object: { type: 'Identifier', name: 'foo' },
- property: { type: 'Identifier', name: 'bar' },
- computed: false
- }
- }
- }
- ]
- }
- );
- expect(createAst(operator + 'foo[bar]')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'UnaryExpression',
- operator: operator,
- prefix: true,
- argument: {
- type: 'MemberExpression',
- object: { type: 'Identifier', name: 'foo' },
- property: { type: 'Identifier', name: 'bar' },
- computed: true
- }
- }
- }
- ]
- }
- );
- });
- });
- it('should give higher precedence to unary operators over multiplicative operators', function() {
- forEach(['!', '+', '-'], function(op1) {
- forEach(['*', '/', '%'], function(op2) {
- expect(createAst(op1 + 'foo' + op2 + op1 + 'bar')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'BinaryExpression',
- operator: op2,
- left: {
- type: 'UnaryExpression',
- operator: op1,
- prefix: true,
- argument: { type: 'Identifier', name: 'foo' }
- },
- right: {
- type: 'UnaryExpression',
- operator: op1,
- prefix: true,
- argument: { type: 'Identifier', name: 'bar' }
- }
- }
- }
- ]
- }
- );
- });
- });
- });
- it('should give binary operators their right precedence', function() {
- var operatorsByPrecedence = [['*', '/', '%'], ['+', '-'], ['<', '>', '<=', '>='], ['==','!=','===','!==']];
- for (var i = 0; i < operatorsByPrecedence.length - 1; ++i) {
- forEach(operatorsByPrecedence[i], function(op1) {
- forEach(operatorsByPrecedence[i + 1], function(op2) {
- expect(createAst('foo' + op1 + 'bar' + op2 + 'baz' + op1 + 'man')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'BinaryExpression',
- operator: op2,
- left: {
- type: 'BinaryExpression',
- operator: op1,
- left: { type: 'Identifier', name: 'foo' },
- right: { type: 'Identifier', name: 'bar' }
- },
- right: {
- type: 'BinaryExpression',
- operator: op1,
- left: { type: 'Identifier', name: 'baz' },
- right: { type: 'Identifier', name: 'man' }
- }
- }
- }
- ]
- }
- );
- });
- });
- }
- });
- it('should understand logical operators', function() {
- forEach(['||', '&&'], function(operator) {
- expect(createAst('foo' + operator + 'bar')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'LogicalExpression',
- operator: operator,
- left: { type: 'Identifier', name: 'foo' },
- right: { type: 'Identifier', name: 'bar' }
- }
- }
- ]
- }
- );
- });
- });
- it('should associate logical operators left-to-right', function() {
- forEach(['||', '&&'], function(op) {
- expect(createAst('foo' + op + 'bar' + op + 'baz')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'LogicalExpression',
- operator: op,
- left: {
- type: 'LogicalExpression',
- operator: op,
- left: { type: 'Identifier', name: 'foo' },
- right: { type: 'Identifier', name: 'bar' }
- },
- right: { type: 'Identifier', name: 'baz' }
- }
- }
- ]
- }
- );
- });
- });
- it('should understand ternary operators', function() {
- expect(createAst('foo?bar:baz')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ConditionalExpression',
- test: { type: 'Identifier', name: 'foo' },
- alternate: { type: 'Identifier', name: 'bar' },
- consequent: { type: 'Identifier', name: 'baz' }
- }
- }
- ]
- }
- );
- });
- it('should associate the conditional operator right-to-left', function() {
- expect(createAst('foo0?foo1:foo2?bar0?bar1:bar2:man0?man1:man2')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ConditionalExpression',
- test: { type: 'Identifier', name: 'foo0' },
- alternate: { type: 'Identifier', name: 'foo1' },
- consequent: {
- type: 'ConditionalExpression',
- test: { type: 'Identifier', name: 'foo2' },
- alternate: {
- type: 'ConditionalExpression',
- test: { type: 'Identifier', name: 'bar0' },
- alternate: { type: 'Identifier', name: 'bar1' },
- consequent: { type: 'Identifier', name: 'bar2' }
- },
- consequent: {
- type: 'ConditionalExpression',
- test: { type: 'Identifier', name: 'man0' },
- alternate: { type: 'Identifier', name: 'man1' },
- consequent: { type: 'Identifier', name: 'man2' }
- }
- }
- }
- }
- ]
- }
- );
- });
- it('should understand assignment operator', function() {
- // Currently, only `=` is supported
- expect(createAst('foo=bar')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'AssignmentExpression',
- left: { type: 'Identifier', name: 'foo' },
- right: { type: 'Identifier', name: 'bar' },
- operator: '='
- }
- }
- ]
- }
- );
- });
- it('should associate assignments right-to-left', function() {
- // Currently, only `=` is supported
- expect(createAst('foo=bar=man')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'AssignmentExpression',
- left: { type: 'Identifier', name: 'foo' },
- right: {
- type: 'AssignmentExpression',
- left: { type: 'Identifier', name: 'bar' },
- right: { type: 'Identifier', name: 'man' },
- operator: '='
- },
- operator: '='
- }
- }
- ]
- }
- );
- });
- it('should give higher precedence to equality than to the logical `and` operator', function() {
- forEach(['==','!=','===','!=='], function(operator) {
- expect(createAst('foo' + operator + 'bar && man' + operator + 'shell')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'LogicalExpression',
- operator: '&&',
- left: {
- type: 'BinaryExpression',
- operator: operator,
- left: { type: 'Identifier', name: 'foo' },
- right: { type: 'Identifier', name: 'bar' }
- },
- right: {
- type: 'BinaryExpression',
- operator: operator,
- left: { type: 'Identifier', name: 'man' },
- right: { type: 'Identifier', name: 'shell' }
- }
- }
- }
- ]
- }
- );
- });
- });
- it('should give higher precedence to logical `and` than to logical `or`', function() {
- expect(createAst('foo&&bar||man&&shell')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'LogicalExpression',
- operator: '||',
- left: {
- type: 'LogicalExpression',
- operator: '&&',
- left: { type: 'Identifier', name: 'foo' },
- right: { type: 'Identifier', name: 'bar' }
- },
- right: {
- type: 'LogicalExpression',
- operator: '&&',
- left: { type: 'Identifier', name: 'man' },
- right: { type: 'Identifier', name: 'shell' }
- }
- }
- }
- ]
- }
- );
- });
- it('should give higher precedence to the logical `or` than to the conditional operator', function() {
- expect(createAst('foo||bar?man:shell')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ConditionalExpression',
- test: {
- type: 'LogicalExpression',
- operator: '||',
- left: { type: 'Identifier', name: 'foo' },
- right: { type: 'Identifier', name: 'bar' }
- },
- alternate: { type: 'Identifier', name: 'man' },
- consequent: { type: 'Identifier', name: 'shell' }
- }
- }
- ]
- }
- );
- });
- it('should give higher precedence to the conditional operator than to assignment operators', function() {
- expect(createAst('foo=bar?man:shell')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'AssignmentExpression',
- left: { type: 'Identifier', name: 'foo' },
- right: {
- type: 'ConditionalExpression',
- test: { type: 'Identifier', name: 'bar' },
- alternate: { type: 'Identifier', name: 'man' },
- consequent: { type: 'Identifier', name: 'shell' }
- },
- operator: '='
- }
- }
- ]
- }
- );
- });
- it('should understand array literals', function() {
- expect(createAst('[]')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ArrayExpression',
- elements: []
- }
- }
- ]
- }
- );
- expect(createAst('[foo]')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ArrayExpression',
- elements: [
- { type: 'Identifier', name: 'foo' }
- ]
- }
- }
- ]
- }
- );
- expect(createAst('[foo,]')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ArrayExpression',
- elements: [
- { type: 'Identifier', name: 'foo' }
- ]
- }
- }
- ]
- }
- );
- expect(createAst('[foo,bar,man,shell]')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ArrayExpression',
- elements: [
- { type: 'Identifier', name: 'foo' },
- { type: 'Identifier', name: 'bar' },
- { type: 'Identifier', name: 'man' },
- { type: 'Identifier', name: 'shell' }
- ]
- }
- }
- ]
- }
- );
- expect(createAst('[foo,bar,man,shell,]')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ArrayExpression',
- elements: [
- { type: 'Identifier', name: 'foo' },
- { type: 'Identifier', name: 'bar' },
- { type: 'Identifier', name: 'man' },
- { type: 'Identifier', name: 'shell' }
- ]
- }
- }
- ]
- }
- );
- });
- it('should understand objects', function() {
- expect(createAst('{}')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ObjectExpression',
- properties: []
- }
- }
- ]
- }
- );
- expect(createAst('{foo: bar}')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ObjectExpression',
- properties: [
- {
- type: 'Property',
- kind: 'init',
- key: { type: 'Identifier', name: 'foo' },
- value: { type: 'Identifier', name: 'bar' }
- }
- ]
- }
- }
- ]
- }
- );
- expect(createAst('{foo: bar,}')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ObjectExpression',
- properties: [
- {
- type: 'Property',
- kind: 'init',
- key: { type: 'Identifier', name: 'foo' },
- value: { type: 'Identifier', name: 'bar' }
- }
- ]
- }
- }
- ]
- }
- );
- expect(createAst('{foo: bar, "man": "shell", 42: 23}')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ObjectExpression',
- properties: [
- {
- type: 'Property',
- kind: 'init',
- key: { type: 'Identifier', name: 'foo' },
- value: { type: 'Identifier', name: 'bar' }
- },
- {
- type: 'Property',
- kind: 'init',
- key: { type: 'Literal', value: 'man' },
- value: { type: 'Literal', value: 'shell' }
- },
- {
- type: 'Property',
- kind: 'init',
- key: { type: 'Literal', value: 42 },
- value: { type: 'Literal', value: 23 }
- }
- ]
- }
- }
- ]
- }
- );
- expect(createAst('{foo: bar, "man": "shell", 42: 23,}')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ObjectExpression',
- properties: [
- {
- type: 'Property',
- kind: 'init',
- key: { type: 'Identifier', name: 'foo' },
- value: { type: 'Identifier', name: 'bar' }
- },
- {
- type: 'Property',
- kind: 'init',
- key: { type: 'Literal', value: 'man' },
- value: { type: 'Literal', value: 'shell' }
- },
- {
- type: 'Property',
- kind: 'init',
- key: { type: 'Literal', value: 42 },
- value: { type: 'Literal', value: 23 }
- }
- ]
- }
- }
- ]
- }
- );
- });
- it('should understand multiple expressions', function() {
- expect(createAst('foo = bar; man = shell')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'AssignmentExpression',
- left: { type: 'Identifier', name: 'foo' },
- right: { type: 'Identifier', name: 'bar' },
- operator: '='
- }
- },
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'AssignmentExpression',
- left: { type: 'Identifier', name: 'man' },
- right: { type: 'Identifier', name: 'shell' },
- operator: '='
- }
- }
- ]
- }
- );
- });
- // This is non-standard syntax
- it('should understand filters', function() {
- expect(createAst('foo | bar')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'CallExpression',
- callee: { type: 'Identifier', name: 'bar'},
- arguments: [
- { type: 'Identifier', name: 'foo' }
- ],
- filter: true
- }
- }
- ]
- }
- );
- });
- it('should understand filters with extra parameters', function() {
- expect(createAst('foo | bar:baz')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'CallExpression',
- callee: { type: 'Identifier', name: 'bar'},
- arguments: [
- { type: 'Identifier', name: 'foo' },
- { type: 'Identifier', name: 'baz' }
- ],
- filter: true
- }
- }
- ]
- }
- );
- });
- it('should associate filters right-to-left', function() {
- expect(createAst('foo | bar:man | shell')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'CallExpression',
- callee: { type: 'Identifier', name: 'shell' },
- arguments: [
- {
- type: 'CallExpression',
- callee: { type: 'Identifier', name: 'bar' },
- arguments: [
- { type: 'Identifier', name: 'foo' },
- { type: 'Identifier', name: 'man' }
- ],
- filter: true
- }
- ],
- filter: true
- }
- }
- ]
- }
- );
- });
- it('should give higher precedence to assignments over filters', function() {
- expect(createAst('foo=bar | man')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'CallExpression',
- callee: { type: 'Identifier', name: 'man' },
- arguments: [
- {
- type: 'AssignmentExpression',
- left: { type: 'Identifier', name: 'foo' },
- right: { type: 'Identifier', name: 'bar' },
- operator: '='
- }
- ],
- filter: true
- }
- }
- ]
- }
- );
- });
- it('should accept expression as filters parameters', function() {
- expect(createAst('foo | bar:baz=man')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'CallExpression',
- callee: { type: 'Identifier', name: 'bar' },
- arguments: [
- { type: 'Identifier', name: 'foo' },
- {
- type: 'AssignmentExpression',
- left: { type: 'Identifier', name: 'baz' },
- right: { type: 'Identifier', name: 'man' },
- operator: '='
- }
- ],
- filter: true
- }
- }
- ]
- }
- );
- });
- it('should accept expression as computer members', function() {
- expect(createAst('foo[a = 1]')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'MemberExpression',
- object: { type: 'Identifier', name: 'foo' },
- property: {
- type: 'AssignmentExpression',
- left: { type: 'Identifier', name: 'a' },
- right: { type: 'Literal', value: 1 },
- operator: '='
- },
- computed: true
- }
- }
- ]
- }
- );
- });
- it('should accept expression in function arguments', function() {
- expect(createAst('foo(a = 1)')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'CallExpression',
- callee: { type: 'Identifier', name: 'foo' },
- arguments: [
- {
- type: 'AssignmentExpression',
- left: { type: 'Identifier', name: 'a' },
- right: { type: 'Literal', value: 1 },
- operator: '='
- }
- ]
- }
- }
- ]
- }
- );
- });
- it('should accept expression as part of ternary operators', function() {
- expect(createAst('foo || bar ? man = 1 : shell = 1')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ConditionalExpression',
- test: {
- type: 'LogicalExpression',
- operator: '||',
- left: { type: 'Identifier', name: 'foo' },
- right: { type: 'Identifier', name: 'bar' }
- },
- alternate: {
- type: 'AssignmentExpression',
- left: { type: 'Identifier', name: 'man' },
- right: { type: 'Literal', value: 1 },
- operator: '='
- },
- consequent: {
- type: 'AssignmentExpression',
- left: { type: 'Identifier', name: 'shell' },
- right: { type: 'Literal', value: 1 },
- operator: '='
- }
- }
- }
- ]
- }
- );
- });
- it('should accept expression as part of array literals', function() {
- expect(createAst('[foo = 1]')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ArrayExpression',
- elements: [
- {
- type: 'AssignmentExpression',
- left: { type: 'Identifier', name: 'foo' },
- right: { type: 'Literal', value: 1 },
- operator: '='
- }
- ]
- }
- }
- ]
- }
- );
- });
- it('should accept expression as part of object literals', function() {
- expect(createAst('{foo: bar = 1}')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'ObjectExpression',
- properties: [
- {
- type: 'Property',
- kind: 'init',
- key: { type: 'Identifier', name: 'foo' },
- value: {
- type: 'AssignmentExpression',
- left: { type: 'Identifier', name: 'bar' },
- right: { type: 'Literal', value: 1 },
- operator: '='
- }
- }
- ]
- }
- }
- ]
- }
- );
- });
- it('should be possible to use parenthesis to indicate precedence', function() {
- expect(createAst('(foo + bar).man')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'MemberExpression',
- object: {
- type: 'BinaryExpression',
- operator: '+',
- left: { type: 'Identifier', name: 'foo' },
- right: { type: 'Identifier', name: 'bar' }
- },
- property: { type: 'Identifier', name: 'man' },
- computed: false
- }
- }
- ]
- }
- );
- });
- it('should skip empty expressions', function() {
- expect(createAst('foo;;;;bar')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: { type: 'Identifier', name: 'foo' }
- },
- {
- type: 'ExpressionStatement',
- expression: { type: 'Identifier', name: 'bar' }
- }
- ]
- }
- );
- expect(createAst(';foo')).toEqual(
- {
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: { type: 'Identifier', name: 'foo' }
- }
- ]
- }
- );
- expect(createAst('foo;')).toEqual({
- type: 'Program',
- body: [
- {
- type: 'ExpressionStatement',
- expression: { type: 'Identifier', name: 'foo' }
- }
- ]
- });
- expect(createAst(';;;;')).toEqual({type: 'Program', body: []});
- expect(createAst('')).toEqual({type: 'Program', body: []});
- …
Large files files are truncated, but you can click here to view the full file