Man Linux: Main Page and Category List

NAME

       vol-1 - afnix programmer’s guide

GETTING STARTED

       This  chapter  is  a  quick  introduction  to  the   AFNIX  Programming
       Language.  AFNIX  is a multi-threaded functional  programming  language
       with   dynamic  symbol  bindings  that  supports  the  object  oriented
       paradigm. The language features a state of the art runtime engine  that
       runs  on  both  32  and  64  bits  platforms.  AFNIX  is an interpreted
       language with a rich syntax that makes  the  functional  programming  a
       pleasant activity. When used interactively, commands are entered on the
       command line and executed when a complete and  valid  syntactic  object
       has  been  constructed.  Alternatively,  the  interpreter can execute a
       source file. The engine does not have a garbage collector but  operates
       with  an  immediate,  scope based, object destruction mechanism.  AFNIX
       is a comprehensive set of application clients and modules. The original
       distribution contains the core interpreter with additional clients like
       the compiler, the librarian and the debugger. The distribution contains
       also  a rich set of modules that are dedicated to a particular domains.
       The basic modules are the standard i/o module, the  system  module  and
       the  networking  module.  The  engine  is  written  in C++ and provides
       runtime compatibility with it. Such compatibility includes the  ability
       to  instantiate  C++  classes,  use  virtual methods and raise or catch
       exceptions. A comprehensive programming interface has been designed  to
       ease  the integration of foreign libraries.  AFNIX  operates with a set
       of reserved keywords and predicates. Standard objects  provide  support
       for  integers,  real  numbers, strings, characters and boolean. Various
       containers like list, vector, hash table, bitset, and graphs  are  also
       available  in  the  core  distribution.  The  language incorporates the
       concept  lambda  expression  with  explicit   closure.   Symbol   scope
       limitation  is  an exclusive feature called gamma expression. Form like
       notation with an easy block  declaration  is  also  an  extension.  The
       object  model  provides  a  single  inheritance  mechanism with dynamic
       symbol resolution. Unique features include instance re-parenting, class
       rebinding  and  instance  inference. Native class derivation and method
       override is also part of the object model with fixed class objects  and
       forms.   AFNIX   implements a true multi-threaded engine with automatic
       object protection mechanism against concurrent access. Read  and  write
       locking  system  and  thread  activation  via condition objects is also
       built in the core system. The engine incorporates an  original  regular
       expression  engine  with  group  matching,  exact  or partial match and
       substitution. An advanced exception engine is also provided with native
       run-time  compatibility.  AFNIX  provides extensions. An extension is a
       module or an application which is not installed by  default.  The  user
       selects  during the installation process which extension is needed. For
       example, the static version of the interpreter is an extension.

       First programs
       The fundamental  AFNIX  syntactic object is a form. A  form  is  parsed
       and  immediately  executed  by  the  AFNIX  engine. A form is generally
       constructed with a function name and a set of arguments. The process of
       executing  a  form  is  called the evaluation. As a simple program, the
       traditional "hello world" program is shown below.

       Hello world
       The "hello  world"  program  is  de  rigueur  when  introducing  a  new
       programming language. Here is the  AFNIX  version of it.

       (axi) println "hello world"

       AFNIX   is  an  interpreted  language.  It  is  possible  to invoke the
       interpreter and enter the above commands, or  use  a  text  editor  and
       execute  the  file.  By  convention,  an   AFNIX   source  file has the
       extension .als. A simple session to run the above program  --  assuming
       the source file is called hello.als -- is shown below.

       zsh> axi hello.als
       hello world

       It  is also possible to invoke the interpreter as to enter the commands
       interactively. The result will be the same. Simply typing  ctrl-d  will
       exit  the  session.  Another  way  to  operate  is  to  call the  AFNIX
       compiler called axc, and then invoke the interpreter with the  compiled
       file,  even  let  the  interpreter  to  figure  this out. Note that the
       interpreter assume the .axc for compiled file.

       zsh> axc hello.als
       zsh> axi hello.axc
       hello world
       zsh> axi hello
       hello world

       The order of search is determined by a special system called  the  file
       resolver.  Its  behavior  is  described  in  a  special chapter of this
       manual.

       Interpreter command
       The  AFNIX  interpreter can be invoked with several options, a file  to
       execute  and  some  program arguments. The -h option prints the various
       interpreter options.

       zsh> axi -h
       usage: axi [options] [file] [arguments]
       [-h]         print this help message
       [-v]         print version information
       [-i] path    add a path to the resolver
       [-f] assert  enable assertion checking
       [-f] nopath  do not set initial path

       The -v option prints the interpreter version and operating system.  The
       -f  option  turn  on  or  off  some  additional  options like assertion
       checking. The program arguments are illustrated later in this  chapter.
       The -i option add a path to the interpreter file path resolver. Several
       -i options can be specified. The order of search is determined  by  the
       option  order.  The  use of the resolver combined with the librarian is
       described in a specific chapter. If the initial file  name  to  execute
       contains  a  directory  path,  such  path is added automatically to the
       interpreter resolver path unless the nopath option is specified.

       Interactive line editing
       Line editing capabilities are supported when the  interpreter  is  used
       interactively.  Error  messages  are  displayed  in red if the terminal
       supports colors. The following table is a resume  of  the  default  key
       bindings.

       Binding     Description
       backspace   Erase the previous character
       delete      Erase at the cursor position
       insert      Toggle insert with in-place
       left        Move the cursor to the left
       right       Move the cursor to the right
       up          Move up in the history list
       down        Move down in the history list
       ctrl-a      Move to the beginning of the line
       ctrl-e      Move to the end of the line
       ctrl-u      Clear the input line
       ctrl-k      Clear from the cursor position

       ctrl-l      Refresh the line editing

       Command line arguments
       The  command  line  arguments to the interpreter are stored in a vector
       called argv which is part of the interp class.  A  complete  discussion
       about  class  data  member  is covered in the class object chapter. The
       example below which illustrates the use of the vector argument.

       # argv.als
       # print the argument length and the first one
       println "argument length: " (interp:argv:length)
       println "first argument : " (interp:argv:get 0)
       zsh> axi argv.als hello world
       2
       hello

       Loading a source file
       The interpreter class provides also the load method to  load  a  source
       file. The argument must be a valid file path or an exception is raised.
       The load method returns nil. When the file is loaded,  the  interpreter
       input,  output  and error streams are used. The load operation read one
       form after another and executes them sequentially.

       # load the source file fred.als
       (axi) interp:load "fred.als"

       If the file has been compiled, the axc extension can be  used  instead.
       This force the interpreter to load the compiled version. If you are not
       sure, or do not care about which file is loaded, the extension  can  be
       omitted.

       # load the compiled file fred.axc
       (axi) interp:load "fred.axc"
       # load whatever is found
       (axi) interp:load "fred"

       Without  extension,  the  compiled file is searched first. If it is not
       found the source file is searched and loaded.

       The compiler
       The client axc is the cross compiler. It generates a binary  file  that
       can  be run across platforms. The -h option prints the various compiler
       options.

       usage: axc [options] [files]
       [-h]      print this help message
       [-v]      print version information
       [-i] path add a path to the resolver

       One or several files can be specified on the command line.  The  source
       file can be searched by the resolver by using the -i option.

       builtin objects
       AFNIX  provides several builtin objects, namely Boolean, Integer, Real,
       Character and String. A builtin object can be constructed literally for
       each of these types. The best way to build such object is to bind it to
       a symbol. The const and trans reserved keywords are used to  declare  a
       new  symbol. A symbol is simply a binding between a name and an object.
       Almost any standard characters can be used to declare a symbol.

       const boolean   true
       const integer   1999
       const real      2000.0
       const string    "afnix"
       const char      ’a’

       None of the symbols -- or names  --  used  in  the  program  above  are
       reserved  keywords.  In  fact, the capitalize names are builtin objects
       such like Integer or String.  The  const  reserved  keyword  creates  a
       constant   symbol   and   returns  the  last  evaluated  object.  As  a
       consequence, nested const constructs are possible like trans b (const a
       1).  The trans reserved keyword declare a new non-constant symbol. That
       is, the symbol can be changed. Note that it  is  the  symbol  which  is
       marked constant, not the object.

       trans a-symbol "hello world"
       trans a-symbol 2000
       println a-symbol

       Comments
       Comments  starts  with the character #. All characters until the end of
       line are consumed. Comments can be placed anywhere in the source  file.
       Comments entered during an interactive session are discarded.

       Forms
       The   previous  program  was  an  illustration  of  the  simplest  form
       declaration, referred as implicit form. An implicit form  is  a  single
       line  command.  When  a  command  is  becoming  complex, the use of the
       standard form notation is more readable. The standard form uses  the  (
       and ) characters to start and close a form. The previous programs could
       have been written with  the  standard  form  notation  instead  of  the
       implicit  one. The use of standard form notation versus the implicit is
       one is a matter of style and readability.  A form causes an evaluation.
       When  a  form  is  evaluated,  each symbol in the form are evaluated to
       their corresponding internal object. Then the  interpreter  treats  the
       first  object  of the form as the object to execute and the rest is the
       argument list for the calling object. The use of form inside a form  is
       the   standard   way   to  perform  recursive  evaluation  for  complex
       expression.

       const three (+ 1 2)

       The previous program defines a symbol which  is  initialized  with  the
       integer  3,  that is the result of the computation (+ 1 2). The program
       shows also that Polish notation is used for arithmetic. If fact, + is a
       builtin operator which causes the arguments to be summed (if possible).
       Evaluation can be nested as well as definition and assignation. When  a
       form  is  evaluated,  the result of the evaluation is made available to
       the calling form. If the result is obtained  at  the  top  level,  that
       result is discarded.

       const    b (trans a (+ 1 2))
       assert a 3
       assert b 3
       trans  a 4
       assert b 3

       This  program  illustrates  the mechanic of the evaluation process. The
       evaluation is done recursively. The (+ 1 2) form is evaluated as 3  and
       the  result  transmitted  to  the  form (trans a 3). This form not only
       creates the symbol a and binds to it the integer 3, but returns also  3
       which  is  the  result  of  the  previous evaluation. Finally, the form
       (const b 3) is evaluated, that is, the symbol  b  is  created  and  the
       result discarded. Internally, things are a little more complex, but the
       idea remains the same. This program illustrates also the usage  of  the
       assert keyword.

       Lambda expression
       A  lambda expression is a function in the  AFNIX  terminology. The term
       come  historically  from  Lisp  to  express  the  fact  that  a  lambda
       expression  is  analog to the concept of expression found in the lambda
       calculus. There are various ways  to  create  a  lambda  expression.  A
       lambda expression is created with the trans reserved keywords. A lambda
       expression takes 0 or more arguments and return  an  object.  A  lambda
       expression  is  also  an  object by itself. When a lambda expression is
       called, the arguments are evaluated from left to right  and  placed  on
       the  interpreter eval stack. The function is then called and the object
       result is transmitted to the calling form. The use of trans vs const is
       explain  later. As an example, we define the factorial of an integer in
       a recursive way.

       # declare the factorial function
       trans fact (n) (
         if (== n 1) 1 (* n (fact (- n 1))))
       # compute factorial 5
       println "factorial 5 = " (fact 5)

       This program calls  for  several  comments.  First  the  trans  keyword
       defines  a  new function object with one argument called n. The body of
       the function is defined with the if reserved keyword and can be  easily
       understood.  The  function  is called in the next form when the println
       reserved keyword is executed. Note that here, the call to fact produces
       an  integer  object,  which  is  converted automatically by the println
       keyword.

       Block form
       The notation used in the fact program is  the  standard  form  notation
       originating  from  Lisp  and the Scheme dialect.  AFNIX  offers another
       notation called the block form notation with the use of  the  {  and  }
       characters. A block form is a syntactic notation where each form in the
       block form is executed sequentially. The form can be either an implicit
       or  a  regular form. The fact procedure can be rewritten with the block
       notation as illustrated below.

       # declare the factorial procedure
       trans fact (n) {
         if (== n 1) 1 (* n (fact (- n 1)))
       }
       # compute factorial 5
       println "factorial 5 = " (fact 5)

       Another way to create a lambda expression is via the  reserved  keyword
       lambda.  Recall  that  a  lambda  expression is an object. So when such
       object is created, it can be bounded to a symbol. The factorial example
       could be rewritten with an explicit lambda call.

       # declare the factorial procedure
       const fact (lambda (n) (
           if (== n 1) 1 (* n (fact (- n 1)))))
       # compute factorial 5
       println "factorial 5 = " (fact 5)

       Note  that here, the symbol fact is a constant symbol. The use of const
       is rather reserved for gamma expression.

       Gamma expression
       A lambda expression can somehow becomes very slow during the execution,
       since  the  symbol  evaluation  is  done within a set of nested call to
       resolve the symbols. In other words, each recursive call to a  function
       creates  a  new  symbol  set  which is linked with its parent. When the
       recursion is becoming deep, so is the path to traverse from  the  lower
       set  to  the  top  one.  Afnix  provides another mechanism called gamma
       expression which binds only the function symbol set to  the  top  level
       one.  The  rest  remains the same. Using a gamma expression can speedup
       significantly the execution.

       # declare the factorial procedure
       const fact (n) (
         if (== n 1) 1 (* n (fact (- n 1))))
       # compute factorial 5
       println "factorial 5 = " (fact 5)

       We will come back later to the concept of gamma expression. The use  of
       the  reserved  keyword  const  to  declare a gamma expression makes now
       sense. Since most function definitions are constant with one level,  it
       was  a  language  choice  to  implement this syntactic sugar. Note that
       gamma is a  reserved  keyword  and  can  be  used  to  create  a  gamma
       expression  object.  On  the other hand, note that the gamma expression
       mechanism does not work for instance method. We  will  illustrate  this
       point later in this book.

       Lambda generation
       A  lambda expression can be used to generate another lambda expression.
       In other word, a function can generate a function, hence the term  that
       AFNIX   is a functional programming language. Suppose one might want to
       write a function which take an argument and generate a  function  which
       add  this  argument to the generated function argument -- got that! --,
       then here is the implementation.

       # a gamma which creates a lambda
       const gen (n) (
         lambda (x) (n) (+ x n))
       # create a function which add 2 to its argument
       const add-2 (gen 2)
       # call add-2 with an argument and check
       println "result = " (add-2 3)

       The interesting part in the previous program is the concept  of  closed
       variables. Looking at the lambda expression inside gen, notice that the
       argument to the gamma is x while n is marked in a form before the  body
       of  the gamma. This notation indicates that the gamma should retain the
       value of the argument n when the closure is created. In the literature,
       you  might  discover  a  similar  mechanism  referenced as a closure. A
       closure is simply a variable which is closed under a  certain  context.
       When  a variable is reference in a context without any definition, such
       variable is called a free variable. We will  see  later  more  programs
       with  closures.  Note that in the  AFNIX  terminology, it is the object
       created by the gamma call which is called a closure. Note also that the
       same  mechanism  apply  with lambda. In short, a lambda expression is a
       function with or without closed  variables,  which  works  with  nested
       symbol sets also called namesets. A gamma expression is a function with
       or without closed variable which is bounded to the top  level  nameset.
       The  reserved  keyword  trans  binds  a lambda expression. The reserved
       keyword const binds a gamma expression. A gamma  expression  cannot  be
       used as an instance method.

       Multiple arguments binding
       A  lambda  or  gamma  expression  can  be  defined  to  work with extra
       arguments using the special args binding.  During  a  lambda  or  gamma
       expression execution, the special symbol args is defined with the extra
       arguments passed at the call. For example, a gamma  expression  with  0
       formal argument and 2 actual arguments has args defined as a cons cell.

       const proc-nilp (args) {
         trans result 0
         for (i) (args) (result:+= i)
         eval result
       }
       assert 3 (proc-nilp 1 2)
       assert 7 (proc-nilp 1 2 4)

       The symbol args can also be defined  with  formal  arguments.  In  that
       case,  args  is  defined  as  a  cons  cell  with  the remaining actual
       arguments.

       # check with arguments
       const proc-args (a b args) {
         trans result (+ a b)
         for (i) (args) (result:+= i)
         eval result
       }
       assert 3 (proc-args 1 2)
       assert 7 (proc-args 1 2 4)

       It is an error to specify formal arguments after  args.  Multiple  args
       formal  definition are not allowed. The symbol args can also be defined
       as a constant argument.

       # check with arguments
       const proc-args (a b (const args)) {
         trans result (+ a b)
         for (i) (args) (result:+= i)
         eval result
       }
       assert 7 (proc-args 1 2 4)

       Nameset and bindings
       A nameset is a container  of  bindings  between  a  name  and  symbolic
       variable.  We  use  the  term  symbolic  variable to denote any binding
       between a name and an object. There are various ways  to  express  such
       bindings. The common one in  AFNIX  is called a symbol. Another type of
       binding is an argument. Despite the fact they are different, they share
       a  set of common properties, like being settable. Another point to note
       is the nature of the nameset. As a matter of fact,  AFNIX  has  various
       type  of  namesets. The top level nameset is called a global set and is
       designed to handle a large number of symbols.  In  a  lambda  or  gamma
       expression,  the  nameset  is  called a local set and is designed to be
       fast with a small number of symbols. The moral of this little story  is
       to  think always in terms of namesets, no matter how it is implemented.
       All namesets support the concept of parent binding. When a  nameset  is
       created  (typically  during the execution of a lambda expression), this
       nameset is linked with its parent one. This means that a symbol  lookup
       is  done  by  traversing  all  nameset  from  the bottom to the top and
       stopping when one is found. In term of  AFNIX   notation,  the  current
       nameset is referenced with the special symbol .. The  parent nameset is
       referenced with the  special  symbol  ...  The  top  level  nameset  is
       referenced with the symbol ....

       Symbol
       A  symbol  is  an  object which defines a binding between a name and an
       object. When a symbol is evaluated, the evaluation process consists  in
       returning  the  associated  object. There are various ways to create or
       set a symbol, and the  different  reserved  keywords  account  for  the
       various nature of binding which has to be done depending on the current
       nameset state. One of the symbol property is to be const or not. When a
       symbol  is  marked as a constant, it cannot be modified. Note here that
       it is the symbol which is constant, not the object.  A  symbol  can  be
       created with the reserved keywords const or trans.

       Creating a nameset
       A  nameset  is an object which can be constructed directly by using the
       object construction notation. Once the object is  created,  it  can  be
       bounded  to a symbol. Here is a nameset called example in the top level
       nameset.

       # create a new nameset called example
       const example (nameset .)
       # bind a symbol in this nameset
       const example:hello "hello"
       println example:hello

       Qualified name
       In the previous example, a symbol is referenced in a given  nameset  by
       using  a  qualified  name like example:hello. A qualified name define a
       path to access a symbol. The  use  of  qualified  name  is  a  powerful
       notation  to  reference  an  object in reference to another object. For
       example, the qualified name .:hello refers to the symbol hello  in  the
       current  nameset.  The  qualified  name  ...:hello refers to the symbol
       hello in the top level nameset.  There  are  other  use  for  qualified
       names, like method call with an instance.

       Symbol binding
       The  trans reserved keyword has been shown in all previous example. The
       reserved keyword trans creates or set a symbol in the current  nameset.
       For example, the form trans a 1 is evaluated as follow. First, a symbol
       named a is  searched  in  the  current  nameset.  At  this  stage,  two
       situations  can  occur.  If  the  symbol  is  found, it is set with the
       corresponding value. If the symbol is not found, it is created  in  the
       current nameset and set. The use of qualified name is also permitted --
       and encouraged -- with trans. The exact nature of  the  symbol  binding
       with  a  qualified  name  depends  on  the  partial  evaluation  of the
       qualified name. For example, trans example:hello 1 will set or create a
       symbol binding in reference to the example object. If example refers to
       a nameset, the symbol is bound in this nameset. If example is a  class,
       hello is bounded as a static symbol for that class. In theory, there is
       no restriction to use trans on any object. If the object does not  have
       a  symbol binding capability, an exception is raised. For example, if n
       is an integer object, the form trans n:i 1  will  fail.  With  3  or  4
       arguments,  trans  defines  automatically  a  lambda  expression.  This
       notation is a syntactic sugar. The  lambda  expression  is  constructed
       from  the  argument  list and bounded to the specified symbol. The rule
       used to set or define the symbol are the same as described above.

       # create automatically a lambda expression
       trans min (x y) (if (< x y) x y)

       Constant binding
       The const reserved keyword is similar to trans, except that it  creates
       a  constant  symbol.  Once the symbol is created, it cannot be changed.
       This constant property is hold by the symbol itself. When trying to set
       a  constant  symbol, an exception is raised. The reserved keyword const
       works also with qualified names. The rules described previously are the
       same.  When  a partial evaluation is done, the partial object is called
       to perform a constant binding. If such capability does  not  exist,  an
       exception is raised. With 3 or 4 arguments, const defines automatically
       a gamma expression. Like trans the rule are the same  except  that  the
       symbol is marked constant.

       # create automatically a gamma expression
       const max (x y) (if (> x y) x y)

       Arguments
       An  expression  argument is similar to a symbol, except that it is used
       only with function argument. The concept of binding between a name  and
       an  object  is  still the same, but with an argument, the object is not
       stored as part of the argument, but rather at another location which is
       the  execution  stack.  An  argument can also be constant. On the other
       hand, a single argument can have multiple bindings. Such  situation  is
       found  during  the  same  function  call  in  two different threads. An
       argument list is part of the lambda or gamma expression declaration. If
       the  argument  is defined as a constant argument a sub form notation is
       used to defined this matter. For example, the max gamma  expression  is
       given below.

       # create a gamma expression with const argument
       const max (gamma ((const x) (const y)) (if (> x y) x y))

       A  special  symbols  named  args  is  defined  during a lambda or gamma
       expression evaluation with the remaining arguments passed at  the  time
       the  call  is  made. The symbol can be either nil or bound to a list of
       objects.

       const proc-args (a b) {
         trans result (+ a b)
         for (i) (args) (result:+= i)
         eval result
       }
       assert 3 (proc-args 1 2)
       assert 7 (proc-args 1 2 4)

       Control flow
       AFNIX  provides various reserved keywords which can be seen as standard
       imperative  statements.  Such  statements  are useful to write readable
       programs but are not necessary the best in terms of efficiency. In most
       cases,  a  statement  returns  the  last  evaluated object. Most of the
       statements are control flow statements.

       If statement
       The if reserved  keyword  takes  two  or  three  arguments.  The  first
       argument  is the boolean condition to check. If the condition evaluates
       to true the second argument is evaluated. The form return the result of
       such  evaluation.  If  the  condition  evaluates  to  false,  the third
       argument is evaluated or nil is returned  if  it  does  not  exist.  An
       interesting  example  which combines the if reserved keyword and a deep
       recursion is the computation of the Fibonacci sequence.

       const fibo (gamma (n) (
           if (< n 2) n (+ (fibo (- n 1)) (fibo (- n 2))))

       While statement
       The while reserved keyword takes 2 or 3 arguments.  With  2  arguments,
       the  loop is constructed with a condition and a form. With 3 arguments,
       the first argument is an initial condition that is executed only  once.
       When  an argument acts as a loop condition, the condition evaluate to a
       boolean. The loop body is executed as long as the boolean condition  is
       true. An interesting example related to integer arithmetic with a while
       loop is the computation of the greatest common divisor or gcd.

       const gcd (u v) {
         while (!= v 0) {
           trans r (u:mod v)
           u:= v
           v:= r
         }
         eval u
       }

       Note in this previous example the use of the symbol  =.  The  qualified
       name u:= is in fact a method call. Here, the integer u is assigned with
       a value. In this case, the symbol is not  changed.  It  is  the  object
       which  is  muted. In the presence of 3 arguments, the first argument is
       an initialization condition that is executed only once. In  this  mode,
       it  is  important  to note that the loop introduce its own nameset. The
       loop condition can be used to initialize a local condition variable.

       while (trans valid (is:valid-p)) (valid) {
         # do something
         # adjust condition
         valid:= (and (is:valid-p) (something-else))
       }

       Do statement
       The do reserved keyword is  similar  to  the  while  reserved  keyword,
       except  that  the loop condition is evaluated after the body execution.
       The syntax call is opposite to the while. The loop can accept either  2
       or  3  arguments. With 2 arguments, the first argument is the loop body
       and the second argument is the exit loop condition. With  3  arguments,
       the first argument is the initial condition that is executed only once.

       # count the number of digits in a string
       const number-of-digits (s) {
         const len (s:length)
         trans index 0
         trans count 0
         do {
           trans c (s:get index)
           if (c:digit-p) (count:++)
         } (< (index:++) len)
         eval count
       }

       Loop statement
       The loop reserved keyword  is  another  form  of  loop.  It  take  four
       arguments.  The  first  is  the initialize form. The second is the exit
       condition. The third is the step form and the fourth  is  the  form  to
       execute  at  each  loop  step.  Unlike  the while and do loop, the loop
       statement creates its  own  nameset,  since  the  initialize  condition
       generally creates new symbol for the loop only.

       # a simple loop from 0 to 10
       loop (trans i 0) (< i 10) (i:++) (println i)

       Switch statement
       The switch reserved keyword is a condition selector. The first argument
       is the switch selector. The second argument is a list of various  value
       which  can be matched by the switch value. A special symbol called else
       can be used to match any value.

       # return the primary color in a rgb
       const get-primary-color (color value) (
         switch color (
           ("red"   (return (value:substr 0 2)))
           ("green" (return (value:substr 2 4)))
           ("blue"  (return (value:substr 4 6)))
         )
       )

       Return statement
       The return reserved keyword indicates an exceptional condition  in  the
       flow of execution within a lambda or gamma expression. When a return is
       executed,  the  associated  argument  is  returned  and  the  execution
       terminates.  If  return  is used at the top level, the result is simply
       discarded.

       # initialize a vector with a value
       const vector-init (length value) {
         # treat nil vector first
         if (<= length 0) return (Vector)
         trans result (Vector)
         do (result:add value) (> (length:--) 0)
       }

       Eval and protect
       The eval reserved keyword forces the evaluation of the object argument.
       The  reserved  keyword  eval  is  typically  used in a function body to
       return a particular symbol value. It can also  be  used  to  force  the
       evaluation of a protected object. In many cases, eval is more efficient
       than return. The protect reserved keyword constructs an object  without
       evaluating it. Typically when used with a form, protect return the form
       itself. It can also be used to prevent a symbol evaluation.  When  used
       with a symbol, the symbol object itself is returned.

       const add (protect (+ 1 2))
       (eval add)

       Note  that  in  the preceding example that the evaluation will return a
       lambda expression which is evaluated immediately and which  return  the
       integer 3.

       Assert statement
       The  assert  reserved  keyword  check  for  equality  between  the  two
       arguments and abort the execution in case of failure. By  default,  the
       assertion  checking  is turn off, and can be activated with the command
       option -f assert. Needless to say that assert  is  used  for  debugging
       purpose.

       assert true   (> 2 0)
       assert 0      (- 2 2)
       assert "true" (String true)

       Block statement
       The  block  reserved  keyword  executes  a form in a new local set. The
       local set is destroyed at the completion of the  execution.  The  block
       reserved  keyword returns the value of the last evaluated form. Since a
       new local set is created, any new symbol created  in  this  nameset  is
       destroyed  at the completion of the execution. In other word, the block
       reserved keyword allows the creation of a local scope.

       trans a 1
       block {
         assert    a 1
         trans     a (+ 1 1)
         assert    a 2
         assert ..:a 1
       }
       assert 1 a

       builtin objects
       AFNIX  provides several  builtin  objects  and  builtin  operators  for
       arithmetic  and  logical  operations.  The Integer and Real classes are
       primarily used to manipulate numbers. The Boolean class is used to  for
       boolean operations. Other builtin objects include Character and String.
       The exact usage of these classes is described in the next chapter.

       Arithmetic operations
       AFNIX  provides various ways to perform arithmetic operations. Most  of
       the operations are done with the +, -, * and / operators. Each of these
       operators works with both integer and real numbers.

       (+ 1 2)
       (- 1)
       (* 3 5.0)
       (/ 4.0 2)

       Logical operations
       The Boolean class is used to  represent  the  boolean  value  true  and
       false.  These  last  two  symbols  are  builtin  in  the interpreter as
       constant symbols.  AFNIX  provides also  some  reserved  keywords  like
       not, and and or. Their usage is self understandable.

       not true
       and true (== 1 0)
       or (< -1 0) (> 1 0)

       Predicates
       A  predicate  is  a  function  which  returns  a boolean object.  AFNIX
       provides several predicates to  check  for  some  builtin  objects.  By
       convention,  a  predicate  terminates  with  the sequence -p. The nil-p
       predicate is a special predicate which returns true if  the  object  is
       nil.  AFNIX  provides a predicate for each builtin objects.

       Predicate     Description
       nil-p         check nil object
       eval-p        check evaluation
       real-p        check real object
       regex-p       check regex object
       string-p      check string object
       number-p      check number object
       boolean-p     check boolean object
       integer-p     check integer object
       character-p   check character object

       For  example,  one  can  write  a  function  which  returns true if the
       argument is a number, that is, an integer or a real number.

       # return true if the argument is a number
       const number-p (n) (
         or (integer-p n) (real-p n))

       Predicates for functional and symbolic  programming  are  also  builtin
       into the  AFNIX  engine.

       Predicate     Description
       class-p       check class object
       thread-p      check thread object
       promise-p     check promise object
       lexical-p     check lexical object
       literal-p     check literal object
       closure-p     check closure object
       nameset-p     check nameset object
       instance-p    check instance object
       qualified-p   check qualified object

       Finally,  for each object, a predicate is also associated. For example,
       cons-p is the predicate for the Cons object.

       Predicate   Description
       cons-p      check a cons object
       list-p      check for a list object
       queue-p     check a queue object
       bitset-p    check a bitset object

       vector-p    check a vector object

       Another issue related to evaluation, is to decide  whether  or  not  an
       object  can  be evaluated. The predicate eval-p which is a special form
       is designed to answer this question. Furthermore, the eval-p  predicate
       is  useful  to  decide  whether  or  not  a  symbol  is defined or if a
       qualified name can be evaluated.

       assert true  (eval-p .)
       assert false (eval-p an-unknown-symbol)

       Class and Instance
       AFNIX  provides support for the object oriented programming paradigm. A
       class  in  the   AFNIX   terminology  is a nameset which can be bounded
       automatically when an instance of that class is  created.  Compared  to
       other  language,  there  is  no  need  to declare the data member for a
       particular  class.  Data  members  are  created  during  the   instance
       construction.  A  class  allows  an  instance to call function with the
       instance nameset visible for that function.

       Class and members
       A class is declared with the reserved keyword  class.  The  class  acts
       like a nameset. Functions can be bounded to this class.

       const Color (class)
       const Color:BLACK "#000000"
       const Color:WHITE "#FFFFFF"

       Any   object  can  be  bounded  as  a  data  member,  including  lambda
       expressions.

       const Color (class)
       const Color:get-primary-from-string (color value) {
         trans val "0x"
         val:+= (switch color (
             ("red"   (value:substr 1 3))
             ("green" (value:substr 3 5))
             ("blue"  (value:substr 5 7))
           ))
         Integer val
       }

       Instances
       An instance of a class is created like any builtin object. If a  method
       called  preset  is  defined  for  that  class, the method is used as an
       initializer of that instance.

       const Color (class)
       trans Color:preset (red green blue) {
         const this:red   (Integer red)
         const this:green (Integer green)
         const this:blue  (Integer blue)
       }
       const red   (Color 255   0   0)
       const green (Color   0 255   0)
       const blue  (Color   0   0 255)

       Instance method
       When a lambda expression is bound to the class or  the  instance,  that
       lambda can be invoked as an instance method. When an instance method is
       invoked, the instance nameset is set as the  parent  nameset  for  that
       lambda.  This  is the main reason why a gamma expression cannot be used
       as an instance method. The instance nameset defines the  instance  data
       members and the special symbol this.

       const int-max (x y)
       if (> x y) (Integer x) (Integer y))
       const Color:RED-FACTOR   0.75
       const Color:GREEN-FACTOR 0.75
       const Color:BLUE-FACTOR  0.75
       trans Color:get-darker nil {
         trans red   (int-max (this:red:*   Color:RED-FACTOR)   0)
         trans green (int-max (this:green:* Color:GREEN-FACTOR) 0)
         trans red   (int-max (this:blue:*  Color:BLUE-FACTOR)  0)
         Color red green blue
       }
       # get a darker color than red
       const dark-red (red:get-darker)

       Miscellaneous features
       AFNIX   provides  several  facilities  for control flow and exceptional
       operations. Most of  these  features  are  available  via  the  use  of
       reserved keywords.

       Iteration
       An  iteration  facility  is provided for some objects known as iterable
       objects. The Cons, List and Vector are typical iterable objects.  There
       are  two  ways to iterate with these objects. The first method uses the
       for reserved keyword. The second method uses an explicit iterator which
       can be constructed by the object.

       # compute the scalar product of two vectors
       const scalar-product (u v) {
         trans result 0
         for (x y) (u v) (result:+= (* x y))
         eval result
       }

       The  for  reserved  keyword  iterate  on  both object u and v. For each
       iteration, the symbol x and y are  set  with  their  respective  object
       value.  In  the  example  above,  the result is obtained by summing all
       intermediate products.

       # test the scalar product function
       const v1 (Vector 1 2 3)
       const v2 (Vector 2 4 6)
       (scalar-product v1 v2)

       The iteration can be done explicitly by creating an iterator  for  each
       vectors and advancing steps by steps.

       # scalar product with explicit iterators
       const scalar-product (u v) {
         trans result 0
         trans u-it   (u:get-iterator)
         trans v-it   (v:get-iterator)
         while (u:valid-p) {
           trans x (u:get-object)
           trans y (v:get-object)
           result:+= (* x y)
           u:next
           v:next
         }
         eval result
       }

       In  the example above, two iterators are constructed for both vectors u
       and v. The iteration is done in a while loop by  invoking  the  valid-p
       predicate.  The  get-object  method  returns  the  object  value at the
       current iterator position.

       Exception
       An exception is an unexpected change in the execution flow. The   AFNIX
       model  for exception is based on a mechanism which throws the exception
       to be caught by a  handler.  The  mechanism  is  also  designed  to  be
       compatible with the native "C++" implementation. An exception is thrown
       with the reserved keyword throw.  When  an  exception  is  thrown,  the
       normal flow of execution is interrupted and an object used to carry the
       exception information is created. Such exception object  is  propagated
       backward  in  the  call  stack until an exception handler catch it. The
       reserved keyword try executes a form and catch an exception if one  has
       been  thrown. With one argument, the form is executed and the result is
       the result of the form execution unless an exception is caught.  If  an
       exception  is  caught,  the  result  is  the  exception  object. If the
       exception is a native one, the result is nil.

       try (+ 1 2)
       try (throw)
       try (throw "hello")
       try (throw "hello" "world")
       try (throw "hello" "world" "folks")

       The exception mechanism  is  also  designed  to  install  an  exception
       handler  and  eventually  retrieve  some information from the exception
       object. The reserved symbol what can be used to retrieve some exception
       information.

       # protected factorial
       const fact (n) {
         if (not (integer-p n)) (throw "number-error" "invalid argument")
         if (== n 0) 1 (* n (fact (- n 1)))
       }
       # exception handler
       const handler nil {
         errorln what:eid ’,’ what:reason
       }
       (try (fact 5)       handler)
       (try (fact "hello") handler)

       Delayed evaluation
       The   AFNIX   interpreter  provides  a  special  mechanism  to delay an
       evaluation. The reserved keyword delay creates a special object  called
       a  promise  which  records the form to be later evaluated. The reserved
       keyword force causes a promise to be evaluated.  Subsequent  call  with
       force will produce the same result.

       trans   y 3
       const   l ((lambda (x) (+ x y)) 1)
       assert  4 (force l)
       trans   y 0
       assert  4 (force l)

       Regular Expressions
       The   AFNIX   interpreter  provides  a  builtin  mechanism  for regular
       expression. A regex is an object which is used to  match  certain  text
       patterns.  Regular  expressions  are  built  implicitly  by  the  AFNIX
       reader wit the use of the [ and ] characters.

       if (== (const re [($d$d):($d$d)]) "12:31") {
         trans hr (re:get 0)
         trans mn (re:get 1)
       }

       In the previous example, a regular expression object is  bound  to  the
       symbol  re.  The regex contains two groups. The call to the operator ==
       returns true if the regex matches the argument string. The  get  method
       can be used to retrieve the group by index.

       Threads
       The   AFNIX  interpreter provides a powerful mechanism which allows the
       concurrent  execution  of  forms  and  the  synchronization  of  shared
       objects.  There  are  two  types  of  threads, namely normal thread and
       daemon thread. They differ only by the interpreter exit condition.  The
       interpreter  will  wait  until all normal threads are completed. On the
       other hand, the interpreter will not wait for daemon threads. They  are
       automatically  stopped  when  all  normal  threads are finished. Normal
       threads are created  with  the  reserved  keyword  launch,  and  daemon
       threads  are created with the reserved keyword daemon. When threads are
       used, the interpreter manages  automatically  the  shared  objects  and
       protect them against concurrent access.

       # shared variable access
       const var 0
       const decr nil (while true (var:= (- var 1)))
       const incr nil (while true (var:= (+ var 1)))
       const prtv nil (while true (println "value = " var))
       # start 3 threads
       launch (prtv)
       launch (decr)
       launch (incr)

       Form synchronization
       Although,   AFNIX   provides an automatic synchronization mechanism for
       reading or writing an object, it is sometimes necessary to control  the
       execution  flow.  There  are  basically two techniques to do so. First,
       protect a form from being executed by several threads. Second, wait for
       one  or several threads to complete their task before going to the next
       execution step. The reserved keyword sync can be used to synchronize  a
       form.  When a form, is synchronized, the  AFNIX  engine guarantees that
       only one thread will execute this form.

       const print-message (code mesg) (
         sync {
           errorln "error  : " code
           errorln "message: " mesg
         }
       )

       The previous example create a gamma expression  which  make  sure  that
       both  the  error  code and error message are printed in one group, when
       several threads call it.

       Thread completion
       The other piece of synchronization is the thread completion  indicator.
       The  thread  descriptor contains a method called wait which suspend the
       calling thread until the thread attached to  the  descriptor  has  been
       completed.  If  the  thread  is  already  completed, the method returns
       immediately.

       # simple flag
       const flag false
       # simple shared tester
       const ftest (val) (flag) (assert val (flag:shared-p))
       # no thread mean not shared
       ftest false
       # in a thread it is shared
       const thr (launch (ftest true))
       thr:wait
       assert true (flag:shared-p)

       This example is taken from the test suites. It  checks  that  a  closed
       variable  becomes  shared when started in a thread. Note the use of the
       wait method to make sure the thread has completed before  checking  for
       the  shared  flag.  It  is  also  worth to note that wait is one of the
       method which guarantees that a thread result is valid. Another  use  of
       the  wait  method  can be made with a vector of thread descriptors when
       one wants to wait until all of them have completed.

       # shared vector of threads descriptors
       const thr-group (Vector)
       # wait until all threads in the group are finished
       const wait-all nil (for (thr) (thr-group) (thr:wait))

       Condition variable
       A condition  variable  is  another  mechanism  to  synchronize  several
       threads.  A  condition  variable is modeled with the Condvar object. At
       construction, the condition variable is initialized to false. A  thread
       calling  the  wait  method will block until the condition becomes true.
       The mark method can be used by a  thread  to  change  the  state  of  a
       condition  variable and eventually awake some threads which are blocked
       on it. The use of condition variable is particularly  recommended  when
       one  need  to make sure a particular thread has been doing a particular
       task.

