targaryen
Version:
Test Firebase security rules without connecting to Firebase.
106 lines (79 loc) • 2.44 kB
JavaScript
/**
* Node handling unary expressions validation and evaluation.
*
* - Only support "-" and "!" operations (doesn't support "+" operation).
* - "!" only support operations on a boolean element and type validation can't
* be delayed until runtime evaluation.
* - "-" only support operations on a number element but validation can be
* delayed.
*
*/
'use strict';
const base = require('./base');
const types = require('../types');
const Node = base.Node;
const ParseError = base.ParseError;
class UnaryNode extends Node {
init(source, astNode, scope) {
this.argument = Node.from(source, astNode.argument, scope);
}
get operator() {
return this.astNode.operator;
}
/**
* Infer the expression and argument types.
*
* It will throw the the type of the logical NOT operator argument is not a
* number.
*
* A unary negation operator argument type check can be done at run time.
*
* @return {string}
*/
inferType() {
const argType = this.argument.inferredType;
const mustComply = true;
const msg = type => `${this.operator} only operate on ${type}`;
switch (this.operator) {
case '!':
this.assertType(argType, 'boolean', {mustComply, msg: msg('boolean')});
return 'boolean';
case '-':
this.assertType(argType, 'number', {msg: msg('number')});
return 'number';
default:
throw new ParseError(this, `Unary expressions may not contain "${this.operator}" operator.`);
}
}
evaluate(state) {
const value = this.argument.evaluate(state);
return this.evaluateWith(state, value);
}
debug(state, cb) {
const argument = this.argument.debug(state, cb);
const value = this.evaluateWith(state, argument.value);
const detailed = `${this.operator}(${argument.detailed})`;
cb({
type: this.astNode.type,
original: this.original,
detailed,
value
});
return {detailed, value};
}
evaluateWith(state, value) {
switch (this.operator) {
case '!':
return !value;
case '-':
this.assertType(types.from(value), 'number', {mustComply: true, msg: '- only operate on number'});
return -value;
default:
throw new ParseError(this, `Unary expressions may not contain "${this.operator}" operator.`);
}
}
toString() {
return `${this.operator}(${this.argument})`;
}
}
Node.register('UnaryExpression', UnaryNode);