m.E.M.E - Minimalist Evaluator for Mathematical Expressions

Miscellaneous programs and scripts, opensource or not, and sometimes, random mathematical stuff.
User avatar
Gamall
Hic sunt dracones
Posts: 4125
Joined: Fri May 26, 2006 11:09 pm
Contact:

m.E.M.E - Minimalist Evaluator for Mathematical Expressions

Postby Gamall » Tue Apr 08, 2008 4:47 pm

->
mEME_Mini-Eval-Math-Exp.zip
(791.17 KiB) Downloaded 390 times


-> on filefront

Code: Select all

*****************************************************************
**                           m.E.M.E                           **
**                              -                              **
**      Minimalist Evaluator for Mathematical Expressions      **
*****************************************************************
 
  #-----------------------------------------------------------# 
  #                      TITLE : m.E.M.E                      # 
  #     Minimalist Evaluator for Mathematical Expressions     # 
  #                      TYPE : Utility                       # 
  #                       VERSION : 0.1                       # 
  #               AUTHOR : Gamall Wednesday Ida               # 
  #               E-MAIL : gamall.ida@gmail.com               # 
  #              WEBSITE : http://gamall-ida.com              # 
  #                                                           # 
  #                    FILESIZE : ~800 Ko                     # 
  #                 OS : Linux, Mac, Windows                  # 
  #                 RELEASE DATE : March 2008                 # 
  #-----------------------------------------------------------# 
 
 
 
