001: /*************************************************************************
002: * *
003: * 1) This source code file, in unmodified form, and compiled classes *
004: * derived from it can be used and distributed without restriction, *
005: * including for commercial use. (Attribution is not required *
006: * but is appreciated.) *
007: * *
008: * 2) Modified versions of this file can be made and distributed *
009: * provided: the modified versions are put into a Java package *
010: * different from the original package, edu.hws; modified *
011: * versions are distributed under the same terms as the original; *
012: * and the modifications are documented in comments. (Modification *
013: * here does not include simply making subclasses that belong to *
014: * a package other than edu.hws, which can be done without any *
015: * restriction.) *
016: * *
017: * David J. Eck *
018: * Department of Mathematics and Computer Science *
019: * Hobart and William Smith Colleges *
020: * Geneva, New York 14456, USA *
021: * Email: eck@hws.edu WWW: http://math.hws.edu/eck/ *
022: * *
023: *************************************************************************/package edu.hws.jcm.functions;
024:
025: import edu.hws.jcm.data.*;
026:
027: /**
028: * An object belonging to a concrete subclass of FunctionParserExtesion is a
029: * mathematical function that can be registered with a Parser and then used in
030: * strings that are parsed by that parser. (See the Function inteface
031: * for more information.)
032: *
033: * <p>Since a FunctionParserExtension object implements the ParserExtension interface, it
034: * has a name and can be registered with a Parser. When the parser
035: * encounters the name of a function in a string, it turns control
036: * of the parsing process over to the function. When a Function
037: * occurs in an ExpressionProgram, the Function is responsible for
038: * evaluating itself and for differentiating itself. This functionality
039: * is defined in the abstract class FunctionParserExtension. The
040: * concrete subclasses of FunctionParserExtension represent actual
041: * functions. They must implement the five methods defined
042: * in the Function interface, but most of the parser- and expression-related
043: * behavior can probably be inherited from this class. (This is good, since
044: * the programming is rather tricky.)
045: *
046: * <p>(The point of all this is that Parsers and Expressions can deal
047: * with Functions, even though there is no reference to Functions in
048: * the Parser or ExpressionProgram classes. Everything is done
049: * through the ParserExtension interface.)
050: */
051: abstract public class FunctionParserExtension implements Function,
052: ParserExtension, ExpressionCommand {
053:
054: /**
055: * The name of this MathObject, possibly null.
056: */
057: protected String name;
058:
059: private boolean parensCanBeOptional; // This variable applies only to functions of
060:
061: // arity 1. It affects the parsing of references
062: // to the function, and then only if the
063: // OPTIONAL_PARENS option is set in the parser.
064: // If both this variable and OPTIONAL_PARENS
065: // are set, then parentheses are optional
066: // around the argument of the function.
067:
068: /**
069: * Call this function with b = true if this is a function of one variable
070: * and you want it to behave like a standard function in that parentheses
071: * can be optional around the argument of the function. For the parentheses
072: * to be treated as optional, the option Parser.OPTIONAL_PARENS must ALSO
073: * be set in the parser. As an example, if you wanted to define the function
074: * sinh and allow the syntax "sinh x", you would turn this option on in
075: * the Function and turn OTPTIONAL_PARENS on in the Parser.
076: *
077: * @param b set whether parenthesis are optional in one variable functions.
078: */
079: public void setParensCanBeOptional(boolean b) {
080: parensCanBeOptional = b;
081: }
082:
083: //------ Methods from the MathObject interfaces ----------
084:
085: /**
086: * Set the name of this object. It is not a good idea to do this
087: * if the object has been registered with a Parser.
088: */
089: public void setName(String name) {
090: this .name = name;
091: }
092:
093: /**
094: * Get the name of this MathObject.
095: */
096: public String getName() {
097: return name;
098: }
099:
100: //----------------- Method from the ParserExtension interface -----------
101:
102: /**
103: * If this ParserExtension is registered with a parser and the parser
104: * encounters the name of the function in the string it is parsing,
105: * then the parser will call this routine. This routine parses
106: * the function's parameter list and generates the code for evaluating
107: * the function, applied to those parameters. When this routine is
108: * called, the name of the function has already been read. The code
109: * that is generated consists of code to evalueate each of the parameters,
110: * leaving the results on the stack, followed by an application of the
111: * function.
112: *
113: * @param parser parser that is parsing the string.
114: * @param context the ParseContext in effect at the time this method is called.
115: */
116: public void doParse(Parser parser, ParserContext context) {
117: int tok = context.next();
118: String open = context.tokenString;
119: if (tok == ParserContext.OPCHARS
120: && (open.equals("(")
121: || (open.equals("[") && (context.options & Parser.BRACKETS) != 0) || (open
122: .equals("{") && (context.options & Parser.BRACES) != 0))) {
123: String close = open.equals("(") ? ")"
124: : (open.equals("[") ? "]" : "}");
125: for (int i = 0; i < getArity(); i++) {
126: if (parser.parseExpression(context))
127: throw new ParseError(
128: "An argument of a function cannot be a logical-valued expression.",
129: context);
130: tok = context.next();
131: if (i == getArity() - 1) {
132: if (tok == ParserContext.OPCHARS
133: && context.tokenString.equals(","))
134: throw new ParseError(
135: "Too many parameters for function \""
136: + getName() + "\".", context);
137: if (tok != ParserContext.OPCHARS
138: || !context.tokenString.equals(close))
139: throw new ParseError(
140: "Expected a \""
141: + close
142: + "\" at the end of the paramter list for function \""
143: + getName() + "\".", context);
144: } else {
145: if (tok != ParserContext.OPCHARS
146: || !context.tokenString.equals(","))
147: throw new ParseError(
148: "Exprected a comma followed by another argument for function \""
149: + getName() + "\".", context);
150: }
151: }
152: } else if (getArity() == 1
153: && (context.options & Parser.OPTIONAL_PARENS) != 0
154: && parensCanBeOptional) {
155: if (parser.parseTerm(context))
156: throw new ParseError(
157: "The argument of a function must be a numerical expression.",
158: context);
159: } else
160: throw new ParseError(
161: "Parentheses required around parameter list of function \""
162: + getName() + "\".", context);
163: context.prog.addCommandObject(this );
164: }
165:
166: //--------------- Methods from the ExpressionCommand interface ----------
167:
168: /**
169: * Evaluate the function applied to argument values popped from the stack,
170: * and leave the result on the stack. The argument values have already been
171: * computed and placed on the stack when this is called. They should be
172: * popped off the stack in reverse order. This general method can't deal
173: * properly with "cases", so it will probably have to be overridden in
174: * subclasses.
175: */
176: public void apply(StackOfDouble stack, Cases cases) {
177: double[] d = new double[getArity()];
178: for (int i = getArity() - 1; i >= 0; i--)
179: d[i] = stack.pop();
180: stack.push(getVal(d));
181: }
182:
183: /**
184: * The function object occurs as a command at index myIndex in prog. The commands for computing
185: * its arguments can be found in prog in positions preceding myIndex. Generate commands for computing
186: * the derivative of the function reference with respect to the Variable wrt and add them to the deriv program. The
187: * computation of the derivative uses the chain rule.
188: *
189: * @param prog program this function object occurs in.
190: * @param myIndex index at which this function occurs.
191: * @param deriv commands for computing the derivative are placed here.
192: * @param wrt the derivative is taken with respect to this Variable.
193: */
194: public void compileDerivative(ExpressionProgram prog, int myIndex,
195: ExpressionProgram deriv, Variable wrt) {
196: int[] opIndex = new int[getArity()];
197: int size = 1;
198: for (int i = 0; i < getArity(); i++) { // Find the indices in prog of the arguments of the function.
199: opIndex[getArity() - i - 1] = myIndex - size;
200: if (i < getArity() - 1)
201: size += prog.extent(myIndex - size);
202: }
203: boolean output = false; // Becomes true after fist term is output.
204: if (dependsOn(wrt)) {
205: output = true;
206: for (int i = 0; i < opIndex.length; i++)
207: prog.copyExpression(opIndex[i], deriv);
208: deriv
209: .addCommandObject((FunctionParserExtension) derivative(wrt));
210:
211: }
212: for (int i = 0; i < getArity(); i++) {
213: if (prog.dependsOn(opIndex[i], wrt)) {
214: for (int j = 0; j < opIndex.length; j++)
215: prog.copyExpression(opIndex[j], deriv);
216: deriv
217: .addCommandObject((FunctionParserExtension) derivative(i + 1));
218: prog.compileDerivative(opIndex[i], deriv, wrt);
219: deriv.addCommand(ExpressionProgram.TIMES);
220: if (output)
221: deriv.addCommand(ExpressionProgram.PLUS);
222: output = true;
223: }
224: }
225: if (!output)
226: prog.addConstant(0);
227: }
228:
229: /**
230: * Return the number of commands in prog that are part of this function reference,
231: * including the space occupied by the commands that compute the values of the
232: * function's arguments.
233: *
234: * @param prog program to check commands against.
235: * @param myIndex index in program.
236: * @return the number of commands in prog that are part of this function reference.
237: */
238: public int extent(ExpressionProgram prog, int myIndex) {
239: int size = 1; // Allow for the function itself.
240: for (int i = 0; i < getArity(); i++)
241: size = size + prog.extent(myIndex - size); // Add on the size of the next argument.
242: return size;
243: }
244:
245: /**
246: * Append a string representation of the function and its arguments to the buffer
247: *
248: * @param prog program whose string representation is being generated.
249: * @param myIndex index of this ExpressionCommand in prog.
250: * @param buffer string representation is placed here.
251: */
252: public void appendOutputString(ExpressionProgram prog, int myIndex,
253: StringBuffer buffer) {
254: int[] opIndex = new int[getArity()];
255: int size = 1;
256: for (int i = 0; i < getArity(); i++) { // Find the locations in prog of the arguemnts of the function.
257: opIndex[getArity() - i - 1] = myIndex - size;
258: if (i < getArity() - 1)
259: size += prog.extent(myIndex - size);
260: }
261: String name = getName();
262: buffer.append(name == null ? "(unnamed function)" : name);
263: buffer.append('(');
264: for (int i = 0; i < getArity(); i++) {
265: prog.appendOutputString(opIndex[i], buffer);
266: if (i < getArity() - 1) {
267: buffer.append(", ");
268: }
269: }
270: buffer.append(')');
271: }
272:
273: } // end class FunctionParserExtesion
|