NUMBERS AND STRINGS

       This chapters covers in detail the builtin objects used  to  manipulate
       numbers  and  strings.  First the integer, relatif and real numbers are
       described.  AFNIX  offers a broad range  of  methods  for  these  three
       objects  to support numerical computation. As a second step, string and
       character  objects  are  described.  Many  examples  show  the  various
       operations  which  can be used as automatic conversion between one type
       and another. Finally, the boolean object is  described.  These  objects
       belongs  to  the  class of literal objects, that is objects that have a
       string representation.

       Integer number
       The fundamental number representation is the Integer. The integer is  a
       64  bits signed 2’s complement number. Even when running with a 32 bits
       machine, the 64 bits representation is used. If a larger representation
       is needed, the Relatif object might be more appropriate.

       Integer format
       The  default literal format for an integer is the decimal notation. The
       minus sign (without blank) indicates a negative number. Hexadecimal and
       binary notations can also be used with prefix 0x and 0b. The underscore
       character can be used to make the notation more readable.

       const a  123
       trans b -255
       const h  0xff
       const b  0b1111_1111

       Integer number are constructed from the literal notation or by using an
       explicit   integer   instance.   The   Integer  class  offers  standard
       constructors. The default constructor creates  an  integer  object  and
       initialize  it  to  0. The other constructors take either an integer, a
       real number, a character or a string.

       const a (Integer)
       const b (Integer 2000)
       const c (Integer "23")

       When the hexadecimal or binary notation is used, care should  be  taken
       to avoid a negative integer. For example, 0x_8000_0000_0000_0000 is the
       smallest negative number.

       Integer arithmetic
       Standard arithmetic operators are available as builtin  operators.  The
       usual  addition  +,  multiplication  *  and division / operate with two
       arguments. The subtraction - operates with one or two arguments.

       + 3 4
       - 3 4
       - 3
       * 3 4
       / 4 2

       As a builtin object, the Integer  object  offers  various  methods  for
       builtin arithmetic which directly operates on the object. The following
       example illustrates these methods.

       trans i 0
       i:++
       i:--
       i:+ 4
       i:= 4
       i:- 1
       i:* 2
       i:/ 2
       i:+= 1
       i:-= 1
       i:*= 2
       i:/= 2

       As a side effect, these methods allows a const symbol to  be  modified.
       Since  the  methods operates on an object, they do not modify the state
       of the symbol. Such methods are called mutable methods.

       const i 0
       i:= 1

       Integer comparison
       The comparison operators works the same. The only  difference  is  that
       they  always  return  a  Boolean  result.  The comparison operators are
       namely equal ==, not equal !=, less than <, less equal  <=,  greater  >
       and greater equal >=. These operators take two arguments.

       == 0 1
       != 0 1

       Like  the arithmetic methods, the comparison operators are supported as
       object methods. These methods return a Boolean object.

       i:=  1
       i:== 1
       i:!= 0

       Integer calculus
       Armed with all these functions, it is possible to develop a battery  of
       functions  operating  with  numbers. As another example, we revisit the
       Fibonacci sequence as demonstrated in the  introduction  chapter.  Such
       example  was  terribly  slow,  because of the double recursion. Another
       method suggested by Springer and Friedman uses two functions to perform
       the same job.

       const fib-it (gamma (n acc1 acc2) (
           if (== n 1) acc2 (fib-it (- n 1) acc2 (+ acc1 acc2))))
       const fiboi (gamma (n) (
           if (== n 0) 0 (fib-it n 0 1)))

       This  later  example  is  by  far  much  faster, since it uses only one
       recursion. Although, it is no the fastest way to write it,  but  nobody
       is going to question the elegant aspect of recursion.

       Other Integer methods
       The Integer class offers other convenient methods. The odd-p and even-p
       are predicates. The mod  take  one  argument  and  returns  the  modulo
       between  the  calling  integer  and  the argument. The to-string method
       returns a string representation of the integer. The abs methods returns
       the absolute value of the calling integer.

       i:even-p
       i:odd-p
       i:mod 2
       i:= -1
       i:abs
       i:to-string

       Relatif number
       A relatif or big-num is an integer with infinite precision. The Relatif
       class is similar to  the  Integer  class  except  that  it  works  with
       infinitely  long  number.  The relatif notation uses a r or R suffix to
       express a relatif number versus an integer one.

       const a  123R
       trans b -255R
       const c  0xffR
       const d  0b1111_1111R
       const e (Relatif)
       const f (Relatif 2000)
       const g (Relatif "23")

       Relatif operations
       Most of the Intege  class  operations  are  supported  by  the  Relatif
       object.  The  only  difference  is  that  there is no limitation on the
       number size. This  naturally  comes  with  a  computational  price.  An
       amazing  example  is to compute the biggest know prime Mersenne number.
       The world record exponent is 6972593. The number is therefore:

       const i 1R
       const m (- (i:shl 6972593) 1)

       This number has 2098960 digits. You can use the println method  if  you
       wish, but you have been warned...

       Real number
       The real class implements the representation for floating point number.
       The internal representation is machine dependent, and generally follows
       the  double  representation  with  64  bits  as  specified  by the IEEE
       754-1985 standard for binary floating  point  arithmetic.  All  integer
       operations are supported for real numbers.

       Real format
       The   AFNIX   reader  supports  two types of literal representation for
       real number. The first representation is the dotted  decimal  notation.
       The second notation is the scientific notation.

       const a  123.0 # a positive real
       const b -255.5 # a negative real
       const c  2.0e3 # year 2000.0

       Real  number  are  constructed from the literal notation or by using an
       explicit real instance. The Real class  offers  standard  constructors.
       The  default constructor creates a real number object and initialize it
       to 0.0. The other constructors takes either an integer, a real  number,
       a character or a string.

       Real arithmetic
       The  real  arithmetic is similar to the integer one. When an integer is
       added to a real number, that number is  automatically  converted  to  a
       real  and  vice  versa.  Ultimately,  a  pure  integer  operation might
       generate a real result.

       + 1999.0 1   # 2000.0
       + 1999.0 1.0 # 2000.0
       - 2000.0 1   # 1999.0
       - 2000.0 1.0 # 1999.0
       * 1000 2.0   # 2000.0
       * 1000.0 2.0 # 2000.0
       / 2000.0 2   # 1000.0
       / 2000.0 2.0 # 1000.0

       Like the  Integer  object,  the  Real  object  has  arithmetic  builtin
       methods.

       trans  r 0.0 # 0.0
       r:++       # 1.0
       r:--       # 0.0
       r:+ 4.0    # 4.0
       r:= 4.0    # 4.0
       r:- 1.0    # 3.0
       r:* 2.0    # 8.0
       r:/ 2.0    # 2.0
       r:+= 1.0   # 5.0
       r:-= 1.0   # 4.0
       r:*= 2.0   # 8.0
       r:/= 2.0   # 4.0

       Real comparison
       The  comparison  operators  works  as the integer one. As for the other
       operators, an implicit conversion between an integer to a real is  done
       automatically.

       == 2000 2000   # true
       != 2000 1999   # true

       Comparison  methods  are  also  available  for  the  Real object. These
       methods take either an integer or a real as argument.

       r:=  1.0 # 1.0
       r:== 1.0 # true
       r:!= 0.0 # true

       A complex example
       One of the most interesting point with functional programming  language
       is  the  ability  to  create complex computation function. For example,
       let’s assume we wish to compute the value at a point x of the  Legendre
       polynomial  of  order  n. One of the solution is to encode the function
       given its order. Another solution is to compute the function  and  then
       compute the value.

       # legendre polynomial order 0 and 1
       const lp-0 (gamma (x) 1)
       const lp-1 (gamma (x) x)
       # legendre polynom of order n
       const lp-n (gamma (n) (
           if (> n 1) {
             const lp-n-1 (lp-n (- n 1))
             const lp-n-2 (lp-n (- n 2))
             gamma (x) (n lp-n-1 lp-n-2)
             (/ (- (* (* (- (* 2 n) 1) x)
                   (lp-n-1 x))
                 (* (- n 1) (lp-n-2 x))) n)
           } (if (== n 1) lp-1 lp-0)
         ))
       # generate order 2 polynom
       const lp-2 (lp-n 2)
       # print lp-2 (2)
       println "lp2 (2) = " (lp-2 2)

       Note  that  the  computation  can  be  done either with integer or real
       numbers. With integers, you might get some strange results anyway,  but
       it  will work. Note also how the closed variable mechanism is used. The
       recursion capture each level of the polynom until it is constructed. As
       an exercise, try to use a lambda expression instead of a gamma one, and
       compare the execution result with large number of n. Note also that  we
       have here a double recursion.

       Other real methods
       The  real  numbers  are  delivered  with  a battery of functions. These
       include the trigonometric functions, the logarithm and  couple  others.
       Hyperbolic  functions like sinh, cosh, tanh, asinh, acosh and atanh are
       also supported. The square root sqrt method return the square  root  of
       the  calling real. The floor and ceiling returns respectively the floor
       and the ceiling of the calling real.

       const r0 0.0       # 0.0
       const r1 1.0       # 1.0
       const r2 2.0       # 2.0
       const rn -2.0      # -2.0
       const rq (r2:sqrt) # 1.414213
       const pi 3.1415926 # 3.141592
       rq:floor           # 1.0
       rq:ceiling         # 2.0
       rn:abs             # 2.0
       r1:log             # 0.0
       r0:exp             # 1.0
       r0:sin             # 0.0
       r0:cos             # 1.0
       r0:tan             # 0.0
       r0:asin            # 0.0
       pi:floor           # 3.0
       pi:ceiling         # 4.0

       Accuracy and formatting
       Real numbers are not necessarily accurate, nor  precise.  The  accuracy
       and  precision  are  highly  dependent  on  the hardware as well as the
       nature of the operation being performed. In any case, never assume that
       a  real value is an exact one. Most of the time, a real comparison will
       fail, even if the numbers are very close together. When comparing  real
       numbers,  it is preferable to use the ?= operator. Such operator result
       is bounded by the internal precision representation and will  generally
       return  the  desired  value. The real precision is an interpreter value
       which is set with the set-epsilon method while the get-epsilon  returns
       the interpreter precision. Note also that the Real object bind the data
       member EPSILON, which can be use with the qualified name  Real:EPSILON.
       By default, the precision is set to 0.00001.

       interp:set-epsilon 0.0001
       const r 2.0
       const s (r:sqrt) # 1.4142135
       (s:?= 1.4142)    # true

       Real  number  formatting  is  another  story. The format method takes a
       precision argument which indicates the number of digits  to  print  for
       the  decimal  part. Note that the format command might round the result
       as indicated in the example below.

       const pi 3.1415926535
       pi:format 3  # 3.142

       If additional formatting is needed, the Stringfill-left and  fill-right
       methods can be used.

       const pi  3.1415926535  # 3.1415926535
       const val (pi:format 4) # 3.1416
       (val:fill-left ’0’ 9)   # 0003.1416

       Character
       The Character object is another builtin object of the  AFNIX  engine. A
       character is internally represented  by  a  quad  by  using  a  31  bit
       representation as specified by the Unicode standard and ISO 10646.

       Character format
       The  standard  quote notation is used to represent a character. In that
       respect,  AFNIX  differs substantially from other  functional  language
       where the quote protect a form.

       const LA01 ’a’ # the character a
       const ND10 ’0’ # the digit 0

       All  characters  from  the  Unicode codeset are supported by the  AFNIX
       engine. The characters are constructed from the literal notation or  by
       using  an  explicit  character  instance.  The  Character  class offers
       standard  constructors.  The  default  constructor   creates   a   null
       character.  The  other constructors take either an integer, a character
       or a string. The string can be either a single quoted character or  the
       literal  notation based on the U+ notation in hexadecimal. For example,
       U+40 is the @ character while U+3A3 is the greek sigma capital  letter.

       const nilc (Character)        # null character
       const a    (Character ’a’)    # a
       const 0    (Character 48)     # 0
       const mul  (Character "*")    # *
       const div  (Character "U+40") # @

       Character arithmetic
       A  character is like an integer, except that it operates in the range 0
       to 0x7FFFFFFF. The character arithmetic  is  simpler  compared  to  the
       integer  one  and  no overflow or underflow checking is done. Note that
       the arithmetic operations take an integer as an argument.

       + ’a’ 1 # ’b’
       - ’9’ 1 # ’8’

       Several Character object  methods  are  also  provided  for  arithmetic
       operations in a way similar to the Integer class.

       trans  c ’a’ # ’a’
       c:++         # ’b’
       trans  c ’9’ # ’9’
       c:--         # ’8’
       c:+ 1        # ’9’
       c:- 9        # ’0’

       Character comparison
       Comparison  operators  are  also working with the Character object. The
       standard operators are namely equal ==, not equal !=, less than <, less
       equal  <=,  greater  >  and  greater equal >=. These operators take two
       arguments.

       == ’a’ ’b’ # false
       != ’0’ ’1’ # true

       Other character methods
       The Character object comes with additional methods.  These  are  mostly
       conversion  methods  and  predicates.  The  to-string  method returns a
       string representation of the calling character. The  to-integer  method
       returns an integer representation the calling character. The predicates
       are alpha-p, digit-p, blank-p, eol-p, eof-p and nil-p.

       const LA01 ’a’  # ’a’
       const ND10 ’0’  # ’0’
       LA01:to-string  # "a"
       LA01:to-integer # 97
       LA01:alpha-p    # true
       ND10:digit-p    # true

       String
       The String object is one of the most important builtin  object  in  the
       AFNIX   engine. Internally, a string is a vector of Unicode characters.
       Because a string operates with Unicode characters, care should be taken
       when using composing characters.

       String format
       The  standard  double  quote  notation is used to represent literally a
       string. Standard escape sequences are  also  accepted  to  construct  a
       string.

       const hello "hello"

       Any  literal  object can be used to construct a string. This means that
       integer, real, boolean or character objects are all valid to  construct
       strings.  The  default  constructor  creates  a null string. The string
       constructor can also takes a string.

       const nils (String)      # ""
       const one  (String 1)    # "1"
       const a    (String ’a’)  # "a"
       const b    (String true) # "true"

       String operations
       With strings, numerous methods can be provided. We illustrate here  the
       most common one.

       const h "hello"
       h:length       # 5
       h:get 0        # ’h’
       h:== "world"   # false
       h:!= "world"   # true
       h:+= " world"  # "hello world"

       The  sub-left  and  sub-right  methods  return  a sub-string, given the
       position index. For sub-left, the index is the terminating index, while
       sub-right is the starting index, counting from 0.

       const msg "hello world"
       msg:sub-left  5 # "hello"
       msg:sub-right 6 # "world"

       The  strip, strip-left and strip-right are methods used to strip blanks
       and tabs. The strip method combines both  strip-left  and  strip-right.
       The  split  method  returns a vector of strings by splitting the string
       according to a break sequence. By default, the break  sequence  is  the
       blank,  tab  and  newline  characters. The break sequence can be one or
       more characters passed as one single argument to the method.

       const str "hello:world"
       const vec str:split ":" # "hello" "world"
       println (vec:length) # 2

       The fill-left and fill-right methods can be used to fill a string  with
       a  character  up  to a certain length. If the string is longer than the
       length, nothing happens.

       const pi  3.1415926535  # 3.1415926535
       const val (pi:format 4) # 3.1416
       val:fill-left ’0’ 9     # 0003.1416

       String hash value
       Computing the hash value of a string is  an  interesting  problem.  The
       algorithm used by the  AFNIX  engine is shown as an example below. Note
       that the hashid method is built in the String object. The program shows
       both internal and computed values.

       # compute string hashid
       const hashid (s) {
         const len (s:length)
         trans cnt 0
         trans val 0
         trans sht 17
         do {
           # compute the hash value
           trans i (Integer (s:get cnt))
           val:= (val:xor (i:shl sht))
           # adjust shift index
           if (< (sht:-= 7) 0) (sht:+= 24)
         } (< (cnt:++) len)
         eval val
       }

       When  run, example 0203.als, the following result is obtained with a 32
       bits machine.

       # test our favorite string
       const hello "hello world"
       hello:hashid # 1054055120
       hashid hello # 1054055120

       As a side note, it is recommended to print  the  shift  amount  in  the
       program. One may notice, that the value remains bounded by 24. Since we
       are "xoring" the final value, it does illustrate that the algorithm  is
       design  for  a 32 bits machine. With a 64 bits machine the algorithm is
       slightly modified to use the extra space.  This  also  means  that  the
       hashid value is not portable across platforms.

