Subversion Repositories tendra.SVN

Rev

Rev 7 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (c) 2002-2005 The TenDRA Project <http://www.tendra.org/>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of The TenDRA Project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific, prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $Id$
 */
/*
                 Crown Copyright (c) 1997

    This TenDRA(r) Computer Program is subject to Copyright
    owned by the United Kingdom Secretary of State for Defence
    acting through the Defence Evaluation and Research Agency
    (DERA).  It is made available to Recipients with a
    royalty-free licence for its use, reproduction, transfer
    to other parties and amendment for any purpose not excluding
    product development provided that any such use et cetera
    shall be deemed to be acceptance of the following conditions:-

        (1) Its Recipients shall ensure that this Notice is
        reproduced upon any copies or amended versions of it;

        (2) Any amended version of it shall be clearly marked to
        show both the nature of and the organisation responsible
        for the relevant amendment or amendments;

        (3) Its onward transfer from a recipient to another
        party shall be deemed to be that party's acceptance of
        these conditions;

        (4) DERA gives no warranty or assurance as to its
        quality or suitability for any purpose and DERA accepts
        no liability whatsoever in relation to any use to which
        it may be put.
*/


#include "config.h"
#include "types.h"
#include "read_types.h"
#include "analyser.h"
#include "file.h"
#include "names.h"
#include "utility.h"


/*
    CURRENT AND PREVIOUS LINE NUMBERS

    The current line in the file is recorded.  The previous line (where
    any errors were likely to have been) is also saved.
*/

long crt_line_no = 1;
long line_no = 1;


/*
    FORM OF INPUT

    This flag controls whether the input should be lisp-like (default)
    or c-like.
*/

boolean func_input = 0;


/*
    ANALYSE FLAGS

    The looked_ahead flag is true to indicate that the next word has
    already been read.  The really_analyse flag is false to indicate
    that the next word may be ignored.
*/

boolean looked_ahead = 0;
static boolean really_analyse = 1;


/*
    INPUT BUFFER

    The input is read into a buffer.
*/

#define BUFFSIZE        5000
static char word_buff[BUFFSIZE];


/*
    LAST WORD READ

    The word just read from the input file is word.  It has length
    word_length and input type word_type.
*/

char *word = "";
long word_length;
int word_type = INPUT_EOF;


/*
    PENDING CHARACTER

    In reading a word we almost always read one too many character.
    This is stored in pending.  A value of 0 indicates that there is
    no pending character.
*/

static int pending = 0;


/*
    READ THE NEXT WORD

    The next word is read from the input file.
*/

