#!/bin/sh

## The scanner generartor (f)lex and its options
##
LEXBIN="flex"
LEXOPT="-L"

## The MuPAD module generator and its options
##
MMGBIN="mmg"
LEXLIB="-I/usr/local/gnu/include -L/usr/local/gnu/lib -L/usr/local/lib -lfl"
MMGOPT="-v -color -a static ${LEXLIB}"

PrintInfo()
{
cat<<EOF
##############################################################################
# FILE   :  mkscan - Creates a (f)lex based scanner module for MuPAD
# AUTHOR :  Andreas Sorgatz (andiuni-paderborn.de)
# CREATED:  24. Nov. 1996
# CHANGED:  17. Apr. 1998
# RELEASE:  MuPAD 1.2.9a, 1.3, 1.4
##############################################################################

USAGE: ./mkscan [-c] name
-c   : create the complete module source code, but do not compile it
name : name of a (f)lex input file, without the suffix '.l'

This script generates a dynamic scanner module for MuPAD by using the scanner
generator (f)lex. To create a scanner named 'x', a (f)lex input file 'x.l' is
needed. The scanner module will provide the following interface functions:

  x::open( file )  --  Opens file 'name' for scanning   (==> DOM_BOOL  )
  x::close()       --  Closes the current file          (==> DOM_NULL  )
  x::token()       --  Returns the next token           (==> DOM_INT   )
  x::text()        --  Returns the text of the  token   (==> DOM_STRING)
  x::line()        --  Returns the current line number  (==> DOM_INT   )
  x::back()        --  Put back the last token          (==> DOM_BOOL  )

Tokens are non-negative integers. x::token() returns the token 0 when the end
of the input file is reached.   The following example 'demo.l' defines a very 
simple scanner 'demo', which allows to count the lines of an input file. Call
'./mkscan demo' to generate the scanner module:

%{
enum     { EOFILE=0, UNKNOWN=1, LINE=2 };
%}
nline    [\n]
%%
{nline}  { return( LINE ); }  
%%

The dynamic scanner module  'demo.mdm'  will be placed in the current working
directory and can be used within a MuPAD session: 

  >> module( "demo" ):
  >> demo::open( "demo.l" ):
  >> l:=0: while demo::token()=2 do l:=l+1 end_while: l;
  >> demo::close();

The dynamic scanner module  'demo.mdm'  will be placed in the current working
For further information refer to the (f)lex manual and the files  'demo.yy.c'
and 'demo.C' created by 'mkscan'.
EOF
}


