/* In Emacs, please make this -*-c-*- mode. Thanks. */

/*******************************************************************************
*
* McStas, neutron ray-tracing package
*         Copyright 1997-2002, All rights reserved
*         Risoe National Laboratory, Roskilde, Denmark
*         Institut Laue Langevin, Grenoble, France
*
* Kernel: instrument.l
*
* %Identification
* Written by: K.N.
* Date: Jul  1, 1997
* Origin: Risoe
* Release: McStas 1.6
* Version: 1.22
*
* Flex scanner for instrument definition files.
*
* $Id: instrument.l,v 1.37 2005/10/21 10:07:42 farhi Exp $
*
* $Log: instrument.l,v $
* Revision 1.37  2005/10/21 10:07:42  farhi
* Restrict parsing to 'normal' chars. Scandinavian stuff removed as some LeX do not support that
*
* Revision 1.36  2005/06/30 14:05:46  farhi
* Now supports \n and \r\n end of lines
*
* Revision 1.35  2004/11/19 16:22:09  farhi
* kernel (parser) now supports escape sequences in comp/instr parameters
* e.g. in Win32 pathes... (reported by R. Peacock, ILL)
*
* Revision 1.34  2003/10/06 15:02:43  farhi
* Added PREVIOUS and PREVIOUS(index) keyword for component reference.
* Works with RELATIVE keyword. index is the index backward and
* PREVIOUS is equivalent to PREVIOUS(1)
*
* Revision 1.33  2003/02/11 12:28:45  farhi
* Variouxs bug fixes after tests in the lib directory
* mcstas_r  : disable output with --no-out.. flag. Fix 1D McStas output
* read_table:corrected MC_SYS_DIR -> MCSTAS define
* monitor_nd-lib: fix Log(signal) log(coord)
* HOPG.trm: reduce 4000 points -> 400 which is enough and faster to resample
* Progress_bar: precent -> percent parameter
* CS: ----------------------------------------------------------------------
*
* Revision 1.22  2002/08/29 16:39:42 ef
* enables %include in C code, embeding .h/.c from mcstas/lib
* embed only once for libraries (.h+.c)
*
* Revision 1.21  2001/09/24 10:00:00  ef
* Added GROUP, EXTEND and SHARE tokens (for McStas 1.6)
*
* Revision 1.20  2000/07/27 09:06:42  kn
* Added the extra tokens necessary to support full C expressions in
* component actual parameters.
*
* Revision 1.19  2000/07/06 12:27:11  kn
* Implement first NXDICT code (still incomplete).
*
* Revision 1.18  2000/02/15 07:42:32  kn
* Handle instrument parameters with different types (double, int, string).
*
* Revision 1.17  1999/03/18 07:31:46  kn
* Handle polarised neutrons.
*
* Revision 1.16  1999/01/28 07:54:44  kn
* Added MCDISPLAY keyword.
*
* Revision 1.15  1998/11/26 08:45:13  kn
* Fix bug with parse_restricted being defined static in this file, but
* declared extern in header file.
*
* Revision 1.14  1998/10/02 08:36:34  kn
* Added output parameters for components.
* Fixed header comment.
*
* Revision 1.13  1998/10/01 11:44:47  kn
* Added support for string expressions.
*
* Revision 1.12  1998/10/01 08:09:49  kn
* Use the search path for %include files.
*
* Revision 1.11  1998/09/23 13:58:47  kn
* Added C++ style ("//") comments.
*
* Revision 1.10  1998/08/26 12:43:21  kn
* Merged in the functionality from component.l.
* Added %include facility.
*
* Revision 1.9  1998/05/13 13:03:29  kn
* Now really fix the number syntax problem.
*
* Revision 1.9  1998/05/13 13:02:35  kn
* Now really fix the number syntax problem.
*
* Revision 1.8  1998/05/04 08:14:32  kn
* Fix problem in number syntax.
*
* Revision 1.7  1998/03/16 08:35:31  kn
* Fixed missing yylval type qualifications.
*
* Revision 1.6  1997/09/07 20:16:03  kn
* Added FINALLY construct.
*
* Revision 1.5  1997/09/07 17:57:40  kn
* Snapshot with (untested) code generation complete.
*
* Revision 1.4  1997/08/13 09:14:48  kn
* First version to properly parse instrument definition files.
*
* Revision 1.3  1997/07/02 07:27:32  kn
* Misc. cleanup.
*
* Revision 1.2  1997/07/01 08:26:21  kn
* Fixed problem when scanning identifiers: now returns a persistent copy
* of the name.
*
*******************************************************************************/


/* Definition section. */