CONTAINER OBJECTS

       This   chapter   covers   the   builtin   container  objects  and  more
       specifically, iterable objects such like Cons, List and Vector. Special
       objects like Queue and Bitset are mentioned at the end.

       Cons builtin object
       Originally, a Cons object or cons cell have been the fundamental object
       of the Lisp or Scheme machine. The cons cell is the building block  for
       list  and is of great importance in  AFNIX  as well. A Cons object is a
       simple element used to build linked list. The cons cell holds an object
       and a pointer to the next cons cell. The cons cell object is called car
       and the next cons cell is called the cdr. This notation, found in  Lisp
       in maintained here for the sake of tradition.

       Cons cell constructors
       The default constructor creates a cons cell those car is initialized to
       the nil object. The constructor can also take one or several objects.

       const nil-cons (Cons)
       const lst-cons (Cons 1 ’a’ "hello")

       The constructor can take any kind of objects. When all objects have the
       same type, the result list is said to be homogeneous. If all objects do
       not have the same type, the result list is said  to  be  heterogeneous.
       List  can  also  be constructed directly from the  AFNIX  reader. Since
       all internal forms are built with cons cell, the  construction  can  be
       achieved by simply protecting the form from being interpreted.

       const blist (protect ((1) ((2) ((3)))))

       Cons cell methods
       A Cons object provides several methods to access the car and the cdr of
       a cons cell. Other methods allows access to a list by index.

       const c (Cons "hello" "world")
       c:length   # 2
       c:get-car  # "hello"
       c:get-cadr # "world"
       c:get 0    # "hello"
       c:get 1    # "world"

       The set-car method set the car of the  cons  cell.  The  append  method
       appends  a  new  cons  cell at the end of the cons list and set the car
       with the specified object.

       List builtin object
       The List builtin object provides the facility of  a  double-link  list.
       The  List object is another example of iterable object. The List object
       provides support for forward and backward iteration.

       List construction
       A list is constructed like a cons cell with  zero  or  more  arguments.
       Unlike the cons cell, the List can have a null size.

       const nil-list (List)
       const dbl-list (List 1 ’a’ "hello")

       List methods
       The  List object methods are similar the Cons object. The append method
       appends an object at the end of the list. The insert method inserts  an
       object at the beginning of the list.

       const list (List "hello" "world")
       list:length         # 2
       list:get 0          # "hello"
       list:get 1          # "world"
       list:append "folks" # "hello" "world" "folks"

       Vector builtin object
       The  Vector  builtin  object provides the facility of an index array of
       objects. The Vector object is another example of iterable  object.  The
       Vector object provides support for forward and backward iteration.

       Vector construction
       A  vector  is  constructed  like  a  cons  cell  or a list. The default
       constructor creates a vector with 0 objects.

       const nil-vector (Vector)
       const obj-vector (Vector 1 ’a’ "hello")

       Vector methods
       The Vector object methods are similar to the List  object.  The  append
       method appends an object at the end of the vector. The set method set a
       vector position by index.

       const vec (Vector "hello" "world")
       vec:length          # 2
       vec:get 0           # "hello"
       vec:get 1           # "world"
       vec:append "folks"  # "hello" "world" "folks"
       vec:set 0 "bonjour" # "bonjour" "world" "folks"

       Set builtin object
       The Set builtin object provides the facility of  an  object  container.
       The  Set  object  is another example of iterable object. The Set object
       provides support for forward iteration. One of the property of a set is
       that  there is only one object representation per set. Adding two times
       the same object results in one object only.

       Set construction
       A set is constructed like a vector. The default constructor  creates  a
       set with 0 objects.

       const nil-set (Set)
       const obj-set (Set 1 ’a’ "hello")

       Set methods
       The Set object methods are similar to the Vector object. The add method
       adds an object in the set. If the object is already  in  the  set,  the
       object  is  not added. The length method returns the number of elements
       in the set.

       const set       (Set "hello" "world")
       set:get-size    # 2
       set:add "folks" # "hello" "world" "folks"

       Iteration
       When an object is iterable, it can be used with  the  reserved  keyword
       for.  The  for  keyword  iterates  on  one or several objects and binds
       associated symbols during each  step  of  the  iteration  process.  All
       iterable objects provides also the method get-iterator which returns an
       iterator for a given object. The use of iterator  is  justified  during
       backward iteration, since for only perform forward iteration.

       Function mapping
       Given  a function func, it is relatively easy to apply this function to
       all objects of an iterable object. The result is a list  of  successive
       calls with the function. Such function is called a mapping function and
       is generally called map.

       const map (obj func) {
         trans result (Cons)
         for (car) (obj) (result:link (func car))
         eval result
       }

       The link method differs from the append method in the  sense  that  the
       object to append is set to the cons cell car if the car and cdr is nil.

       Multiple iteration
       Multiple iteration can be done with one call to for. The computation of
       a scalar product is a simple but illustrative example.

       # compute the scalar product of two vectors
       const scalar-product (u v) {
         trans result 0
         for (x y) (u v) (result:+= (* x y))
         eval result
       }

       Note  that  the  function  scalar-product  does not make any assumption
       about the object to iterate.  One  could  compute  the  scalar  product
       between a vector a list for example.

       const u (Vector 1 2 3)
       const v (List   2 3 4)
       scalar-product u v

       Conversion of iterable objects
       The  use  of  an iterator is suitable for direct conversion between one
       object and another. The conversion to a vector can be simply defined as
       indicted below.

       #convert an iterable object to a vector
       const to-vector (obj) {
         trans result (Vector)
         for (i) (obj) (result:append i)
         eval result
       }

       Explicit iterator
       An  explicit  iterator  is constructed with the get-iterator method. At
       construction, the iterator is reset to the beginning position. The get-
       object  method returns the object at the current iterator position. The
       next advances the iterator to its next  position.  The  valid-p  method
       returns  true if the iterator is in a valid position. When the iterator
       supports backward operations, the prev method move the iterator to  the
       previous  position.  Note  that  Cons  objects  do not support backward
       iteration. The begin method reset the iterator to  the  beginning.  The
       end  method  moves  the  iterator  the  last  position.  This method is
       available only with backward iterator.

       # reverse a list
       const reverse-list (obj) {
         trans result (List)
         trans itlist (obj:get-iterator)
         itlist:end
         while (itlist:valid-p) {
           result:append (itlist:get-object))
         itlist:prev
       }
       eval result
       }

       Special Objects
       The  AFNIX  engine provides several builtin container objects which are
       special case of container objects. Such objects are Queue and Bitset

       Queue object
       A queue is a special object which acts as container with a FIFO policy.
       When an object is placed in the queue, it remains there  until  it  has
       been dequeued.

       # create a queue with objects
       const q (Queue "hello" "world")
       q:empty-p # false
       q:length  # 2
       # dequeue some object
       q:dequeue # hello
       q:dequeue # world
       q:empty-p # true

       BitSet object
       A  bit set is a special container for bit. A bit set can be constructed
       with a specific size. When the bit set is constructed, each bit can  be
       marked and tested by index.

       # create a bit set
       const bs (BitSet)
       bitset-p bs # true
       # check, mark and clear
       assert false (bs:get 0)
       bs:mark 0
       assert true  (bs:get 0)
       bs:clear 0
       assert false (bs:get 0)

