com.sri.ai.expresso.core.DefaultSymbol.java Source code

Java tutorial

Introduction

Here is the source code for com.sri.ai.expresso.core.DefaultSymbol.java

Source

/*
 * Copyright (c) 2013, SRI International
 * All rights reserved.
 * Licensed under the The BSD 3-Clause License;
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 * 
 * http://opensource.org/licenses/BSD-3-Clause
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of the aic-expresso nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.sri.ai.expresso.core;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import com.google.common.annotations.Beta;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.sri.ai.expresso.ExpressoConfiguration;
import com.sri.ai.expresso.api.Expression;
import com.sri.ai.expresso.api.ExpressionAndContext;
import com.sri.ai.expresso.api.Symbol;
import com.sri.ai.expresso.api.SyntaxLeaf;
import com.sri.ai.expresso.api.SyntaxTree;
import com.sri.ai.expresso.helper.Expressions;
import com.sri.ai.expresso.helper.SyntaxTrees;
import com.sri.ai.grinder.api.RewritingProcess;
import com.sri.ai.grinder.core.AbstractNonQuantifiedExpression;
import com.sri.ai.util.AICUtilConfiguration;
import com.sri.ai.util.Util;
import com.sri.ai.util.math.Rational;

/**
 * An (atomic) {@link Expression} representing a symbol.
 * 
 * @author braz
 */
@Beta
public class DefaultSymbol extends AbstractNonQuantifiedExpression implements Symbol {
    private static final long serialVersionUID = 1L;

    private Object value;

    // this field is merely a cache; this Expression class is not based on syntax trees like previous ones did; it merely provides a syntax tree when requested.
    private SyntaxLeaf cachedSyntaxTree;

    @Override
    public Object getValue() {
        return value;
    }

    @Override
    public Iterator<ExpressionAndContext> getImmediateSubExpressionsAndContextsIterator() {
        return Util.iterator();
    }

    @Override
    public Object getSyntacticFormType() {
        return "Symbol";
    }

    @Override
    public SyntaxTree getSyntaxTree() {
        return cachedSyntaxTree;
    }

    @Override
    public Expression replaceSymbol(Expression symbol, Expression newSymbol, RewritingProcess process) {
        Expression result = this;
        if (this.equals(symbol)) {
            result = newSymbol;
        }
        return result;
    }

    //
    private int hashCode = -1; // lazy init and re-use the calculated hashCode.

    @Override
    public int hashCode() {
        if (hashCode == -1) {
            hashCode = getValue().hashCode();
        }
        return hashCode;
    }

    /**
     * The semantics of comparison of expressions based on Symbols 
     * is the comparison of the underlying syntax trees.
     * If the other object is a syntax tree itself, it is considered its own underlying syntax tree.
     * If it is not an Expression or a SyntaxTree, a Symbol is built on it and used.
     */
    @Override
    public boolean equals(Object another) {
        if (this == another) {
            return true;
        }

        SyntaxTree anotherSyntaxTree;

        if (another instanceof SyntaxTree) {
            anotherSyntaxTree = (SyntaxTree) another;
        } else if (another instanceof Expression) {
            anotherSyntaxTree = ((Expression) another).getSyntaxTree();
        } else {
            anotherSyntaxTree = SyntaxTrees.makeSyntaxLeaf(another);
        }

        boolean result = getSyntaxTree().equals(anotherSyntaxTree);
        return result;
    }

    @Override
    public Expression clone() {
        return Expressions.makeSymbol(getValue());
    }

    @Override
    public boolean booleanValue() {
        if (getSyntaxTree().getLabel() instanceof Boolean) {
            return ((Boolean) getSyntaxTree().getLabel()).booleanValue();
        }
        throw new Error("Expression.intValue() invoked on " + this + ", which is not a boolean.");
    }

    @Override
    public int intValue() {
        if (getSyntaxTree().getLabel() instanceof Number) {
            return ((Number) getSyntaxTree().getLabel()).intValue();
        }
        throw new Error("Expression.intValue() invoked on " + this + ", which is not a number.");
    }