/* Do not use the `yywrap feature' - only scan a single file (see Flex manual). */
%option noyywrap


%{
#include <string.h>
#include <math.h>
#include <stdio.h>

#include "mcstas.h"
#include "instrument.tab.h"

/* Fix things for bison option %pure_parser. */
#define YY_DECL int yylex(YYSTYPE *yylvalp)
#define yylval (*yylvalp)


/* Structure to hold the state of a file being parsed. */
struct file_state
  {
    YY_BUFFER_STATE buffer;
    char *filename;
    char *switch_line;
    int line;
    int oldstate;   /* Saved lexer start condition. */
    int visible_eof;    /* If true, tell parser about end-of-file. */
  };

#define MAX_INCLUDE 256
static struct file_state file_stack[MAX_INCLUDE + 1];
static int file_stack_ptr = 0;
static char *switch_line = NULL;

static void push_include(char *name);

%}


/* Lexer states. */

/* The state ccomment is used for scanning c-style comments. The state ccode
   is used when scanning embedded C code blocks. */
%x ccomment
%x ccode

/* The state initial_token is only used to output an initial token to
   discriminate between parsing general instrument definitions and autoloaded
   component definitions. */
%x initial_token

/* Get file name in %include. */
%x inclname

/* Get full %include line within C code blocks. */
%x cfullincl

/* Get file name in %include within C code blocks. */
%x cinclname


/* Abbreviations. */
DIGIT   [0-9]
ALPHA   [A-Za-z]
ALPHANUM  {ALPHA}|{DIGIT}|"_"

NUMBER    -?({DIGIT}*".")?{DIGIT}+([Ee][+-]?{DIGIT}+)?
ID    {DIGIT}*{ALPHA}{ALPHANUM}*
EOL             (\r\n|\n|\r)
INCLUDE "%include"

%%

 /* Initially, output a single token to the parser to tell it whether to parse
    general instrument definitions or autoloaded component definitions. */
<initial_token>.|\n |
<initial_token><<EOF>> {
      yyless(0);
      BEGIN(INITIAL);
      return parse_restricted ? TOK_RESTRICTED : TOK_GENERAL;
    }

ABSOLUTE      return TOK_ABSOLUTE;
AT            return TOK_AT;
COMPONENT     return TOK_COMPONENT;
DECLARE       return TOK_DECLARE;
DEFINE        return TOK_DEFINE;
DEFINITION    return TOK_DEFINITION;
END           return TOK_END;
MCDISPLAY     return TOK_MCDISPLAY;
FINALLY       return TOK_FINALLY;
EXTERN        return TOK_EXTERN;
INITIALIZE    return TOK_INITIALIZE;
INSTRUMENT    return TOK_INSTRUMENT;
OUTPUT        return TOK_OUTPUT;
PARAMETERS    return TOK_PARAMETERS;
POLARISATION  return TOK_POLARISATION;
RELATIVE      return TOK_RELATIVE;
ROTATED       return TOK_ROTATED;
PREVIOUS      return TOK_PREVIOUS;
SETTING       return TOK_SETTING;
STATE         return TOK_STATE;
TRACE         return TOK_TRACE;
SHARE         return TOK_SHARE;       /* ADD: E. Farhi Sep 20th, 2001 */
EXTEND        return TOK_EXTEND;      /* ADD: E. Farhi Sep 20th, 2001 */
GROUP         return TOK_GROUP;       /* ADD: E. Farhi Sep 24th, 2001 */
NEXUS         return TOK_NEXUS;       /* ADD: E. Farhi Aug 6th, 2002 */
DICTFILE      return TOK_DICTFILE;    /* ADD: E. Farhi Aug 6th, 2002 */
HDF           return TOK_HDF;         /* ADD: E. Farhi Aug 6th, 2002 */
SAVE          return TOK_SAVE;        /* ADD: E. Farhi Aug 25th, 2002 */

"("|")"|"["|"]"|"{"|"}"|"," return yytext[0]; /* Punctuation. */
"="|"*"       return yytext[0]; /* Operator. */

{NUMBER}  yylval.number = str_dup(yytext); return TOK_NUMBER;
 /* Note: Since ID overlaps with NUMBER (eg. "2E3"), ID must come
    after NUMBER */
{ID}    yylval.string = str_dup(yytext); return TOK_ID;

 /* Scanning all other C tokens used in expressions for component
 * actual parameters.
 * IMPORTANT!: Whenever a token is removed from here to make an independent
 * separate token, the new token must be added to the parser rules for
 * genatexp/topatexp.
 */