CLASSES

       This  chapter  covers  the   AFNIX   class  model  and  its  associated
       operations. The  AFNIX  class model is slightly different  compared  to
       traditional one. Because  AFNIX  has dynamic symbol bindings, it is not
       necessary to declare the class data members. A class is an object which
       can  be manipulated by itself. Such class is said to belongs to a group
       of meta class as described  later  in  this  chapter.  Once  the  class
       concept has been detailed, the chapter moves to the concept of instance
       of that class and shows how instance data members and functions can  be
       used.  The  chapter  terminates  with  a  description  of dynamic class
       programming.

       Class object
       A class object in the  AFNIX  terminology is simply a nameset which can
       be replicated via a construction mechanism. A class is created with the
       reserved keyword class. The result is an object  of  type  Class  which
       supports various symbol binding operations.

       Class declaration and bindings
       A  new class is an object created with the reserved keyword class. Such
       class is an object which can be bound to a symbol.

       const Color (class)

       A list of initial instance data members can be specified as an argument
       to the class reserved keyword.

       const Complex (class (re im))

       Because  a  class  acts like a nameset, it is possible to bind directly
       symbols with the qualified name notation.

       const Color (class)
       const Color:RED-FACTOR    0.75
       const Color:BLUE-FACTOR   0.75
       const Color:GREEEN-FACTOR 0.75

       When a data is defined in the class nameset, it is common to  refer  it
       as  a  static  data  member. A static data member is invariant over the
       instance of that class. When the data member is declared with the const
       reserved  keyword, the symbol binding is const in the class nameset. It
       is also possible to use the trans reserved keyword.

       Class closure binding
       A lambda or gamma expression can be define for a class. If the class do
       not  reference  an  instance  of  that  class, the resulting closure is
       called a static method of that  class.  Static  methods  are  invariant
       among the class instances. The standard declaration syntax for a lambda
       or gamma expression is still valid with a class.

       const Color:get-primary-from-string (color value) {
         trans val "0x"
         val:+= (switch color (
             ("red"   (value:substr 1 3))
             ("green" (value:substr 3 5))
             ("blue"  (value:substr 5 7))
           ))
         I      Integer val
       }

       The invocation of a static method is done with the  standard  qualified
       name notation.

       Color:get-primary-from-string "red"   "#23c4e5"
       Color:get-primary-from-string "green" "#23c4e5"
       Color:get-primary-from-string "blue"  "#23c4e5"

       Class symbol access
       A  class  acts  as  a  nameset  and therefore provides the mechanism to
       evaluate any symbol with the qualified name notation.

       const Color:RED-VALUE "#ff0000"
       const Color:print-primary-colors (color) {
         println "red   color " (Color:get-primary-color "red"   color)
         println "green color " (Color:get-primary-color "green" color)
         println "blue  color " (Color:get-primary-color "blue"  color)
       }
       # print the color components for the red color
       Color:print-primary-colors Color:RED-VALUE

       Instance
       An instance of a class is an  AFNIX  object which is constructed  by  a
       special  class  method called a constructor. If an instance constructor
       does not exist, the instance is said to have a default construction. An
       instance  acts  also as a nameset. The only difference with a class, is
       that a symbol resolution is done first in the instance nameset and then
       in  the  instance  class.  As  a  consequence,  creating an instance is
       equivalent to define a default nameset hierarchy.

       Instance construction
       By default, a instance of the class  is  an  object  which  defines  an
       instance  nameset.  The simplest way to define an anonymous instance is
       to create it directly.

       const i     ((class))
       const Color (class)
       const red   (Color)

       The example above define an instance of an anonymous class. If a  class
       object  is  bound  to  a  symbol,  such symbol can be used to create an
       instance of that class. When an instance is created, the special symbol
       named  this  is defined in the instance nameset. This symbol is bounded
       to the instance object and can be used to reference in an anonymous way
       the instance itself.

       Instance initialization
       When  an  instance  is  created, the  AFNIX  engine looks for a special
       lambda expression called preset. This lambda expression, if it  exists,
       is  executed  after  the  default  instance  has been constructed. Such
       lambda expression is a method since it can refer to the this symbol and
       bind  some  instance symbols. The arguments which are passed during the
       instance construction are passed to the preset method.

       const Color (class)
       trans Color:preset (red green blue) {
         const this:red   (Integer red)
         const this:green (Integer green)
         const this:blue  (Integer blue)
       }
       # create some default colors
       const Color:RED   (Color 255   0   0)
       const Color:GREEN (Color   0 255   0)
       const Color:BLUE  (Color   0   0 255)
       const Color:BLACK (Color   0   0   0)
       const Color:WHITE (Color 255 255 255)

       In the example above, each time a color  is  created,  a  new  instance
       object  is  created.  The  constructor  is invoked with the this symbol
       bound to the newly created  instance.  Note  that  the  qualified  name
       this:red  defines  a new symbol in the instance nameset. Such symbol is
       sometimes referred as an instance data member. Note as well that  there
       is  no  ambiguity  in  resolving  the  symbol  red.  Once the symbol is
       created, it shadows the one defined as a constructor argument.

       Initialization with data member list
       If the class was defined with a list of data members, the  instance  is
       created  with  these  data  members  initialized to nil. Each symbol is
       defined as a transient symbol since they are supposed  to  be  modified
       later.  As  a  consequence,  it is possible to use the reserved keyword
       trans inside the preset method.

       const Complex (class (re im))
       trans Complex:preset (re im) {
         trans this:re (Real re)
         trans this:im (Real im)
       }

       The use of a class data  member  list  is  primarily  dictated  by  the
       existence  of  a  copy  constructor  for that class. If a method try to
       construct an object, an evaluation of an unbound data member with trans
       might  trigger  an  inner instance data member to be set instead of the
       real one. This behavior exists only with trans. When const is used, the
       implementation  guarantee that the symbol binding will be local to that
       instance.

       Instance symbol access
       An instance acts as a nameset. It is therefore possible to bind locally
       to  an  instance  a  symbol.  When  a symbol needs to be evaluated, the
       instance nameset is searched first. If the symbol  is  not  found,  the
       class  nameset  is searched. When an instance symbol and a class symbol
       have the same name, the instance symbol is said  to  shadow  the  class
       symbol. The simple example below illustrates this property.

       const c   (class)
       const c:a 1
       const i   (c)
       const j   (c)
       const i:a 2
       # class symbol access
       println   c:a
       # shadow symbol access
       println   i:a
       # non shadow access
       println   j:a

       When  the  instance is created, the special symbol meta is bound in the
       instance nameset with  the  instance  class  object.  This  symbol  can
       therefore be used to access a shadow symbol.

       const c   (class)
       const i   (c)
       const c:a 1
       const i:a 2
       println   i:a
       println   i:meta:a

       The   symbol   meta  must  be  used  carefully,  especially  inside  an
       initializer since it might create an infinite recursion as shown below.

       const c (class)
       trans c:preset nil (const i (this:meta))
       const i (c)

       Instance method
       When  lambda  expression  is  defined  within the class or the instance
       nameset, that lambda expression is callable from the  instance  itself.
       If the lambda expression uses the this symbol, that lambda is called an
       instance method since the  symbol  this  is  defined  in  the  instance
       nameset.  If  the  instance method is defined in the class nameset, the
       instance method is said to be global, that is, callable by any instance
       of  that  class. If the method is defined in the instance nameset, that
       method is said to be local and is callable by the instance only. Due to
       the nature of the nameset parent binding, only lambda expression can be
       used. Gamma expressions will not  work  since  the  gamma  nameset  has
       always the top level nameset as its parent one.

       const Color (class)
       # class constructor
       trans Color:preset (red green blue) {
         const this:red   (Integer red)
         const this:green (Integer green)
         const this:blue  (Integer blue)
       }
       const Color:RF 0.75
       const Color:GF 0.75
       const Color:BF 0.75
       # this method returns a darker color
       trans Color:darker nil {
         trans lr (Integer (max (this:red:*   Color:RF) 0))
         trans lg (Integer (max (this:green:* Color:GF) 0))
         trans lb (Integer (max (this:blue:*  Color:BF) 0))
         Color lr lg lb
       }
       # get a darker color than yellow
       const yellow      (Color 255 255 0)
       const dark-yellow (yellow:darker)

       Instance operators
       Any  operator  can  be  defined  at  the  class  or the instance level.
       Operators like == or != generally requires the ability to assert if the
       argument  is  of  the same type of the instance. The global operator ==
       will return true if two classes are the same. With the use of the  meta
       symbol, it is possible to assert such equality.

       # this method checks that two colors are equals
       trans Color:== (color) {
         if (== Color color:meta) {
           if (!= this:red   color:red)   (return false)
           if (!= this:green color:green) (return false)
           if (!= this:blue  color:blue)  (return false)
           eval true
         } false
       }
       # create a new yellow color
       const  yellow (Color 255 255 0)
       (yellow:== (Color 255 255 0)) # true

       The  global  operator  ==  returns true if both arguments are the same,
       even for classes. Method operators are left open to the user.

       Complex number example
       As a final example, a class simulating the behavior of a complex number
       is  given  hereafter.  The  interesting point to note is the use of the
       operators. As illustrated before, the class uses uses a default  method
       method to initialize the data members.

       # class declaration
       const Complex (class (re im))
       # constructor initializer
       trans Complex:preset (re im) {
         trans this:re (Real re)
         trans this:im (Real im)
       }
       # class mutators
       trans Complex:set-re (x) (trans this:re re)
       trans Complex:set-im (x) (trans this:im im)
       # class accessors
       trans Complex:get-re nil (Real this:re)
       trans Complex:get-im nil (Real this:im)
       trans Complex:module nil {
         trans result (Real (+ (* this:re this:re) (* this:im this:im)))
         result:sqrt
       }
       trans Complex:format nil {
         trans result (String this:re)
         result:+= "+i"
         result:+= (String this:im)
       }
       # complex predicate
       const complex-p (c) (
         if (instance-p c) (== Complex c:meta) false)
       # operators
       trans Complex:== (c) (
         if (complex-p c) (and (this:re:== c:re) (this:im:== c:im)) (
           if (number-p c)  (and (this:re:== c) (this:im:zero-p)) false))
       trans Complex:= (c) {
         if (complex-p c) {
           this:re:= (Real c:re)
           this:im:= (Real c:im)
           return this
         }
         this:re:= (Real c)
         this:im:= 0.0
         return this
       }
       trans Complex:+ (c) {
         trans result (Complex this:re this:im)
         if (complex-p c) {
           result:re:+= c:re
           result:im:+= c:im
           return result
         }
         result:re:+= (Real c)
         eval result
       }

       Inheritance
       Inheritance  is  the mechanism by which a class or an instance inherits
       methods and data member access from a parent object. The  AFNIX   class
       model  is  based on a single inheritance model. When an instance object
       defines a parent object, such object is called a  super  instance.  The
       instance  which  has a super instance is called a derived instance. The
       main utilization of inheritance is the ability  to  reuse  methods  for
       that super instance.

       Derivation construction
       A  derived object is generally defined within the preset method of that
       instance by setting the super data member. The super  reserved  keyword
       is  set  to nil at the instance construction. The good news is that any
       object can be defined as a super instance, including builtin object.

       const c (class)
       const c:preset nil {
         trans this:super 0
       }

       In the example above, an instance of class c is constructed. The  super
       instance  is  with an integer object. As a consequence, the instance is
       derived from the Integer instance. Another consequence of  this  scheme
       is  that  derived  instance  do not have to be built from the same base
       class.

       Derived symbol access
       When an instance is derived from another one, any symbol which  belongs
       to  the  super  instance  can  be access with the use of the super data
       member. If the super class  can  evaluate  a  symbol,  that  symbol  is
       resolved automatically by the derived instance.

       const c       (class)
       const i       (c)
       trans i:a     1
       const j       (c)
       trans j:super i
       println j:a

       When a symbol is evaluated, a set of search rules is applied. The AFNIX
       engine gives the priority to the class nameset vs the  super  instance.
       As  a  consequence,  a static data member might shadow a super instance
       data member. The rule  associated  with  a  symbol  evaluation  can  be
       summarized as follow.
              Look in the instance nameset.
              Look in the class nameset.
              Look in the super instance if it exists.
              Look in the base object.

       Instance re-parenting
       The  ability  to  set  dynamically  the parent instance make the  AFNIX
       object model an ideal candidate to support  instance  re-parenting.  In
       this  model, a change in the parent instance is automatically reflected
       at the instance method call.

       const c (class)
       const i (c)
       trans i:super 0
       println (i:to-string) # 0
       trans i:super "hello world"
       println (i:to-string) # hello world

       In this example,  the  instance  is  originally  set  with  an  Integer
       instance  parent.  Then  the  instance  is  re-parented  with  a String
       instance parent. The call to  the  to-string  method  illustrates  this
       behavior.

       Instance re-binding
       The  ability  to set dynamically the instance class is another powerful
       feature of the  AFNIX  object model. In  this  approach,  the  instance
       meta   class   can   be  changed  dynamically  with  the  mute  method.
       Furthermore, it is  also  possible  to  create  initially  an  instance
       without any class binding, which is later muted.

       # create a point class
       const  point (class)
       # point class initializer
       trans point:preset (x y) {
         trans this:x x
         trans this:y y
       }
       # create an empty instance
       const p (Instance)
       # bind the point class
       p:mute point 1 2

       In  this  example,  when  the  instance  is muted, the preset method is
       called automatically with the extra arguments.

       Instance inference
       The ability to instantiate dynamically inferred instance is offered  by
       the   AFNIX   object model. An instance b is said to be inferred by the
       instance a when the instance a is the super instance of the instance b.
       The  instance  inference  is  obtained by binding the infer symbol to a
       class. When an instance of that class is created, the inferred instance
       is also created.

       # base class A
       const A  (class)
       # inferred class B
       const B  (class)
       const A:infer B
       # create an instance from A
       const  x (A)
       assert B (x:meta)
       assert A (x:super:meta)

       In this example, when the instance is created, the inferred instance is
       also created and returned by  the  instantiation  process.  The  preset
       method is only called for the inferred instance if possible or the base
       instance if there is no inferring class. Because the base preset preset
       method  is not called automatically, the inferred method is responsible
       to do such call.

       trans B:preset (x y) {
         trans this:xb x
         trans this:yb y
         if (== A this:super:meta) (this:super:preset x y)
       }

       Because the class can mute from  one  call  to  another  and  also  the
       inferred   class,   the  preset  method  call  must  be  used  after  a
       discrimination of the meta class has been  made  as  indicated  by  the
       above example.

