|
Packit |
d394d9 |
/* Copyright (C) 1995 Bjoern Beutel. */
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/* Description. =============================================================*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/* This module supports scanning (lexical analysis) of malaga source files. */
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/* Includes. ================================================================*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
#include <string.h>
|
|
Packit |
d394d9 |
#include <stdio.h>
|
|
Packit |
d394d9 |
#include <errno.h>
|
|
Packit |
d394d9 |
#include <setjmp.h>
|
|
Packit |
d394d9 |
#include <glib.h>
|
|
Packit |
d394d9 |
#include "basic.h"
|
|
Packit |
d394d9 |
#include "files.h"
|
|
Packit |
d394d9 |
#include "scanner.h"
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/* Constants. ===============================================================*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/* List of all keywords and their token codes.
|
|
Packit |
d394d9 |
* (This list must be maintained in alphabetical order.) */
|
|
Packit |
d394d9 |
static struct { string_t name; int_t code; } keywords[ NUMBER_OF_KEYWORDS ] =
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
{ "accept", TOK_ACCEPT },
|
|
Packit |
d394d9 |
{ "allo_rule", TOK_ALLO_RULE },
|
|
Packit |
d394d9 |
{ "and", TOK_AND },
|
|
Packit |
d394d9 |
{ "assert", TOK_ASSERT },
|
|
Packit |
d394d9 |
{ "break", TOK_BREAK },
|
|
Packit |
d394d9 |
{ "choose", TOK_CHOOSE },
|
|
Packit |
d394d9 |
{ "combi_rule", TOK_COMBI_RULE },
|
|
Packit |
d394d9 |
{ "continue", TOK_CONTINUE },
|
|
Packit |
d394d9 |
{ "default", TOK_DEFAULT },
|
|
Packit |
d394d9 |
{ "define", TOK_DEFINE },
|
|
Packit |
d394d9 |
{ "else", TOK_ELSE },
|
|
Packit |
d394d9 |
{ "elseif", TOK_ELSEIF },
|
|
Packit |
d394d9 |
{ "end", TOK_END },
|
|
Packit |
d394d9 |
{ "end_rule", TOK_END_RULE },
|
|
Packit |
d394d9 |
{ "error", TOK_ERROR },
|
|
Packit |
d394d9 |
{ "foreach", TOK_FOREACH },
|
|
Packit |
d394d9 |
{ "greater", TOK_GREATER },
|
|
Packit |
d394d9 |
{ "greater_equal", TOK_GREATER_EQUAL },
|
|
Packit |
d394d9 |
{ "if", TOK_IF },
|
|
Packit |
d394d9 |
{ "in", TOK_IN },
|
|
Packit |
d394d9 |
{ "include", TOK_INCLUDE },
|
|
Packit |
d394d9 |
{ "initial", TOK_INITIAL },
|
|
Packit |
d394d9 |
{ "input_filter", TOK_INPUT_FILTER },
|
|
Packit |
d394d9 |
{ "less", TOK_LESS },
|
|
Packit |
d394d9 |
{ "less_equal", TOK_LESS_EQUAL },
|
|
Packit |
d394d9 |
{ "matches", TOK_MATCHES },
|
|
Packit |
d394d9 |
{ "not", TOK_NOT },
|
|
Packit |
d394d9 |
{ "or", TOK_OR },
|
|
Packit |
d394d9 |
{ "output_filter", TOK_OUTPUT_FILTER },
|
|
Packit |
d394d9 |
{ "parallel", TOK_PARALLEL },
|
|
Packit |
d394d9 |
{ "pruning_rule", TOK_PRUNING_RULE },
|
|
Packit |
d394d9 |
{ "repeat", TOK_REPEAT },
|
|
Packit |
d394d9 |
{ "require", TOK_REQUIRE },
|
|
Packit |
d394d9 |
{ "result", TOK_RESULT },
|
|
Packit |
d394d9 |
{ "return", TOK_RETURN },
|
|
Packit |
d394d9 |
{ "robust_rule", TOK_ROBUST_RULE },
|
|
Packit |
d394d9 |
{ "rules", TOK_RULES },
|
|
Packit |
d394d9 |
{ "select", TOK_SELECT },
|
|
Packit |
d394d9 |
{ "stop", TOK_STOP },
|
|
Packit |
d394d9 |
{ "subrule", TOK_SUBRULE },
|
|
Packit |
d394d9 |
{ "then", TOK_THEN },
|
|
Packit |
d394d9 |
{ "while", TOK_WHILE }
|
|
Packit |
d394d9 |
};
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/* Types. ===================================================================*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
typedef struct /* A source stream for lexical analysis. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
list_node_t *next; /* The next (including) source stream. */
|
|
Packit |
d394d9 |
FILE *stream; /* The input stream for this include level. */
|
|
Packit |
d394d9 |
string_t file_name; /* The name of the input file. */
|
|
Packit |
d394d9 |
text_t *line; /* The current line. */
|
|
Packit |
d394d9 |
string_t next_char_p; /* Pointer to the next char in LINE to be read. */
|
|
Packit |
d394d9 |
int_t column; /* Column that has been read. */
|
|
Packit |
d394d9 |
int_t line_number; /* Number of the line that has been read. */
|
|
Packit |
d394d9 |
int_t next_char; /* Buffer NEXT_CHAR if this source is backed up. */
|
|
Packit |
d394d9 |
int_t next_token; /* Buffer NEXT_TOKEN if this source is backed up. */
|
|
Packit |
d394d9 |
} source_t;
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/* Global variables. ========================================================*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
int_t next_token;
|
|
Packit |
d394d9 |
string_t token_name;
|
|
Packit |
d394d9 |
char_t *token_string;
|
|
Packit |
d394d9 |
double token_number;
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/* Variables. ===============================================================*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
static list_t sources; /* The list of sources, current source first. */
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
static string_t scanner_input;
|
|
Packit |
d394d9 |
/* If no file is included, the scanner reads its input from SCANNER_INPUT. */
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
static int_t next_char; /* The next unicode char to be read. */
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
static text_t *token_text; /* The text of the next token. */
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/* Functions. ===============================================================*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
static void
|
|
Packit |
d394d9 |
read_next_char( void )
|
|
Packit |
d394d9 |
/* Read the next char from input into NEXT_CHAR.
|
|
Packit |
d394d9 |
* If end of input stream is reached, return EOF.
|
|
Packit |
d394d9 |
* If no input stream is selected, read input from INPUT_BUFFER.
|
|
Packit |
d394d9 |
* If reading from stream, update column information. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
source_t *source;
|
|
Packit |
d394d9 |
int_t c;
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
source = (source_t *) sources.first;
|
|
Packit |
d394d9 |
if (scanner_input != NULL) /* Read from a string. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
if (*scanner_input == EOS)
|
|
Packit |
d394d9 |
next_char = EOF;
|
|
Packit |
d394d9 |
else
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
next_char = g_utf8_get_char( scanner_input );
|
|
Packit |
d394d9 |
scanner_input = g_utf8_next_char( scanner_input );
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else if (source != NULL) /* Read from a file. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
/* Read a new line if current line is empty. */
|
|
Packit |
d394d9 |
if (*source->next_char_p == EOS)
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
clear_text( source->line );
|
|
Packit |
d394d9 |
do
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
c = getc( source->stream );
|
|
Packit |
d394d9 |
if (c == EOS)
|
|
Packit |
d394d9 |
complain( "Null byte in \"%s\"", source->file_name );
|
|
Packit |
d394d9 |
else if (c == EOF)
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
if (ferror( source->stream ))
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
complain( "Can't read from \"%s\": %s.",
|
|
Packit |
d394d9 |
source->file_name, strerror( errno ) );
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else
|
|
Packit |
d394d9 |
break;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else
|
|
Packit |
d394d9 |
ADD_CHAR_TO_TEXT( source->line, c );
|
|
Packit |
d394d9 |
} while (c != '\n');
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
if (! g_utf8_validate( source->line->buffer, -1, NULL ))
|
|
Packit |
d394d9 |
complain( "Illegal UTF-8 character in \"%s\".", source->file_name );
|
|
Packit |
d394d9 |
source->next_char_p = source->line->buffer;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
if (*source->next_char_p == EOS)
|
|
Packit |
d394d9 |
next_char = EOF;
|
|
Packit |
d394d9 |
else
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
/* Get next char from current line. */
|
|
Packit |
d394d9 |
next_char = g_utf8_get_char( source->next_char_p );
|
|
Packit |
d394d9 |
source->next_char_p = g_utf8_next_char( source->next_char_p );
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/* Update line and column information. */
|
|
Packit |
d394d9 |
if (next_char == '\t')
|
|
Packit |
d394d9 |
source->column = (source->column + 8) & ~7;
|
|
Packit |
d394d9 |
else if (next_char == '\n')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
source->column = 0;
|
|
Packit |
d394d9 |
source->line_number++;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else if (next_char == '\r')
|
|
Packit |
d394d9 |
source->column = 0;
|
|
Packit |
d394d9 |
else if (next_char != EOF)
|
|
Packit |
d394d9 |
source->column++;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else
|
|
Packit |
d394d9 |
next_char = EOF;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
string_t
|
|
Packit |
d394d9 |
current_file_name( void )
|
|
Packit |
d394d9 |
/* Return the name of the file reading from or NULL. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
source_t *source;
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
source = (source_t *) sources.first;
|
|
Packit |
d394d9 |
if (source == NULL)
|
|
Packit |
d394d9 |
return NULL;
|
|
Packit |
d394d9 |
return source->file_name;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
int_t
|
|
Packit |
d394d9 |
current_line_number( void )
|
|
Packit |
d394d9 |
/* Return the line number where the last char has been read or -1. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
source_t *source;
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
source = (source_t *) sources.first;
|
|
Packit |
d394d9 |
if (source == NULL)
|
|
Packit |
d394d9 |
return -1;
|
|
Packit |
d394d9 |
return source->line_number;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
int_t
|
|
Packit |
d394d9 |
current_column( void )
|
|
Packit |
d394d9 |
/* Return the column where the last char has been read or -1. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
source_t *source;
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
source = (source_t *) sources.first;
|
|
Packit |
d394d9 |
if (source == NULL)
|
|
Packit |
d394d9 |
return -1;
|
|
Packit |
d394d9 |
if (source->column == 0)
|
|
Packit |
d394d9 |
return 0;
|
|
Packit |
d394d9 |
return source->column - 1; /* Let columns start with 0. */
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
void
|
|
Packit |
d394d9 |
set_scanner_input( string_t input )
|
|
Packit |
d394d9 |
/* Make the scanner use INPUT as scanner input
|
|
Packit |
d394d9 |
* until "set_scanner_input( NULL )" is called.
|
|
Packit |
d394d9 |
* INPUT must remain valid until then. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
source_t *source;
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
source = (source_t *) sources.first;
|
|
Packit |
d394d9 |
scanner_input = input;
|
|
Packit |
d394d9 |
if (input != NULL)
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
if (source != NULL)
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
source->next_char = next_char;
|
|
Packit |
d394d9 |
source->next_token = next_token;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
read_next_token();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else if (source != NULL)
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
next_char = source->next_char;
|
|
Packit |
d394d9 |
next_token = source->next_token;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
void
|
|
Packit |
d394d9 |
begin_include( string_t file_name )
|
|
Packit |
d394d9 |
/* Open a new level of inclusion and read tokens from file FILE_NAME. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
FILE *stream;
|
|
Packit |
d394d9 |
source_t *source;
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
source = (source_t *) sources.first;
|
|
Packit |
d394d9 |
stream = open_stream( file_name, "r" );
|
|
Packit |
d394d9 |
/* Next char of old source should be read later. */
|
|
Packit |
d394d9 |
if (source != NULL)
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
source->next_char = next_char;
|
|
Packit |
d394d9 |
source->next_token = next_token;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
/* Create new source description. */
|
|
Packit |
d394d9 |
source = new_node( &sources, sizeof( source_t ), LIST_START );
|
|
Packit |
d394d9 |
source->line = new_text();
|
|
Packit |
d394d9 |
source->next_char_p = source->line->buffer;
|
|
Packit |
d394d9 |
source->file_name = file_name;
|
|
Packit |
d394d9 |
source->line_number = 1;
|
|
Packit |
d394d9 |
source->column = 0;
|
|
Packit |
d394d9 |
source->stream = stream;
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
read_next_token();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
void
|
|
Packit |
d394d9 |
end_include( void )
|
|
Packit |
d394d9 |
/* Stop reading from current source stream and read from former stream. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
source_t *source;
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
source = (source_t *) sources.first;
|
|
Packit |
d394d9 |
close_stream( &source->stream, source->file_name );
|
|
Packit |
d394d9 |
free_text( &source->line );
|
|
Packit |
d394d9 |
free_first_node( &sources );
|
|
Packit |
d394d9 |
if (sources.first != NULL)
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
source = (source_t *) sources.first;
|
|
Packit |
d394d9 |
next_char = source->next_char;
|
|
Packit |
d394d9 |
next_token = source->next_token;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
void
|
|
Packit |
d394d9 |
end_includes( void )
|
|
Packit |
d394d9 |
/* Stop reading from all nested source streams. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
while (sources.first != NULL)
|
|
Packit |
d394d9 |
end_include();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
void
|
|
Packit |
d394d9 |
init_scanner( void )
|
|
Packit |
d394d9 |
/* Initialise the scanner. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
token_text = new_text();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
void
|
|
Packit |
d394d9 |
terminate_scanner( void )
|
|
Packit |
d394d9 |
/* Terminate the scanner, even when it's scanning. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
source_t *source;
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
scanner_input = NULL;
|
|
Packit |
d394d9 |
FOREACH_FREE( source, sources )
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
close_stream( &source->stream, NULL );
|
|
Packit |
d394d9 |
free_text( &source->line );
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
token_name = NULL;
|
|
Packit |
d394d9 |
free_text( &token_text );
|
|
Packit |
d394d9 |
free_mem( &token_string );
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
static void
|
|
Packit |
d394d9 |
read_name( void )
|
|
Packit |
d394d9 |
/* Read rule name, variable, or keyword into TOKEN_NAME. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
token_name = NULL;
|
|
Packit |
d394d9 |
clear_text( token_text );
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
while (next_char != EOF
|
|
Packit |
d394d9 |
&& (g_unichar_isalnum( next_char )
|
|
Packit |
d394d9 |
|| next_char == '_' || next_char == '&' || next_char == '|'))
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
add_unichar_to_text( token_text, next_char );
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
token_name = token_text->buffer;
|
|
Packit |
d394d9 |
if (*token_name == EOS)
|
|
Packit |
d394d9 |
complain( "Illegal character in name." );
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
static int_t
|
|
Packit |
d394d9 |
keyword_code( string_t name )
|
|
Packit |
d394d9 |
/* Look up NAME in the keyword table and return its token value.
|
|
Packit |
d394d9 |
* If NAME is no keyword, return TOK_IDENT. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
int_t lower, upper, middle, result;
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/* We do a binary search on the keywords.
|
|
Packit |
d394d9 |
* A keyword must be in the range of keywords[ lower..upper ]. */
|
|
Packit |
d394d9 |
lower = 0;
|
|
Packit |
d394d9 |
upper = NUMBER_OF_KEYWORDS - 1;
|
|
Packit |
d394d9 |
while (lower <= upper)
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
middle = (lower + upper) / 2;
|
|
Packit |
d394d9 |
result = strcmp_no_case( name, keywords[ middle ].name );
|
|
Packit |
d394d9 |
if (result < 0)
|
|
Packit |
d394d9 |
upper = middle - 1;
|
|
Packit |
d394d9 |
else if (result > 0)
|
|
Packit |
d394d9 |
lower = middle + 1;
|
|
Packit |
d394d9 |
else
|
|
Packit |
d394d9 |
return keywords[ middle ].code;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
return TOK_IDENT;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
static void
|
|
Packit |
d394d9 |
read_number( void )
|
|
Packit |
d394d9 |
/* Read a floating point number. Save its value in TOKEN_NUMBER. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
token_name = NULL;
|
|
Packit |
d394d9 |
clear_text( token_text );
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
while (next_char >= '0' && next_char <= '9')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
add_char_to_text( token_text, next_char );
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
if (next_char == 'l' || next_char == 'L')
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
else if (next_char == 'r' || next_char == 'R')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
insert_char_in_text( token_text, '-', 0 );
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
if (next_char == '.')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
add_char_to_text( token_text, next_char );
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
if (next_char < '0' || next_char >'9')
|
|
Packit |
d394d9 |
complain( "Missing digits after \".\"." );
|
|
Packit |
d394d9 |
while (next_char >= '0' && next_char <= '9')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
add_char_to_text( token_text, next_char );
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
if (next_char == 'E' || next_char == 'e')
|
|
Packit |
d394d9 |
{ /* Read an exponent. */
|
|
Packit |
d394d9 |
add_char_to_text( token_text, next_char );
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
if (next_char == '-' || next_char == '+')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
add_char_to_text( token_text, next_char );
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
if (next_char < '0' || next_char > '9')
|
|
Packit |
d394d9 |
complain( "Missing exponent." );
|
|
Packit |
d394d9 |
while (next_char >= '0' && next_char <= '9')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
add_char_to_text( token_text, next_char );
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
if (sscanf( token_text->buffer, "%lf", &token_number ) != 1)
|
|
Packit |
d394d9 |
complain( "Illegal number." );
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
static void
|
|
Packit |
d394d9 |
read_string( void )
|
|
Packit |
d394d9 |
/* Read a string. Save its value in TOKEN_STRING. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
int_t i;
|
|
Packit |
d394d9 |
u_int_t code;
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
token_name = NULL;
|
|
Packit |
d394d9 |
clear_text( token_text );
|
|
Packit |
d394d9 |
read_next_char(); /* Overread beginning '"'. */
|
|
Packit |
d394d9 |
while (next_char != '\"')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
if (next_char == EOF || next_char == '\n')
|
|
Packit |
d394d9 |
complain( "Unterminated string at end of line." );
|
|
Packit |
d394d9 |
if (next_char != '\\')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
add_unichar_to_text( token_text, next_char );
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
if (next_char == '\\' || next_char == '\"')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
add_char_to_text( token_text, next_char );
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else if (next_char >= '0' && next_char <= '7')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
code = 0;
|
|
Packit |
d394d9 |
for (i = 0; i < 3; i++)
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
if (next_char >= '0' && next_char <= '7')
|
|
Packit |
d394d9 |
code = 8 * code + (next_char - '0');
|
|
Packit |
d394d9 |
else
|
|
Packit |
d394d9 |
complain( "Escape sequence must have 3 octal digits." );
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
if (! g_unichar_validate( code ))
|
|
Packit |
d394d9 |
complain( "Escape sequence defines invalid character." );
|
|
Packit |
d394d9 |
add_unichar_to_text( token_text, code );
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else
|
|
Packit |
d394d9 |
complain( "Illegal escape sequence in string." );
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
read_next_char(); /* Read over final '"'. */
|
|
Packit |
d394d9 |
free_mem( &token_string ); /* Free old token string. */
|
|
Packit |
d394d9 |
token_string = new_string( token_text->buffer, NULL );
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
void
|
|
Packit |
d394d9 |
read_next_token( void )
|
|
Packit |
d394d9 |
/* Read the next token from current source into NEXT_TOKEN.
|
|
Packit |
d394d9 |
* If end of input stream is reached, return EOF. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
/* Read chars until a token has been recognised. */
|
|
Packit |
d394d9 |
while (TRUE)
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
switch (next_char)
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
case EOF:
|
|
Packit |
d394d9 |
next_token = EOF;
|
|
Packit |
d394d9 |
return;
|
|
Packit |
d394d9 |
case ' ':
|
|
Packit |
d394d9 |
case '\t':
|
|
Packit |
d394d9 |
case '\n': /* Read over whitespace. */
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
break;
|
|
Packit |
d394d9 |
case '\r':
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
if (next_char != '\n')
|
|
Packit |
d394d9 |
complain( "Carriage return without line feed." );
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
break;
|
|
Packit |
d394d9 |
case '#': /* Read over a comment. */
|
|
Packit |
d394d9 |
do
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
} while (next_char != '\n' && next_char != EOF);
|
|
Packit |
d394d9 |
break;
|
|
Packit |
d394d9 |
case '\"': /* Read a string. */
|
|
Packit |
d394d9 |
read_string();
|
|
Packit |
d394d9 |
next_token = TOK_STRING;
|
|
Packit |
d394d9 |
return;
|
|
Packit |
d394d9 |
case ':': /* Read a ":", ":=", ":=+", ":=-", ":=*", ":=/". */
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
if (next_char == '=')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
if (next_char == '+')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
next_token = TOK_ASSIGN_PLUS;
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else if (next_char == '-')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
next_token = TOK_ASSIGN_MINUS;
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else if (next_char == '*')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
next_token = TOK_ASSIGN_ASTERISK;
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else if (next_char == '/')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
next_token = TOK_ASSIGN_SLASH;
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else
|
|
Packit |
d394d9 |
next_token = TOK_ASSIGN;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else
|
|
Packit |
d394d9 |
next_token = ':';
|
|
Packit |
d394d9 |
return;
|
|
Packit |
d394d9 |
case '/': /* Read a "/", a "/=" or a "/~". */
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
if (next_char == '=')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
next_token = TOK_NOT_EQUAL;
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else if (next_char == '~')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
next_token = TOK_NOT_CONGRUENT;
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else
|
|
Packit |
d394d9 |
next_token = '/';
|
|
Packit |
d394d9 |
return;
|
|
Packit |
d394d9 |
case '0': case '1': case '2': case '3': case '4':
|
|
Packit |
d394d9 |
case '5': case '6': case '7': case '8': case '9':
|
|
Packit |
d394d9 |
/* Read a number. */
|
|
Packit |
d394d9 |
read_number();
|
|
Packit |
d394d9 |
next_token = TOK_NUMBER;
|
|
Packit |
d394d9 |
return;
|
|
Packit |
d394d9 |
case '$':
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
read_name();
|
|
Packit |
d394d9 |
next_token = TOK_VARIABLE;
|
|
Packit |
d394d9 |
return;
|
|
Packit |
d394d9 |
case '@':
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
read_name();
|
|
Packit |
d394d9 |
next_token = TOK_CONSTANT;
|
|
Packit |
d394d9 |
return;
|
|
Packit |
d394d9 |
default:
|
|
Packit |
d394d9 |
if (g_unichar_isalpha( next_char )
|
|
Packit |
d394d9 |
|| next_char == '_' || next_char == '&' || next_char == '|')
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
read_name();
|
|
Packit |
d394d9 |
next_token = keyword_code( token_name );
|
|
Packit |
d394d9 |
return;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
else
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
next_token = next_char;
|
|
Packit |
d394d9 |
read_next_char();
|
|
Packit |
d394d9 |
return;
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
string_t
|
|
Packit |
d394d9 |
token_as_text( int_t token )
|
|
Packit |
d394d9 |
/* Return TOKEN as a string readable for humans.
|
|
Packit |
d394d9 |
* The string must be freed after use. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
int_t i;
|
|
Packit |
d394d9 |
char token_buffer[2];
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/* Look if TOKEN is a keyword. */
|
|
Packit |
d394d9 |
for (i = 0; i < NUMBER_OF_KEYWORDS; i++)
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
if (keywords[i].code == token)
|
|
Packit |
d394d9 |
return concat_strings( "\"", keywords[i].name, "\"", NULL );
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
switch (token)
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
case EOF:
|
|
Packit |
d394d9 |
return new_string( "end of input", NULL );
|
|
Packit |
d394d9 |
case TOK_STRING:
|
|
Packit |
d394d9 |
return new_string( "string", NULL );
|
|
Packit |
d394d9 |
case TOK_IDENT:
|
|
Packit |
d394d9 |
return new_string( "identifier", NULL );
|
|
Packit |
d394d9 |
case TOK_VARIABLE:
|
|
Packit |
d394d9 |
return new_string( "variable", NULL );
|
|
Packit |
d394d9 |
case TOK_CONSTANT:
|
|
Packit |
d394d9 |
return new_string( "constant", NULL );
|
|
Packit |
d394d9 |
case TOK_NUMBER:
|
|
Packit |
d394d9 |
return new_string( "number", NULL );
|
|
Packit |
d394d9 |
case TOK_ASSIGN:
|
|
Packit |
d394d9 |
return new_string_readable( ":=", NULL );
|
|
Packit |
d394d9 |
case TOK_ASSIGN_PLUS:
|
|
Packit |
d394d9 |
return new_string_readable( ":=+", NULL );
|
|
Packit |
d394d9 |
case TOK_ASSIGN_MINUS:
|
|
Packit |
d394d9 |
return new_string_readable( ":=-", NULL );
|
|
Packit |
d394d9 |
case TOK_ASSIGN_ASTERISK:
|
|
Packit |
d394d9 |
return new_string_readable( ":=*", NULL );
|
|
Packit |
d394d9 |
case TOK_ASSIGN_SLASH:
|
|
Packit |
d394d9 |
return new_string_readable( ":=/", NULL );
|
|
Packit |
d394d9 |
case TOK_NOT_EQUAL:
|
|
Packit |
d394d9 |
return new_string_readable( "/=", NULL );
|
|
Packit |
d394d9 |
case TOK_NOT_CONGRUENT:
|
|
Packit |
d394d9 |
return new_string_readable( "/~", NULL );
|
|
Packit |
d394d9 |
default:
|
|
Packit |
d394d9 |
token_buffer[0] = token;
|
|
Packit |
d394d9 |
token_buffer[1] = EOS;
|
|
Packit |
d394d9 |
return new_string_readable( token_buffer, NULL );
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
void
|
|
Packit |
d394d9 |
test_token( int_t token )
|
|
Packit |
d394d9 |
/* Test if TOKEN is the next token. If it's not, report an error. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
if (next_token != token)
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
complain( "Expected %s, not %s.",
|
|
Packit |
d394d9 |
token_as_text( token ), token_as_text( next_token ) );
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/*---------------------------------------------------------------------------*/
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
void
|
|
Packit |
d394d9 |
parse_token( int_t token )
|
|
Packit |
d394d9 |
/* Test if TOKEN is the next token and read next token. */
|
|
Packit |
d394d9 |
{
|
|
Packit |
d394d9 |
test_token( token );
|
|
Packit |
d394d9 |
read_next_token();
|
|
Packit |
d394d9 |
}
|
|
Packit |
d394d9 |
|
|
Packit |
d394d9 |
/* End of file. =============================================================*/
|