Next Previous Contents

4. Declarations and Scope Rules

A Dino program is block structured. Each block introduces a new identifier scope. A block consists of executive statements and declarations and may contain nested blocks. Each identifier used in a program should be declared in a declaration in the program, unless it is a predeclared identifier.

          Block = "{"  StmtList "}"

          StmtList = { Stmt }
        
          Stmt = ExecutiveStmt
               | Declaration
When declaring an identifier, you also specify certain permanent properties of a declaration, such as whether it is a variable, a function, or a class. The identifier is then used to refer to the associated declaration (more correctly with the declaration instance).
          Declaration = VarDeclarations
                      | AccessClause
                      | ExternDeclarations
                      | FuncClassExtDeclaration
                      | IncludeDeclaration
The scope of a declaration is textually from the start (not from the point of declaration!) to the end of the block to which the declaration belongs and hence to which the declaration is local. It excludes the scopes of declarations with the same identifier which are in nested blocks. In a block, a maximum of one declaration of the same identifier is possible.

It is important to understand the notion of instantiation of the declaration. This notion reflects program execution, not the static structure of program. An instance exists in a context. Actually, a context is an execution environment consisting of the covering block instances and/or class objects. A new instance of the block is created when execution of the block starts. There may be more than one instance of the same block, e.g. when the block is a function or class body (in this case the block instance is a class object), or when the block is executed on different threads (parallel execution branches) or when there is a reference to a block instance after its execution. When a new instance of the block starts, all the block declarations are instantiated too. For a variable declaration, it means a new instance of variable is created in the given context. For a function or class declaration, it means that the function or class is bound to the given context.

Example: The following program illustrates a case when a reference to a block instance exists after its execution. The program outputs the result 8.

          var i, f;
        
          for (i = 0; i < 10; i++)
            if (i % 4 == 0)
              {
                var j = i;
                func r () {return j;}
                f = r;
              }
          putln (f ());

Declaration is always either private or public. Private declaration is accessible only inside the declaration scope or inside functions or classes which are declared as friend in the declaration block. A public declaration instance is always accessible when association (see below) of the identifier is successful. By default, [instances of] declarations in a class block are public. In all other places, the (instances of) declarations are private by default. The following constructions are used for declaring an identifier to be public, private, or as friend:

          AccessClause = (public | private | friend) AccessList ";"

          AccessList = IDENT { "," IDENT }
Examples:
          public param1, param2;
          private call_count;
          friend class2;
Association of an identifier and the corresponding declaration instance is performed by the following rules: The following identifiers are predeclared on the top level (in the implicit block covering the whole program). They are described in more detail later in the report.
  anode          argv           atan2
  chdir          chgmod         chomod         chumod
  clock          close          cmpv           context
  cos            curr_thread
  del
  eltype         env            error_anode    errors
  except         excepts        exit           exp
  fatime         fctime         fget           fgetf
  fgetln         fgmode         fgn            file
  flush          fmtime         fomode         fprint
  fprintln       fput           fputln         fscan
  fscanln        fsize          ftype          fumode
  fun
  gc             get            getcwd         getegn
  geteun         getgn          getgroups      getf
  getln          getpid         getun          gmatch
  gsub
  ins            inside         insv           invaccesses
  invcalls       invexterns     invindexes     invkeys
  invops         invparsers     invregexps     isatty
  keys
  log            log10
  main_thread    match          max            min
  mkdir
  nil_anode
  open
  parser         pclose         popen          pow
  print          println        put            putln
  rand           readdir        remove         rename
  rev            rmdir
  scan           scanln         seek           signals
  sin            sort           split          split_regex
  sprint         sprintln       sput           sputln
  sqrt           srand          stderr         stdin
  stdout         strtime        sub            subv
  syserrors      system         systemcalls
  tell           time           time_format    token
  tolower        toupper        trans
  version
The following identifiers are predeclared in the class except mentioned above.
  error
The following identifiers are predeclared in the class error mentioned above.
  deadlock
  invaccess      invcall        invenv         invindex
  invkey         invop
  signal         syncwait
