Finally released treep that supports ISO 8601 date handling in format "YYYY-MM-DD HH:MM:SS" in range from "1901-12-13 20:45:52" until "2038-01-19 03:14:07". Regular expression that is hard to find on web is:
^(1901-12-13 (20:(45:5[2-9]|(4[6-9]|5[0-9]):[0-5][0-9])|2[1-3]:[0-5][0-9]:[0-5][0-9])|(1901-12-(1[4-9]|2[0-9]|3)|(19(0[2-9]|[1-9][0-9])|20([0-9]|3[0-7]))-((0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-8])|(0[1,3-9]|1[0-2])-(29|30)|(0|1)-31)|(190|(19|201)|(19|20)|203)-02-29|2038-01-(0[1-9]|1[0-8])) ([0-1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]|2038-01-19 (0[0-2]:[0-5][0-9]:[0-5][0-9]|03:((0[0-9]|1[0-3]):[0-5][0-9]|14:0[0-7])))$
I have made new version of treep:
Treep is a simple language for doing symbolical computations. It operates
on numbers and strings that can be organized in more complex objects. These
objects are lists of name-value pairs that are stored in memory as AVL
trees. It has about sixty built-in functions to operate on such data and
a way to define new functions. Treep syntax very much resembles Lisp.
Power of treep is it's simplicity.
Treep reads expressions from standard input and evaluates them until end of
file is reached. Evaluation can make some more data be read from standard
input and some data be printed on stadard output or standard error.
Every expression that is read from standard input must be a literal value,
function call or function definition. Literal value is a number, a name or
a text. This is all that can be put on program input.
Numbers in Treep are defined by sign, set of integral numbers, dot and set
of decimal numbers. All these parts are obligatory. Exception is zero, which
is defined as "0.0". For example these are valid numbers: "+45.0", "-99.123",
"+0.12". But these are not valid numbers: "-0.0" (sign not allowed for zero),
"45.0" (sign is missing), "-34" (decimal part is missing), "-34.1000" (extra
zeros at the end), "+04.56" (extra zeros at the beginning). Program checks
if number is not bigger than allowed precission (long double in C).
Names in Treep are set of letters, digits and underscores, but first
character must be a letter, last character must not be underscore and no two
underscores could be one after another. Names must match regular expression
"^[A-Za-z](_?[A-Za-z0-9])*$". For example these are valid names: "alfa_1",
"beta_1_XP", "my_name". These are not valid names: "my-home" (minus is
neither letter, digit or underscore), "4you" (not a letter at the beginning),
"Alfa_23_" (underscore at the end), "Alfa__34" (two undercores in a row).
Text in Treep is a set of printable characters ([:print:] in C) enclosed in
double quotes. There are allowed two escape sequences: "\"" (double quote)
and "\\" (backslash).
Function call is a set of expressions enclosed in parentheses. The first
expression should evaluate to the name of function to be called. The rest of
expressions are function call parameters. Parameters are evaluated in order
from left to right. This means, that between parentheses there should be at
least one expression. These are valid function calls:
(add +4.5 -9.0)
(info "Hello World!")
((evaluate_function_name) +5.6) .
It is possible to define a new function. New function definitions are made
of asterisk *, name of function, names of parameters enclosed in parentehses
and body of the function. The body is an expression to evaluate when the
function is called. Inside the body all parameters of the function are
accessible as if they were defined as separate functions. Here is example
of function that computes average of two numbers:
* average (x y) (div (add (x) (y)) +2.0) .
Name of function is "average" and it has two parameters named "x" and "y".
This funciton is called like this:
(average +4.0 +6.0) .
While evaluation name "x" is assigned value "+4.0" and "y" is assigned value
"+6.0". In the body of the function parameters can be accessed as if they
were normal functions, so in example "(x)" will evaluate to "+4.0" and "(y)"
to "+6.0". Treep then add these numbers and divides them by "+2.0". Finally
whole function call has value "+5.0".
Standard input to Treep besides expressions to evaluate and definitions of new
functions could contain data that is read during expression evaluation. For
example in such input:
Treep will evaluate only two expressions, two calls of function read, because
function "read" will swallow name "alfa" and then "beta" from standard input.
Treep has only one complex data type -- this is a list of name-value pairs
that is stored in memory as AVL binary tree for fast access. Values can also
be lists, so it is possible to read, build, analise very complex data types
such as graphs, trees, linked lists.
There is a full set of built-in functions that operate on trees. One can
create new empty tree ("new" function), add and remove named elements
from tree ("ins", "del"), modify value of named element in tree ("set"),
retrieve value of named element ("get"). It is possible to access all elements
of tree directly ("frst" -- first element of tree, "last", "left", "rght").
Treep program works in a three-step loop. In the first step it skips spaces,
tabs and new lines. In the second step it reads an expression. In the third
step it evaluates that expression. It sounds very typical, but in the third
step treep can evaluate functions "read" or "write" that cause some data be
read from standard input or written to standard output. This is different
from other programs, because expressions to evaluate and data for that
expressions are all in the same data flow -- on standard input.
Function "read" reads one piece of data from standard input. This can be a
number, a name, a text, or a single character (one of space, tab, new line,
left parenthesis or right parenthesis). Function "write" wirtes data to
standard output. For example such treep input:
(wrte (add (read) (read)))+4.0+2.0
will cause treep to write "+6.0" on standard output.
Treep has about sixty built-in functions. Some functions like "add" or
"sub" are evaluated ordinarly -- treep first evaluates parameters of such a
function, and then runs a code to compute the result. Other functions like
"read" or "write" change state of input and output, functions "new", "set",
"get", "ins", "del" change state of memory where data is stored. Treep has
also three very special and important functions -- these are "if", "do" and
Function "if" is used to evaluate expressions conditionally. First parameter
is a condition to check. This is an expression that should have boolean
value. If value is true, then following expression is evaluated and no more
conditions are considered. When value of condition is false then following
expression is skipped and next condition is considered. Finally if there
is only one parameter left then it is used as "else expression" -- this is
evaluated if all conditions are false.
For example one can define function that computes nth Fibonacci number
* fib (n)
(equ (n) 0.0) 0.0
(equ (n) +1.0) +1.0
(add (fib (sub (n) +1.0)) (fib (sub (n) +2.0)))
Function retunrs result that is evaluation of "if" function. Function "if"
has five parameters. First parameter is a condition "(equ (n) 0.0)" that checks
if number "n" is equal zero. If this is true, then following expression "0.0"
is evaluated -- this is the result of whole function "fib" call in a case "n"
is equal zero. If number "n" is not equal zero, then next condition "(equ (n)
+1.0)" is checked. If this condition is true, then expression "+1.0" becomes
a result. If both conditions are false, then the last expression is evaluated
which adds two numbers "(fib (sub (n) +1.0))" and "(fib (sub (n) +2.0))".
Function "as" is used to remember values by name, and thus to omit multiple
evaluation of the same expression. This is extremely important for evaluating
functions that cause side effects. For example function "read" gets data from
input and one cannot read that data again -- after beeing read it has
to be stored somewhere. This is can be done like this:
* about_numbers ()
"For " (x) " and " (y)
" sum is " (add (x) (y))
" and difference is " (sub (x) (y)) i
Treep reads and stores defintion of function "about_numbers". Then it reads
first expression "(about_numbers)". The body of function "about_numbers" is
computed. Function "as" first reads name "x", evaluates expression "(read)"
and result is stored as name "x". The same happens with name "y". Then last
parameter of function "as" is computed, where names "x" and "y" can be used as
ordinary functions. In the example parameters of function "info" are computed,
names "x" and "y" are used many times, but no more data is read from input.
Function "do" is used to evaluate many expressions, especially when they
cause side effects, by discarding some of results. Parameters of function
"do" are evaluated from left to right and the value of last parameter becomes
the value of the whole expression. Here is an example of function that divides
numbers and when division by zero would occur it prints warning to standard
error and returns zero:
* safe_divide (x y)
(equ 0.0 (y)) (do (info "division by zero occured") 0.0)
(div (x) (y))
(safe_divide +2.0 +5.0)
(safe_divide +9.99 0.0)
(safe_divide +4.0 -3.0) .
In directory "read.d" there is a set of files that should be passed on
standard input to "treep" program. This files demonstrate Treep usage step
by step and are intended to show all treep functionality in detail.
# n-th fibbonaci number
* fib (n)
(if (gre (n) +2.0)
(add (fib (dec (n))) (fib (dec (dec (n)))))
(info (fib +4.0))
(info (fib +5.0))
(info (fib +6.0))
(info (fib +7.0))
Here is example how looks code for treep:
# List is an ordered set of elements. Each element has a name and a parame-
# ters. Here is example of list:
# Lustro_Omni (Qty -45.0 Who "Kowlaski Sp. z o.o")
# Lustro_Atos (Qty -4.0)
# Lustro_Pluton (Qty -9.0 When '2009-12-31')
# Lustro_Helios (Qty -1.0)
# ) .
# Every list element has fields "name", "parm" and "next".
# read or write a list
* list_read ()
(if (is lpar (read))
(do (info "Can not compute \"list_read\" call. Parenthesis was expected.") (exit (no)))
(list_read_elem (do (skip) (read)))
* list_read_elem (n)
(if (is rpar (n))
(if (is name (n))
(do (info "Can not compute \"list_read\" call. Name of list element was expected.") (exit (no)))
(ins (l) name (n))
(ins (l) parm (do (skip) (parm_read)))
(ins (l) next (list_read_elem (do (skip) (read))))
* list_wrte (l)
* list_wrte_elem (l)
(if (is void (l))
(wrte (get (l) name))
(parm_wrte (get (l) parm))
(list_wrte_elem (get (l) next))
# apply notes to list
* list_note (l n)
(if (is void (l))
(note_aply (n) (get (l) name) (get (l) parm))
(list_note (get (l) next) (n))
An open, cross-platform journaling program.
A scientific plotting package.