ADVANCED CONCEPTS

       This  chapter  covers  advanced  concepts  of  the   AFNIX  programming
       language. The first subject is the exception model. The second  subject
       covers some properties of the namesets. Finally, the interpreter object
       is described in details.

       Exception
       An exception is an unexpected change in the execution flow. The   AFNIX
       model  for exception is based on a mechanism which throws the exception
       to be caught by a  handler.  The  mechanism  is  also  designed  to  be
       compatible with the native "C++" implementation.

       Throwing an exception
       An  exception  is  thrown  with  the  reserved  keyword  throw. When an
       exception is thrown, the normal flow of execution is interrupted and an
       object  used  to  carry  the  exception  information  is  created. Such
       exception object is propagated backward in  the  call  stack  until  an
       exception handler catch it.

       if (not (number-p n))
       (throw "type-error" "invalid object found" n)

       The  example above is the general form to throw an exception. The first
       argument is the the exception id. The second argument is the  exception
       reason.  The  third  argument is the exception object. The exception id
       and reason are always a string. The exception object can be any  object
       which is carried by the exception. The reserved keyword throw accepts 0
       or more arguments.

       throw
       throw "type-error"
       throw "type-error" "invalid argument"

       With 0 argument, the exception is thrown with the exception id  set  to
       "user-exception".  With one argument, the argument is the exception id.
       With 2 arguments, the exception id and reason are  set.  Within  a  try
       block,  an  exception can be thrown again by using the exception object
       represented with the what symbol.

       try {
         ...
       } {
         println "exception caught and re-thrown"
         throw what
       }

       Exception handler
       The reserved keyword try executes a form and catch an exception if  one
       has been thrown. With one argument, the form is executed and the result
       is the result of the form execution unless an exception is  caught.  If
       an  exception  is  caught,  the  result is the exception object. If the
       exception is a native one, the result is nil.

       try (+ 1 2)
       try (throw)
       try (throw "hello")
       try (throw "hello" "world")
       try (throw "hello" "world" "folks")

       In its second form, the try reserved keyword can accept a  second  form
       which  is  executed  when  an exception is caught. When an exception is
       caught, a new nameset is created and the special symbol what is bounded
       with  the  exception  object. In such environment, the exception can be
       evaluated. The  what:eid  qualified  name  is  the  exception  id.  The
       what:reason  qualified  name is the exception reason and what:object is
       the exception object.

       try (throw "hello")
       (eval what:eid)
       try (throw "hello" "world")
       (eval what:reason)
       try (throw "hello" "world" 2000)
       (eval what:object)

       Exceptions are useful to notify abruptly  that  something  went  wrong.
       With  an  untyped  language  like   AFNIX  ,  it  is  also a convenient
       mechanism to abort an expression call if some arguments  do  not  match
       the expected types.

       # protected factorial
       const fact (n) {
         if (not (integer-p n))
         (throw "number-error" "invalid argument in fact")
         if (== n 0) 1 (* n (fact (- n 1)))
       }
       try (fact 5) 0
       try (fact "hello") 0

       Nameset
       A  nameset  is  created  with  the  reserved  keyword  nameset. Without
       argument, the  nameset  reserved  keyword  creates  a  nameset  without
       setting  its  parent.  With  one argument, a nameset is created and the
       parent set with the argument.

       const nset (nameset)
       const nset (nameset ...)

       Default namesets
       When a nameset is created, the symbol . is  automatically  created  and
       bound  to  the  newly  created nameset. If a parent nameset exists, the
       symbol .. is also automatically created. The use of the current nameset
       is  a useful notation to resolve a particular name given a hierarchy of
       namesets.

       trans a 1 # 1
       block {
         trans   a (+ a 1) # 2
         println ..:a 1    # 1
       }
       println a           # 1

       Nameset and inheritance
       When a nameset is  set  as  the  super  object  of  an  instance,  some
       interesting  results  are obtained. Because symbols are resolved in the
       nameset hierarchy, there is no limitation to use a nameset to  simulate
       a  kind of multiple inheritance. The following example illustrates this
       point.

       const   cls (class)
       const   ins (cls)
       const   ins:super (nameset)
       const   ins:super:value 2000
       const   ins:super:hello "hello world "
       println ins:hello ins:value # hello world 2000

       Delayed Evaluation
       The  AFNIX  engine provides a mechanism called delayed evaluation. Such
       mechanism permits the encapsulation of a form to be evaluated inside an
       object called a promise.

       Creating a promise
       The reserved keyword delay creates  a  promise.  When  the  promise  is
       created,  the  associated  object is not evaluated. This means that the
       promise evaluates to itself.

       const a (delay (+ 1 2))
       promise-p a # true

       The previous example creates a promise and store the argument form. The
       form  is not yet evaluated. As a consequence, the symbol a evaluates to
       the promise object.

       Forcing a promise
       The reserved keyword force  the  evaluation  of  a  promise.  Once  the
       promise has been forced, any further call will produce the same result.
       Note also that, at this stage, the promise evaluates to  the  evaluated
       form.

       trans   y 3
       const   l ((lambda (x) (+ x y)) 1)
       assert  4 (force l)
       trans   y 0
       assert  4 (force l)

       Enumeration
       Enumeration,  that  is,  named  constant  bound  to  an  object, can be
       declared with the reserved keyword enum. The enumeration is built  with
       a list of literal and evaluated as is.

       const  e    (enum E1 E2 E3)
       assert true (enum-p e)

       The  complete  enumeration  evaluates  to  an  Enum object. Once built,
       enumeration item evaluates by literal and returns an Item object.

       assert true   (item-p e:E1)
       assert "Item" (e:E1:repr)

       Items are comparable objects. Only items can be compared. For  a  given
       item,  the source enumeration can be obtained with the get-enum method.

       # check for item equality
       const i1 e:E1
       const i2 e:E2
       assert true  (i1:== i1)
       assert false (== i1 i2)
       # get back the enumeration
       assert true (enum-p (i1:get-enum))

       Logger
       The Looger class is a message logger that stores messages in  a  buffer
       with  a  level.  The  default  level  is  the level 0. A negative level
       generally indicates a warning or an error message but this  is  just  a
       convention  which  is not enforced by the class. A high level generally
       indicates a less important  message.  The  messages  are  stored  in  a
       circular  buffer.  When  the  logger is full, a new message replace the
       oldest one. By default, the logger is initialized with a  256  messages
       capacity that can be resized.

       const log    (Logger)
       assert true  (logger-p log)

       When  a  message is added, the message is stored with a timestamp and a
       level. The timestamp is used later to  format  a  message.  The  length
       method  returns  the  number of logged messages. The get-message method
       returns a message by index. Because the system operates with a circular
       buffer, the get-message method manages the indexes in such way that the
       old messages are accessible with the oldest index.  For  example,  even
       after  a  buffer  circulation,  the  index  0  will point to the oldest
       message. The get-message-level returns the message level and  the  get-
       message-time returns the message posted time.

       const mesg (log:get-message 0)

       In  term  of  usage,  the logger facility can be conveniently used with
       other derived classes. The standard i/o module provides several classes
       that permits to manage logging operations in a convenient way.

       Interpreter
       The   AFNIX  interpreter is by itself a special object with specialized
       methods  which  do  not  have  equivalent  using  the  standard   AFNIX
       notation.  The  interpreter  is always referred with the special symbol
       interp. The following table is a summary of  the  symbols  and  methods
       bound to the interpreter.

       Symbol          Description
       argv            Command arguments vector
       os-name         Operating system name
       os-type         Operating system type
       version         Full afnix version
       program-name    Interpreter program name
       major-version   Major version number
       minor-version   Minor version number
       patch-version   Patch version number
       afnix-uri       Official uri name
       load            Load a file and execute it
       launch          Launch a normal thread
       daemon          Launch a daemon thread
       library         Load and initialize a library
       set-epsilon     Set real number precision
       get-epsilon     Set real number precision

       Arguments vector
       The  interp:argv  qualified name evaluates to a vector of strings. Each
       argument is stored in the vector during the interpreter initialization.

       zsh> axi hello world
       (axi) println (interp:argv:length) # 2
       (axi) println (interp:argv:get 0)  # hello

       Interpreter version
       Several  symbols  can  be used to track the interpreter version and the
       operating system. The full  version  is  bound  to  the  interp:version
       qualified  name.  The  full version is composed of the major, minor and
       patch number. The operating system name is bound to the qualified  name
       interp:os-name.  The  operating  system type is bound to the interp:os-
       type.

       println "major version number   : " interp:major-version
       println "minor version number   : " interp:minor-version
       println "patch version number   : " interp:patch-version
       println "interpreter version    : " interp:version
       println "operating system name  : " interp:os-name
       println "operating system type  : " interp:os-type
       println "afnix official url     : " interp:afnix-url

       File loading
       The interp:load method  loads  and  execute  a  file.  The  interpreter
       interactive  command  session  is suspended during the execution of the
       file. In case of error or if an exception is raised, the file execution
       is  terminated. The process used to load a file is governed by the file
       resolver. Without extension, a compiled file is searched first  and  if
       not found a source file is searched.

       Library loading
       The   interp:library  method  loads  and  initializes  a  library.  The
       interpreter maintains a list of opened library. Multiple  execution  of
       this  method  for the same library does nothing. The method returns the
       library object.

       interp:library "afnix-sys"
       println "random number: " (afnix:sys:get-random)

       Interpreter duplication
       The interpreter can be duplicated with the  help  of  the  dup  method.
       Without  argument,  a  clone  of  the current interpreter is made and a
       terminal object is attached to it. When used in  conjunction  with  the
       roll   method,   this   approach   permits  to  create  an  interactive
       interpreter. The dup method also accepts a terminal object.

       # duplicate the interpreter
       const si (interp:dup)
       # change the primary prompt
       si:set-primary-prompt "(si)"

       Interpreter loop
       The interpreter loop can be run with the roll.  The  loop  operates  by
       reading  the  interpreter  input  stream.  If  the interpreter has been
       cloned with the  help  of  the  dup  method,  this  method  provides  a
       convenient way to operate in interactive mode. The method is not called
       loop because it is a reserved keyword  and  starting  a  loop  is  like
       having the ball rolling.

       # duplicate the interpreter
       const si (interp:dup)
       # loop with this interpreter
       si:roll