"->"|"."|"!"|"~"|"++"|"--"|"+"|"-"|"&"|"sizeof"|"*"|"/"|"%"|"+"|"-"|"<<"|">>"|"<"|"<="|">"|">="|"=="|"!="|"^"|"|"|"&&"|"||"|"?"|":"|"+="|"-="|"*="|"/="|"%="|"&="|"^="|"|="|"<<="|">>=" {
      yylval.string = str_dup(yytext);
      return TOK_CTOK;
\
    }

  /* Scanning embedded C code. */

"%""{"[\t ]*{EOL} {
      yylval.linenum = instr_current_line;
      instr_current_line++;
      BEGIN(ccode);
      return TOK_CODE_START;
    }
"%""{"[^\n]*{EOL} {
      instr_current_line++;
      print_error("%%{ token not on a line by itself "
                  "on line %d of file '%s': %s.\n",
              instr_current_line, instr_current_filename, yytext);
      return TOK_INVALID;
    }

<ccode>{
    /* normal %} symbol to end C code block */
    [\t ]*"%""}"[\t ]*{EOL} instr_current_line++; BEGIN(INITIAL); return TOK_CODE_END;
    /* %} symbol surrounded by some unrelevant stuff */
    [^\n]*"%""}"[^\n]*{EOL} {
        instr_current_line++;
        print_warn(NULL, "%%} terminator not on a line by itself "
                    "on line %d of file '%s': %s.\n",
              instr_current_line, instr_current_filename, yytext);
      }
    /* %include full line -> jump to cfullincl state */
    [\t ]*{INCLUDE}[^\n]*{EOL} {
      yyless(0);  /* re-use the current line, but within cfullincl state */
      BEGIN(cfullincl);
      }
    /* full line as C code */
    [^\n]*{EOL}   {
        instr_current_line++;
        yylval.string = str_dup(yytext);
        return TOK_CODE_LINE;
      }
    } /* end ccode */

 /* Quoted strings. Simple version: no escape sequences. */
\"[^\"\n\\]*\"    {
        yylval.string =
          str_dup_n(&yytext[1], strlen(yytext) - 2);
        return TOK_STRING;
      }
 /* Quoted strings with escape sequence (e.g Win32 path): preserve all chars */
\"[^\"\n]*\\[^\"\n]*\"  {
        yylval.string = str_dup_n(&yytext[1], strlen(yytext) - 2);
        return TOK_STRING;
      }
\"[^\n\"]*{EOL}   {
        print_error("Error: Unterminated string "
                    "on line %d of file '%s': %s.\n",
              instr_current_line, instr_current_filename, yytext);
      }

 /* %-style comments - ignore everything to end of line. */
"%"{EOL}    instr_current_line++; /* Ignore comment. */
"% "[^\n]*{EOL} instr_current_line++; /* Ignore comment. */

 /* Include files for McStas comp/instr (INITIAL state). */
 /* then next token is the file name */
{INCLUDE}[ \t]+\" BEGIN(inclname);
<inclname>{
  /* name ends with a quote char -> include as INITIAL state */
  [^\"\n]+\" {
        yytext[yyleng - 1] = '\0';
        BEGIN(INITIAL);
        if (verbose) fprintf(stderr, "Embedding %s\n", yytext);
        push_include(yytext);
      }
  /* name contains char, including quote and \n -> not valid */
  [\"\n].*  {
        print_error("Error in %%include statement "
                    "on line %d of file '%s': %s.\n",
              instr_current_line, instr_current_filename, yytext);
        BEGIN(INITIAL);
      }
  } /* end inclname */
 /* Include files within C code blocks (ccode state)*/
 /* next token is full line, regenerated by yyless(0) */
<cfullincl>{INCLUDE}[ \t]+\"  BEGIN(cinclname);
<cinclname>{
  /* name ends with a quote char, with extension -> include as ccode state */
  [^\"\n]+\.+.\"  {
        yytext[yyleng - 1] = '\0';
        BEGIN(ccode);
        if (verbose) fprintf(stderr, "Embedding file      %s\n", yytext);
        push_include(yytext);
      }
  /* name ends with a quote char, but no ext -> include as ccode state
   * this occurs when importing a library .h/.c The .c is only included
   * when instr->runtime option is true
   */
  [^\"\n]+\"  {
        char *tmp0, *tmp1;
        yytext[yyleng - 1] = '\0';
        tmp0 = str_dup(yytext);
        if (!symtab_lookup(lib_instances, tmp0))
        {
          tmp1 = str_cat(tmp0, ".h", NULL);
          if (instrument_definition->include_runtime)
          {
            switch_line = str_cat(tmp0, ".c", NULL);
          }
          else
            fprintf(stderr,"Dependency: %s.o\n", tmp0);

          BEGIN(ccode);
          if (verbose) fprintf(stderr, "Embedding library   %s\n", tmp1);

          push_include(tmp1);
          symtab_add(lib_instances, tmp0, NULL);
          str_free(tmp1);
        }
        else
        {
          BEGIN(ccode);
          instr_current_line++;  /* library was previously embedded */
        }
        str_free(tmp0);
      }
  /* name contains char, including quote and \n -> not valid */
  [\"\n].*  {
        print_error("Error in %%include statement "
                    "on line %d of file '%s': %s.\n",
              instr_current_line, instr_current_filename, yytext);
        BEGIN(ccode);
      }
  } /* end  cinclname */

 /* C++ "//"-style comments - ignore everything to end of line. */