    @Override
    public int intValueExact() throws ArithmeticException {
        if (getSyntaxTree().getLabel() instanceof Rational) {
            return ((Rational) getSyntaxTree().getLabel()).intValueExact();
        }
        throw new Error("Expression.intValueExact() invoked on " + this + ", which is not a number.");
    }

    @Override
    public double doubleValue() {
        if (getSyntaxTree().getLabel() instanceof Number) {
            return ((Number) getSyntaxTree().getLabel()).doubleValue();
        }
        throw new Error("Expression.doubleValue() invoked on " + this + ", which is not a number.");
    }

    @Override
    public Rational rationalValue() {
        if (getSyntaxTree().getLabel() instanceof Number) {
            return (Rational) getSyntaxTree().getLabel();
        }
        throw new Error("Expression.rationalValue() invoked on " + this + ", which is not a number.");
    }

    public static Symbol createSymbol(Object value) {
        Symbol result = null;
        // If global symbol table to be used and the symbol's value is not
        // an expression - i.e. quoted expressions of the form:
        // <X>
        // as these need to be instantiated each time in order to be
        // parsed correctly.
        if (useGlobalSymbolTable && !(value instanceof Expression)) {

            result = globalSymbolTable.getIfPresent(value);
            if (result == null) {
                result = new DefaultSymbol(value);
                if (!(!cacheNumericSymbols && result.getValue() instanceof Number)) {
                    globalSymbolTable.put(value, result);
                }
            }
        } else {
            result = new DefaultSymbol(value);
        }

        return result;
    }

    // Note: End users can only instantiate Symbols via the factory method.
    private DefaultSymbol(Object value) {

        if (value instanceof Number && !(value instanceof Rational)) {
            value = new Rational(((Number) value).doubleValue());
        } else if (value.equals("true")) {
            value = Boolean.TRUE;
        } else if (value.equals("false")) {
            value = Boolean.FALSE;
        } else if (value instanceof String) {
            try {
                value = new Rational((String) value);
            } catch (NumberFormatException e) {
                // ignore
            }
        }

        cachedSyntaxTree = DefaultSyntaxLeaf.createSyntaxLeaf(value);
        this.value = value;
    }

    public static void flushGlobalSymbolTable() {
        if (AICUtilConfiguration.isRecordCacheStatistics()) {
            System.out.println("Global Symbol Table Cache Stats=" + globalSymbolTable.stats());
        }
        // Causes relevant flags to be reset.
        useGlobalSymbolTable = ExpressoConfiguration.isUseGlobalSymbolTable();
        cacheNumericSymbols = ExpressoConfiguration.isGlobalSymbolTableToCacheNumerics();

        if (globalSymbolTable != null) {
            globalSymbolTable.invalidateAll();
        }
        globalSymbolTable = newSymbolTable();
        // Add well known symbols to the table
        // The Booleans.
        globalSymbolTable.put(true, SYMBOL_TRUE);
        globalSymbolTable.put(Boolean.TRUE, SYMBOL_TRUE);
        globalSymbolTable.put("true", SYMBOL_TRUE);
        globalSymbolTable.put(false, SYMBOL_FALSE);
        globalSymbolTable.put(Boolean.FALSE, SYMBOL_FALSE);
        globalSymbolTable.put("false", SYMBOL_FALSE);
        // Common Numbers
        globalSymbolTable.put("0", SYMBOL_0);
        globalSymbolTable.put(new Integer(0), SYMBOL_0);
        globalSymbolTable.put(new Rational(0), SYMBOL_0);
        globalSymbolTable.put("1", SYMBOL_1);
        globalSymbolTable.put(new Integer(1), SYMBOL_1);
        globalSymbolTable.put(new Rational(1), SYMBOL_1);
        globalSymbolTable.put("2", SYMBOL_2);
        globalSymbolTable.put(new Integer(2), SYMBOL_2);
        globalSymbolTable.put(new Rational(2), SYMBOL_2);
        globalSymbolTable.put("3", SYMBOL_3);
        globalSymbolTable.put(new Integer(3), SYMBOL_3);
        globalSymbolTable.put(new Rational(3), SYMBOL_3);
        globalSymbolTable.put("4", SYMBOL_4);
        globalSymbolTable.put(new Integer(4), SYMBOL_4);
        globalSymbolTable.put(new Rational(4), SYMBOL_4);
        globalSymbolTable.put("5", SYMBOL_5);
        globalSymbolTable.put(new Integer(5), SYMBOL_5);
        globalSymbolTable.put(new Rational(5), SYMBOL_5);
        globalSymbolTable.put("6", SYMBOL_6);
        globalSymbolTable.put(new Integer(6), SYMBOL_6);
        globalSymbolTable.put(new Rational(6), SYMBOL_6);
        globalSymbolTable.put("7", SYMBOL_7);
        globalSymbolTable.put(new Integer(7), SYMBOL_7);
        globalSymbolTable.put(new Rational(7), SYMBOL_7);
        globalSymbolTable.put("8", SYMBOL_8);
        globalSymbolTable.put(new Integer(8), SYMBOL_8);
        globalSymbolTable.put(new Rational(8), SYMBOL_8);
        globalSymbolTable.put("9", SYMBOL_9);
        globalSymbolTable.put(new Integer(9), SYMBOL_9);
        globalSymbolTable.put(new Rational(9), SYMBOL_9);
    }

