Source: logEventMatcher/lemInterpreter.js

/**
 * Constructs an interpreter that uses the given ast
 * 
 * @classdesc
 * 
 * <p>
 * LEMInterpreter interprets a text query given its
 * {@link AST abstract syntax tree} form and a {@link LogEvent} to check.
 * </p>
 * 
 * <p>
 * Upon calling {@link LEMInterpreter#interpret}, this class uses its visitFoo
 * methods to traverse the provided AST. Thus, this class is part of the
 * {@link http://en.wikipedia.org/wiki/Visitor_pattern visitor pattern} and
 * implements the ASTVisitor interface.
 * </p>
 * 
 * @constructor
 * @param {AST} ast The abstract syntax tree that represents the query
 */
function LEMInterpreter(ast) {

    /** @private */
    this.ast = ast;

}

/**
 * Checks if the query associated with this LEMInterpreter matches the provided
 * LogEvent. This object's abstract syntax tree is interpreted using the
 * provided LogEvent's fields as the environment - the binding of identifiers to
 * values.
 * 
 * @param {LogEvent} logEvent The LogEvent to check
 * @returns {Boolean} True if the query represented by this.ast matches the
 *          provided log event
 */
LEMInterpreter.prototype.interpret = function(logEvent) {
    var env = logEvent.getFields();
    env["event"] = logEvent.getText(); // TODO
    var ret = this.ast.accept(this, env);
    return ret.val;
};

/**
 * <p>
 * Visits and interprets a {@link BinaryOp} in accordance with the visitor
 * pattern
 * </p>
 * 
 * <p>
 * This method always produces a boolean-valued {@link LEMInterpreterValue}.
 * The exact value of that boolean is the result of evaluating the binary
 * operation represented by the BinaryOp that this method is visiting. See
 * {@link BinaryOp} for information on the semantics of each operator.
 * </p>
 * 
 * @protected
 * @param {BinaryOp} ast
 * @param {Object<String, String>} env The environment - the binding of
 *            identifiers to values as pure-object mapping of strings to
 *            strings.
 * @returns {LEMInterpreterValue} The value resulting from visiting and handling
 *          the ast node.
 */
LEMInterpreter.prototype.visitBinaryOp = function(ast, env) {
    var lhs = ast.getLHS().accept(this, env);
    var rhs = ast.getRHS().accept(this, env);

    if (ast.getOp() == BinaryOp.EQUALS || ast.getOp() == BinaryOp.NOT_EQUALS) {
        var val = false;
        if (rhs.getType() == LEMInterpreterValue.REGEX) {
            val = rhs.getVal().test(lhs.getVal());
        }
        else if (rhs.getType() == LEMInterpreterValue.STRING) {
            val = (lhs.getVal() == rhs.getVal());
        }
        else {
            throw new Exception("RHS must be a regex or string.");
        }

        if (ast.getOp() == LEMInterpreterValue.NOT_EQUALS) {
            val = !val;
        }

        return new LEMInterpreterValue(LEMInterpreterValue.BOOLEAN, val);
    }
    else if (ast.getOp() == BinaryOp.OR) {
        return new LEMInterpreterValue(LEMInterpreterValue.BOOLEAN, lhs.getVal() || rhs.getVal());
    }
    else if (ast.getOp() == BinaryOp.XOR) {
        return new LEMInterpreterValue(LEMInterpreterValue.BOOLEAN, lhs.getVal() ^ rhs.getVal());
    }
    else if (ast.getOp() == BinaryOp.AND) {
        return new LEMInterpreterValue(LEMInterpreterValue.BOOLEAN, lhs.getVal() && rhs.getVal());
    }
    else {
        throw new Exception("Invalid BinaryOp");
    }

};

/**
 * <p>
 * Visits and interprets an {@link Identifer} in accordance with the visitor
 * pattern
 * </p>
 * 
 * <p>
 * The value returned will be a string-valued {@link LEMInterpreterValue} whose
 * value is the value bound to the identifier represented by the
 * {@link Identifier} ast node that this method is visiting. If the identifier
 * is unbound in the current environment, an exception is thrown
 * </p>
 * 
 * @protected
 * @param {BinaryOp} ast
 * @param {Object<String, String>} env The environment - the binding of
 *            identifiers to values as pure-object mapping of strings to
 *            strings.
 * @returns {LEMInterpreterValue} The value resulting from visiting and handling
 *          the ast node.
 * @throws {Exception} an exception if the identifier does not exist in the
 *             current environment
 */
