001/* An actor that reads expressions and parses them into tokens.
002
003 @Copyright (c) 1998-2014 The Regents of the University of California.
004 All rights reserved.
005
006 Permission is hereby granted, without written agreement and without
007 license or royalty fees, to use, copy, modify, and distribute this
008 software and its documentation for any purpose, provided that the
009 above copyright notice and the following two paragraphs appear in all
010 copies of this software.
011
012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016 SUCH DAMAGE.
017
018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023 ENHANCEMENTS, OR MODIFICATIONS.
024
025 PT_COPYRIGHT_VERSION 2
026 COPYRIGHTENDKEY
027 */
028package ptolemy.actor.lib.conversions;
029
030import java.util.Set;
031
032import ptolemy.data.StringToken;
033import ptolemy.data.Token;
034import ptolemy.data.expr.ASTPtRootNode;
035import ptolemy.data.expr.ModelScope;
036import ptolemy.data.expr.ParseTreeEvaluator;
037import ptolemy.data.expr.ParserScope;
038import ptolemy.data.expr.PtParser;
039import ptolemy.data.expr.Variable;
040import ptolemy.data.type.BaseType;
041import ptolemy.data.type.Type;
042import ptolemy.kernel.CompositeEntity;
043import ptolemy.kernel.util.IllegalActionException;
044import ptolemy.kernel.util.NameDuplicationException;
045
046///////////////////////////////////////////////////////////////////
047//// ExpressionToToken
048
049/**
050 This actor reads a string expression from the input port and outputs
051 the token resulting from the evaluation.  The type of the output port
052 is by default undeclared. To use this actor, you may need to declare
053 the output type. Depending on how the output is used, if
054 backward type resolution is enabled in the model
055 (the default, see the parameters of the top-level model), then
056 the type will resolve to the most general type acceptable to
057 the downstream actors. If the downstream actors all propagate
058 type constraints backwards, then it might be possible for
059 the output type to be inferred. If backward type resolution is not enabled,
060 then you will always need to set the type of the output
061 port to the type of the expression that is expected.
062 If the input string parses to something that does not match
063 the specified output data type, then a run-time type check error
064 will occur when this actor tries to produce its output.
065 <p>
066 The expression input can refer to any variable that is in scope
067 for this actor.
068
069 @author  Steve Neuendorffer
070 @version $Id$
071 @since Ptolemy II 2.1
072 @Pt.ProposedRating Yellow (neuendor)
073 @Pt.AcceptedRating Red (liuj)
074 */
075public class ExpressionToToken extends Converter {
076    /** Construct an actor with the given container and name.
077     *  @param container The container.
078     *  @param name The name of this actor.
079     *  @exception IllegalActionException If the actor cannot be contained
080     *   by the proposed container.
081     *  @exception NameDuplicationException If the container already has an
082     *   actor with this name.
083     */
084    public ExpressionToToken(CompositeEntity container, String name)
085            throws IllegalActionException, NameDuplicationException {
086        super(container, name);
087        input.setTypeEquals(BaseType.STRING);
088        // With backward type resolution, the following is not appropriate.
089        // output.setTypeEquals(BaseType.GENERAL);
090    }
091
092    ///////////////////////////////////////////////////////////////////
093    ////                         public methods                    ////
094
095    /** Output a token that results from evaluating the expression given
096     *  by the input string token.
097     *  @exception IllegalActionException If there is no director, or
098     *   if the expression read from the input cannot be parsed.
099     */
100    @Override
101    public void fire() throws IllegalActionException {
102        super.fire();
103        String string = ((StringToken) input.get(0)).stringValue();
104        Token result;
105
106        try {
107            if (_parser == null) {
108                _parser = new PtParser();
109            }
110
111            ASTPtRootNode parseTree = _parser.generateParseTree(string);
112
113            if (_parseTreeEvaluator == null) {
114                _parseTreeEvaluator = new ParseTreeEvaluator();
115            }
116
117            if (_scope == null) {
118                _scope = new ExpressionScope();
119            }
120
121            result = _parseTreeEvaluator.evaluateParseTree(parseTree, _scope);
122        } catch (IllegalActionException ex) {
123            // Chain exceptions to get the actor that threw the exception.
124            throw new IllegalActionException(this, ex, "Expression invalid.");
125        }
126
127        if (result == null) {
128            throw new IllegalActionException(this,
129                    "Expression yields a null result: " + string);
130        }
131
132        output.broadcast(result);
133    }
134
135    /** Return true if and only if an input is present.
136     *  @exception IllegalActionException If there is no director, or
137     *   if no connection has been made to the input.
138     */
139    @Override
140    public boolean prefire() throws IllegalActionException {
141        if (input.hasToken(0)) {
142            return super.prefire();
143        } else {
144            return false;
145        }
146    }
147
148    /** Wrapup execution of this actor.  This method overrides the
149     *  base class to discard the internal parser to save memory.
150     */
151    @Override
152    public void wrapup() {
153        _parser = null;
154    }
155
156    ///////////////////////////////////////////////////////////////////
157    ////                         inner classes                     ////
158    private class ExpressionScope extends ModelScope {
159        /** Look up and return the attribute with the specified name in the
160         *  scope. Return null if such an attribute does not exist.
161         *  @return The attribute with the specified name in the scope.
162         */
163        @Override
164        public Token get(String name) throws IllegalActionException {
165            Variable result = getScopedVariable(null, ExpressionToToken.this,
166                    name);
167
168            if (result != null) {
169                return result.getToken();
170            }
171
172            return null;
173        }
174
175        /** Look up and return the type of the attribute with the
176         *  specified name in the scope. Return null if such an
177         *  attribute does not exist.
178         *  @return The attribute with the specified name in the scope.
179         */
180        @Override
181        public Type getType(String name) throws IllegalActionException {
182            Variable result = getScopedVariable(null, ExpressionToToken.this,
183                    name);
184
185            if (result != null) {
186                return (Type) result.getTypeTerm().getValue();
187            }
188
189            return null;
190        }
191
192        /** Look up and return the type term for the specified name
193         *  in the scope. Return null if the name is not defined in this
194         *  scope, or is a constant type.
195         *  @return The InequalityTerm associated with the given name in
196         *  the scope.
197         *  @exception IllegalActionException If a value in the scope
198         *  exists with the given name, but cannot be evaluated.
199         */
200        @Override
201        public ptolemy.graph.InequalityTerm getTypeTerm(String name)
202                throws IllegalActionException {
203            Variable result = getScopedVariable(null, ExpressionToToken.this,
204                    name);
205
206            if (result != null) {
207                return result.getTypeTerm();
208            }
209
210            return null;
211        }
212
213        /** Return the list of identifiers within the scope.
214         *  @return The list of identifiers within the scope.
215         */
216        @Override
217        public Set identifierSet() {
218            return getAllScopedVariableNames(null, ExpressionToToken.this);
219        }
220    }
221
222    ///////////////////////////////////////////////////////////////////
223    ////                         private members                   ////
224
225    /** The parser to use. */
226    private PtParser _parser = null;
227
228    /** The parse tree evaluator to use. */
229    private ParseTreeEvaluator _parseTreeEvaluator = null;
230
231    /** The scope for the parser. */
232    private ParserScope _scope = null;
233}