    private static Cache<Object, Symbol> newSymbolTable() {
        CacheBuilder<Object, Object> cb = CacheBuilder.newBuilder();

        long maximumSize = ExpressoConfiguration.getGlobalSymbolTableMaximumSize();
        // Note: a maximumSize of 
        // < 0 means no size restrictions
        // = 0 means no cache
        // > 0 means maximum size of cache
        if (maximumSize >= 0L) {
            cb.maximumSize(maximumSize);
        }
        if (AICUtilConfiguration.isRecordCacheStatistics()) {
            cb.recordStats();
        }

        Cache<Object, Symbol> result = cb.build();

        return result;
    }

    //
    // Commonly used symbol expressions
    private static final Symbol SYMBOL_TRUE = Expressions.makeSymbol(true);

    private static final Symbol SYMBOL_FALSE = Expressions.makeSymbol(false);

    private static final Symbol SYMBOL_0 = Expressions.makeSymbol(new Rational(0));

    private static final Symbol SYMBOL_1 = Expressions.makeSymbol(new Rational(1));

    private static final Symbol SYMBOL_2 = Expressions.makeSymbol(new Rational(2));

    private static final Symbol SYMBOL_3 = Expressions.makeSymbol(new Rational(3));

    private static final Symbol SYMBOL_4 = Expressions.makeSymbol(new Rational(4));

    private static final Symbol SYMBOL_5 = Expressions.makeSymbol(new Rational(5));

    private static final Symbol SYMBOL_6 = Expressions.makeSymbol(new Rational(6));

    private static final Symbol SYMBOL_7 = Expressions.makeSymbol(new Rational(7));

    private static final Symbol SYMBOL_8 = Expressions.makeSymbol(new Rational(8));

    private static final Symbol SYMBOL_9 = Expressions.makeSymbol(new Rational(9));

    //
    private static boolean useGlobalSymbolTable = ExpressoConfiguration.isUseGlobalSymbolTable();

    private static boolean cacheNumericSymbols = ExpressoConfiguration.isGlobalSymbolTableToCacheNumerics();

    private static Cache<Object, Symbol> globalSymbolTable = newSymbolTable();

    static {
        flushGlobalSymbolTable();
    }

    @Override
    public String makeToString() {
        return getSyntaxTree().toString();
    }

    @Override
    public Expression getFunctor() {
        return null;
    }

    @Override
    public List<Expression> getArguments() {
        return Collections.emptyList();
    }

    @Override
    public Expression set(int i, Expression newIthArgument) {
        throw new Error("set(int, Expression) not defined for " + getClass());
    }
}