LEMInterpreter.prototype.visitIdentifier = function(ast, env) {
    var name = ast.getName();
    if (!env.hasOwnProperty(name)) {
        throw new Exception("Unbound identifier: " + name);
    }
    return new LEMInterpreterValue(LEMInterpreterValue.STRING, env[name]);
};

/**
 * <p>
 * Visits and interprets a {@link StringLiteral} in accordance with the visitor
 * pattern
 * </p>
 * 
 * <p>
 * The value returned will be a string-valued {@link LEMInterpreterValue} whose
 * value is the string literal represented by the {@link StringLiteral} ast node
 * this method is visiting.
 * </p>
 * 
 * @protected
 * @param {BinaryOp} ast
 * @param {Object<String, String>} env The environment - the binding of
 *            identifiers to values as pure-object mapping of strings to
 *            strings.
 * @returns {LEMInterpreterValue} The value resulting from visiting and handling
 *          the ast node.
 */
LEMInterpreter.prototype.visitStringLiteral = function(ast, env) {
    return new LEMInterpreterValue(LEMInterpreterValue.STRING, ast.getText());
};

/**
 * <p>
 * Visits and interprets a {@link RegexLiteral} in accordance with the visitor
 * pattern
 * </p>
 * 
 * <p>
 * The value returned will be a regex-valued {@link LEMInterpreterValue} whose
 * value is the regex literal represented by the {@link RegexLiteral} ast node
 * this method is visiting.
 * </p>
 * 
 * @protected
 * @param {BinaryOp} ast
 * @param {Object<String, String>} env The environment - the binding of
 *            identifiers to values as pure-object mapping of strings to
 *            strings.
 * @returns {LEMInterpreterValue} The value resulting from visiting and handling
 *          the ast node.
 */
LEMInterpreter.prototype.visitRegexLiteral = function(ast, env) {
    return new LEMInterpreterValue(LEMInterpreterValue.REGEX, ast.getRegex());
};

/**
 * <p>
 * Visits and interprets an {@link ImplicitSearch} in accordance with the
 * visitor pattern
 * </p>
 * 
 * <p>
 * The value returned will be boolean-valued {@link LEMInterpreterValue}. The
 * boolean value will be true if the {@link ImplictSearch}'s query can be
 * "found" in the environment. For a precise definition of found, see
 * {@link ImplicitSearch}
 * </p>
 * 
 * @protected
 * @param {BinaryOp} ast
 * @param {Object<String, String>} env The environment - the binding of
 *            identifiers to values as pure-object mapping of strings to
 *            strings.
 * @returns {LEMInterpreterValue} The value resulting from visiting and handling
 *          the ast node.
 */
LEMInterpreter.prototype.visitImplicitSearch = function(ast, env) {
    for (var key in env) {
        if (env[key].toLowerCase().indexOf(ast.getText().toLowerCase()) >= 0) {
            return new LEMInterpreterValue(LEMInterpreterValue.BOOLEAN, true);
        }
    }
    return new LEMInterpreterValue(LEMInterpreterValue.BOOLEAN, false);
};

/**
 * Constructs an LEMInterpreterValue of the specified type and value
 * 
 * @classdesc
 * 
 * LEMInterpreterValue represents a value produced during the interpretation of
 * an abstract syntax tree. It essentially just holds the type of the value
 * along with the actual value
 * 
 * @constructor
 * @param {*} type The type of the LEMInterpreterValue. This must be one of the
 *            constants defined in LEMInterpreterValue such as
 *            LEMInterpreterValue.REGEX
 * @param {*} val The value represented by this LEMInterpreterValue
 */
function LEMInterpreterValue(type, val) {

    /** @private */
    this.type = type;

    /** @private */
    this.val = val;
}

/** @const */
LEMInterpreterValue.REGEX = "REGEX";

/** @const */
LEMInterpreterValue.BOOLEAN = "BOOLEAN";

/** @const */
LEMInterpreterValue.STRING = "STRING";

/**
 * Gets the type of this LEMInterpreterValue
 * 
 * @returns {*} The type. This value will be one of the constants defined in
 *          LEMInterpreterValue such as LEMInterpreterValue.REGEX
 */
LEMInterpreterValue.prototype.getType = function() {
    return this.type;
};

/**
 * Gets the value of this LEMInterpreterValue
 * 
 * @returns {*} the value
 */
LEMInterpreterValue.prototype.getVal = function() {
    return this.val;
};