CreateModuleSource()
{
    CreateLexCode $1
    echo '## Creating the module source code #################################'
    echo '##'
    echo mkscan '==>' $1.C

cat<<EOF > $1.C
/******************************************************************************/
/* FILE   :  ???.C  -- A module interface for scanners generated by (f)lex    */
/* AUTHOR :  Andreas Sorgatz (andi@uni-paderborn.de)                          */
/* CREATED:  24. Nov. 1996                                                    */
/* CHANGED:  25. Nov. 1996                                                    */
/******************************************************************************/

// Options for the MuPAD Module Generator //////////////////////////////////////
//
MMG( info = "Dynamic scanner module for (f)lex scanners" )


// Internal variables - do not change this !!! /////////////////////////////////
//
#define       ScanLen                    1024    // Length of string buffers
static char   ScanTokenString[ScanLen] = "";     // Token string buffer
static char*  ScanFileName             = NULL;   // Name of current file
static long   ScanLine                 = 1;      // Current line number
static short  ScanBack                 = 1;      // A token was put back ?


// ECHO returns unknown Tokens (UNKNOWN) - refer to 'man flex' and 'lex.yy.c'.
// The constant UNKNOWN has to be defined in 'lex.yy.c'.
//
#define ECHO { return( UNKNOWN ); }


// Include the C source code generated by (f)lex - do not change this !!! //////
// #include "lex.yy.c"
//
// ----------------------------------------------------------------------------
extern "C" {
`cat $1.yy.c`
}
// ----------------------------------------------------------------------------


// The scanner functions: open, close, token, text, line, back /////////////////
//
/******************************************************************************/
/* FUNCTION:  scan::open( "FILENAME" ) - Open a file to be scanned            */
/******************************************************************************/
MFUNC( open, MCnop )
{
    MFnargsCheck(1);
    MFargCheck(1,DOM_STRING);

    if( ScanFileName != NULL ) {
        ScanFileName = NULL;
        ScanLine     = 1;
        fclose(yyin);
    }
    if( (yyin = fopen(MFstring(MFarg(1)),"r")) == NULL ) 
        MFreturn( MFcopy(MVfalse) );

    ScanFileName = MFstring(MFarg(1));
    ScanTokenString[0] = '\0';
    yyrestart(yyin);

    MFreturn( MFcopy(MVtrue) );
} MFEND

/******************************************************************************/
/* FUNCTION:  scan::close() - Close the currently scanned file                */
/******************************************************************************/
MFUNC( close,  MCnop )
{
    MFnargsCheck(0);

    if( ScanFileName != NULL ) {
        ScanFileName = NULL;
        fclose(yyin);
    }
    MFreturn( MFcopy(MVnull) );
} MFEND

/******************************************************************************/
/* FUNCTION:  scan::token() - Return the next token                           */
/******************************************************************************/
MFUNC( token, MCnop )
{
    MFnargsCheck(0);

    if( ScanFileName == NULL ) 
        MFerror( "Open input file first" );

    MTcell token = MFlong(yylex());

    ScanBack = 0;
    if( yyleng >= ScanLen ) 
        MFerror( "Fatal error, token exceeded max length" );

    for( int i = 0; i < yyleng; ) {
        ScanTokenString[  i] = yytext[i];
        ScanTokenString[++i] = 0;
    }
    MFreturn( token );
} MFEND

/******************************************************************************/
/* FUNCTION:  scan::text() - Return the string of the last scanned token     */
/******************************************************************************/
MFUNC( text, MCnop )
{
    MFnargsCheck(0);

    if( ScanBack ) 
        MFreturn( MFstring("") );

    MFreturn( MFstring(ScanTokenString) );
} MFEND

/******************************************************************************/
/* FUNCTION:  scan::back() - Put back the last scanned token                  */
/******************************************************************************/
MFUNC( back, MCnop )
{
    MFnargsCheck(0);

    if( ScanBack ) 
        MFreturn( MFcopy(MVfalse) );
    ScanBack = 1;
    yyless(0);
    MFreturn( MFcopy(MVtrue) );
} MFEND

/******************************************************************************/
/* FUNCTION:  scan::line() - Return the current line number                   */
/******************************************************************************/
MFUNC( line, MCnop )
{
    MFnargsCheck(0);

    MFreturn( MFlong(ScanLine) );
} MFEND
EOF

    if [ ! -r $1.C ] ; then
       echo "Creation failed: module source file \"$1.C\""
       echo ' '
       exit 2
    fi
}