The following identifiers are predeclared in the class signal mentioned above.
  sigabrt        sigfpe         sigill         sigint
  sigsegv        sigterm
The following identifiers are predeclared in the class invop mentioned above.
  optype         opvalue
The following identifiers are predeclared in the class invindex mentioned above.
  indexop        indextype      indexvalue
The following identifiers are predeclared in the class invkey mentioned above.
  keyop          keyvalue
The following identifiers are predeclared in the class invcall mentioned above.
  callop
  eof
  internal       invenvar       invextern      invfmt
  invinput       invparser      invregexp      invresult
  parnumber      partype
  syncthreadcall syserror       systemcall
The following identifiers are predeclared in the class syserror mentioned above.
  eaccess        eagain         ebadf          ebusy
  echild         edeadlk        edom           eexist
  efault         efbig          eintr          einval
  eio            eisdir         emfile         emlink
  enametoolong   enfile         enodev         enoent
  enoexec        enolck         enomem         enospc
  enosys         enotdir        enotempty      enotty
  enxio          eperm          epipe          erange
  erofs          espipe         esrch          exdev
The following identifiers are predeclared in the class systemcall mentioned above.
  noshell
  systemfail
The following identifiers are predeclared in the class invparser mentioned above.
  invgrammar     invtoken
  pmemory
The following identifiers are predeclared in the class invregexp mentioned above.
  badpat
  ebrack         ectype         eend           eescape
  eparen         erange         esize          espace
  esubreg
The following identifiers are predeclared in the class invextern mentioned above.
  libclose
  noextern       noexternsupp
The following identifiers are predeclared in the class invaccess mentioned above.
  accessop       accessvalue
  immutable

4.1 Variable Declarations

Dino is an imperative language. In other words it has variables which are named containers of values. A variable can contain any value. This means that DINO is a dynamically-typed language. The declaration of a variable also may define the initial value of the variable. Assigning of the initial value to the variable instance is made after execution of the previous statements of the block. By default the initial value of variables is the special value nil. The value of the variable can not be changed after its initialization if its declaration contains the keyword final.

          VarDeclarations = var  VarParList ";"

          VarParList = VarPar { "," VarPar }
        
          VarPar = [final] IDENT [ "="  Expr]
Examples:
          var i = 0, j, k;
          var final constant = 10, final nil_constant, l;

4.2 External Declarations

Dino permits to use functions written in other languages, e.g. C. The functions should have special prototypes and must have to access to the DINO standard procedural interface (SPI). Dino can also have access to variables of a special type declared in the source code in another language. The details of the implementation of such features and the DINO SPI are not described here (some details are given in appendix B). As rule, the external functions and variables will be implemented as dynamically loaded libraries. This is the powerful instrument of DINO extension. The external functions and variables are declared after keyword extern. An external function identifier is followed by (). All external declarations (e.g. in different blocks) with the same identifier refer the the same external function or variable.

          ExternDeclarations = extern  ExternItem { "," ExternItem } ";"

          ExternItem = IDENT 
                     | IDENT  "(" ")"
Examples:
          extern function (), variable;

4.3 Functions, Classes, Extensions

A function/class declaration consists of a function/class header and a function/class block (body). The header specifies the function identifier and formal parameters. A function can return the result with the aid of the statement return. If the result value after the keyword return is absent or the return statement is absent or is not executed, the function returns nil by default. A class call returns an object of the class which can be considered as a block instance of the class body. The return-statement for classes must be without a result. Thread-functions are analogous to general functions. The difference is in that a new execution thread is created during the thread-function call, the return-statement inside thread-function must be without an expression, and the thread-function returns the corresponding (execution) thread. The execution thread finishes when the corresponding thread block finishes. Execution threads are executed parallelly. Originally only one thread (called the main thread) exists in a DINO program.