+   TABLE OF CONTENTS                                           
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-o          +
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
        1. BRIEF DESCRIPTION                                           
       
        2. WHAT IS AN EXPRESSION?       
           -Literals and simple expressions
           -Bound expressions
           -Units           
           -Logic and conditional expressions                     
       
        3. THE STANDALONE PROGRAM                                       
           -The run modes                                                 
           -Application of 'replace' mode to
            Quake configuration files.
       
        4. THE PACKAGE
       
        5. CONTACT
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 
+   BRIEF DESCRIPTION                                           
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-o          +
 
 m.E.M.E  (pronounce m-ee-m-ee) stands for "Minimalist Evaluator
 for Mathematical Expressions". And that is exactly what it  is.
 
 'Evaluator for Mathematical Expressions' because its purpose is
 to  compute mathematical expressions. For instance "2+2" yields
 4. Less trivially, "let x = pi/2 in 1+ sqrt cos  (x/2)"  yields
 1.84089641525.  It  is  meant to be used as a library for other
 programs, but the  standalone  executable  can  be  used  as  a
 command-line  calculator  and  a preprocessor for configuration
 files.                                                         
 
 'Minimalist' because there are points which make it simpler and
 faster (it is *very* fast) but limit its range:                 
 
 -> It uses floating-point  arithmetic  (64bits,  as  per  OCaml
 `float'  type)  instead  of  exact  arithmetic, hence a loss of
 precision on some expressions:                                 
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       4.63246686343E-08 + 1
       + exp(2+sin(log(cos(
         acos(-pi/2+tan(atan(pi))-1)))))
           /sqrt(pi*5E15)
       > 1.00000009265
       
       exp(2+sin(log(cos(
       acos(-pi/2+tan(atan(pi))-1)))))
         /sqrt(pi*5E15)
       - 4.63246686343E-08 + 1
       > 1
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 -> It supports variables but not functions. So  while  you  can
 write                                                           
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       let x = pi/2 in 1+ sqrt cos (x/2)
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 ... you can't write anything like                               
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       let f(x) = 1+ sqrt cos (x/2) in f(pi/2)
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 -> It does not support any kind of loops. So for instance there
 is  no  sum  (big  Sigma  symbol  in  math)  or product (big Pi
 symbol), because that kind of functionality couldn't  be  added
 elegantly to the program. This is a consequence of the previous
 point.                                                         
 
 ->  It  is  not,  of course, a system for symbolic computation.
 
 
+   WHAT IS AN EXPRESSION?                                       
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-o          +
 
 Here I'll define informally  what  kinds  of  expressions  mEME
 recognises   and   computes.  Formally,  the  complete  grammar
 generating the language of  mEME  can  be  found  in  the  file
 mathparser.mly,  and  all  the  lexems  are  in  mathlexer.mll.
 
 So, what is an expression ?                                     
 
 
   -   Literals and simple expressions                           
   -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~o          -
 
 Numbers, such as "2" and "3.14", are expressions. So  is  "2  +
 3.14", and any combination of numbers, operators, and functions
 (among those supported) which makes sense.                     
 
 Most  common  functions  and  operators,  and a few more exotic
 ones, are supported. Refer to the aforementionned files  for  a
 complete list.                                                 
 
 
   -   Bound expressions                                         
   -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~o          -
 
 Variables  can  be  declared  (that  is, a value is bound to an
 identifier)  in  an  expression.  The  syntax  is  as  follows:
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
         let x = VALUE in EXPRESSION(x)
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Note:  `in'  can  be  replaced  by  `;', and `let' is optional.
 Of course, the expression doesn't *have* to depend  on  x,  and
 can  define  other variables. Multiple declarations can also be
 made:                                                           
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
         let x = VALUE and y = VALUE and ... in EXPRESSION
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Note: `and' can be replaced by `,'                             
 Note: The values are computed from left to right,  so  `y'  can
 depend on `x'.                                                 
 Here  is  an example, convoluted and stupid, but coined to show
 some non-trivial things about variables:                       
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       let x = 1, y = 5 in
       (z=pi; 2) + x / (let a = 666/y in z**2- a*x)
       > 1.99189169876
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 This is the same expression as                                 
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       2 + 1/( pi**2 - (666/5)*1 )
       > 1.99189169876
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Or is it ? Well, not exactly. If you ask mEME to show  you  its
 internal  variables  table after that (provided you didn't stop
 the session after computing this)                               
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       @vars
       Bound variables:
                            x =                      1
                            y =                      5
                            z =          3.14159265359
                            a =                  133.2
       Total: 4 variables
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 ...you will see that the variables are *still* there,  and  can
 be   used   again   or  redefined  in  subsequent  expressions.
 
 This is where the term "bound expression" and the "let  ..  in"
 syntax  are  somewhat  misleading,  since the variables are not
 local to the expression in which they  are  defined.  In  fact,
 they  are  defined everywhere to the *right* of the definition.
 In the expression                                               
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       let x = 1, y = 5 in
       (z=pi; 2) + x / (let a = 666/y in z**2- a*x)
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 this gives sense  to  "z**2".  If  z  had  been  local  to  the
 expression for which it was defined, z could not be used there.
 
 By  the  way,  you  will  also  have  noticed  how  strange the
 expression "z=pi; 2" is:                                       
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       let z = pi in 2
       > 2
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 This is just an expression where  the  bound  variable  is  not
 used.  It  amounts  to a value, but with a change of state. But
 does that mean that you have to return a value even if you just
 want to define a  variable?  Of  course  not.  This  notion  of
 `change  of  state without value' is called a `Unit', just like
 in ML.                                                         
 
 
   -   Units                                                     
   -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~o          -
 
 Let's consider our silly example one more  time.  It  could  be
 written by computing first                                     
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       let x=1, y=5, z=pi
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 and then                                                       
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       2 + x / (z**2 - [666/y]*x)
       > 1.99189169876
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 You  will  notice  that  "let  x=1, y=5, z=pi" does not seem to
 yield any result. In fact it  *does*  yield  the  result  "()",
 which  means  `Unit', but the program is set to not print units
 by  default.  If   you   activate   unit   printing   you   get
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       ./meme pu
       let x=1, y=5, z=pi
       > ()
       @vars
       Bound variables:
                            x =               1.000000
                            y =               5.000000
                            z =               3.141593
       Total: 3 variables
       > ()
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 `Unit'  simply  means:  "There  is  no  result  to  return, but
 something has been done, like creating a variable  or  printing
 something to the screen".                                       
 
 "()"  is  also  an  expression,  which  you  can  use.  In fact
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       let x=1, y=5, z=pi
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 is equivalent to                                               
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       let x=1, y=5, z=pi in ()
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Units are contagious, so any  expression  with  a  unit  in  it
 becomes a unit.                                                 
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       5 + ()/2
       > ()
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Beware, there is a catch: how many expressions do you think are
 defined by the expression                                       
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       5 + x=1; () + (y = 2 in y)
       > ()
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 The answer is: only one, because the parser stops as soon as it
 meets   ().  It  doesn't  even  read  the  right  part  of  the
 expression, so `y' is never defined.                           
 
 A consequence of this is that an incorrect  expression  with  a
 unit in it can yield () instead of Error.                       
 
 There  are  other  kinds  of  side-effects,  such  as the print
 function: 'print x' yields x, but also  prints  it  on  STDOUT.
 Print  has a very strong priority, and can be useful to display
 intermediate results in a long expression.                     
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       let x = cos( print pi / 2 ) in x
       3.14159265359
       > 6.12303176911e-17
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 
   -   Logic and conditional expressions                         
   -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~o          -
 
 Booleans are supported in a very dirty way, similar to that  of
 languages such as C, C++ and their ilk; since there is only one
 datatype  in mEME (floating point), the boolean values TRUE and
 FALSE are defined as 1 and 0 respectively.                     
 
 More specifically, the same point of view than in C  is  taken:
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       x = true   <==> x is not 0
       
       x = false  <==> x is not true
                  <==> x = 0
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 With   this   in  mind  here  is  the  syntax  for  conditional
 expressions:                                                   
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       if EXPRESSION then EXPRESSION_TRUE
                     else EXPRESSION_FALSE
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Of course, those  combine  naturally  into  "if...then...  else
 if... ...else ..." constructs.                                 
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       x = 3
       > ()
       if x == 2 then 10 else if x == 3 then 20 else 90
       > 20
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 the  ternary  operator, more compact but less readable, is also
 supported:                                                     
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       EXPRESSION? EXPRESSION_TRUE : EXPRESSION_FALSE
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 It is important to note that the if...then...else construct  is
 an  *expression*,  and  has a *value*, unlike the equivalent in
 imperative programming languages.                               
 
 It is also worth mentionning that there  is  no  short-circuit:
 *both*  EXPRESSION_TRUE  and EXPRESSION_FALSE are evaluated, no
 matter which one  will  be  returned.  Consider  the  following
 examples:                                                       
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       if true then [x=6 in x] else [y=8 in y]
       > 6
       @vars
       Bound variables:
                            x =               6.000000
                            y =               8.000000
       Total: 2 variables
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Though  the  rightmost expression, [y=8 in y], is not returned,
 it has been evaluated and the variable `y'  has  been  created.
 
 This   becomes   funny   when   you   start   involving  units:
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       if condition then () else [a=5 in a]
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 will return () and *not* define `a' regardless of the value  of
 `condition'...                                                 
 
 Now,  a  few  words  of  warning  about  those  "floating-point
 booleans"... let us  say  we  are  working  with  trigonometric
 functions,    for    instance,   and   write   the   following:
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       let x = Pi/2 in if cos x then 1 else 2
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Formally, this expression  is  supposed  to  yield  2,  because
 cos(Pi  / 2) is 0, and 0 is false. But, when this expression is
 run through mEME:                                               
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       let x = Pi/2 in if cos x then 1 else 2
       > 1
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 ... it returns 1. Why? Is something broken in mEME? No. This is
 a  problem  common  to  everything  that  uses   floating-point
 calculus:  since  the  numbers  are approximated, it is hard to
 determine if they are  formally  the  same.  All  you  know  is
 whether  they  have  the  same  approximated  value,  and as it
 happens                                                         
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       cos (Pi/2)
       > 6.12303176911e-17
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 ...  the  approximation  of  pi  combined   with   the   series
 approximating  the  cosine  function  yield  a  very  small but
 definitely non-zero value, therefore                           
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       cos (Pi/2) == 0
       > 0
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 cosine  of  Pi/2  is  not  zero  as  far  as   if...then...else
 expressions go.                                                 
 
 The  right  way  to  handle  that kind of cases is to specify a
 range around zero:                                             
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       let x = cos(Pi/2) and notzero = |x| > E-10 in
       if notzero then 1 else 2
       > 1
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 |x| is the absolute value of x, of course.  And  this  time  it
 works as expected.                                             
 
 Last word: the boolean operators are the same than in C: || for
 `or' && for `and', == for `equals', and the usual <, >, <=, >=.
 `different from' is noted <>, != or |=. `not' is written either
 "not"   or   "!".   There   is   also   a  `xor'  operator:  ^^
 
 When in doubt about the syntax,  try  things.  There  are  many
 synonyms  for  everything.  All  identifiers  are  in the lexer
 source file mathlexer.mll.                                     
 
 
+   THE STANDALONE PROGRAM                                       
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-o          +
 
 
   -   The run modes                                             
   -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~o          -
 
 First, let us see the help page:                               
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       $ ./meme --help
       m.E.M.E - Minimalist Evaluator for Mathematical Expressions
       
       usage: calc [options] [mode]
       
         modes:
           +command-line (c) sequentially evaluate command-line arguments
           +stdin        (s) evaluate expressions from standard input
           +replace      (r) search and replace expressions inside a line (on stdin)
       
         options:  (modes c and s)
           --print-xp     (pxp) reprint the unevaluated expression
           --print-newline (pn) goto newline before printing the result
           --print-unit    (pu) print units, ie. stand-alone assignments
           --print-symbol  (ps) print a symbol before the result
       
         options (mode s)
           --display-welcome (dw)
             Display the welcome message at beginning of session
       
         options (modes s and r)
           --eval-now (en) xp1...xpN
             Evaluate a list of command-line expressions before processing stdin.
       
         options (mode r)
           --delimiters (del) <left del> <right del>
             Set the delimiters for mathematical expressions.
       
         options  (all modes)
           --eval-file    (ef) <str>
             Silently parse a file. Useful to define a bunch of variables.
           --unit-string  (us) <str>
             Set the string representing a unit (pure side-effect evaluation)
           --print-errors (pe)
             The parser prints debug error messages on stderr
           --show-error   (se)
             The parser prints a line with ^^^ where the error is
           --help
             Print this help page.
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 There are three main ways to run the mEME executable.  Firstly,
 you can run it in STDIN mode. Expressions will be read from the
 standard input. Example:                                       
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       $ ./meme
       
       ** m.E.M.E. v0.1
       ** by Gamall Wednesday Ida
       ** email : gamall.ida@gmail.com
       ** web   : gamall-ida.com
       
       let x = cos(Pi/2) and notzero = |x| > E-10
       @vars
       Bound variables:
                          x =   6.12303176911e-17
                    notzero =                   0
       Total: 2 variables
       sin [x*pi]
       > 1.92360716235e-16
       @quit
       Bye-bye!
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 ... or from a file:                                             
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       $ ./meme < testexpr
       
       ** m.E.M.E. v0.1
       ** by Gamall Wednesday Ida
       ** email : gamall.ida@gmail.com
       ** web   : gamall-ida.com
       
       > 2440
       {End of file reached}
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 The  second  mode  is the COMMAND-LINE mode, where command-line
 arguments are run in a sequence:                               
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       $ ./meme c "x = 5" "pi = x" "cos (x*pi)**2"
       pi = x
          ^
       {Parsing failed on pos 3: `='}
       {Perhaps you are trying to bind a variable with a reserved name, such as `pi', `log' etc.}
       > Error.
       > -0.12476077673
       
       $ ./meme c "x = 5"  "cos (x*pi)**2"
       > -0.12476077673
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Notice the error messages, which are  printed  to  stderr,  not
 stdout.  There  are options to control how much is displayed in
 case of errors.                                                 
 
 The third mode is the REPLACE mode, in which mEME filters STDIN
 line by line to find expressions inside any text, processes all
 expressions, and returns the original line on STDOUT, replacing
 the expression with  its  value.  The  default  delimiters  for
 expressions  are  [[...]],  but  can  be  changed  to anything.
 
 Most of the time, this mode will be used reading  from  a  file
 (we  will  see  a  detailed  application to configuration files
 later on), so let us write a small  text  file,  repex.txt,  to
 illustrate this:                                               
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       Hello !
       Nothing Happens because there is no expression...
       The value of Pi is [[pi]]
       Let x be [[x = 25 in x]], and y = [[y = x**2 in y]] its square.
       Ouups the order of evaluation in one line is unspecified!
       [[x = 25, y = x**2]]
       Let x be [[x]], and y = [[y]] its square.
       Or
       Let x be [[x = 25, y = x**2 in x]], and y = [[y]] its square.
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 This  yields  the following result: (again, note that the error
 messages are on STDERR, not STDOUT, so if you do $ ./meme  r  <
 repex.txt   >   result   you   will   just   see  the  errors.)
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       ./meme r < repex.txt
       Hello !
       Nothing Happens because there is no expression...
       The value of Pi is 3.14159265359
       y = x**2 in y
           ^
       {Parsing failed on pos 4: `x'}
       {Unbound variable: `x'}
       Let x be 25, and y = Error. its square.
       Ouups the order of evaluation in one line is unspecified!
       ()
       Let x be 25, and y = 625 its square.
       Or
       Let x be 25, and y = 625 its square.
       {End of file reached}
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 There is a subtelty there in the order  of  the  evaluation  of
 expressions written on the same line, which causes the error in
 `y  =  x**2':  the  longuest  expression is evaluated first. In
 general, avoid relying on the order of  evaluation:  use  [[let
 ...  and  ...]]  or  span  declarations on several lines if the
 expressions gradually depend on one another.                   
 
 
   -   Application of 'replace' mode                             
   -   to Quake configuration files.                             
   -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~o          -
 
 This system can be used to preprocess configuration files, with
 a view to inject some flexibility into  otherwise  static  text
 files.  I  will take some examples in Quake configuration files
 for the Jedi Knight game servers, but the  same  ideas  can  be
 applied to many systems.                                       
 
 Quake  CFG  files  are  absolutely  static: there is no `if' or
 anything like that. You just bind values  to  names,  or  CVARS
 (Configuration  VARiables).  `value',  in  that  context, means
 `string'; numbers are directly converted from the string by the
 game. So if you write                                           
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       seta x 2
         or
       seta x "2"
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 and the game looks for an integer  in  `x',  it  will  use  the
 integer  value  2.  Up  to  there everything is fine but let us
 suppose you write                                               
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       seta x "2+2"
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 well then things begin to go downhill. The meaning  of  2+2  is
 quite clear to you: if x is seen as a number, then this "means"
 4.  But the game only sees the string of the characters 2, then
 +, then 2. If it tries to get an integer out of an  expression,
 it   will   either   fail   or   yield   an  incorrect  result.
 
 In fact, I would bet that "2+2" would be intepreted by the game
 as... 2,  because  it  probably  uses  the  standard  C  atoi()
 function to do its string -> int conversion, which simply stops
 at the first non-numerical character it reads.                 
 
 But  then,  if  you  use  mEME to preprocess your configuration
 file,                                                           
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       seta x [[2+2]]
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 will become                                                     
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       seta x 4
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 and the problem is solved.                                     
 
 Hallelujah! but WHY would you want to write things like 2+2  in
 your configuration file? Because you don't just get the ability
 to  evaluate 2+2, you get all the mEME arsenal, namely standard
 math,   variables,   logic   and    conditional    expressions.
 
 A  configuration file is just a place where tons of options are
 dumped. These options  are  not  often,  in  practice,  totally
 independant  from  one  another. Generally, when you set out to
 modify your configuration file, there are groups of options you
 want to alter together, or whose values obviously depend on one
 another.                                                       
 
 In  short,  some  configuration  variables  are  mathematically
 and/or   logically   linked.   (for  your  purposes  at  least)
 
 But, as long as the system does  not  directly  implement  this
 reliationship  between  them,  you have to modify everything by
 hand, which makes combining ideas difficult.                   
 
 Time for some  examples:  first,  a  well-known  example  of  a
 strictly  mathematical  relationship  between  cvars  in  Quake
 servers: the number of clients and the maximum rate. These  two
 parameters  are  two  sides  of  the  same  coin, linked by the
 formula                                                         
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       max rate =
         speed of your connection
           /
         (8 * max number of clients)
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Using the appropriate unit: BPS. Since you are likely  to  know
 the  value  in  KBPS,  you  also need a small conversion before
 applying the formula. Also you might want to put a fixed  limit
 on the rate.                                                   
 
 You  can  do  that  by  hand,  but  here  is  a  way  to  do it
 automatically:                                                 
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       // EXAMPLE 1: Max rate and clients: A mathematical relation
       
       // set the connection speed (in kbps): [[ let connspeed = 512, rcs = 1000*connspeed ]]
       // set the number of clients on your server: [[ clients = 16 ]]
       // set the maximum rate you want to allow: [[ maxrate = 30000 ]]
       
       // get the rate as a function of the number of clients you wish to allow
       seta sv_maxclients = [[clients]]
       seta sv_maxrate    = [[rate = floor rcs / (clients * 8) in if rate < maxrate then rate else maxrate]]
       
       // a tad more original approach: get the clients as a function of the rate you desire
       seta sv_maxrate    = [[let rate = 4000 in rate]]
       seta sv_maxclients = [[res = floor rcs / (8 * rate) in res <= 32 ? res : 32]]
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Once you have set up the formulae, you just need to change what
 you *want*, here the number of clients  (or  connspeed  if  you
 change  your  modem), and the rate will compute by itself. Once
 run through mEME, this file yields                             
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       $ ./meme r < zaza.cfg
       // EXAMPLE 1: Max rate and clients: A mathematical relation
       
       // set the connection speed (in kbps): ()
       // set the number of clients on your server: ()
       // set the maximum rate you want to allow: ()
       
       // get the rate as a function of the number of clients you wish to allow
       seta sv_maxclients = 16
       seta sv_maxrate    = 4000
       
       // a tad more original approach: get the clients as a function of the rate you desire
       seta sv_maxrate    = 4000
       seta sv_maxclients = 16
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Notice that the formulae when the variables are  declared  have
 been  replaced  by a unit: "()". The variable declarations were
 in the comments, but those are useless  know,  and  might  grow
 beyond  the  size limit on CFG files (around 16Ko if I remember
 right). There are two ways around this: The first  is  to  make
 every  computation  in  another file, loaded by the --eval-file
 option, and the second to filter the output of  mEME,  using  a
 configuration cleaner like                                     
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       http://www.gamall-ida.com/f/viewtopic.php?f=3&t=391
        ->
       http://www.gamall-ida.com/f/viewtopic.php?p=4847#p4847
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 and  then  the  output  comes  stripped  of all but the values.
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       $ ./meme r < zaza.cfg | ./confclean
       seta sv_maxclients = 16
       seta sv_maxrate    = 4000
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Let us take another,  more  common  example:  bitvalues.  Quick
 reminder:  a bitvalue is a number which is interpreted as a set
 of options, activated or deactivated. This is done  by  reading
 the  numbers in base two, as a series of 0 and 1, each position
 (bit) corresponding to an option.                               
 
 Bitvalues  are  commonly  used  in  configuration  files,   for
 instance  in  JKA, to select the weapons and force powers to be
 allowed on the server, and in  various  mods  such  as  JKA  to
 determine access to admin privileges and so on.                 
 
 The problem with bitvalues is that they are very impractical to
 use. First when you want to write one. You have to remember the
 value  associated  to  each  option, and compute their sum. And
 then, when you want to modify your configuration file, you have
 a *very* hard time remembering what the hell the value  9726693
 means...                                                       
 
 Programs  out  there,  bivalues calculators, such as the one on
 japlus.net, take partially care of those problems: to compute a
 bitvalue you merely have to check boxes, and then you copy  and
 paste the result to your configuration file.                   
 
 Still,  none of those I know of take care of the other problem:
 when you already have a bitvalue and  want  to  know  *what  it
 means*.  For  instance, is such or such admin provilege allowed
 to Knights? You simply can't tell, unless  you  manually  break
 the number.                                                     
 
 A  trivial  fix  would  be  to  make  those  programs check the
 appropriate boxes when you paste a value. But there is a better
 way: using mEME  to  define  the  values,  and  then  sum  them
 directly in the configuration file.                             
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       // EXAMPLE 2: Force powers (and any other bitvalue, the principle is the same...)
       
       // Define the constants for force powers:
       // [[heal = 1, jump = 2, speed = 2**2, push = 2**3, pull = 2**4,
        mind_trick = 2**5, grip = 2**6, light = 2**7, rage = 2**8,
        protect = 2**9, absorb = 2**10, team_heal = 2**11,
        team_energize = 2**12]]
       // [[drain = 2**13, seeing = 2**14, sab_attack = 2**15,
        sab_defend = 2**16, sab_throw = 2**17]]
       
       // then you can write the powers directly... you don't need a
        calculator and you can see at first glance what your
        configuration file really means.
       
       seta g_forcePowerDisable = [[speed+push+pull+grip+drain]]
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Which will be computed as                                       
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       seta g_forcePowerDisable = 8284
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 I  have written a small file (jka.meme) containing declarations
 for the most common JKA and JA+ bitvalues.  If  you  load  this
 file  with  mEME,  you  can  then write the bitvalues naturally
 without  needing   anything   in   your   configuration   file:
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       // Bitvalues computation examples
       
       seta g_forcePowerDisable = [[lightning + drain + rage]]
       
       seta g_weaponDisable = [[all_weapons - saber - bryar - blaster]]
       
       seta jp_votesDisable = [[sleep + kick + silence]]
       
       seta jp_councilAllowedCMD = [[all_admin]]
       
       seta jp_knightAllowedCMD = [[all_admin - amweather - ammerc]]
       
       seta jp_instructorAllowedCMD = [[amstatus + amorigin + amtele + amwhois + amvstr]]
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 becomes                                                         
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       $ ./meme ef jka.meme r < bitvals.cfg
       {Evaluating file `jka.meme'}
       
       // Bitvalues computation examples
       
       seta g_forcePowerDisable = 8576
       
       seta g_weaponDisable = 524230
       
       seta jp_votesDisable = 2592
       
       seta jp_councilAllowedCMD = 1073741822
       
       seta jp_knightAllowedCMD = 536805374
       
       seta jp_instructorAllowedCMD = 270270480
       {End of file reached}
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 ...  it's much more convenient, I think, if you want to disable
 'ammap' for your Knights, to just type '-  ammap'  rather  than
 going  to  a  bitvalue  calculator, trying to find out what the
 current value means and whether or not ammap is activated, then
 modifying it accordingly and  pasting  it  again  in  the  CFG.
 
 And  you  can do some nice things, like defining the privileges
 of one group in function of those of another:                   
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       seta jp_councilAllowedCMD =
         [[let adm_council = all_admin in adm_council]]
       
       seta jp_knightAllowedCMD =
         [[let adm_knight = adm_council - amweather - ammerc in adm_knight]]
       
       seta jp_instructorAllowedCMD =
         [[let adm_ins = adm_knight -amkick -amban -amempower -ammap in adm_ins]]
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Then if you happen to deactivate a feature for admins, it  will
 be  automatically  deactivated  for  knights  and  instructors.
 
 You might also want to create  variables  to  store  groups  of
 options,   for  instance  "adm_punitive  =  amban  +  amkick  +
 amslap..." and then simply use those variables.                 
 
 Two more ideas:                                                 
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       // EXAMPLE 3: Options
       
       // Define "big" options: [[let bigop1 = true, bigop2 = false]] ... and little options follow logically
       
       // for instance, this option will be activated if both the big options are, BUT if the rate is small, it will be activated regardless of the options...
       seta someop = [[bigop1 && bigop2 || rate < 5000]]
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 and                                                             
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       // EXAMPLE 4: Two random colours (distinct with a [[ 100* 6/7 ]]% probability)
       
       // [[col1 = floor #7+1]]
       // [[col2 = let x = floor #7+1 in x == col1 ? floor #7+1: x]]
       
       seta message = "Here ^[[col1]]is a ^[[col2]]randomly^[[col1]] colored^[[col2]] message!"
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 
+   THE PACKAGE                                                 
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-o          +
 
 
   -   The Source Code                                           
   -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~o          -
 
 The source code for m.E.M.E is released under the  GNU  General
 Public License.                                                 
 
 This  program  is written in Objective Caml, using ocamllex and
 ocamlyacc to build the lexer and the parser.                   
 
 
   -   The Binaries                                             
   -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~o          -
 
 m.E.M.E  is  shipped  with  statically  compiled  binaries  for
 GNU/Linux,  which  may  or  may not work on your own system. If
 they don't you will just have to compile it  from  source.  You
 will  need an OCaml compiler. In most cases, you will just need
 to run the build script in the /src directory.                 
 
 The Windows binary should work on any Windows  system  from  95
 on.                                                             
 
 
+   CONTACT                                                     
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-o          +
 
 If  you  need  help,  or  have  suggestions, comments, insults,
 praise or in general, anything to say about  this  program  you
 expect  me  to read and answer to, please post on the program's
 topic on my website:                                           
 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
       http://gamall-ida.com/f/viewtopic.php?f=3&t=396
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~     
 
 Do NOT, *EVER*, contact me by mail unless  I  specifically  ask
 you  to  do  so, or if my forum is unavailable for a long time.
 
 
+   END OF FILE                                                 
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-o          +
 




























  +-----------------------------+
  | File generated with 'GaTeX',|
  | an ASCII typesetting system |
  | by  Gamall  Wednesday  Ida. |
  |   http://gamall-ida.com     |
  +-----------------------------+
  Build: Tue Apr 15 15:53:03 2008
  File : F:readme-mEME.GaTeX.source
{<§ Gamall Wednesday Ida §>}
{ Mods and Programs - Mods TES-IV Oblivion }

User avatar
Gamall
Hic sunt dracones
Posts: 4125
Joined: Fri May 26, 2006 11:09 pm
Contact:

Re: m.E.M.E - Minimalist Evaluator for Mathematical Expressions

Postby Gamall » Tue Apr 15, 2008 6:02 pm

Released 0.1
{<§ Gamall Wednesday Ida §>}
{ Mods and Programs - Mods TES-IV Oblivion }

User avatar
Maikoru
Jedi Perpétuellement Affamé
Posts: 480
Joined: Sun Aug 27, 2006 11:15 pm

Re: m.E.M.E - Minimalist Evaluator for Mathematical Expressions

Postby Maikoru » Tue Apr 22, 2008 12:22 pm

Hey, t'es en première page sur jk3files. ^^
"..." -- Link

User avatar
Gamall
Hic sunt dracones
Posts: 4125
Joined: Fri May 26, 2006 11:09 pm
Contact:

Re: m.E.M.E - Minimalist Evaluator for Mathematical Expressions

Postby Gamall » Tue Apr 22, 2008 3:26 pm

La gloire :hum

edit: make.sh for use as a library:

Code: Select all

ocamlyacc -v mathparser.mly
ocamlopt -c mathparser.mli

ocamllex mathlexer.mll
ocamlopt -for-pack Meme -c toolkit.ml mathlexer.ml mathcommon.ml mathparser.ml  matheval.ml

ocamlopt -pack -o meme.cmx toolkit.cmx mathlexer.cmx mathcommon.cmx mathparser.cmx matheval.cmx
{<§ Gamall Wednesday Ida §>}
{ Mods and Programs - Mods TES-IV Oblivion }


Return to “Freewares, game mods, Java, C++, OCaml & Maths”

Who is online

Users browsing this forum: No registered users and 17 guests