Annotation of OpenXM_contrib/gmp/demos/expr/README, Revision 1.1
1.1 ! ohara 1: Copyright 2001 Free Software Foundation, Inc.
! 2:
! 3: This file is part of the GNU MP Library.
! 4:
! 5: The GNU MP Library is free software; you can redistribute it and/or modify
! 6: it under the terms of the GNU Lesser General Public License as published by
! 7: the Free Software Foundation; either version 2.1 of the License, or (at your
! 8: option) any later version.
! 9:
! 10: The GNU MP Library is distributed in the hope that it will be useful, but
! 11: WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 12: or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
! 13: License for more details.
! 14:
! 15: You should have received a copy of the GNU Lesser General Public License
! 16: along with the GNU MP Library; see the file COPYING.LIB. If not, write to
! 17: the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
! 18: 02111-1307, USA.
! 19:
! 20:
! 21:
! 22:
! 23:
! 24:
! 25: GMP EXPRESSION EVALUATION
! 26: -------------------------
! 27:
! 28:
! 29:
! 30: THIS CODE IS PRELIMINARY AND MAY BE SUBJECT TO INCOMPATIBLE CHANGES IN
! 31: FUTURE VERSIONS OF GMP.
! 32:
! 33:
! 34:
! 35: The files in this directory implement a simple scheme of string based
! 36: expression parsing and evaluation, supporting mpz, mpq, mpf and mpfr.
! 37:
! 38: This will be slower than direct GMP library calls, but may be convenient in
! 39: various circumstances, such as while prototyping, or for letting a user
! 40: enter values in symbolic form. "2**5723-7" for example is a lot easier to
! 41: enter or maintain than the equivalent written out in decimal.
! 42:
! 43:
! 44:
! 45: BUILDING
! 46:
! 47: Nothing in this directory is a normal part of libgmp, and nothing is built
! 48: or installed, but various Makefile rules are available to compile
! 49: everything.
! 50:
! 51: All the functions are available through a little library (there's no shared
! 52: library since upward binary compatibility is not guaranteed).
! 53:
! 54: make libexpr.a
! 55:
! 56: In a program, prototypes are available using
! 57:
! 58: #include "expr.h"
! 59:
! 60: run-expr.c is a sample program doing evaluations from the command line.
! 61:
! 62: make run-expr
! 63: ./run-expr '1+2*3'
! 64:
! 65: t-expr.c is self-test program, it prints nothing if successful.
! 66:
! 67: make t-expr
! 68: ./t-expr
! 69:
! 70: The expr*.c sources don't depend on gmp-impl.h and can be compiled with just
! 71: a standard installed GMP. This isn't true of t-expr though, since it uses
! 72: some of the internal tests/libtests.la.
! 73:
! 74:
! 75:
! 76: OPTIONAL MPFR
! 77:
! 78: The mpfr support in libexpr.a is built if GMP is configured with
! 79: --enable-mpfr. Programs must include mpfr.h before expr.h to get the extra
! 80: prototypes,
! 81:
! 82: #include "mpfr.h"
! 83: #include "expr.h"
! 84:
! 85: If the expr sources are being compiled outside a GMP build tree, then change
! 86: HAVE_MPFR in expr-impl.h if necessary to indicate whether mpfr is available.
! 87: exprfr.c and exprfra.c are the C files providing mpfr support.
! 88:
! 89:
! 90:
! 91:
! 92: SIMPLE USAGE
! 93:
! 94: int mpz_expr (mpz_t res, int base, const char *e, ...);
! 95: int mpq_expr (mpq_t res, int base, const char *e, ...);
! 96: int mpf_expr (mpf_t res, int base, const char *e, ...);
! 97: int mpfr_expr (mpfr_t res, int base, const char *e, ...);
! 98:
! 99: These functions evaluate simple arithmetic expressions. For example,
! 100:
! 101: mpz_expr (result, 0, "123+456", NULL);
! 102:
! 103: Numbers are parsed by mpz_expr and mpq_expr the same as mpz_set_str with the
! 104: given base. mpf_expr and mpfr_expr follow mpf_set_str and mpfr_set_str
! 105: respectively, but supporting an "0x" prefix for hex when base==0.
! 106:
! 107: mpz_expr (result, 0, "0xAAAA * 0x5555", NULL);
! 108:
! 109: White space, as indicated by <ctype.h> isspace(), is ignored except for the
! 110: purpose of separating tokens.
! 111:
! 112: Variables can be included in expressions by putting them in the varargs list
! 113: after the string. "a", "b", "c" etc in the expression string designate
! 114: those values. For example,
! 115:
! 116: mpq_t foo, bar;
! 117: ...
! 118: mpq_expr (q, 10, "2/3 + 1/a + b/2", foo, bar, NULL);
! 119:
! 120: Here "a" will be the value from foo and "b" from bar. Up to 26 variables
! 121: can be included this way. The NULL must be present to indicate the end of
! 122: the list.
! 123:
! 124: Variables can also be written "$a", "$b" etc. This is necessary when using
! 125: bases greater than 10 since plain "a", "b" etc will otherwise be interpreted
! 126: as numbers. For example,
! 127:
! 128: mpf_t quux;
! 129: mpf_expr (f, 16, "F00F@-6 * $a", quux, NULL);
! 130:
! 131: All the standard C operators are available, with the usual precedences, plus
! 132: "**" for exponentiation at the highest precedence (and right associative).
! 133:
! 134: Operators Precedence
! 135: ** 220
! 136: ~ ! - (unary) 210
! 137: * / % 200
! 138: + - 190
! 139: << >> 180
! 140: <= < >= > 170
! 141: == != 160
! 142: & 150
! 143: ^ 140
! 144: | 130
! 145: && 120
! 146: || 110
! 147: ? : 100/101
! 148:
! 149: Currently only mpz_expr has the bitwise ~ % & ^ and | operators. The
! 150: precedence numbers are of interest in the advanced usage described below.
! 151:
! 152: Various functions are available too. For example,
! 153:
! 154: mpz_expr (res, 10, "gcd(123,456,789) * abs(a)", var, NULL);
! 155:
! 156: The following is the full set of functions,
! 157:
! 158: mpz_expr
! 159: abs bin clrbit cmp cmpabs congruent_p divisible_p even_p fib fac
! 160: gcd hamdist invert jacobi kronecker lcm lucnum max min nextprime
! 161: odd_p perfect_power_p perfect_square_p popcount powm
! 162: probab_prime_p root scan0 scan1 setbit sgn sqrt
! 163:
! 164: mpq_expr
! 165: abs, cmp, den, max, min, num, sgn
! 166:
! 167: mpf_expr
! 168: abs, ceil, cmp, eq, floor, integer_p, max, min, reldiff, sgn,
! 169: sqrt, trunc
! 170:
! 171: mpfr_expr
! 172: abs, agm, ceil, cmp, cos, eq, exp, floor, inf_p, log, max, min,
! 173: nan_p, number_p, reldiff, sgn, sin, sqrt, trunc
! 174:
! 175: In addition mpfr_expr has constants "log2" and "pi". For example,
! 176:
! 177: mpfr_expr (res, 0, "2 * pi - log2", NULL);
! 178:
! 179: All these are the same as the GMP library functions, except that min and max
! 180: don't exist in the library. Note also that min, max, gcd and lcm take any
! 181: number of arguments, not just two.
! 182:
! 183:
! 184: mpf_expr and mpfr_expr do all calculations to the precision of the
! 185: destination variable. mpfr_expr rounds using GMP_RNDZ (but this can be
! 186: changed at build time in expr-impl.h).
! 187:
! 188:
! 189: Expression parsing can succeed or fail. The return value indicates this,
! 190: and will be one of the following
! 191:
! 192: MPEXPR_RESULT_OK
! 193: MPEXPR_RESULT_BAD_VARIABLE
! 194: MPEXPR_RESULT_BAD_TABLE
! 195: MPEXPR_RESULT_PARSE_ERROR
! 196: MPEXPR_RESULT_NOT_UI
! 197:
! 198: BAD_VARIABLE is when a variable is referenced that hasn't been provided.
! 199: For example if "c" is used when only two parameters have been passed.
! 200: BAD_TABLE is applicable to the advanced usage described below.
! 201:
! 202: PARSE_ERROR is a general syntax error, returned for any mal-formed input
! 203: string.
! 204:
! 205: NOT_UI is returned when an attempt is made to use an operand that's bigger
! 206: than an "unsigned long" with a function that's restricted to that range.
! 207: For example "fib" is mpz_fib_ui and only accepts an "unsigned long".
! 208:
! 209:
! 210:
! 211:
! 212: ADVANCED USAGE
! 213:
! 214: int mpz_expr_a (const struct mpexpr_operator_t *table,
! 215: mpz_ptr res, int base, const char *e, size_t elen,
! 216: mpz_srcptr var[26])
! 217: int mpq_expr_a (const struct mpexpr_operator_t *table,
! 218: mpq_ptr res, int base, const char *e, size_t elen,
! 219: mpq_srcptr var[26])
! 220: int mpf_expr_a (const struct mpexpr_operator_t *table,
! 221: mpf_ptr res, int base, unsigned long prec,
! 222: const char *e, size_t elen,
! 223: mpf_srcptr var[26])
! 224: int mpfr_expr_a (const struct mpexpr_operator_t *table,
! 225: mpfr_ptr res, int base, unsigned long prec,
! 226: const char *e, size_t elen,
! 227: mpfr_srcptr var[26])
! 228:
! 229: These functions are an advanced interface to expression parsing.
! 230:
! 231: The string is taken as pointer and length. This makes it possible to parse
! 232: an expression in the middle of somewhere without copying and null
! 233: terminating it.
! 234:
! 235: Variables are an array of 26 pointers to the appropriate operands, or NULL
! 236: for variables that are not available. Any combination of variables can be
! 237: given, for example just "x" and "y" (var[23] and var[24]) could be set.
! 238:
! 239: Operators and functions are specified with a table. This makes it possible
! 240: to provide additional operators or functions, or to completely change the
! 241: syntax. The standard tables used by the simple functions above are
! 242: available as
! 243:
! 244: const struct mpexpr_operator_t * const mpz_expr_standard_table;
! 245: const struct mpexpr_operator_t * const mpq_expr_standard_table;
! 246: const struct mpexpr_operator_t * const mpf_expr_standard_table;
! 247: const struct mpexpr_operator_t * const mpfr_expr_standard_table;
! 248:
! 249: struct mpexpr_operator_t is the following
! 250:
! 251: struct mpexpr_operator_t {
! 252: const char *name;
! 253: mpexpr_fun_t fun;
! 254: int type;
! 255: int precedence;
! 256: };
! 257:
! 258: typedef void (*mpexpr_fun_t) (void);
! 259:
! 260: As an example, the standard mpz_expr table entry for multiplication is as
! 261: follows. See the source code for the full set of standard entries.
! 262:
! 263: { "*", (mpexpr_fun_t) mpz_mul, MPEXPR_TYPE_BINARY, 200 },
! 264:
! 265: "name" is the string to parse, "fun" is the function to call for it, "type"
! 266: indicates what parameters the function takes (among other things), and
! 267: "precedence" sets its operator precedence.
! 268:
! 269: A NULL for "name" indicates the end of the table, so for example an mpf
! 270: table with nothing but addition could be
! 271:
! 272: struct mpexpr_operator_t table[] = {
! 273: { "+", (mpexpr_fun_t) mpf_add, MPEXPR_TYPE_BINARY, 190 },
! 274: { NULL }
! 275: };
! 276:
! 277: A special type MPEXPR_TYPE_NEW_TABLE makes it possible to chain from one
! 278: table to another. For example the following would add a "mod" operator to
! 279: the standard mpz table,
! 280:
! 281: struct mpexpr_operator_t table[] = {
! 282: { "mod", (mpexpr_fun_t) mpz_fdiv_r, MPEXPR_TYPE_BINARY, 125 },
! 283: { (const char *) mpz_expr_standard_table, NULL, MPEXPR_TYPE_NEW_TABLE }
! 284: };
! 285:
! 286: Notice the low precedence on "mod", so that for instance "45+26 mod 7"
! 287: parses as "(45+26)mod7".
! 288:
! 289:
! 290: Functions are designated by a precedence of 0. They always occur as
! 291: "foo(expr)" and so have no need for a precedence level. mpq_abs in the
! 292: standard mpq table is
! 293:
! 294: { "abs", (mpexpr_fun_t) mpq_abs, MPEXPR_TYPE_UNARY },
! 295:
! 296: Functions expecting no arguments as in "foo()" can be given with
! 297: MPEXPR_TYPE_0ARY, or actual constants to be parsed as just "foo" are
! 298: MPEXPR_TYPE_CONSTANT. For example if a "void mpf_const_pi(mpf_t f)"
! 299: function existed (which it doesn't) it could be,
! 300:
! 301: { "pi", (mpexpr_fun_t) mpf_const_pi, MPEXPR_TYPE_CONSTANT },
! 302:
! 303:
! 304: Parsing of operator names is done by seeking the table entry with the
! 305: longest matching name. So for instance operators "<" and "<=" exist, and
! 306: when presented with "x <= y" the parser matches "<=" because it's longer.
! 307:
! 308: Parsing of function names, on the other hand, is done by requiring a whole
! 309: alphanumeric word to match. For example presented with "fib2zz(5)" the
! 310: parser will attempt to find a function called "fib2zz". A function "fib"
! 311: wouldn't be used because it doesn't match the whole word.
! 312:
! 313: The flag MPEXPR_TYPE_WHOLEWORD can be ORed into an operator type to override
! 314: the default parsing style. Similarly MPEXPR_TYPE_OPERATOR into a function.
! 315:
! 316:
! 317: Binary operators are left associative by default, meaning they're evaluated
! 318: from left to right, so for example "1+2+3" is treated as "(1+2)+3".
! 319: MPEXPR_TYPE_RIGHTASSOC can be ORed into the operator type to work from right
! 320: to left as in "1+(2+3)". This is generally what's wanted for
! 321: exponentiation, and for example the standard mpz table has
! 322:
! 323: { "**", (mpexpr_fun_t) mpz_pow_ui,
! 324: MPEXPR_TYPE_BINARY_UI | MPEXPR_TYPE_RIGHTASSOC, 220 }
! 325:
! 326: Unary operators are postfix by default. For example a factorial to be used
! 327: as "123!" might be
! 328:
! 329: { "!", (mpexpr_fun_t) mpz_fac_ui, MPEXPR_TYPE_UNARY_UI, 215 }
! 330:
! 331: MPEXPR_TYPE_PREFIX can be ORed into the type to get a prefix operator. For
! 332: instance negation (unary minus) in the standard mpf table is
! 333:
! 334: { "-", (mpexpr_fun_t) mpf_neg,
! 335: MPEXPR_TYPE_UNARY | MPEXPR_TYPE_PREFIX, 210 },
! 336:
! 337:
! 338: The same operator can exist as a prefix unary and a binary, or as a prefix
! 339: and postfix unary, simply by putting two entries in the table. While
! 340: parsing the context determines which style is sought. But note that the
! 341: same operator can't be both a postfix unary and a binary, since the parser
! 342: doesn't try to look ahead to decide which ought to be used.
! 343:
! 344: When there's two entries for an operator, both prefix or both postfix (or
! 345: binary), then the first in the table will be used. This makes it possible
! 346: to override an entry in a standard table, for example to change the function
! 347: it calls, or perhaps its precedence level. The following would change mpz
! 348: division from tdiv to cdiv,
! 349:
! 350: struct mpexpr_operator_t table[] = {
! 351: { "/", (mpexpr_fun_t) mpz_cdiv_q, MPEXPR_TYPE_BINARY, 200 },
! 352: { "%", (mpexpr_fun_t) mpz_cdiv_r, MPEXPR_TYPE_BINARY, 200 },
! 353: { (char *) mpz_expr_standard_table, NULL, MPEXPR_TYPE_NEW_TABLE }
! 354: };
! 355:
! 356:
! 357: The type field indicates what parameters the given function expects. The
! 358: following styles of functions are supported. mpz_t is shown, but of course
! 359: this is mpq_t for mpq_expr_a, mpf_t for mpf_expr_a, etc.
! 360:
! 361: MPEXPR_TYPE_CONSTANT void func (mpz_t result);
! 362:
! 363: MPEXPR_TYPE_0ARY void func (mpz_t result);
! 364: MPEXPR_TYPE_I_0ARY int func (void);
! 365:
! 366: MPEXPR_TYPE_UNARY void func (mpz_t result, mpz_t op);
! 367: MPEXPR_TYPE_UNARY_UI void func (mpz_t result, unsigned long op);
! 368: MPEXPR_TYPE_I_UNARY int func (mpz_t op);
! 369: MPEXPR_TYPE_I_UNARY_UI int func (unsigned long op);
! 370:
! 371: MPEXPR_TYPE_BINARY void func (mpz_t result, mpz_t op1, mpz_t op2);
! 372: MPEXPR_TYPE_BINARY_UI void func (mpz_t result,
! 373: mpz_t op1, unsigned long op2);
! 374: MPEXPR_TYPE_I_BINARY int func (mpz_t op1, mpz_t op2);
! 375: MPEXPR_TYPE_I_BINARY_UI int func (mpz_t op1, unsigned long op2);
! 376:
! 377: MPEXPR_TYPE_TERNARY void func (mpz_t result,
! 378: mpz_t op1, mpz_t op2, mpz_t op3);
! 379: MPEXPR_TYPE_TERNARY_UI void func (mpz_t result, mpz_t op1, mpz_t op2,
! 380: unsigned long op3);
! 381: MPEXPR_TYPE_I_TERNARY int func (mpz_t op1, mpz_t op2, mpz_t op3);
! 382: MPEXPR_TYPE_I_TERNARY_UI int func (mpz_t op1, mpz_t op2,
! 383: unsigned long op3);
! 384:
! 385: Notice the pattern of "UI" for the last parameter as an unsigned long, or
! 386: "I" for the result as an "int" return value.
! 387:
! 388: It's important that the declared type for an operator or function matches
! 389: the function pointer given. Any mismatch will have unpredictable results.
! 390:
! 391: For binary functions, a further type attribute is MPEXPR_TYPE_PAIRWISE which
! 392: indicates that any number of arguments should be accepted, and evaluated by
! 393: applying the given binary function to them pairwise. This is used by gcd,
! 394: lcm, min and max. For example the standard mpz gcd is
! 395:
! 396: { "gcd", (mpexpr_fun_t) mpz_gcd,
! 397: MPEXPR_TYPE_BINARY | MPEXPR_TYPE_PAIRWISE },
! 398:
! 399: Some special types exist for comparison operators (or functions).
! 400: MPEXPR_TYPE_CMP_LT through MPEXPR_TYPE_CMP_GE expect an MPEXPR_TYPE_I_BINARY
! 401: function, returning positive, negative or zero like mpz_cmp and similar.
! 402: For example the standard mpf "!=" operator is
! 403:
! 404: { "!=", (mpexpr_fun_t) mpf_cmp, MPEXPR_TYPE_CMP_NE, 160 },
! 405:
! 406: But there's no obligation to use these types, for instance the standard mpq
! 407: table just uses a plain MPEXPR_TYPE_I_BINARY and mpq_equal for "==".
! 408:
! 409: Further special types MPEXPR_TYPE_MIN and MPEXPR_TYPE_MAX exist to implement
! 410: the min and max functions, and they take a function like mpf_cmp similarly.
! 411: The standard mpf max function is
! 412:
! 413: { "max", (mpexpr_fun_t) mpf_cmp,
! 414: MPEXPR_TYPE_MAX | MPEXPR_TYPE_PAIRWISE },
! 415:
! 416: These can be used as operators too, for instance the following would be the
! 417: >? operator which is a feature of GNU C++,
! 418:
! 419: { ">?", (mpexpr_fun_t) mpf_cmp, MPEXPR_TYPE_MAX, 175 },
! 420:
! 421: Other special types are used to define "(" ")" parentheses, "," function
! 422: argument separator, "!" through "||" logical booleans, ternary "?" ":", and
! 423: the "$" which introduces variables. See the sources for how they should be
! 424: used.
! 425:
! 426:
! 427: User definable operator tables will have various uses. For example,
! 428:
! 429: - a subset of the C operators, to be rid of infrequently used things
! 430: - a more mathematical syntax like "." for multiply, "^" for powering,
! 431: and "!" for factorial
! 432: - a boolean evaluator with "^" for AND, "v" for OR
! 433: - variables introduced with "%" instead of "$"
! 434: - brackets as "[" and "]" instead of "(" and ")"
! 435: - new mpfr operators ^+^ or _+_ which round in a particular direction
! 436:
! 437: The only fixed parts of the parsing are the treatment of numbers, whitespace
! 438: and the two styles of operator/function name recognition.
! 439:
! 440: As a final example, the following would be a complete mpz table implementing
! 441: some operators with a more mathematical syntax. Notice there's no need to
! 442: preserve the standard precedence values, anything can be used so long as
! 443: they're in the desired relation to each other. There's also no need to have
! 444: entries in precedence order, but it's convenient to do so to show what comes
! 445: where.
! 446:
! 447: static const struct mpexpr_operator_t table[] = {
! 448: { "^", (mpexpr_fun_t) mpz_pow_ui,
! 449: MPEXPR_TYPE_BINARY_UI | MPEXPR_TYPE_RIGHTASSOC, 9 },
! 450:
! 451: { "!", (mpexpr_fun_t) mpz_fac_ui, MPEXPR_TYPE_UNARY_UI, 8 },
! 452: { "-", (mpexpr_fun_t) mpz_neg,
! 453: MPEXPR_TYPE_UNARY | MPEXPR_TYPE_PREFIX, 7 },
! 454:
! 455: { "*", (mpexpr_fun_t) mpz_mul, MPEXPR_TYPE_BINARY, 6 },
! 456: { "/", (mpexpr_fun_t) mpz_fdiv_q, MPEXPR_TYPE_BINARY, 6 },
! 457:
! 458: { "+", (mpexpr_fun_t) mpz_add, MPEXPR_TYPE_BINARY, 5 },
! 459: { "-", (mpexpr_fun_t) mpz_sub, MPEXPR_TYPE_BINARY, 5 },
! 460:
! 461: { "mod", (mpexpr_fun_t) mpz_mod, MPEXPR_TYPE_BINARY, 6 },
! 462:
! 463: { ")", NULL, MPEXPR_TYPE_CLOSEPAREN, 4 },
! 464: { "(", NULL, MPEXPR_TYPE_OPENPAREN, 3 },
! 465: { ",", NULL, MPEXPR_TYPE_ARGSEP, 2 },
! 466:
! 467: { "$", NULL, MPEXPR_TYPE_VARIABLE, 1 },
! 468: { NULL }
! 469: };
! 470:
! 471:
! 472:
! 473:
! 474: INTERNALS
! 475:
! 476: Operator precedence is implemented using a control and data stack, there's
! 477: no C recursion. When an expression like 1+2*3 is read the "+" is held on
! 478: the control stack and 1 on the data stack until "*" has been parsed and
! 479: applied to 2 and 3. This happens any time a higher precedence operator
! 480: follows a lower one, or when a right-associative operator like "**" is
! 481: repeated.
! 482:
! 483: Parentheses are handled by making "(" a special prefix unary with a low
! 484: precedence so a whole following expression is read. The special operator
! 485: ")" knows to discard the pending "(". Function arguments are handled
! 486: similarly, with the function pretending to be a low precedence prefix unary
! 487: operator, and with "," allowed within functions. The same special ")"
! 488: operator recognises a pending function and will invoke it appropriately.
! 489:
! 490: The ternary "? :" operator is also handled using precedences. ":" is one
! 491: level higher than "?", so when a valid a?b:c is parsed the ":" finds a "?"
! 492: on the control stack. It's a parse error for ":" to find anything else.
! 493:
! 494:
! 495:
! 496: FUTURE
! 497:
! 498: The ternary "?:" operator evaluates the "false" side of its pair, which is
! 499: wasteful, though it ought to be harmless. It'd be better if it could
! 500: evaluate only the "true" side. Similarly for the logical booleans "&&" and
! 501: "||" if they know their result already.
! 502:
! 503: Functions like MPEXPR_TYPE_BINARY could return a status indicating operand
! 504: out of range or whatever, to get an error back through mpz_expr etc. That
! 505: would want to be just an option, since plain mpz_add etc have no such
! 506: return.
! 507:
! 508: Could have assignments like "a = b*c" modifying the input variables.
! 509: Assignment could be an operator attribute, making it expect an lvalue.
! 510: There would want to be a standard table without assignments available
! 511: though, so user input could be safely parsed.
! 512:
! 513: The closing parethesis table entry could specify the type of open paren it
! 514: expects, so that "(" and ")" could match and "[" and "]" match but not a
! 515: mixture of the two. Currently "[" and "]" can be added, but there's no
! 516: error on writing a mixed expression like "2*(3+4]". Maybe also there could
! 517: be a way to say that functions can only be written with one or the other
! 518: style of parens.
! 519:
! 520:
! 521:
! 522: ----------------
! 523: Local variables:
! 524: mode: text
! 525: fill-column: 76
! 526: End:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>