The formal parameters are considered to be declared in a function/class block and to be initialized by values of actual parameters during a call of the function/class. The function can be called with any number of actual parameters. If the number of actual parameters is less than the formal parameters number, the remaining formal parameters are initialized by the special value nil. Otherwise if the number of actual parameters is more than the number of formal parameters, the remaining actual parameter values are ignored. In order to process all actual parameters, you should place ... at the end of the list of formal parameter declarations. This means that the formal parameter with the identifier args will be declared implicitly. The value of the parameter will be a vector whose elements will be the remaining actual parameter values. If the number of actual parameters is less or equal to the number of formal parameters (not taking the implicit parameter args into account), the value of args will be the empty vector. The formal parameter can be initialized by a default value in a way analogous to variable initialization. The initialization is made only when the corresponding actual parameter value is nil.

If a class contains a function with the name destroy, the function will be called when the class object becomes garbage during the garbage collection process or at the end of the program. The function can also be called explicitly if it is declared as public. You should be remember that although the function may have parameters and return a value, the garbage collector (or finishing the program) ignores the result value and does not pass actual parameters. The single exception when the function destroy is not called by finishing the program is the case when memory can not be allocated more. So the values of the parameters will be nil if the function is called by the garbage collector (or finishing the program). You may prevent removing the corresponding object in the function destroy by assigning the object to a variable. It means that the function can be called several times (during several garbage collections) for the same object. But you should also avoid creation of objects during the call of function destroy because it may result in increase of the heap.

Instead of inheritance usually used in object-oriented languages, Dino supports extension. This feature permits to modify function/class behaviour. All code inside an extension body is inserted at the end of body of the function/class declared with the same identifier in the same block in the same order as the extensions are placed in the block. A function/class declared as final can not be extended.

          FuncClassExtDeclaration = Header Block

          Header = [final] FuncThreadClass IDENT FormalParameters
                 | ext IDENT

          FuncThreadClass = func 
                          | thread 
                          | class 
        
          FormalParameters = "("  [ VarParList ] ")"
                           | "("  VarParList "," "..."  ")"
                           | "(" "..."  ")"
Examples:

The following is a parameterless class header:

          class stack ()
The following is a class header with an initialization:
          class stack (max_height = var a = 1, b = 2;

putln (a, " ", b);
a<=>b;
putln (a, " ", b);

var ar = [1, 2, 3];
println (ar);
ar[0]<=>ar[2];
println (ar);

class s (i) {}
var c1 = s (0), c2 = s (3);
putln (c1.i, ' ', c2.i);
c1.i<=>c2.i;
putln (c1.i, ' ', c2.i);

var t = {"s" : 1, "t" : 2};
putln (t{"s"}, ' ', t{"t"});
t{"s"}<=>t{"t"};
putln (t{"s"}, ' ', t{"t"});
100)
The following is a function with a variable number of parameters:
          func print_args (...)
            {
              for (i = 0; i < #args; i++)
                println (args[i]);
            }
The following example illustrates the usage of extensions:
           class point (x = 0, y = 0) {
           }
           ext point {
             class circle (radius = 1) {
               func square () {return 3.14 * radius * radius;}
             }
           }
           ext point {
             ext circle {
               class ellipse (width) {
                 func square () {
                   ...
                 }
               }
           }
The following example is a class with the function destroy:
          var objs_number = 0;
          class obj () {
            private n, destroy;
            var n = objs_number;
            objs_number++;
            func destroy () {objs_number--; objs_number--;}
          }
The following example illustrates threads:
          class buffer (length = 3) {
            var b = [length:nil], first = 0, free = 0, empty = 1;
            private b, first, free, length;
            func consume () {
              var res;
          
              wait (!empty);
              res = b [first];
              first = (first + 1) % length;
              wait (1) empty = first == free;
              return res;
            }
            func produce (val) {
              wait (empty || free != first);
              b [free] = val;
              free = (free + 1) % length;
              wait (1) empty = 0;
            }
          }
          
          thread consumer (buffer) {
              func produce (val) {
                buffer.produce (val);
                put ("produce: ");
                println (val);
              }
              produce (10);
              produce (10.5);
              produce ("string");
              produce ('c');
              produce (nil);
          }
          
          thread producer (buffer) {
            var val;
          
            for (;;) {
              val = buffer.consume ();
              if (val == nil)
                break;
              put ("consume: ");
              println (val);
            }
          }
          
          var queue = buffer ();
          consumer (queue);
          producer (queue);


Next Previous Contents