##############################################################################
# CreateModuleHelp ( BASENAME )
##############################################################################
CreateModuleHelp()
{
    echo '## Creating the module help file ###################################'
    echo '##'
    echo mkscan '==>' $1.mdh

cat<<EOF > $1.mdh

MODULE
    $1 - A MuPAD module interface to a (f)lex scanner

INTRODUCTION
    This module defines a MuPAD interface to a scanner created with the
    scanner generator (f)lex. The function token() returns non-negative
    integers, with EOFILE=0. The string of the current token can be ex-
    tracted with text(). back() allows the user to put back the current
    token to be re-scanned with the next call of the function token().

    For technical information please refer to the manual of flex(1) and 
    also to the source file of this module.   MuPAD scanner modules are 
    static modules which will not be unloaded automatically.   They can
    be created with the shell script 'mkscan',  which is available from
    official MuPAD ftp sites.

INTERFACE
    doc, back, close, line, open, text, token
<!-- BEGIN-FUNC doc -->
NAME
    $1::doc - Online documentation

SYNOPSIS
    $1::doc()
    $1::doc( func )

ARGUMENTS
    func - Function name without the prefix "$1::" (DOM_STRING)

DESCRIPTION
    Displays a brief description of this  scanner module , respectively
    the function $1::'func'. 

EXAMPLE:
    >> $1::doc("token");
    [...]

SEE ALSO
    module::help
<!-- END-FUNC -->
<!-- BEGIN-FUNC back -->
NAME
    $1::back - Puts back the current token to be re-scanned later

SYNOPSIS
    $1::back()

ARGUMENTS
    -

DESCRIPTION
    Puts back the current token, to be re-scanned with the next call of
    the function token().  back() cannot be called again before the put
    back token was re-scanned by token().  The function returns TRUE if
    the token was put back and FALSE if not. 

    NOTE:  After back() was called,  text() returns the empty character
           string ("") until the token is re-scanned by token().

EXAMPLE:
    >> $1::token();
    13

    >> $1::back();
    TRUE

    >> $1::token(), $1::token();
    13, 7

SEE ALSO
    $1::token
<!-- END-FUNC -->
<!-- BEGIN-FUNC close -->
NAME
    $1::close - Closes the current input file

SYNOPSIS
    $1::close()

ARGUMENTS
    -

DESCRIPTION
    Closes the current input file of this scanner module.  The function
    returns the value DOM_NULL.
EXAMPLE:
    >> $1::open("test");
    TRUE

    >> $1::close();


SEE ALSO
    $1::open
<!-- END-FUNC -->
<!-- BEGIN-FUNC line -->
NAME
    $1::line - Returns the current line number.

SYNOPSIS
    $1::line()

ARGUMENTS
    -

DESCRIPTION
    Returns the current line number. The line numbers starts with 1.

EXAMPLE:
    >> $1::open("test");
    TRUE

    >> $1::line();
    1

SEE ALSO
    $1::text
<!-- END-FUNC -->
<!-- BEGIN-FUNC open -->
NAME
    $1::open - Opens a new scanner input file

SYNOPSIS
    $1::open( file )

ARGUMENTS
    file - A file name (DOM_STRING)

DESCRIPTION
    Opens a new scanner input file. If there is an open scanner file of
    this scanner module then it is closed before.  The function returns
    TRUE if "file" has been opened and FALSE otherwise.

EXAMPLE:
    >> $1::open("test");
    TRUE

    >> $1::token();
    2

SEE ALSO
    $1::close, $1::token
<!-- END-FUNC -->
<!-- BEGIN-FUNC text -->
NAME
    $1::text - Returns the character string of the current token

SYNOPSIS
    $1::text()

ARGUMENTS
    -

DESCRIPTION
    Returns the character string of the current token. Before the first
    token is read as well as at the end of the input file this function
    returns the empty character string ("").  This is also be done when
    back() was called and the token was not re-scanned yet.

EXAMPLE:
    >> $1::text();
    "42"

    >> $1::token();
    0

    >> $1::text();
    ""

SEE ALSO
    $1::line, $1::token
<!-- END-FUNC -->
<!-- BEGIN-FUNC token -->
NAME
    $1::token - Returns the next token read from the input file

SYNOPSIS
    $1::token()

ARGUMENTS
    -

DESCRIPTION
    Returns the next token read from the current input file. Tokens are
    non-negative integers with EOFILE=0, UNKNOWN=1, ... 

EXAMPLE:
    >> $1::token();
    2

    >> $1::text();
    "while"

    >> $1::token();
    0

SEE ALSO
    $1::line, $1::text
<!-- END-FUNC -->
EOF

    if [ ! -r $1.mdh ] ; then
       echo "Creation failed: module help file \"$1.mdh\""
       echo ' '
       exit 3
    fi
}


##############################################################################
# CreateLexCode ( BASENAME )
##############################################################################
CreateLexCode()
{
    echo '## Creating (f)lex scanner source code #############################'
    echo '##'
    echo $1.l '==>' $1.yy.c

    if [ ! -r $1.l ] ; then
       echo "Cannot read: (f)lex input file \"$1.l\""
       echo ' '
       exit 1
    fi

    ${LEXBIN} ${LEXOPT} -o$1.yy.c $1.l

    if [ ! -r $1.yy.c ] ; then
       echo "Creation failed: (f)lex output file \"$1.yy.c\""
       echo ' '
       exit 1
    fi
}


##############################################################################
# CreateModule ( BASENAME )
##############################################################################
CreateModule()
{
    echo '## Compiling the dynamic scanner module ############################'
    echo '##'
    echo $1.l, $1.C '==>' $1.mdm

    ${MMGBIN} ${MMGOPT} $2 $1.C
}


##############################################################################
# MAIN
##############################################################################
if [ $# -eq 0 ] ; then
   PrintInfo | more
   exit 0
fi

CREATE="YES"                 # Compile a dynamic scanner module ? Yes!
SMNAME="NONAME"              # The scanner module name ? Unknown !

while [ $# -gt 0 ] ; do
   if [ "$1" = "-c" ] ; then
      CREATE="NO"
      shift
   else
      SMNAME="$1"
      shift
   fi
done

if [ "$SMNAME" = "noname" ] ; then
   echo "usage: mkscan [-c] name"
   echo ' '
   exit 4
fi

CreateModuleSource $SMNAME 
CreateModuleHelp   $SMNAME

if [ "$CREATE" = "YES" ] ; then
   CreateModule $SMNAME 
fi

echo ' '