THREADS OPERATIONS

       This  chapter  covers  the  threads  facilities  builtin  in the  AFNIX
       interpreter.  The  thread  subsystem  allows  for  the   execution   of
       concurrent forms with an automatic synchronization mechanism. Designing
       a good program with concurrent execution is a difficult task. It  takes
       a  while  to get used with the various synchronization mechanisms which
       ensure a safe execution, that  is  no  race  condition  or  dead  lock.
       Fortunately,   AFNIX   provides  some  unique features that should ease
       such design.

       Normal and daemon threads
       The interpreter supports two types of threads, called normal and daemon
       threads. A normal thread is started with the reserved keyword launch. A
       daemon  thread  is  started  with  the  reserved  keyword  daemon.  The
       difference  between  a normal thread and a daemon thread is only in the
       termination of the interpreter. An  AFNIX  program  is  completed  when
       all  normal  threads have terminated. This means that the master thread
       (i.e the first thread) is suspended until all normal threads have  been
       executed.  With  daemon  threads,  the master thread terminates even if
       some daemon threads are still running.

       Starting a normal thread
       A normal thread is started with the reserved keyword launch.  The  form
       to  execute in a thread is the argument. The simplest thread to execute
       is the nil thread.

       launch (nil)

       Even the nil thread does nothing in term of computation, it does a  lot
       of things internally by turning on the shared objects subsystem.

       Thread object and result
       When a thread terminate, the thread object holds the result of the last
       executed form. The thread object is returned by the  launch  or  daemon
       command. The thread-p predicates returns true if the object is a thread
       descriptor. The thread type can be check with the normal-p or  daemon-p
       predicates.

       const thr (launch (nil))
       println   (thread-p thr) # true
       println   (thr:normal-p) # true

       The  member  data  result  of the thread object holds the result of the
       thread. Although the result can be accessed at any time,  the  returned
       value will be nil until the thread as completed its execution.

       const thr (launch (nil))
       println   (thr:result)   # nilp

       Although  the   AFNIX   engine will ensure that the result is nil until
       the thread has completed its execution, it does not mean that it  is  a
       reliable  approach  to  test  until  the  result is not nil. The engine
       provides various mechanisms to synchronize a thread and eventually wait
       for its completion.

       Shared objects
       The whole purpose of using a multi-threaded environment is to provide a
       concurrent execution with  some  shared  variables.  Although,  several
       threads  can execute concurrently without sharing data, the most common
       situation is that one or more global variable are accessed -- and  even
       changed  -- by one or more threads. Various scenarios are possible. For
       example, a variable is changed by one thread,  the  other  thread  just
       read  its  value. Another scenario is one read, multiple write, or even
       more complicated, multiple read and multiple write. In  any  case,  the
       interpreter subsystem must ensure that each objects are in a good state
       when such operation do occur. The  AFNIX  engine provides an  automatic
       synchronization mechanism for global objects, where only one thread can
       modify an object, but several thread can read it. This mechanism  known
       as  read-write  locking  guarantees  that there is only one writer, but
       eventually multiple reader. When a thread start to modify an object, no
       other  thread  are  allowed  to  read  or  write  this object until the
       transaction has been completed. On the opposite, no thread  is  allowed
       to  change  (i.e. write) an object, until all thread which access (i.e.
       read) the object  value  have  completed  the  transaction.  Because  a
       context  switch  can  occur  at any time, the object read-write locking
       will ensure a safe protection during  each  concurrent  access.  Shared
       objects  can be very complicated to detect. For example, if a vector is
       shared by various threads, the engine will make sure  that  all  vector
       objects  are  also  shared.  A  closed  variable  in  a lambda or gamma
       expression is another example of  potential  shared  object.  Executing
       such  lambda  form  in  a  thread  will  automatically  mark the closed
       variables as shared objects. Additionally, when the  thread  system  is
       started, all object in the global nameset are marked shared.

       Shared object predicate
       The  object  predicate  method  shared-p  returns  true if an object is
       shared. Since all global objects are  marked  shared  as  soon  as  the
       thread  system  is  turned  on,  the  following example shows how a nil
       thread marks a shared variable.

       # create simple symbol
       const  a     1
       assert false (a:shared-p)
       # turn on the thread system
       launch (nil)
       assert true (a:shared-p)
       # check another symbol
       trans b 1
       assert true (b:shared-p)

       When an object is marked shared, it will remain in this state for  rest
       of   the  session.  Note  that  when  an  object  is  copied  (by  copy
       construction), the shared state is not copied. The copied  object  will
       become shared depending on its surrounding context. Such context can be
       a nameset or any other type of container which is shared or not.

       Shared protection access
       We illustrate the previous discussion with an interesting  example  and
       some  variations  around  it.  Let’s  consider a form which increase an
       integer object and another form which decrease the same integer object.
       If  the  integer  is  initialized  to  0,  and the two forms run in two
       separate threads, we might expect to see the value bounded by the  time
       allocated for each thread. In other word, this simple example is a very
       good illustration of your machine scheduler.

       # shared variable access
       const var 0
       # increase method
       const incr nil (while true
         (println "increase: " (var:= (+ var 1))))
       # decrease method
       const decr nil (while true
         (println "decrease: " (var:= (- var 1))))
       # start both threads
       launch (decr)
       launch (incr)

       In the previous example, var is  initialized  to  0.  The  incr  thread
       increments  var  while the decr thread decrements var. Depending on the
       operating system, the result stays bounded within a certain range.  The
       previous  example  can  be  changed by using the main thread or a third
       thread to print the variable value. The end result is the same,  except
       that there is more threads competing for the shared variable.

       # shared variable access
       const var 0
       # incrementer, decrementer and printer
       const incr nil (while true (var:= (+ var 1)))
       const decr nil (while true (var:= (- var 1)))
       const prtv nil (while true (println "value = " var)
         # start all threads
         launch (decr)
         launch (incr)
         launch (prtv)

       Synchronization
       Although,   AFNIX   provides an automatic synchronization mechanism for
       reading or writing an object, it is sometimes necessary to control  the
       execution  flow.  There  are  basically two techniques to do so. First,
       protect a form from being executed by several threads. Second, wait for
       one  or several threads to complete their task before going to the next
       execution step.

       Form synchronization
       The reserved keyword sync can be used to synchronize  a  form.  When  a
       form,  is  synchronized,  the   AFNIX   engine guarantees that only one
       thread will execute this form.

       const print-message (code mesg) (
         sync {
           errorln "error  : " code
           errorln "message: " mesg
         }
       )

       The previous example create a gamma expression  which  make  sure  that
       both  the  error  code and error message are printed in one group, when
       several threads call it.

       Thread completion
       The other piece of synchronization is the thread completion  indicator.
       The  thread  descriptor contains a method called wait which suspend the
       calling thread until the thread attached to  the  descriptor  has  been
       completed.  If  the  thread  is  already  completed, the method returns
       immediately.

       # simple flag
       const flag false
       # simple shared tester
       const ftest (val) (flag) (assert val (flag:shared-p))
       # no thread mean not shared
       ftest false
       # in a thread it is shared
       const thr (launch (ftest true))
       thr:wait
       assert true (flag:shared-p)

       This example is taken from the test suites. It  checks  that  a  closed
       variable  becomes  shared when started in a thread. Note the use of the
       wait method to make sure the thread has completed before  checking  for
       the  shared  flag.  It  is  also  worth to note that wait is one of the
       method which guarantees that a thread result is valid. Another  use  of
       the  wait  method  can be made with a vector of thread descriptors when
       one wants to wait until all of them have completed.

       # shared vector of threads descriptors
       const thr-group (Vector)
       # wait until all threads in the group are finished
       const wait-all nil (for (thr) (thr-group) (thr:wait))

       Complete example
       We illustrate the previous discussion with a complete example. The idea
       is  to  perform a matrix multiplication. A thread is launched when when
       multiplying one line with one column.  The  result  is  stored  in  the
       thread  descriptor.  A vector of thread descriptor is used to store the
       result.

       # initialize the shared library
       interp:library "afnix-sys"
       # shared vector of threads descriptors
       const thr-group (Vector)
       # this procedure waits until all threads in
       # the group are finished
       const wait-all nil (for (thr) (thr-group) (thr:wait))
       # this procedure initialize a matrix with random numbers
       # the matrix is a square one with its size as an argument
       const init-matrix (n) {
         trans i (Integer 0)
         const m (Vector)
         do {
           trans v (m:append (Vector))
           trans j (Integer)
           do {
             v:append (afnix:sys:get-random)
           } (< (j:++) n)
         } (< (i:++) n)
         eval m
       }
       # this procedure multiply one line with one column
       const mult-line-column (u v) {
         assert (u:length) (v:length)
         trans result 0
         for (x y) (u v) (result:+= (* x y))
         eval result
       }
       # this procedure multiply two vectors assuming one
       # is a line and one is a column coming from the matrix
       const mult-matrix (mx my) {
         for (lv) (mx) {
           assert true (vector-p lv)
           for (cv) (my) {
             assert true (vector-p cv)
             thr-group:append (launch (mult-line-column lv cv))
           }
         }
       }
       # check for some arguments
       # note the use of errorln method
       if (== 0 (interp:argv:length)) {
         errorln "usage: axi 0607.als size"
         afnix:sys:exit 1
       }
       # get the integer and multiply
       const n (Integer (interp:argv:get 0))
       mult-matrix (init-matrix n) (init-matrix n)
       # wait for all threads to complete
       wait-all
       # make sure we have the right number
       assert (* n n) (thr-group:length)

       Condition variable
       A condition  variable  is  another  mechanism  to  synchronize  several
       threads.  A  condition  variable is modeled with the Condvar object. At
       construction, the condition variable is initialized to false. A  thread
       calling  the  wait  method will block until the condition becomes true.
       The mark method can be used by a  thread  to  change  the  state  of  a
       condition  variable and eventually awake some threads which are blocked
       on it. The following example shows how the  main  thread  blocks  until
       another change the state of the condition.

       # create a condition variable
       const cv (Condvar)
       # this function runs in a thread - does some computation
       # and mark the condition variable
       const do-something nil {
         # do some computation
         ....
         # mark the condition
         cv:mark
       }
       # start some computation in a thread
       launch (do-something)
       # block until the condition is changed
       cv:wait-unlock
       # continue here

       In  this  example,  the condition variable is created at the beginning.
       The thread is started and the  main  thread  blocks  until  the  thread
       change the state of the condition variable. It is important to note the
       use of the wait-unlock method.  When  the  main  thread  is  re-started
       (after  the  condition  variable has been marked), the main thread owns
       the lock associated with the condition variable. The wait-unlock method
       unlocks that lock when the main thread is restarted. Note also that the
       wait-unlock method reset the condition variable. if the wait method was
       used  instead  of wait-unlock the lock would still be owned by the main
       thread. Any attempt by other thread  to  call  the  mark  method  would
       result  in  the calling thread to block until the lock is released. The
       Condvar class has several methods which can  be  used  to  control  the
       behavior  of  the  condition variable. Most of them are related to lock
       control. The reset method reset the condition variable.  The  lock  and
       unlock control the condition variable locking. The mark, wait and wait-
       unlock method controls the synchronization among several threads.

REGULAR EXPRESSIONS

       This chapter covers the  AFNIX  regular expressions or regex syntax and
       programming  use.  The  AFNIX  regex is an original implementation with
       its own syntax and execution model.

       Regular expression syntax
       AFNIX  implements a regular  expression  engine  via  a  special  Regex
       object. A regular expression can be built implicitly or explicitly with
       the use of the Regex  object.  The  regex  syntax  uses  the  [  and  ]
       characters as block delimiters. When used in a source file, the lexical
       analyzer  automatically  recognizes  a  regex  and  built  the   object
       accordingly.  In  other word, the regex system is builtin in the  AFNIX
       language. The following example shows two equivalent way to define  the
       same regex expression.

       # syntax builtin regex
       (== [$d+] 2000)         # true
       # explicit builtin regex
       (== (Regex "$d+") 2000) # true

       In  its  first  form,  the  [  and  ]  characters  are  used  as syntax
       delimiters. The lexical analyzer automatically recognizes this token as
       a  regex  and built the equivalent Regex object. The second form is the
       explicit construction of the Regex object. Note also that the [  and  ]
       characters are also used as regex block delimiters.

       Regex characters and meta-characters
       Any character, except the one used as operators can be used in a regex.
       The $ character is used as a meta-character -- or control character  --
       to represent a particular set of characters. For example, [hello world]
       is a regex which match only the "hello world" string. The  [$d+]  regex
       matches  one  or more digits. The following meta characters are builtin
       in the regex engine.

       Character   Description
       $a          matches any letter or digit
       $b          matches any blank characters
       $d          matches any digit
       $e          matches eol, cr and eof
       $l          matches any lower case letter
       $n          matches eol or cr
       $s          matches any letter
       $u          matches any upper case letter
       $v          matches any valid afnix constituent
       $w          matches any word constituent
       $x          matches any hexadecimal characters

       The uppercase version is the complement of the corresponding  lowercase
       character set.

       Character   Description
       $A          any character except letter or digit
       $B          any character except blank characters
       $D          any character except digit
       $E          any character except eol, cr and eof
       $L          any character except lower case letter
       $N          any character except eol or cr
       $S          any character except letter
       $U          any character except upper case letter
       $V          any character except afnix constituent
       $W          any character except word constituent
       $X          any character except hex characters

       A  character  which  follows  a  $  character  and  that  is not a meta
       character is treated as a normal character. For example  $[  is  the  [
       character.  A  quoted  string  can be used to define character matching
       which could otherwise be interpreted as control characters or operator.
       A quoted string also interprets standard escaped sequences but not meta
       characters.

       (== [$d+]   2000) # true
       (== ["$d+"] 2000) # false

       Regex character set
       A character set is defined with the < and >  characters.  Any  enclosed
       character  defines  a character set. Note that meta characters are also
       interpreted inside a character set. For example, <$d+-> represents  any
       digit  or a plus or minus. If the first character is the ^ character in
       the character set, the character set is complemented  with  regards  to
       its definition.

       Regex blocks and operators
       The  [  and ] characters are the regex sub-expressions delimiters. When
       used at the top level of a  regex  definition,  they  can  identify  an
       implicit  object.  Their use at the top level for explicit construction
       is optional. The following example is strictly equivalent.

       # simple real number check
       const real-1 (Regex "$d*.$d+")
       # another way with [] characters
       const real-2 (Regex "[$d*.$d+]")

       Sub-expressions can be nested -- that’s their role -- and combined with
       operators. There is no limit in the nesting level.

       # pair of digit testing
       (== [$d$d[$d$d]+] 2000)  # true
       (== [$d$d[$d$d]+] 20000) # false

       The  following  unary  operators  can  be  used  with single character,
       control characters and sub-expressions.

       Operator   Description
       *          match 0 or more times
       +          match 1 or more times
       ?          match 0 or 1 time
       |          alternation

       Alternation is an operator which work with a secondary expression. Care
       should  be taken when writing the right sub-expression. For example the
       following regex [$d|hello] is  equivalent  to  [[$d|h]ello].  In  other
       word,  the  minimal  first  sub-expression  is  used when compiling the
       regex.

       Grouping
       Groups of sub-expressions are created with the ( and ) characters. When
       a  group  is matched, the resulting sub-string is placed on a stack and
       can be used later. In this respect, the regex engine  can  be  used  to
       extract  sub-strings. The following example extracts the month, day and
       year from a particular date  format:  [($d$d):($d$d):($d$d$d$d)].  This
       regex assumes a date in the form mm:dd:yyyy.

       if (== (const re [($d$d):($d$d)]) "12:31") {
         trans hr (re:get 0)
         trans mn (re:get 1)
       }

       Grouping  is  the  mechanism  to  retrieve  sub-strings when a match is
       successful. If the regex is bound to a symbol, the get  method  can  be
       used to get the sub-string by index.

       Regex object
       Although  a regex can be built implicitly, the Regex object can also be
       used to build a new regex. The argument is a string which  is  compiled
       during the object construction.

       Literal object
       A  Regex  object  is  a  literal  object. This means that the to-string
       method is available and that a call to the println  special  form  will
       work directly.

       const   re (Regex "$d+")
       println re           # $d+
       println re:to-string # [$d+]

       Regex operators
       The  ==  and  != operators are the primary operators to perform a regex
       match. The == operator returns true if the  regex  matches  the  string
       argument from the beginning to the end of string. Such operator implies
       the begin and end of string anchoring. The < operator returns  true  if
       the regex matches the string or a substring or the string argument.

       Regex methods
       The  primary  regex method is the get method which returns by index the
       sub-string when a group has been matched. The length method returns the
       number of group match.

       if (== (const re [($d$d):($d$d)]) "12:31") {
         re:length # 2
         re:get 0  # 12
         re:get 1  # 31
       }

       The  match  method  returns  the  first  string which is matched by the
       regex.

       const regex [$d+]
       regex:match "Happy new year 2000" # 2000

       The replace method any occurrence  of  the  matching  string  with  the
       string argument.

       const regex [$d+]
       regex:replace "Hello year 2000" "3000" # hello year 3000

       Argument conversion
       The use of the Regex operators implies that the arguments are evaluated
       as literal object. For this reason, an implicit  string  conversion  is
       made  during such operator call. For example, passing the integer 12 or
       the string "12" is strictly equivalent. Care should be taken when using
       this implicit conversion with real numbers.

FUNCTIONAL PROGRAMMING

       This  chapter covers the interesting aspects of  AFNIX  with respect to
       the functional programming paradigm. Functional  programming  is  often
       described as the ability to create functions that creates functions. As
       a matter of fact, it is a far bigger subject that finds its root in the
       lambda  calculus.  A  language  --  like   AFNIX   -- that supports the
       functional programming paradigm is also sometimes called a  high  order
       language.

       Function expression
       A  lambda  expression or a gamma expression can be seen like a function
       object with no name. During  the  evaluation  process,  the  expression
       object  is  evaluated as well as the arguments -- from left to right --
       and a result is produced by applying those arguments  to  the  function
       object.  An  expression  can  be  built  dynamically  as  part  of  the
       evaluation process.

       (axi) println ((lambda (n) (+n 1)) 1)
       2

       The difference between a lambda expression and a  gamma  expression  is
       only  in  the nameset binding during the evaluation process. The lambda
       expression nameset is linked with the  calling  one,  while  the  gamma
       expression  nameset  is  linked  with the top level nameset. The use of
       gamma expression is particularly interesting with  recursive  functions
       as  it  can  generate  a  significant  execution  speedup. The previous
       example will behaves the same with a gamma expression.

       (axi) println ((gamma (n) (+n 1)) 1)
       2

       Self reference
       When combining a function expression with recursion, the need  for  the
       function  to  call  itself  is  becoming  a problem since that function
       expression does not have a name. For this reason,  AFNIX  provides  the
       reserved  keyword  self that is a reference to the function expression.
       We illustrate this capability with the well-known factorial  expression
       written in pure functional style.

       (axi) println ((gamma (n)
           (if (<= n 1) 1 (* n (self (- n 1))))) 5)
       120

       The use of a gamma expression versus a lambda expression is a matter of
       speed. Since the gamma expression does not  have  free  variables,  the
       symbol resolution is not a concern here.

       Closed variables
       One of the  AFNIX  characteristic is the treatment of free variables. A
       variable is said to be free if  it  is  not  bound  in  the  expression
       environment  or  its children at the time of the symbol resolution. For
       example, the expression ((lambda (n) (+ n x)) 1) computes  the  sum  of
       the  argument  n with the free variable x. The evaluation will succeeds
       if x is defined in one of the parent environment. Actually this example
       can  also  illustrates the difference between a lambda expression and a
       gamma expression. Let’s consider the following forms.

       trans x 1
       const do-print nil {
         trans x 2
         println ((lambda (n) (+ n x)) 1)
       }

       The gamma expression do-print will produce 3 since it sums the argument
       n  bound to 1, with the free variable x which is defined in the calling
       environment as 2. Now if we rewrite the previous example with  a  gamma
       expression  the result will be one, since the expression parent will be
       the top level environment that defines x as 1.

       trans x 1
       const do-print nil {
         trans x 2
         println ((gamma (n) (+ n x)) 1)
       }

       With this example, it is easy to see that there is a need to be able to
       determine a particular symbol value during the expression construction.
       Doing so is  called  closing  a  variable.  Closing  a  variable  is  a
       mechanism  that  binds  into  the expression a particular symbol with a
       value and such symbol is called a closed variable, since its  value  is
       closed  under  the  current  environment  evaluation.  For example, the
       previous example can be rewritten to close the symbol x.

       trans x 1
       const do-print nil {
         trans x 2
         println ((gamma (n) (x) (+ n x)) 1)
       }

       Note that the list of closed variable immediately follow  the  argument
       list. In this particular case, the function do-print will print 3 since
       x has been closed with the value 2 has  defined  in  the  function  do-
       print.

       Dynamic binding
       Because  AFNIX  has a dynamic binding symbol resolution, it is possible
       to have under some circumstances a free or closed variable.  This  kind
       of  situation  can  happen  when a particular symbol is defined under a
       condition.

       lambda (n) {
         if (<= n 1) (trans x 1)
         println (+ n x)
       }

       With this example, the symbol x is a free variable if the argument n is
       greater  than  1. While this mechanism can be powerful, extreme caution
       should be made when using such  feature.  Note  also  that  many  other
       language  do  not allow this kind of behavior. That kind of restriction
       is primarily driven by the need to have a language with static binding.
       The  bad news is that it is impossible to write a compiler with dynamic
       symbol binding.

       Functional objects
       Everything in  AFNIX  is an object. As a consequence, an object can  be
       manipulated, even if it is lexical element, a symbol or a closure.

       Lexical and qualified names
       The  basic  forms elements are the lexical and qualified names. Lexical
       and qualified names are constructed by the  AFNIX  reader. Although the
       evaluation process make that lexical object transparent, it is possible
       to manipulate them directly.

       (axi) const sym (protect lex)
       (axi) println   (sym:repr)
       Lexical

       In this example, the protect reserved keyword  is  used  to  avoid  the
       evaluation  of  the  lexical object named lex. Therefore the symbol sym
       refers to a lexical object. Since a  lexical  --  and  a  qualified  --
       object  is  a also a literal object, the println reserved function will
       work and print the object name. In fact, a literal object provides  the
       to-string  method  that  returns the string representation of a literal
       object.

       (axi) const sym (protect lex)
       (axi) println   (sym:to-string)
       lex

       Symbol and argument access
       Each nameset maintains a table  of  symbols.  A  symbol  is  a  binding
       between  a name and an object. Eventually, the symbol carries the const
       flag. During the lexical evaluation process, the lexical  object  tries
       to find an object in the nameset hierarchy. Such object can be either a
       symbol or an argument. Again, this process is transparent, but  can  be
       controlled  manually.  Both lexical and qualified named object have the
       map method that returns the first  object  associated  in  the  nameset
       hierarchy.

       (axi) const obj 0
       (axi) const lex (protect obj)
       (axi) const sym (lex:map)
       (axi) println   (sym:repr)
       Symbol

       A  symbol  is  also  a  literal object, so the to-string and to-literal
       methods will return the symbol name. Symbol  methods  are  provided  to
       access  or  modify the symbol values. It is also possible to change the
       const symbol flag with the set-const method.

       (axi) println (sym:get-const)
       true
       (axi) println (sym:get-object)
       0
       (axi) sym:set-object true
       (axi) println (sym:get-object)
       true

       A symbol name cannot be modified, since the name must  be  synchronized
       with  the  nameset  association.  On  the  other  hand, a symbol can be
       explicitly constructed. As any object, the = operator can  be  used  to
       assign  a  symbol  value. The operator will behaves like the set-object
       method.

       (axi) const sym (Symbol "symbol")
       (axi) println sym
       symbol
       (axi) sym:= 0
       (axi) println (eval sym)
       0

       Closure
       As an object, the Closure can be manipulated  outside  the  traditional
       declarative  way.  A closure is a special object that holds an argument
       list, a set of closed variables and a form to execute. The mechanic  of
       a closure evaluation has been described earlier. What we are interested
       here is the ability to manipulate a closure as an object and eventually
       modify  it.  Note  that by default a closure is constructed as a lambda
       expression. With a boolean argument set to  true  the  same  result  is
       obtained. With false, a gamma expression is created.

       (axi) const f (Closure)
       (axi) println (closure-p f)
       true

       This   example  creates  an  empty  closure.  The  default  closure  is
       equivalent to the trans f nil nil. The same can be obtained with  const
       f  (Closure  true).  For  a  gamma  expression, the following forms are
       equivalent, const f (Closure false) and const f nil nil. Remember  that
       it  is  trans and const that differentiate between a lambda and a gamma
       expression. Once the closure object is defined, the set-form method can
       be used to bind a form.

       # the simple way
       trans f nil (println "hello world")
       # the complex way
       const f    (Closure)
       f:set-form (protect (println "hello world"))

       There are numerous situations where it is desirable to mute dynamically
       a closure expression. The simplest one is the closure that mute  itself
       based  on  some context. With the use of self, a new form can be set to
       the one that is executed. Another use is a mechanism call advice, where
       some  new  computation  are  inserted prior the closure execution. Note
       that appending to a closure can lead to some  strange  results  if  the
       existing  closure  expression  uses  return  special forms. In a multi-
       threaded environment, the ability to change  a  closure  expression  is
       particularly  handy.  For  example  a  special  thread could be used to
       monitor some  context.  When  a  particular  situation  develops,  that
       threads  might  trigger  some  closure  expression  changes.  Note that
       changing a closure expression does not affect the one that is executed.
       If such change occurs during a recursive call, that change is seen only
       at the next call.

LIBRARIAN AND RESOLVER

       This chapter covers the use of the axl librarian utility as well as the
       Librarian object. The file path resolver is also described as a mean to
       search for a particular file to execute in a program.

       Librarian object
       A librarian file is a special  file  that  acts  as  a  containers  for
       various  files.  A  librarian  file  is  created with the axl --  AFNIX
       cross librarian -- utility. Once a librarian file is created, it can be
       added  to  the interpreter resolver. The file access is later performed
       automatically by name with the standard interpreter load method.

       Creating a librarian
       The axl utility is the preferred way to create a librarian. Given a set
       of files, axl combines them into a single one.

       zsh: axl -h
       usage: axl [options] [files]
       [-h]      print this help message
       [-v]      print version information
       [-c]      create a new librarian
       [-x]      extract from the librarian
       [-s]      get file names from the librarian
       [-t]      report librarian contents
       [-f] lib  set the librarian file name

       The  -c  option  creates  a  new  librarian. The librarian file name is
       specified with the -f option.

       zsh: axl -c -f librarian.axl file-1.als file-2.als

       The previous command combines file-1.als and file-2.als into  a  single
       file  called  librarian.axl  .  Note that any file can be included in a
       librarian.

       Using the librarian
       Once a librarian is created, the interpreter -i option can be  used  to
       specify  it.  The  -i  option  accepts  either  a  directory  name or a
       librarian file. Once the librarian has  been  opened,  the  interpreter
       load method can be used as usual.

       zsh> axi -i librarian.axl
       (axi) interp:load "file-1.als"
       (axi) interp:load "file-2.als"

       The  librarian  acts like a file archive. The interpreter file resolver
       takes care to extract the file from the librarian when the load  method
       is invoked.

       Librarian contents
       The axl utility provides the -t and -s options to look at the librarian
       contents. The -s option returns all file name in the librarian. The  -t
       option returns a one line description for each file in the librarian.

       zsh: axl -t -f librarian.axl
       --------       1234 file-1.als
       --------       5678 file-2.als

       The one line report contains the file flags, the file size and the file
       name. The file flags are not used at this time. One possible use in the
       future is for example, an auto-load bit or any other useful things.

       Librarian extraction
       The  -x  option permits to extract file from the librarian. Without any
       file argument, all files are extracted. With some file arguments,  only
       those specified files are extracted.

       zsh: axl -x -f librarian.axl
       zsh: axl -x -f librarian.axl file-1.als

       Librarian object
       The  Librarian  object  can  be  used  within  an   AFNIX  program as a
       convenient way to create a collection of files or to  extract  some  of
       them.

       Output librarian
       The  Librarian  object  is  a standard  AFNIX  object. Its predicate is
       librarian-p. Without argument, a librarian is created in  output  mode.
       With a string argument, the librarian is opened in input mode, with the
       file name argument. The output mode is used to create a  new  librarian
       by adding file into it. The input mode is created to read file from the
       librarian.

       # create a new librarian
       const lbr (Librarian)
       # add a file into it
       lbr:add "file-1.als"
       # write it
       lbr:write "librarian.axl"

       The add method adds a new file into the librarian. The write method the
       full librarian as a single file those name is write method argument.

       Input librarian
       With  an  argument, the librarian object is created in input mode. Once
       created, file can be read or extracted. The length method -- which also
       work  with  an  output  librarian -- returns the number of files in the
       librarian. The  exists-p  predicate  returns  true  if  the  file  name
       argument exists in the librarian. The get-names method returns a vector
       of file names in this librarian. The extract method  returns  an  input
       stream object for the specific file name.

       # open a librarian for reading
       const lbr (Librarian "librarian.axl")
       # get the number of files
       println (lbr:length)
       # extract the first file
       const is (lbr:extract "file-1.als")
       # is is an input stream - dump each line
       while (is:valid-p) (println (is:readln))

       Most  of  the  time,  the  librarian  object  is  used  to extract file
       dynamically. Because a librarian is mapped into the memory at the right
       offset,  there is no worry to use big librarian, even for a small file.
       Note that any type of file can be used, text or binaries.

       File resolver
       The  AFNIX file resolver is a special object used by the interpreter to
       resolve  file  path based on the search path. The resolver uses a mixed
       list of directories and librarian files in its search path. When a file
       path  needs  to be resolved, the search path is scanned until a matched
       is found. Because the librarian resolution  is  integrated  inside  the
       resolver, there is no need to worry about file extraction. That process
       is done automatically. The resolver can also be used inside  an   AFNIX
       program to perform any kind of file path resolution.

       Resolver object
       The  resolver object is created without argument. The add method adds a
       directory path or a librarian file to the resolver.  The  valid  method
       checks  for the existence of a file. The lookup method returns an input
       stream object associated with the object.

       # create a new resolver
       const rslv (Resolver)
       assert true (resolver-p rslv)
       # add the local directory on the search path
       rslv:add "."
       # check if file test.als exists
       # if this is ok - print its  contents
       if (rslv:valid-p "test.als") {
         const is (rslv:lookup "test.als")
         while (is:valid-p) (println (is:readln))
       }