void
read_word(void)
{
    int c;
    char *p;
    int negate = 0;
    unsigned base = 10;

    /* If we have looked ahead one, return last value */
    if (looked_ahead) {
        looked_ahead = 0;
        return;
    }

    /* Get the first letter */
    if (pending) {
        c = pending;
        if (c == EOF) {
            word_type = INPUT_EOF;
            return;
        }
        pending = 0;
    } else {
        c = getc(input);
        if (c == '\n')crt_line_no++;
    }

    /* Step over any white space and comments */
    while (white_space(c) || c == '#') {
        if (c == '#') {
            /* Comments go to the end of the line */
            while (c = getc(input), c != '\n') {
                if (c == EOF) {
                    is_fatal = 0;
                    input_error("End of file in comment");
                    word_type = INPUT_EOF;
                    pending = EOF;
                    return;
                }
            }
            crt_line_no++;
        } else {
            c = getc(input);
            if (c == '\n')crt_line_no++;
        }
    }
    line_no = crt_line_no;

    /* Check for end of file */
    if (c == EOF) {
        word_type = INPUT_EOF;
        pending = EOF;
        return;
    }

    /* Check for open brackets */
    if (c == '(') {
        word = "(";
        word_type = INPUT_OPEN;
        return;
    }

    /* Check for close brackets */
    if (c == ')') {
        word = ")";
        word_type = INPUT_CLOSE;
        return;
    }

    if (func_input) {
        /* Check for commas (c-like input only) */
        if (c == ',') {
            word = ",";
            word_type = INPUT_COMMA;
            return;
        }

        /* Check for semicolons (c-like input only) */
        if (c == ';') {
            word = ";";
            word_type = INPUT_SEMICOLON;
            return;
        }
    }

    /* Check for strings */
    if (c == '"') {
        boolean escaped;
        p = word_buff;
        do {
            boolean ignore = 0;
            escaped = 0;
            c = getc(input);
            if (c == '\n') {
                is_fatal = 0;
                input_error("New line in string");
                crt_line_no++;
                line_no = crt_line_no;
                ignore = 1;
            }
            if (c == '\\') {
                escaped = 1;
                c = getc(input);
                if (c == '\n') {
                    crt_line_no++;
                    line_no = crt_line_no;
                    ignore = 1;
                } else if (c == 'n') {
                    c = '\n';
                } else if (c == 't') {
                    c = '\t';
                } else if (octal_digit(c)) {
                    int e = (c - '0');
                    c = getc(input);
                    if (!octal_digit(c)) {
                        is_fatal = 0;
                        input_error("Invalid escape sequence");
                        c = '0';
                    }
                    e = 8 * e + (c - '0');
                    c = getc(input);
                    if (!octal_digit(c)) {
                        is_fatal = 0;
                        input_error("Invalid escape sequence");
                        c = '0';
                    }
                    e = 8 * e + (c - '0');
                    c = e;
                    if (c >= 256) {
                        is_fatal = 0;
                        input_error("Invalid escape sequence");
                        c = 0;
                    }
                }
            }
            if (c == EOF) {
                is_fatal = 0;
                input_error("End of file in string");
                word_type = INPUT_EOF;
                pending = EOF;
                return;
            }
            if (!ignore)*(p++) = (char)c;
        } while (c != '"' || escaped);
        *(--p) = 0;
#if 0
        c = getc(input);
        if (c == '\n')crt_line_no++;
        if (!terminator(c)) {
            is_fatal = 0;
            input_error("Terminator character expected");
        }
        pending = c;
#endif
        word = word_buff;
        word_length = (int)(p - word);
        word_type = INPUT_STRING;
        return;
    }

    /* Check for words */
    if (alpha(c)) {
        p = word_buff;
        do {
            *(p++) = (char)c;
            c = getc(input);
            if (c == '\n')crt_line_no++;
        } while (alphanum(c));
        *p = 0;
        if (!terminator(c)) {
            is_fatal = 0;
            input_error("Terminator character expected");
        }
        pending = c;
        word = word_buff;
        word_type = INPUT_WORD;
        return;
    }

    /* Check for bars */
    if (c == '|') {
        c = getc(input);
        if (c == '\n')crt_line_no++;
        if (!terminator(c)) {
            is_fatal = 0;
            input_error("Terminator character expected");
        }
        pending = c;
        word = "|" ;
        word_type = INPUT_BAR;
        return;
    }

    /* Check for a single dash and arrow */
    if (c == '-') {
        c = getc(input);
        if (c == '\n')crt_line_no++;
        if (terminator(c)) {
            pending = c;
            word = "-" ;
            word_type = INPUT_BLANK;
            return;
        }
        if (func_input && c == '>') {
            pending = 0;
            word = "->";
            word_type = INPUT_ARROW;
            return;
        }
        negate = 1;
    }

    /* Step over any signs */
    while (c == '-' || c == '+') {
        if (c == '-')negate = 1 - negate;
        c = getc(input);
        if (c == '\n')crt_line_no++;
    }

    /* Check for numbers */
    if (c == '0') {
        base = 8;
        c = getc(input);
        if (c == '\n')crt_line_no++;
        if (terminator(c)) {
            pending = c;
            word = "0";
            word_type = INPUT_NUMBER;
            return;
        }
        if (c == 'x' || c == 'X') {
            base = 16;
            c = getc(input);
            if (c == '\n')crt_line_no++;
        }
    } else if (!dec_digit(c)) {
        is_fatal = 0;
        input_error("Illegal character, %c",(unsigned char)c);
        pending = 0;
        read_word();
        return;
    }

    /* Set up buffer */
    p = word_buff + BUFFSIZE;
    *(--p) = 0;
    *(--p) = '0';
    *(--p) = 0;

    /* Read the number */
    do {
        unsigned n;
        if (dec_digit(c)) {
            n = (unsigned)(c - '0');
        } else if (c >= 'A' && c <= 'F') {
            n = 10 + (unsigned)(c - 'A');
        } else if (c >= 'a' && c <= 'f') {
            n = 10 + (unsigned)(c - 'a');
        } else {
            is_fatal = 0;
            input_error("Illegal digit, %c",(unsigned char)c);
            n = 0;
        }
        if (n >= base) {
            is_fatal = 0;
            input_error("Illegal digit, %c",(unsigned char)c);
            n = 0;
        }
        if (really_analyse) {
            p = word_buff + (BUFFSIZE - 2);
            do {
                if (*p == 0) {
                    *(p - 1) = 0;
                } else {
                    n += base *(unsigned)(*p - '0');
                }
                *p = (char)('0' + (n & 7));
                n >>= 3;
                p--;
            } while (n || *p);
        }
        c = getc(input);
        if (c == '\n')crt_line_no++;
    } while (!terminator(c));

    /* Find the start of the number */
    if (really_analyse) {
        for ( p = word_buff + ( BUFFSIZE - 2 ) ; *p ; p-- ) /* empty */ ;
        if (negate)*(p--) = '-';
    }
    pending = c;
    word = p + 1;
    if (streq(word, "-0"))word = "0";
    word_type = INPUT_NUMBER;
    return;
}


/*
    HOW MANY WORDS TO THE NEXT CLOSE BRACKET?

    This routine skips over the input until the first closing bracket
    unmatched by an open bracket is read.  The routine returns the
    number of word read at the highest bracket level.  (Not currently
    used.)
*/

long
skip_words(void)
{
    long n = 0;
    int level = 1;
    really_analyse = 0;
    while (level) {
        read_word();
        switch (word_type) {
            case INPUT_OPEN: level++; break;
            case INPUT_CLOSE: level--; break;
            case INPUT_EOF: {
                input_error("Unexpected end of file");
                return(n);
            }
        }
        if (level == 1)n++;
    }
    really_analyse = 1;
    return(n);
}


/*
    STORE THE CURRENT POSITION

    The current position in the input file is stored in p.
*/

void
store_position(position *p)
{
    p->line = crt_line_no;
    p->posn = ftell(input);
    p->pending = pending;
    p->ahead = looked_ahead;
    return;
}


/*
    SET THE CURRENT POSITION

    The position in the input file is set from p.
*/

void
set_position(position *p)
{
    crt_line_no = p->line;
    pending = p->pending;
    looked_ahead = p->ahead;
    if (fseek(input, p->posn, SEEK_SET)) {
        fatal_error("Illegal seek command");
    }
    return;
}