Reference

Syntax

The grammar is small on purpose: you write what you would write on paper, and the parser reads it. This page is the whole of it - the operators, the 163 callable functions, and the half-dozen places where it does something other than what you expect.

Everything below is what the parser actually does, checked against the source rather than remembered.

What surprises people

Four of these have caught every person who has used the library, including its author. They are listed first for that reason.

Power is ** - not ^

^ is exclusive or, not exponentiation. x ^ 2 quietly returns x xor 2, which is a perfectly good number and almost never the one you meant.

Write x ** 2, or spell it x degree 2.

Two slashes are a root, not a comment

// takes a root: 8 // 3 is 2. There are no comments inside a formula, so nothing is being ignored.

Comparison answers minus one

A true comparison returns -1, false returns 0 - the Pascal convention for a boolean stored as a number. So (3 > 2) + 1 is 0, not 2.

Use AsBoolean if you want a Pascal Boolean back.

Case never separates two names

Sin, sin and SIN are one built-in, and a variable registered as Rate also answers to rate and RATE. Since case does not tell two names apart, registering a second rate beside an existing Rate is refused rather than shadowing it.

Operators

Higher binds tighter. The two that matter: power and root bind tighter than multiplication, so 2 * 3 ** 2 is 18, and comparison binds loosest, so a + b > c compares the sum.

OperatorMeansBinds
+ - * /the usual fournormal
**power - 2 ** 10 is 1024higher
//root - 8 // 3 is 2higher
degreepower again, spelled outhigher
div modinteger quotient and remaindernormal
!logical not, written before the valuelower
= <> > < >= <=comparison - answers -1 or 0lower
and or xor notlogiclower
& | ~ bxorbitwise and, or, not, xorlower
shl shrbit shiftsnormal

Values

A formula can hold integers of every width, single, double, and extended floats, booleans, strings, dates, and pointers. You never declare any of it - ask for the type you want and the conversion happens on the way out.

CallFormulaAnswer
AsInteger2 ** 101024
AsDoublepi / 60.5235988
AsExtendedsqrt(2)1.4142136
AsBoolean3 > 2True
AsString2 + 2'4'

Control inside a formula

A formula is not limited to arithmetic. if is lazy - only the branch that is taken gets evaluated, so if(x <> 0, 1 / x, 0) is safe. Loops carry their own counter, and exit ends the whole script with a value.

WrittenDoes
if(cond, then, else)evaluates one branch, never both
while(cond, body)repeats while the condition holds
repeat(body, cond)runs the body, then tests
for(name, from, to, body)counted loop with its own variable
exit(value)ends the script there and then
tryexcept(body, fallback)the fallback answers if the body raises
new(name, value) get setvariables that live inside the script

Two that read their own arguments

parse("2 + 3") compiles a formula while the outer formula is running. deriv("x ** 2", "x") differentiates symbolically and returns the derivative - not a numeric approximation of it.

The 163 callable functions

Grouped by what they are for. Every name is registered at startup and can be replaced or extended with your own.

The parser answers to 249 names in all. Besides the 163 functions below, 62 of them are constants (pi, true, the month and weekday names, maxint64, kilobyte), 15 are service entries that drive the parser itself (new, get, set, execute, parse, script), and 9 are operators written as words (and, or, div, mod, shl). All these counts come from a probe built against the sources of this release, not from memory.

Algebra28

sqr sqrt int round roundto trunc abs frac ln lg log log2 log10 lnxp1 exp intpower ldexp ceil floor poly factorial deriv sign iszero samevalue ensurerange comparevalue equalsvalue

Trigonometry26

sin cos tan cotan sec csc arcsin arccos arctan arccotan arcsec arccsc sinh cosh tanh cotanh sech csch arcsinh arccosh arctanh arccotanh arcsech arccsch arctan2 hypot

Angles12

radtodeg radtograd radtocycle degtorad degtograd degtocycle gradtorad gradtodeg gradtocycle cycletorad cycletodeg cycletograd

Statistics12

mean sum sumint sumofsquares minvalue maxvalue stddev popnstddev variance popnvariance totalvariance norm

Date and time66

date time datetime year month day hour minute second dayofweek encodedate encodetime encodedatetime yearsbetween monthsbetween daysbetween hoursbetween minutesbetween secondsbetween millisecondsbetween weeksbetween weekoftheyear dayofthemonth dayoftheyear comparedate comparetime comparedatetime samedate sametime samedatetime ...

Random4

random randg randomrange randomfrom

Text and parsing7

strtoint strtointdef strtofloat strtofloatdef strtodate strtotime strtodatetime

Control and scope8

if ifthen while repeat for exit tryexcept tryfinally

Adding your own

program Extend;

{$APPTYPE CONSOLE}

uses ParseTypes, ValueTypes, ValueUtils, Parser;

type
  TPricing = class
    function Discount(const Header: PScriptHeader; const AFunction: PFunction; const AType: PType;
      const PA: TParameterArray): TValue;
  end;

function TPricing.Discount(const Header: PScriptHeader; const AFunction: PFunction; const AType: PType;
  const PA: TParameterArray): TValue;
begin
  Result := MakeDouble(GetDouble(PA[0].Value) * (1 - GetDouble(PA[1].Value) / 100));
end;

var
  P: TMathParser;
  Pricing: TPricing;
  Handle: TFunctionHandle;
  Rate: Double;
begin
  P := TMathParser.Create(nil);
  Pricing := TPricing.Create;
  try
    P.AddFunction('discount', Handle, fkMethod, MakeFunctionMethod(Pricing.Discount, 2, pkValue), False);
    P.AddVariable('rate', Rate);
    Rate := 20;
    Writeln(P.AsDouble('discount(1000, rate)'):0:2);
  finally
    Pricing.Free;
    P.Free;
  end;
end.