"//"[^\n]*{EOL} instr_current_line++; /* Ignore comment. */

 /* C-style comments. */
"/*"    BEGIN(ccomment);
<ccomment>{
 [^*\n]*  /* Ignore comment. */
 [^*\n]*{EOL} instr_current_line++; /* Ignore comment. */
 "*"+[^*/\n]* /* Ignore comment. */
 "*"+[^*/\n]*{EOL}  instr_current_line++; /* Ignore comment. */
 "*"+"/"  BEGIN(INITIAL); /* End of comment. */
}

[ \t]+      /* Ignore whitespace. */
[ \t]*{EOL}   instr_current_line++; /* Ignore whitespace. */

<INITIAL,ccode><<EOF>> {
      if(file_stack_ptr <= 0)
      {
        /* EOF on main instrument file. */
        yyterminate();
      }
      else
      {
        --file_stack_ptr;
        yy_delete_buffer(YY_CURRENT_BUFFER);
        yy_switch_to_buffer(file_stack[file_stack_ptr].buffer);
        instr_current_filename = file_stack[file_stack_ptr].filename;
        instr_current_line = file_stack[file_stack_ptr].line;
        switch_line        = file_stack[file_stack_ptr].switch_line;
        if (switch_line)
        {
          char *tmp0;
          tmp0 = str_dup(switch_line);
          str_free(switch_line);
          switch_line = NULL;
          BEGIN(ccode);
          if (verbose) fprintf(stderr, "Embedding library   %s\n", tmp0);
          push_include(tmp0);
          str_free(tmp0);
        }
        else
        {
          BEGIN(file_stack[file_stack_ptr].oldstate);
          if(file_stack[file_stack_ptr].visible_eof)
            yyterminate();
        }

      }
    }

.               {
      print_error("Invalid character `%s' "
                  "on line %d of file '%s'.\n",
            yytext, instr_current_line, instr_current_filename);
      return TOK_INVALID;
    }



%%

/* User code section. */

/* This flag is set when autoloading component definitions to make the lexer
   output the special initial token to switch the parser to restricted
   mode. */
int parse_restricted = FALSE;

/* Prepare to run lexical analysis on new file. */
void
lex_new_file(FILE *file)
{
  parse_restricted = FALSE;
  BEGIN(initial_token);
  yyrestart(file);
}


/* This handles the details of switching the lexer to a new file. */
static void
push_file(FILE *file, int restricted, int visible_eof)
{
  if(file_stack_ptr >= MAX_INCLUDE)
    fatal_error("Too deeply nested includes "
                "on line %d of file '%s'.\n",
              instr_current_line, instr_current_filename);
  file_stack[file_stack_ptr].buffer = YY_CURRENT_BUFFER;
  file_stack[file_stack_ptr].filename = instr_current_filename;
  file_stack[file_stack_ptr].line = instr_current_line;
  file_stack[file_stack_ptr].oldstate = YY_START;
  file_stack[file_stack_ptr].visible_eof = visible_eof;
  file_stack[file_stack_ptr].switch_line = switch_line;
  file_stack_ptr++;

  instr_current_line = 1;

  yy_switch_to_buffer(yy_create_buffer(file, YY_BUF_SIZE));
  parse_restricted = restricted;
}

/* Handle a new %include file. */
void
push_include(char *name)
{
  FILE *file;

  file = open_file_search(name);
  if(file == NULL)
    fatal_error("Cannot open include file '%s' "
                "on line %d of file '%s'.\n",
              name, instr_current_line, instr_current_filename);
  push_file(file, FALSE, FALSE);
  instr_current_filename = name;
  instr_current_line = 1;
}

/* Handle a new autoincluded file (uses recursive parser call). */
void
push_autoload(FILE *file)
{
  push_file(file, TRUE, TRUE);
  BEGIN(initial_token);
}



syntax highlighted by Code2HTML, v. 0.9.1