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.data;
024:
025: /**
026: * A Parser can take a string and compile it into an ExpressionProgram.
027: * MathObjects, such as variables and functions, can be registered with
028: * the Parser. This means that the Parser will recognize them in the
029: * strings that it parses. There are a few options that can be set to
030: * control certain aspects of the parsing. If a string does not have
031: * the correct syntax for an expression, then the Parser will throw a
032: * ParseError when it tries to parse that string. A Parser can have a
033: * parent. It inherits any MathObjects registered with its parent, but
034: * a MathObject registered with a Parser will hide any MathObject of
035: * the same name that is registered with its parent.
036: * Every parser recognizes the constants pi and e and the operators
037: * +, -, *, /, ^, and **. The ** operator is a synonym for ^, the
038: * exponentiation operator. Both unary and binary + and - are recognized.
039: * The exponentiation operator is right associative. The others are
040: * left associative.
041: */
042: public class Parser implements java.io.Serializable {
043:
044: /**
045: * An option that can be set for this parser.
046: * If enabled, identifiers are case-sensitive.
047: * For example, Sin, sin, and SIN will be
048: * treated as separate identifiers. It really
049: * only makes sense to enable this at the time the
050: * Parser is first constructed.
051: */
052: public static final int CASE_SENSITIVE = 1;
053:
054: /**
055: * An that can be set for this parser.
056: * If enabled, mutltiplication can be indicated
057: * implicitely, as well as with a "*". For
058: * example, 2x will mean 2*x.
059: */
060: public static final int OPTIONAL_STARS = 2;
061:
062: /**
063: * An option that can be set for this parser.
064: * If enabled, spaces are not required to separate
065: * identifiers. This only has an effect if one of
066: * OPTIONAL_STARS or OPTIONAL_PARENS is also enabled.
067: * For example, xsin(x) will be read as x*sin(x),
068: * and sine will be read as sin(e).
069: */
070: public static final int OPTIONAL_SPACES = 4;
071:
072: /**
073: * An option that can be set for this parser.
074: * If enabled, brackets, [ and ], can be used for grouping.
075: */
076: public static final int BRACKETS = 8;
077:
078: /**
079: * An option that can be set for this parser.
080: * If enabled, braces, { and }, can be used for grouping.
081: */
082: public static final int BRACES = 16;
083:
084: /**
085: * An option that can be set for this parser.
086: * If enabled, the "?" operator can be used in expressions, along with the
087: * logical operators &, |, ~, =, <, >, <>, <=, >=.
088: * The words "and", "or", and "not" can be used
089: * in place of &, |, and ~. These words are
090: * treated in a case-insensitive way, even if
091: * the CASE_SENSITIVE option is on. When this
092: * option is set, it is legal to call the
093: * parseLogical method to parse a boolean-valued
094: * expression. This option is enabled by default.
095: */
096: public static final int BOOLEANS = 32;
097:
098: /**
099: * An option that can be set for this parser.
100: * If enabled, the factorial operator, !, is recognized.
101: */
102: public static final int FACTORIAL = 64;
103:
104: /**
105: * An option that can be set for this parser.
106: * The character "_", which can usually
107: * be used just like a letter, is
108: * not allowed in identifers.
109: */
110: public static final int NO_UNDERSCORE_IN_IDENTIFIERS = 128;
111:
112: /**
113: * An option that can be set for this parser.
114: * Digits 0 through 9, which can usually be
115: * used in an identifier after the first
116: * character, are not allowed in identifiers.
117: */
118: public static final int NO_DIGITS_IN_IDENTIFIERS = 256;
119:
120: /**
121: * An option that can be set for this parser.
122: * If enabled, parentheses are optional around
123: * the parameter of a standard function. If the
124: * parentheses are omited, then the argument is
125: * the term that follows the function name.
126: * For example, "sin x + 1" means "sin(x) + 1"
127: * while "sin x * cos x" means "sin( x*cos(x) )".
128: */
129: public static final int OPTIONAL_PARENS = 512;
130:
131: /**
132: * An option that can be set for this parser.
133: * When enabled, the standard functions are
134: * registered with the parser. This option
135: * is enabled by default. The standard
136: * functions are: sin, cos, tan, cot, sec,
137: * csc, arcsin, arccos, arctan, exp, ln,
138: * log2, log10, sqrt, cubert, abs, round,
139: * floor, ceiling, trunc.
140: */
141: public static final int STANDARD_FUNCTIONS = 1024;
142:
143: /**
144: * The default options set that is used for
145: * a newly created Parser, if none is specified
146: * in the Constructor. It includes the options BOOLEANS and STANDARD_FUNCTIONS.
147: */
148: public static final int DEFAULT_OPTIONS = BOOLEANS
149: | STANDARD_FUNCTIONS;
150:
151: /**
152: * The set of options that have been enabled for this parser.
153: */
154: protected int options;
155:
156: /**
157: * The symbol table that contains the MathObjects
158: * that have been registered with this parser.
159: */
160: protected SymbolTable symbols;
161:
162: /**
163: * Construct a Parser with no parent and with the default options,
164: * BOOLEANS and STANDARD_FUNCTIONS.
165: */
166: public Parser() {
167: this (null, DEFAULT_OPTIONS);
168: }
169:
170: /**
171: * Create a Parser with the specified parent. The options for this
172: * parser are inherited from the parent, if parent is non-null.
173: * If parent is null, the option set is empty.
174: */
175: public Parser(Parser parent) {
176: this (parent, 0);
177: }
178:
179: /**
180: * Create a Parser with the spedified option set and with no parent.
181: */
182: public Parser(int options) {
183: this (null, options);
184: }
185:
186: /**
187: * Create a Parser with the specified parent. The options for this
188: * parser consist of the option set from the parent, together with
189: * any additional options in the specified options set.
190: *
191: * @param parent parent of this Parser, possibly null.
192: * @param options additional options, in addition to ones inherited from parent.
193: */
194: public Parser(Parser parent, int options) {
195: if (parent == null) {
196: symbols = new SymbolTable();
197: symbols.add(new Constant("e", Math.E));
198: symbols.add(new Constant("pi", Math.PI));
199: } else {
200: symbols = new SymbolTable(parent.symbols);
201: this .options = parent.options;
202: }
203: addOptions(options);
204: }
205:
206: /**
207: * Add the options in the option set newOptions to this Parser's option set.
208: * The value of newOptions can be one of the option constants defined in this
209: * class, such as OPTIONAL_STARS, or it can consist of several option constants
210: * OR-ed together.
211: *
212: */
213: public void addOptions(int newOptions) {
214: if (((newOptions & STANDARD_FUNCTIONS) != 0)
215: && ((options & STANDARD_FUNCTIONS) == 0)) {
216: for (int opCode = ExpressionProgram.CUBERT; opCode <= ExpressionProgram.SIN; opCode++)
217: symbols.add(new StandardFunction(opCode));
218: }
219: options = options | newOptions;
220: }
221:
222: /**
223: * Parse the string str and create the corresponding expression.
224: * The expression must be numeric-valued, not logical. There can't
225: * be any extra characters in str after the expression. If a syntax
226: * error is found, a ParseError will be thrown.
227: *
228: * @param str String to parse.
229: * @return the expression defined by the string.
230: */
231: public ExpressionProgram parse(String str) {
232: ParserContext context = new ParserContext(str, options, symbols);
233: // The ParserContext holds all the information relevant to the
234: // parsing of str, including str itself and the ExpressionProgram
235: // that is being generated. See the ParserContext class for more info.
236: if (str == null)
237: throw new ParseError("Can't parse a null string.", context);
238: if (context.look() == ParserContext.END_OF_STRING)
239: throw new ParseError("Can't parse an empty string.",
240: context);
241: boolean isBool;
242: if ((options & BOOLEANS) != 0)
243: isBool = parseLogicalExpression(context);
244: else
245: isBool = parseExpression(context);
246: if (context.look() != ParserContext.END_OF_STRING)
247: throw new ParseError(
248: "Extra data found after the end of a complete legal expression.",
249: context);
250: if (isBool)
251: throw new ParseError(
252: "Found a logical-valued expression instead of a numeric expression.",
253: context);
254: context.prog.trim();
255: context.prog.sourceString = str;
256: return context.prog;
257: }
258:
259: /**
260: * Parse the String, str, and create a corresponding logical-valued expression.
261: * The expression must be logical-valued, such as "x > 0", not numeric. There can't
262: * be any extra characters in str after the expression. If a syntax
263: * error is found, a ParseError will be thrown. It is not legal to call this
264: * method if the BOOLEANS option is not set.
265: *
266: * @param str String to parse.
267: * @return the logical-valued expression defined by str.
268: */
269: public ExpressionProgram parseLogical(String str) {
270: if ((options & BOOLEANS) == 0)
271: throw new IllegalArgumentException(
272: "Internal Error: Attempt to parse a logical-valued expression, but BOOLEANS option is not turned on.");
273: ParserContext context = new ParserContext(str, options, symbols);
274: if (str == null)
275: throw new ParseError("Can't parse a null string.", context);
276: if (context.look() == ParserContext.END_OF_STRING)
277: throw new ParseError("Can't parse an empty string.",
278: context);
279: boolean isBool = parseLogicalExpression(context);
280: if (context.look() != ParserContext.END_OF_STRING)
281: throw new ParseError(
282: "Extra data found after the end of a complete legal expression.",
283: context);
284: if (!isBool)
285: throw new ParseError(
286: "Found a numeric-valued expression instead of a logical expression.",
287: context);
288: context.prog.trim();
289: return context.prog;
290: }
291:
292: //---------- Wrapper functions for accessing the symbol table --------------
293:
294: /**
295: * Get the MathObject that has been registered with the parser
296: * under the given name. If the CASE_SENSITIVE option is not set,
297: * names are converted to lower case for the purpose of
298: * registering and retrieving registered objects.
299: */
300: public MathObject get(String name) {
301: if ((options & Parser.CASE_SENSITIVE) != 0)
302: return symbols.get(name);
303: else
304: return symbols.get(name.toLowerCase());
305: }
306:
307: /**
308: * Register the MathObject with the Parser, associating it with its
309: * name. An error will occur if the name is null. If the CASE_SENSITIVE
310: * option is not set, names are converted to lower case for the purpose of
311: * registering and retrieving registered objects.
312: */
313: public void add(MathObject sym) {
314: if ((options & Parser.CASE_SENSITIVE) != 0)
315: symbols.add(sym);
316: else
317: symbols.add(sym.getName().toLowerCase(), sym);
318: }
319:
320: /**
321: * Deregister the MathObject with the given name, if there is one
322: * registered with the Parser. If the name is not registered, nothing
323: * happens and no error occurs.
324: *
325: * @param name MathObject to deregister.
326: */
327: public void remove(String name) {
328: if (name == null)
329: return;
330: else if ((options & Parser.CASE_SENSITIVE) != 0)
331: symbols.remove(name);
332: else
333: symbols.remove(name.toLowerCase());
334: }
335:
336: // ------------------------- The parsing code -------------------------
337:
338: // The remaining routines in this class implement a recursive descent parser
339: // for expressions. These routines would be private, except that it might
340: // be necessary for a ParserExtension to call them. The ParserContext parameter
341: // holds information such as the string that is being parsed and the ExpressionProgram
342: // that is being generated. See the ParseContext class for more information.
343:
344: /**
345: * Called as part of the parsing process. From outside this class, this would
346: * probably be called only by a ParserExtension.
347: */
348: public boolean parseLogicalExpression(ParserContext context) {
349: boolean isBool = parseLogicalTerm(context);
350: int tok = context.look();
351: if (tok == ParserContext.OPCHARS
352: && context.tokenString.equals("&") && !isBool)
353: throw new ParseError(
354: "The AND operator can only be used with logical expressions.",
355: context);
356: while (tok == ParserContext.OPCHARS
357: && context.tokenString.equals("&")) {
358: context.next();
359: if (!parseLogicalTerm(context))
360: throw new ParseError(
361: "The AND operator can only be used with logical expressions.",
362: context);
363: context.prog.addCommand(ExpressionProgram.AND);
364: tok = context.look();
365: }
366: if (tok == ParserContext.OPCHARS
367: && context.tokenString.equals("?")) {
368: if (!isBool)
369: throw new ParseError(
370: "The conditional operator, ?, can only be applied to a logical-valued expression.",
371: context);
372: ExpressionProgram trueCase, falseCase;
373: ExpressionProgram saveProg = context.prog;
374: context.next();
375: trueCase = new ExpressionProgram();
376: context.prog = trueCase;
377: if (parseLogicalExpression(context))
378: throw new ParseError(
379: "The cases in a conditional expression cannot be logical-valued expressions.",
380: context);
381: tok = context.look();
382: if (tok == ParserContext.OPCHARS
383: && context.tokenString.equals(":")) {
384: context.next();
385: falseCase = new ExpressionProgram();
386: context.prog = falseCase;
387: if (parseLogicalExpression(context))
388: throw new ParseError(
389: "The cases in a conditional expression cannot be logical-valued expressions.",
390: context);
391: } else
392: falseCase = null;
393: context.prog = saveProg;
394: context.prog.addCommandObject(new ConditionalExpression(
395: trueCase, falseCase));
396: return false;
397: } else
398: return isBool;
399: }
400:
401: /**
402: * Called as part of the parsing process. From outside this class, this would
403: * probably be called only by a ParserExtension.
404: */
405: public boolean parseLogicalTerm(ParserContext context) {
406: boolean isBool = parseLogicalFactor(context);
407: int tok = context.look();
408: if (tok == ParserContext.OPCHARS
409: && context.tokenString.equals("|") && !isBool)
410: throw new ParseError(
411: "The OR operator can only be used with logical expressions.",
412: context);
413: while (tok == ParserContext.OPCHARS
414: && context.tokenString.equals("|")) {
415: context.next();
416: if (!parseLogicalFactor(context))
417: throw new ParseError(
418: "The OR operator can only be used with logical expressions.",
419: context);
420: context.prog.addCommand(ExpressionProgram.OR);
421: tok = context.look();
422: }
423: return isBool;
424: }
425:
426: /**
427: * Called as part of the parsing process. From outside this class, this would
428: * probably be called only by a ParserExtension.
429: */
430: public boolean parseLogicalFactor(ParserContext context) {
431: int tok = context.look();
432: int notCt = 0;
433: while (tok == ParserContext.OPCHARS
434: && context.tokenString.equals("~")) {
435: context.next();
436: tok = context.look();
437: notCt++;
438: }
439: boolean isBool = parseRelation(context);
440: if (notCt > 0 && !isBool)
441: throw new ParseError(
442: "The NOT operator can only be used with logical expressions.",
443: context);
444: if (notCt % 2 == 1)
445: context.prog.addCommand(ExpressionProgram.NOT);
446: return isBool;
447: }
448:
449: /**
450: * Called as part of the parsing process. From outside this class, this would
451: * probably be called only by a ParserExtension.
452: */
453: public boolean parseRelation(ParserContext context) {
454: boolean isBool = parseExpression(context);
455: int tok = context.look();
456: if (tok != ParserContext.OPCHARS)
457: return isBool;
458: int rel = 0;
459: if (context.tokenString.equals("="))
460: rel = ExpressionProgram.EQ;
461: else if (context.tokenString.equals("<"))
462: rel = ExpressionProgram.LT;
463: else if (context.tokenString.equals(">"))
464: rel = ExpressionProgram.GT;
465: else if (context.tokenString.equals("<="))
466: rel = ExpressionProgram.LE;
467: else if (context.tokenString.equals(">="))
468: rel = ExpressionProgram.GE;
469: else if (context.tokenString.equals("<>"))
470: rel = ExpressionProgram.NE;
471: if (rel == 0)
472: return isBool;
473: if (isBool)
474: throw new ParseError(
475: "A relational operator can only be used with numerical expressions.",
476: context);
477: context.next();
478: if (parseExpression(context))
479: throw new ParseError(
480: "A relational operator can only be used with numerical expressions.",
481: context);
482: tok = context.look();
483: if (tok == ParserContext.OPCHARS
484: && (context.tokenString.equals("=")
485: || context.tokenString.equals("<")
486: || context.tokenString.equals(">")
487: || context.tokenString.equals("<=")
488: || context.tokenString.equals(">=") || context.tokenString
489: .equals("<>")))
490: throw new ParseError(
491: "It is illegal to string together relations operators; use \"AND\" instead.",
492: context);
493: context.prog.addCommand(rel);
494: return true;
495: }
496:
497: /**
498: * Called as part of the parsing process. From outside this class, this would
499: * probably be called only by a ParserExtension.
500: */
501: public boolean parseExpression(ParserContext context) {
502: boolean neg = false;
503: int tok = context.look();
504: if (tok == ParserContext.OPCHARS
505: && (context.tokenString.equals("+") || context.tokenString
506: .equals("-"))) {
507: neg = (context.tokenString.equals("-"));
508: context.next();
509: }
510: boolean isBool = parseTerm(context);
511: if (neg) {
512: if (isBool)
513: throw new ParseError(
514: "A unary + or - cannot be applied to a logical expression.",
515: context);
516: context.prog.addCommand(ExpressionProgram.UNARY_MINUS);
517: }
518: tok = context.look();
519: if (tok == ParserContext.OPCHARS
520: && (context.tokenString.equals("+") || context.tokenString
521: .equals("-")) && isBool)
522: throw new ParseError(
523: "A + or - operator cannot be applied to logical operands.",
524: context);
525: while (tok == ParserContext.OPCHARS
526: && (context.tokenString.equals("+") || context.tokenString
527: .equals("-"))) {
528: context.next();
529: int opcode = (context.tokenString.equals("+") ? ExpressionProgram.PLUS
530: : ExpressionProgram.MINUS);
531: if (parseTerm(context))
532: throw new ParseError(
533: "A + or - operator cannot be applied to logical operands.",
534: context);
535: context.prog.addCommand(opcode);
536: tok = context.look();
537: }
538: return isBool;
539: }
540:
541: /**
542: * Called as part of the parsing process. From outside this class, this would
543: * probably be called only by a ParserExtension.
544: */
545: public boolean parseTerm(ParserContext context) {
546: boolean implicitStar = false;
547: boolean isBool = parsePrimary(context);
548: int tok = context.look();
549: String ts = context.tokenString;
550: implicitStar = !isBool
551: && (options & OPTIONAL_STARS) != 0
552: && (tok == ParserContext.NUMBER
553: || tok == ParserContext.IDENTIFIER || (tok == ParserContext.OPCHARS && (ts
554: .equals("(")
555: || ts.equals("[") || ts.equals("{"))));
556: if (tok == ParserContext.OPCHARS
557: && (ts.equals("*") || ts.equals("/")) && isBool)
558: throw new ParseError(
559: "A * or / operator cannot be applied to logical operands.",
560: context);
561: while (implicitStar || tok == ParserContext.OPCHARS
562: && (ts.equals("*") || ts.equals("/"))) {
563: if (!implicitStar)
564: context.next();
565: int opcode = (implicitStar || ts.equals("*") ? ExpressionProgram.TIMES
566: : ExpressionProgram.DIVIDE);
567: if (parsePrimary(context))
568: throw new ParseError(
569: "A * or / operator cannot be applied to logical operands.",
570: context);
571: context.prog.addCommand(opcode);
572: tok = context.look();
573: ts = context.tokenString;
574: implicitStar = !isBool
575: && (options & OPTIONAL_STARS) != 0
576: && (tok == ParserContext.NUMBER
577: || tok == ParserContext.IDENTIFIER || (tok == ParserContext.OPCHARS && (ts
578: .equals("(")
579: || ts.equals("[") || ts.equals("{"))));
580: }
581: return isBool;
582: }
583:
584: /**
585: * Called as part of the parsing process. From outside this class, this would
586: * probably be called only by a ParserExtension.
587: */
588: public boolean parsePrimary(ParserContext context) {
589: boolean isBool = parseFactor(context);
590: int tok = context.look();
591: if (tok == ParserContext.OPCHARS
592: && context.tokenString.equals("^")) {
593: if (isBool)
594: throw new ParseError(
595: "The exponentiation operator cannot be applied to logical operands.",
596: context);
597: context.next();
598: if (parsePrimary(context))
599: throw new ParseError(
600: "The exponentiation operator cannot be applied to logical operands.",
601: context);
602: context.prog.addCommand(ExpressionProgram.POWER);
603: }
604: return isBool;
605: }
606:
607: /**
608: * Called as part of the parsing process. From outside this class, this would
609: * probably be called only by a ParserExtension.
610: */
611: public boolean parseFactor(ParserContext context) {
612: boolean isBool = false;
613: int tok = context.next();
614: if (tok == ParserContext.NUMBER)
615: context.prog.addConstant(context.tokenValue);
616: else if (tok == ParserContext.IDENTIFIER)
617: parseWord(context);
618: else if (tok == ParserContext.END_OF_STRING)
619: throw new ParseError(
620: "Data ended in the middle of an incomplete expression.",
621: context);
622: else if (tok != ParserContext.OPCHARS)
623: throw new ParseError(
624: "Internal error: Unknown token type.", context);
625: else if (context.tokenString.equals("("))
626: isBool = parseGroup('(', ')', context);
627: else if (context.tokenString.equals("[")
628: && ((options & Parser.BRACKETS) != 0))
629: isBool = parseGroup('[', ']', context);
630: else if (context.tokenString.equals("{")
631: && ((options & Parser.BRACES) != 0))
632: isBool = parseGroup('{', '}', context);
633: else if (context.tokenString.equals("}")
634: && ((options & Parser.BRACES) != 0))
635: throw new ParseError(
636: "Misplaced right brace with no matching left brace.",
637: context);
638: else if (context.tokenString.equals("]")
639: && ((options & Parser.BRACKETS) != 0))
640: throw new ParseError(
641: "Misplaced right bracket with no matching left bracket.",
642: context);
643: else if (context.tokenString.equals(")"))
644: throw new ParseError(
645: "Misplaced right parenthesis with no matching left parenthesis.",
646: context);
647: else
648: throw new ParseError("Illegal or misplaced character \""
649: + context.tokenString.charAt(0) + "\"", context);
650: if ((options & FACTORIAL) != 0) {
651: tok = context.look();
652: while (tok == ParserContext.OPCHARS
653: && context.tokenString.equals("!")) {
654: if (isBool)
655: throw new ParseError(
656: "The factorial operator cannot be applied to a logical value.",
657: context);
658: context.next();
659: context.prog.addCommand(ExpressionProgram.FACTORIAL);
660: tok = context.look();
661: }
662: }
663: return isBool;
664: }
665:
666: // Helper routine for parsePrimary. If a ParserExtension needs to call this,
667: // it should look ahead to make sure there is a (, [, or { and then call
668: // parsePrimary.
669: /**
670: * Called as part of the parsing process. From outside this class, this would
671: * probably be called only by a ParserExtension.
672: */
673: private boolean parseGroup(char open, char close,
674: ParserContext context) {
675: boolean isBool = (options & Parser.BOOLEANS) == 0 ? parseExpression(context)
676: : parseLogicalExpression(context);
677: int tok = context.look();
678: if (tok == ParserContext.OPCHARS
679: && context.tokenString.equals("" + close))
680: context.next();
681: else
682: throw new ParseError("Missing \"" + close
683: + "\" to match a previous \"" + open + "\".",
684: context);
685: return isBool;
686: }
687:
688: // Helper routine for parsePrimary. If a ParserExtension needs to call this,
689: // it should look ahead to make sure there is an identifier and then call
690: // parsePrimary.
691: /**
692: * Called as part of the parsing process. From outside this class, this would
693: * probably be called only by a ParserExtension.
694: */
695: private void parseWord(ParserContext context) {
696: if (context.tokenObject == null)
697: throw new ParseError("Unknown word \""
698: + context.tokenString
699: + "\" encountered in an expression.", context);
700: if (context.tokenObject instanceof Variable
701: || context.tokenObject instanceof Constant)
702: context.prog
703: .addCommandObject((ExpressionCommand) context.tokenObject);
704: else if (context.tokenObject instanceof StandardFunction) {
705: StandardFunction f = (StandardFunction) context.tokenObject;
706: int tok = context.look();
707: if (tok == ParserContext.OPCHARS
708: && (context.tokenString.equals("(")
709: || (context.tokenString.equals("[") && ((options & BRACKETS) != 0)) || (context.tokenString
710: .equals("{") && ((options & BRACES) != 0)))) {
711: context.next();
712: boolean isBool;
713: if (context.tokenString.equals("("))
714: isBool = parseGroup('(', ')', context);
715: else if (context.tokenString.equals("["))
716: isBool = parseGroup('[', ']', context);
717: else
718: isBool = parseGroup('{', '}', context);
719: if (isBool)
720: throw new ParseError(
721: "The argument of a function must be a numerical expression.",
722: context);
723: } else {
724: if ((options & OPTIONAL_PARENS) == 0)
725: throw new ParseError(
726: "Parentheses required around argument of standard function \""
727: + f.getName() + "\".", context);
728: if (parseTerm(context))
729: throw new ParseError(
730: "The argument of a function must be a numerical expression.",
731: context);
732: }
733: context.prog.addCommand(f.getOpCode());
734: } else if (context.tokenObject instanceof ParserExtension)
735: ((ParserExtension) context.tokenObject).doParse(this ,
736: context);
737: else if (!(context.tokenObject instanceof ExpressionCommand))
738: throw new ParseError("Unexpected word \""
739: + context.tokenObject.getName()
740: + "\" encountered in an expression.", context);
741: else
742: throw new ParseError("Unimplemented word \""
743: + context.tokenObject.getName()
744: + "\" encountered in an expression.", context);
745: }
746:
747: } // end class Parser
|