/*
 *  FormattedIO.sli
 *
 *  This file is part of NEST.
 *
 *  Copyright (C) 2004 The NEST Initiative
 *
 *  NEST is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  NEST is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with NEST.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

%%
%% High-level input output fascilities for sli
%%

%% this file must be run AFTER typeinit and mathematica

%% The i/o facilities model those of Mathematica.
%%

/*BeginDocumentation 
Name: ReadModes - dictionary with type specifiers for read functions
Description:
The following read modes are available:

    /Number   - read a number (as double)
    /Word     - read a whitespace separated string
    /Double   - read a double
    /Integer  - read an integer
    /String   - read a newline separated line

Author: Marc-Oliver Gewaltig
FirstVersion: Jun 17 2000
*/
/ReadModes
  <<
    /Number    /ReadDouble load
    /Word      /ReadWord   load
    /Double    /ReadDouble load
    /Integer   /ReadInt    load
    /String    /getline    load
   >> 
def

/ArrayQ trie [/arraytype] true  addtotrie
             [/anytype]   false addtotrie
def

/LiteralQ trie [/literaltype] true  addtotrie
               [/anytype]   false   addtotrie
def

/*BeginDocumentation
Name: Read - read an object of a certain type from a stream
Synopsis: istream [type] Read -> istream [result] true
                              -> istream false
Example: cin [/Number] Read -> cin [1]
Description:
Author: Marc-Oliver Gewaltig
FirstVersion: Jun 17 2000
*/

/Read
[/istreamtype /arraytype]
{
  mark
  3 1 roll
  ReadModes begin
    {
      LiteralQ {cvn} if
      exec
      {
	exch
      } if
    } forall
    counttomark 1 add 1 roll
    counttomark arraystore
    exch pop    % remove mark
  end
  size 0 gt {true} {pop false} ifelse
} def

/*BeginDocumentation
Name: ReadList - read a list of specified format from a stream
Synopsis: istream [types] ReadList -> [result]
Parameters: istream : an input stream
            [types] : an array with one or more of the following type specifiers:
                      Number - specifies a real number
                      Double - specifies a real number
                      Integer- specifies an integer
                      Word   - a white space separated string
                      String - a text line terminated with a newline character
 
Description: ReadList reads the rest of a file into an array. If [types] contains
             more than one type specifier, the array is constructed from subarray with
             elements of the types, specified by  [types].

 	     ReadList tries to allocate the result array in advance. The option
	     BufferSize can be specified to optimize this behaviour.

             The valid type specifiers are defined in the ReadModes dictionary.              
         
Examples: istream [/Number /Number] ReadList -> [[1 2] [3 4] ...]
          istream [/Number] ReadList -> [1 2 3 4 ...] 
 
References: ReadList approximates the Mathematica function.
Author: Marc-Oliver Gewaltig
FirstVersion: Jun 17 2000
SeeAlso: Read, ReadModes, Export
*/

/ReadList
<< /BufferSize 1000 >> 
Options

/ReadList
[/istreamtype /arraytype]
{
  {cvn} Map % Convert all literals to names, 
  << >> begin
  [] /ReadList /BufferSize GetOption reserve_a
  %istream [result] array
  exch_
  size_a 1 eq
  {
    %istream [result] array
    cvx /read Set
    exch_
    % [result] istream
    ReadModes begin
    {
      read % call the read handler
      {
        % [] istream obj
        3 -1 roll exch_
        append_a
        exch_
      }
      {
        exit
      } ifelse_
    } loop
    pop_
    end
  }
  {
    %istream [] array
    /types Set
    exch_
    {
      types Read
      {
        % [] istream [result] 
        3 -1 roll exch_
        append_a
        exch_
      }
      {
        exit
      } ifelse_
    } loop
    pop_
  } ifelse_
  end
} def

/*BeginDocumentation
Name: SaveDictionary - Save contents of a dictionary to a file.
Synopsis: dict (fname) SaveDictionary -> -
Description: SaveDictionary tries to save the contents of the dicitonary
 to a file. For this, it reproduces the input syntax of the dictionary.
 Thus, the resulting file is a readable ASCII-file. 
 
 SaveDictionary only processes those definitions which have a
 keyboard input form. Currently, the following types are saved
   - integertype
   - doubletype
   - stringtype
   - arraytype
SeeAlso: RestoreDictionary, MergeDictionary
*/

/SaveDictionary
[/dictionarytype /stringtype]
{
  (w) file %
  exch
  cva 2 2 Partition
  {
    arrayload pop
    :out
  } forall
  close
} def

/_:out
{
    rollu
    cvlit <--
    ( ) <-
    exch <--
    ( def \n) <-
} def
  
/:out [/ostreamtype /nametype /anytype] {2 npop} def
/:out [/ostreamtype /nametype /doubletype] 
{
    rollu
    cvlit <--
    ( ) <-
    showpoint exch <-
    ( def \n) <-
} def

/:out [/ostreamtype /nametype /integertype] 
/_:out load def

/:out [/ostreamtype /nametype /stringtype]
/_:out load def

/:out [/ostreamtype /nametype /arraytype] 
/_:out load def

%/:out [/ostreamtype /nametype /proceduretype] 
%/_:out load def

/*BeginDocumentation
Name: MergeDictionary - Merge all definitions of a dictionary with the current dicitonary.
Synopsis: dict MergeDictionary -> -
SeeAlso: SaveDictionary, RestoreDictionary
*/
/MergeDictionary
[/dictionarytype]
{
  cva 2 2 Partition
  {
    arrayload pop
    def
  } forall
} def

/*BeginDocumentation
Name: RestoreDictionary - Read a dictionary definition from a file.
Synopsis: (fname) RestoreDictionary -> dict
SeeAlso: SaveDictionary, MergeDictionary
*/
/RestoreDictionary [/stringtype]
{
  << >> begin
  (r) file
  cvx exec
  currentdict 
  end
} def


/*BeginDocumentation
Name: Export - save in a foreign file format 
Synopsis: 
(fname) any         Export -> 
(fname) any literal Export ->
Examples:    
(spike.gdf) [ [1 121] [2 127] ]         Export --> 
(hello.dat) [ [3 121 4.3] [9 127 4.2] ] Export --> 
Description:
The currently supported formats are GDF (.gdf) and Table (.dat), 
only the first argument style is currently supported. 
Export raises an /UnknownExportFormat error if an unsupported
output format is requested.
Author: Diesmann
FirstVersion: 10.5.2001
Remarks: 
The Mathematica implementation is much more general.
Mathematica does not support GDF. 
References:
   [1] The Mathematica Book "Export"
SeeAlso: ReadList
*/
/Export
{
 % filename array
 exch
 dup
 (.) breakup Last
 % array filename (gdf)
 [(gdf) (dat)] exch  MemberQ   
 not
 {exch /Export /UnknownExportFormat raiseerror} if 
 % array filename
 (w) file
 exch
 % file array 
 {
  dup  
  [1 -2] Take 
  exch
  Last
  rollu
  {
   <- (\t) <-
  }
  forall
  exch <- endl 
 }
 forall
 closeostream
} def