8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-30 19:23:03 +01:00
firebird-mirror/src/qli/lex.cpp

1261 lines
27 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Command Oriented Query Language
* MODULE: lex.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: Lexical analyser
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
* 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define
*
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/ib_stdio.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "../qli/dtr.h"
#include "../qli/parse.h"
#include "../jrd/gds.h"
2001-05-23 15:26:42 +02:00
#include "../qli/all_proto.h"
#include "../qli/err_proto.h"
#include "../qli/hsh_proto.h"
#include "../qli/lex_proto.h"
#include "../qli/proc_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/utl_proto.h"
#include "../jrd/gdsassert.h"
2001-05-23 15:26:42 +02:00
2001-07-12 07:46:06 +02:00
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_CTYPES_H
#include <ctypes.h>
#endif
2001-05-23 15:26:42 +02:00
#ifdef VMS
#include <descrip.h>
#define LIB$_INPSTRTRU 0x15821c
#endif
#ifdef UNIX
#define UNIX_LINE 1
#endif
#if (defined WIN_NT)
#include <io.h>
#define UNIX_LINE 1
#define PC_FILE_SEEK
#endif
2003-07-06 09:04:03 +02:00
#ifdef SMALL_FILE_NAMES
#define SCRATCH "fb_q"
#else
#define SCRATCH "fb_query_"
#endif
2001-05-23 15:26:42 +02:00
#ifndef FOPEN_INPUT_TYPE
#define FOPEN_INPUT_TYPE "r"
#endif
2003-09-10 19:52:12 +02:00
extern bool sw_verify;
extern bool sw_trace;
2001-05-23 15:26:42 +02:00
2003-09-10 19:52:12 +02:00
static bool get_line(IB_FILE *, TEXT *, USHORT);
static int nextchar(const bool);
static void next_line(const bool);
static void retchar(const SSHORT);
2003-09-10 19:52:12 +02:00
static bool scan_number(SSHORT, TEXT **);
2001-05-23 15:26:42 +02:00
static int skip_white(void);
static LLS QLI_statements;
static int QLI_position;
static IB_FILE *input_file = NULL, *trace_file = NULL;
static char trace_file_name[MAXPATHLEN];
2001-05-23 15:26:42 +02:00
static SLONG trans_limit;
#define TRANS_LIMIT 10
const char CHR_ident = char(1);
const char CHR_letter = char(2);
const char CHR_digit = char(4);
const char CHR_quote = char(8);
const char CHR_white = char(16);
const char CHR_eol = char(32);
const char CHR_IDENT = CHR_ident;
const char CHR_LETTER = CHR_letter + CHR_ident;
const char CHR_DIGIT = CHR_digit + CHR_ident;
const char CHR_QUOTE = CHR_quote;
const char CHR_WHITE = CHR_white;
const char CHR_EOL = CHR_eol;
static const char* const eol_string = "end of line";
static const char* const eof_string = "*end_of_file*";
2001-05-23 15:26:42 +02:00
static const char classes[256] =
{
2001-05-23 15:26:42 +02:00
0, 0, 0, 0, 0, 0, 0, 0,
0, CHR_WHITE, CHR_EOL, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
CHR_WHITE, 0, CHR_QUOTE, CHR_IDENT, CHR_LETTER, 0, 0, CHR_QUOTE,
0, 0, 0, 0, 0, 0, 0, 0,
CHR_DIGIT, CHR_DIGIT, CHR_DIGIT, CHR_DIGIT, CHR_DIGIT, CHR_DIGIT,
CHR_DIGIT, CHR_DIGIT,
CHR_DIGIT, CHR_DIGIT, 0, 0, 0, 0, 0, 0,
0, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER,
CHR_LETTER,
CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER, CHR_LETTER, 0, 0, 0, 0, CHR_IDENT,
0, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER,
CHR_LETTER,
CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER, CHR_LETTER, 0
};
2003-09-10 19:52:12 +02:00
bool LEX_active_procedure(void)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ a c t i v e _ p r o c e d u r e
*
**************************************
*
* Functional description
2003-09-10 19:52:12 +02:00
* Return true if we're running out of a
* procedure and false otherwise. Somebody
2001-05-23 15:26:42 +02:00
* somewhere may care.
*
**************************************/
2003-09-10 19:52:12 +02:00
return (QLI_line->line_type == line_blob);
2001-05-23 15:26:42 +02:00
}
void LEX_edit( SLONG start, SLONG stop)
{
/**************************************
*
* L E X _ e d i t
*
**************************************
*
* Functional description
* Dump the last full statement into a scratch file, then
* push the scratch file on the input stack.
*
**************************************/
TEXT filename[MAXPATHLEN];
2001-05-23 15:26:42 +02:00
IB_FILE* scratch = (IB_FILE*) gds__temp_file(TRUE, SCRATCH, filename);
2001-05-23 15:26:42 +02:00
if (scratch == (IB_FILE *) - 1)
IBERROR(61); // Msg 61 couldn't open scratch file
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
stop--;
#endif
if (ib_fseek(trace_file, start, 0)) {
ib_fseek(trace_file, (SLONG) 0, 2);
IBERROR(59); // Msg 59 ib_fseek failed
2001-05-23 15:26:42 +02:00
}
while (++start <= stop) {
const SSHORT c = ib_getc(trace_file);
2001-05-23 15:26:42 +02:00
if (c == EOF)
break;
ib_putc(c, scratch);
}
ib_fclose(scratch);
if (gds__edit(filename, TRUE))
2003-09-10 19:52:12 +02:00
LEX_push_file(filename, true);
2001-05-23 15:26:42 +02:00
unlink(filename);
ib_fseek(trace_file, (SLONG) 0, 2);
}
TOK LEX_edit_string(void)
{
/**************************************
*
* L E X _ e d i t _ s t r i n g
*
**************************************
*
* Functional description
* Parse the next token as an edit_string.
*
**************************************/
SSHORT c;
TOK token = QLI_token;
2001-05-23 15:26:42 +02:00
do {
2001-05-23 15:26:42 +02:00
c = skip_white();
} while (c == '\n');
TEXT* p = token->tok_string;
2001-05-23 15:26:42 +02:00
*p = c;
if (!QLI_line) {
token->tok_symbol = NULL;
token->tok_keyword = KW_none;
return NULL;
}
while (!(classes[c] & (CHR_white | CHR_eol))) {
*p++ = c;
if (classes[c] & CHR_quote)
for (;;) {
const SSHORT d = nextchar(false);
if (d == '\n') {
2001-05-23 15:26:42 +02:00
retchar(d);
break;
}
*p++ = d;
if (d == c)
break;
}
2003-09-10 19:52:12 +02:00
c = nextchar(true);
2001-05-23 15:26:42 +02:00
}
retchar(c);
if (p[-1] == ',')
retchar(*--p);
token->tok_length = p - token->tok_string;
*p = 0;
token->tok_symbol = NULL;
token->tok_keyword = KW_none;
if (sw_trace)
ib_puts(token->tok_string);
return token;
}
TOK LEX_filename(void)
{
/**************************************
*
* L E X _ f i l e n a m e
*
**************************************
*
* Functional description
* Parse the next token as a filename.
*
**************************************/
SSHORT c;
2001-05-23 15:26:42 +02:00
TOK token = QLI_token;
2001-05-23 15:26:42 +02:00
do {
2001-05-23 15:26:42 +02:00
c = skip_white();
} while (c == '\n');
TEXT* p = token->tok_string;
2001-05-23 15:26:42 +02:00
*p++ = c;
// If there isn't a line, we're all done
2001-05-23 15:26:42 +02:00
if (!QLI_line) {
token->tok_symbol = NULL;
token->tok_keyword = KW_none;
return NULL;
}
// notice if this looks like a quoted file name
2001-05-23 15:26:42 +02:00
SSHORT save = 0;
2001-05-23 15:26:42 +02:00
if (classes[c] & CHR_quote) {
token->tok_type = tok_quoted;
save = c;
}
// Look for white space or end of line, allowing embedded quoted strings.
2001-05-23 15:26:42 +02:00
for (;;) {
2003-09-10 19:52:12 +02:00
c = nextchar(true);
char char_class = classes[c];
2001-05-23 15:26:42 +02:00
if (c == '"' && c != save) {
*p++ = c;
for (;;) {
2003-09-10 19:52:12 +02:00
c = nextchar(true);
char_class = classes[c];
if ((char_class & CHR_eol) || c == '"')
2001-05-23 15:26:42 +02:00
break;
*p++ = c;
}
}
if (char_class & (CHR_white | CHR_eol))
2001-05-23 15:26:42 +02:00
break;
*p++ = c;
}
retchar(c);
// Drop trailing semi-colon to avoid confusion
2001-05-23 15:26:42 +02:00
if (p[-1] == ';') {
retchar(c);
--p;
}
// complain on unterminated quoted string
2001-05-23 15:26:42 +02:00
if ((token->tok_type == tok_quoted) && (p[-1] != save))
IBERROR(60); // Msg 60 unterminated quoted string
2001-05-23 15:26:42 +02:00
token->tok_length = p - token->tok_string;
*p = 0;
token->tok_symbol = NULL;
token->tok_keyword = KW_none;
if (sw_trace)
ib_puts(token->tok_string);
return token;
}
void LEX_fini(void)
{
/**************************************
*
* L E X _ f i n i
*
**************************************
*
* Functional description
* Shut down LEX in a more or less clean way.
*
**************************************/
if (trace_file && (trace_file != (IB_FILE *) - 1)) {
ib_fclose(trace_file);
unlink(trace_file_name);
2001-05-23 15:26:42 +02:00
}
}
void LEX_flush(void)
{
/**************************************
*
* L E X _ f l u s h
*
**************************************
*
* Functional description
* Flush the input stream after an error.
2001-05-23 15:26:42 +02:00
*
**************************************/
trans_limit = 0;
if (!QLI_line)
return;
// Pop off line sources until we're down to the last one.
2001-05-23 15:26:42 +02:00
while (QLI_line->line_next)
LEX_pop_line();
// Look for a semi-colon
2001-05-23 15:26:42 +02:00
if (QLI_semi)
while (QLI_line && QLI_token->tok_keyword != KW_SEMI)
LEX_token();
else
while (QLI_line && QLI_token->tok_type != tok_eol)
LEX_token();
}
#ifdef UNIX_LINE
2003-09-10 19:52:12 +02:00
bool LEX_get_line(TEXT * prompt,
TEXT * buffer,
int size)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ g e t _ l i n e ( U N I X )
*
**************************************
*
* Functional description
* Give a prompt and read a line. If the line is terminated by
2003-09-10 19:52:12 +02:00
* an EOL, return true. If the buffer is exhausted and non-blanks
2001-05-23 15:26:42 +02:00
* would be discarded, return an error. If EOF is detected,
2003-09-10 19:52:12 +02:00
* return false. Regardless, a null terminated string is returned.
2001-05-23 15:26:42 +02:00
*
**************************************/
SSHORT c;
// UNIX flavor
2001-05-23 15:26:42 +02:00
if (prompt)
ib_printf(prompt);
errno = 0;
TEXT* p = buffer;
2001-05-23 15:26:42 +02:00
bool overflow_flag = false;
2003-09-10 19:52:12 +02:00
while (true) {
2001-05-23 15:26:42 +02:00
c = ib_getc(input_file);
if (c == EOF) {
if (SYSCALL_INTERRUPTED(errno) && !QLI_abort) {
errno = 0;
continue;
}
// The check for this actually being a terminal that is at
// end of file is to prevent looping through a redirected
// ib_stdin (e.g., a script file).
2001-05-23 15:26:42 +02:00
if (prompt && isatty(ib_fileno(ib_stdin))) {
ib_rewind(ib_stdin);
ib_putchar('\n');
}
if (QLI_abort)
continue;
else
break;
}
if (--size > 0)
*p++ = c;
else if (c != ' ' && c != '\n')
2003-09-10 19:52:12 +02:00
overflow_flag = true;
2001-05-23 15:26:42 +02:00
if (c == '\n')
break;
}
*p = 0;
if (c == EOF)
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
if (overflow_flag) {
buffer[0] = 0;
IBERROR(476); // Msg 476 input line too long
2001-05-23 15:26:42 +02:00
}
if (sw_verify)
ib_fputs(buffer, ib_stdout);
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
#endif
#ifdef VMS
2003-09-10 19:52:12 +02:00
bool LEX_get_line(TEXT * prompt,
TEXT * buffer,
int size)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ g e t _ l i n e ( V M S )
*
**************************************
*
* Functional description
* Give a prompt and read a line. If the line is terminated by
2003-09-10 19:52:12 +02:00
* an EOL, return true. If the buffer is exhausted and non-blanks
2001-05-23 15:26:42 +02:00
* would be discarded, return an error. If EOF is detected,
2003-09-10 19:52:12 +02:00
* return false. Regardless, a null terminated string is returned.
2001-05-23 15:26:42 +02:00
*
**************************************/
struct dsc$descriptor_s line_desc, prompt_desc;
SLONG status;
SSHORT length;
// We're going to add a null to the end, so don't read too much
2001-05-23 15:26:42 +02:00
--size;
line_desc.dsc$b_class = DSC$K_CLASS_S;
line_desc.dsc$b_dtype = DSC$K_DTYPE_T;
line_desc.dsc$w_length = size;
line_desc.dsc$a_pointer = buffer;
if (prompt) {
prompt_desc.dsc$b_class = DSC$K_CLASS_S;
prompt_desc.dsc$b_dtype = DSC$K_DTYPE_T;
prompt_desc.dsc$w_length = strlen(prompt);
prompt_desc.dsc$a_pointer = prompt;
status = lib$get_input(&line_desc, &prompt_desc, &length);
}
else
status = lib$get_input(&line_desc, NULL, &length);
SCHAR* p = buffer + length;
2001-05-23 15:26:42 +02:00
if (!(status & 1)) {
if (status != LIB$_INPSTRTRU)
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
buffer[0] = 0;
IBERROR(476); // Msg 476 input line too long
2001-05-23 15:26:42 +02:00
}
else if (length < size)
*p++ = '\n';
*p = 0;
if (sw_verify) {
line_desc.dsc$w_length = length;
lib$put_output(&line_desc);
}
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
#endif
void LEX_init(void)
{
/**************************************
*
* L E X _ i n i t
*
**************************************
*
* Functional description
* Initialize for lexical scanning. While we're at it, open a
* scratch trace file to keep all input.
*
**************************************/
2003-02-08 01:58:10 +01:00
trace_file = (IB_FILE*) gds__temp_file(TRUE, SCRATCH, trace_file_name);
2001-05-23 15:26:42 +02:00
if (trace_file == (IB_FILE *) - 1)
IBERROR(61); // Msg 61 couldn't open scratch file
2001-05-23 15:26:42 +02:00
QLI_token = (TOK) ALLOCPV(type_tok, MAXSYMLEN);
QLI_line = (LINE) ALLOCPV(type_line, 0);
QLI_line->line_size = sizeof(QLI_line->line_data);
QLI_line->line_ptr = QLI_line->line_data;
QLI_line->line_type = line_stdin;
QLI_line->line_source = (FRBRD *) ib_stdin;
2001-05-23 15:26:42 +02:00
QLI_semi = FALSE;
input_file = ib_stdin;
HSH_init();
}
void LEX_mark_statement(void)
{
/**************************************
*
* L E X _ m a r k _ s t a t e m e n t
*
**************************************
*
* Functional description
* Push a position in the trace file onto
* the statement stack.
*
**************************************/
LINE temp;
for (temp = QLI_line;
temp->line_next && QLI_statements;
temp = temp->line_next)
{
if (temp->line_next->line_position == (SLONG) QLI_statements->lls_object)
return;
}
2001-05-23 15:26:42 +02:00
LLS statement = (LLS) ALLOCP(type_lls);
2001-05-23 15:26:42 +02:00
statement->lls_object = (BLK) temp->line_position;
statement->lls_next = QLI_statements;
QLI_statements = statement;
}
void LEX_pop_line(void)
{
/**************************************
*
* L E X _ p o p _ l i n e
*
**************************************
*
* Functional description
* We're done with the current line source. Close it out
* and release the line block.
*
**************************************/
LINE temp = QLI_line;
2001-05-23 15:26:42 +02:00
QLI_line = temp->line_next;
if (temp->line_type == line_blob)
PRO_close(temp->line_database, temp->line_source);
else if (temp->line_type == line_file)
ib_fclose((IB_FILE *) temp->line_source);
2001-07-12 07:46:06 +02:00
ALL_release((FRB) temp);
2001-05-23 15:26:42 +02:00
}
void LEX_procedure( DBB database, FRBRD *blob)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ p r o c e d u r e
*
**************************************
*
* Functional description
* Push a blob-resident procedure onto the input line source
* stack;
*
**************************************/
LINE temp = (LINE) ALLOCPV(type_line, QLI_token->tok_length);
2001-05-23 15:26:42 +02:00
temp->line_source = blob;
strncpy(temp->line_source_name, QLI_token->tok_string,
QLI_token->tok_length);
temp->line_type = line_blob;
temp->line_database = database;
temp->line_size = sizeof(temp->line_data);
temp->line_ptr = temp->line_data;
temp->line_position = QLI_position;
temp->line_next = QLI_line;
QLI_line = temp;
}
bool LEX_push_file(const TEXT* filename,
const bool error_flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ p u s h _ f i l e
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Open a command file. If the open succeedes, push the input
* source onto the source stack. If the open fails, give an error
* if the error flag is set, otherwise return quietly.
*
**************************************/
IB_FILE *file;
if (!(file = ib_fopen(filename, FOPEN_INPUT_TYPE))) {
TEXT buffer[64];
2001-05-23 15:26:42 +02:00
sprintf(buffer, "%s.com", filename);
if (!(file = ib_fopen(buffer, FOPEN_INPUT_TYPE))) {
if (error_flag)
ERRQ_msg_put(67, filename, NULL, NULL, NULL, NULL);
// Msg 67 can't open command file \"%s\"\n
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
}
}
LINE line = (LINE) ALLOCPV(type_line, strlen(filename));
2001-05-23 15:26:42 +02:00
line->line_type = line_file;
line->line_source = (FRBRD *) file;
2001-05-23 15:26:42 +02:00
line->line_size = sizeof(line->line_data);
line->line_ptr = line->line_data;
*line->line_ptr = 0;
strcpy(line->line_source_name, filename);
line->line_next = QLI_line;
QLI_line = line;
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
bool LEX_push_string(const TEXT* const string)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ p u s h _ s t r i n g
*
**************************************
*
* Functional description
* Push a simple string on as an input source.
*
**************************************/
LINE line = (LINE) ALLOCPV(type_line, 0);
2001-05-23 15:26:42 +02:00
line->line_type = line_string;
line->line_size = strlen(string);
line->line_ptr = line->line_data;
strcpy(line->line_data, string);
line->line_data[line->line_size] = '\n';
line->line_next = QLI_line;
QLI_line = line;
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
void LEX_put_procedure( FRBRD *blob, SLONG start, SLONG stop)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ p u t _ p r o c e d u r e
*
**************************************
*
* Functional description
* Write text from the scratch trace file into a blob.
*
**************************************/
2003-04-16 12:18:51 +02:00
ISC_STATUS_ARRAY status_vector;
2001-05-23 15:26:42 +02:00
if (ib_fseek(trace_file, start, 0)) {
ib_fseek(trace_file, (SLONG) 0, 2);
IBERROR(62); // Msg 62 ib_fseek failed
2001-05-23 15:26:42 +02:00
}
int length = stop - start;
TEXT buffer[1024];
2001-05-23 15:26:42 +02:00
while (length) {
TEXT* p = buffer;
2001-05-23 15:26:42 +02:00
while (length) {
--length;
const SSHORT c = ib_getc(trace_file);
*p++ = c;
2001-05-23 15:26:42 +02:00
if (c == '\n') {
#ifdef PC_FILE_SEEK
// account for the extra line-feed on OS/2 and Windows NT
2001-05-23 15:26:42 +02:00
if (length)
--length;
#endif
break;
}
}
const SSHORT l = p - buffer;
if (l)
2003-08-30 04:12:44 +02:00
if (gds__put_segment(status_vector, &blob, l, buffer))
BUGCHECK(58); // Msg 58 gds__put_segment failed
2001-05-23 15:26:42 +02:00
}
ib_fseek(trace_file, (SLONG) 0, 2);
}
void LEX_real(void)
{
/**************************************
*
* L E X _ r e a l
*
**************************************
*
* Functional description
* If the token is an end of line, get the next token.
*
**************************************/
while (QLI_token->tok_type == tok_eol)
LEX_token();
}
LLS LEX_statement_list(void)
{
/**************************************
*
* L E X _ s t a t e m e n t _ l i s t
*
**************************************
*
* Functional description
* Somebody outside needs to know
* where the top of the statement list
* is.
*
**************************************/
return QLI_statements;
}
TOK LEX_token(void)
{
/**************************************
*
* L E X _ t o k e n
*
**************************************
*
* Functional description
* Parse and return the next token.
*
**************************************/
SSHORT c;
2001-05-23 15:26:42 +02:00
TOK token = QLI_token;
TEXT* p = token->tok_string;
2001-05-23 15:26:42 +02:00
// Get next significant byte. If it's the last EOL of a blob, throw it away
2001-05-23 15:26:42 +02:00
for (;;) {
c = skip_white();
if (c != '\n' || QLI_line->line_type != line_blob)
break;
LINE prior = QLI_line;
2003-09-10 19:52:12 +02:00
next_line(true);
2001-05-23 15:26:42 +02:00
if (prior == QLI_line)
break;
}
// If we hit end of file, make up a phoney token
2001-05-23 15:26:42 +02:00
if (!QLI_line) {
const TEXT* q = eof_string;
2001-05-23 15:26:42 +02:00
while (*p++ = *q++);
token->tok_type = tok_eof;
token->tok_keyword = KW_none;
return NULL;
}
*p++ = c;
QLI_token->tok_position = QLI_line->line_position +
QLI_line->line_ptr - QLI_line->line_data - 1;
// On end of file, generate furious but phone end of line tokens
2001-05-23 15:26:42 +02:00
char char_class = classes[c];
2001-05-23 15:26:42 +02:00
if (char_class & CHR_letter) {
for (c = nextchar(true); classes[c] & CHR_ident; c = nextchar(true))
2001-05-23 15:26:42 +02:00
*p++ = c;
retchar(c);
token->tok_type = tok_ident;
}
else if (((char_class & CHR_digit) || c == '.') && scan_number(c, &p))
2001-05-23 15:26:42 +02:00
token->tok_type = tok_number;
else if (char_class & CHR_quote) {
2001-05-23 15:26:42 +02:00
token->tok_type = tok_quoted;
SSHORT next;
2003-09-10 19:52:12 +02:00
while (true) {
if (!(next = nextchar(false)) || next == '\n') {
2001-05-23 15:26:42 +02:00
retchar(next);
IBERROR(63); // Msg 63 unterminated quoted string
2001-05-23 15:26:42 +02:00
break;
}
*p++ = next;
if ((p - token->tok_string) >= MAXSYMLEN)
ERRQ_msg_put(470, (TEXT *) MAXSYMLEN, NULL, NULL, NULL, NULL); // Msg 470 literal too long
2001-05-23 15:26:42 +02:00
// If there are 2 quotes in a row, interpret 2nd as a literal
2001-05-23 15:26:42 +02:00
if (next == c) {
const SSHORT peek = nextchar(false);
2001-05-23 15:26:42 +02:00
retchar(peek);
if (peek != c)
break;
2003-09-10 19:52:12 +02:00
nextchar(false);
2001-05-23 15:26:42 +02:00
}
}
}
else if (c == '\n') {
// end of line, signal it properly with a phoney token.
2001-05-23 15:26:42 +02:00
token->tok_type = tok_eol;
--p;
const TEXT* q = eol_string;
2001-05-23 15:26:42 +02:00
while (*q)
*p++ = *q++;
}
else {
token->tok_type = tok_punct;
2003-09-10 19:52:12 +02:00
*p++ = nextchar(true);
2001-05-23 15:26:42 +02:00
if (!HSH_lookup(token->tok_string, 2))
retchar(*--p);
}
token->tok_length = p - token->tok_string;
*p = '\0';
if (token->tok_string[0] == '$' &&
trans_limit < TRANS_LIMIT && (p = getenv(token->tok_string + 1)))
{
2001-05-23 15:26:42 +02:00
LEX_push_string(p);
++trans_limit;
token = LEX_token();
--trans_limit;
return token;
}
SYM symbol = HSH_lookup(token->tok_string, token->tok_length);
token->tok_symbol = symbol;
2001-05-23 15:26:42 +02:00
if (symbol && symbol->sym_type == SYM_keyword)
token->tok_keyword = (KWWORDS) symbol->sym_keyword;
else
token->tok_keyword = KW_none;
if (sw_trace)
ib_puts(token->tok_string);
return token;
}
2003-09-10 19:52:12 +02:00
static bool get_line(IB_FILE * file,
TEXT * buffer,
USHORT size)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g e t _ l i n e
*
**************************************
*
* Functional description
* Read a line. If the line is terminated by
2003-09-10 19:52:12 +02:00
* an EOL, return true. If the buffer is exhausted and non-blanks
2001-05-23 15:26:42 +02:00
* would be discarded, return an error. If EOF is detected,
2003-09-10 19:52:12 +02:00
* return false. Regardless, a null terminated string is returned.
2001-05-23 15:26:42 +02:00
*
**************************************/
2003-09-10 19:52:12 +02:00
bool overflow_flag = false;
2001-05-23 15:26:42 +02:00
SSHORT c;
errno = 0;
TEXT* p = buffer;
SLONG length = size;
2001-05-23 15:26:42 +02:00
2003-09-10 19:52:12 +02:00
while (true) {
2001-05-23 15:26:42 +02:00
c = ib_getc(file);
if (c == EOF) {
if (SYSCALL_INTERRUPTED(errno) && !QLI_abort) {
errno = 0;
continue;
}
if (QLI_abort)
continue;
else
break;
}
if (--length > 0)
*p++ = c;
else if (c != ' ' && c != '\n')
2003-09-10 19:52:12 +02:00
overflow_flag = true;
2001-05-23 15:26:42 +02:00
if (c == '\n')
break;
}
*p = 0;
if (c == EOF)
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
if (overflow_flag)
IBERROR(477); // Msg 477 input line too long
2001-05-23 15:26:42 +02:00
if (sw_verify)
ib_fputs(buffer, ib_stdout);
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
static int nextchar(const bool eof_ok)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* n e x t c h a r
*
**************************************
*
* Functional description
* Get the next character from the input stream.
*
**************************************/
SSHORT c;
// Get the next character in the current line. If we run out,
// get the next line. If the line source runs out, pop the
// line source. If we run out of line sources, we are distinctly
// at end of file.
2001-05-23 15:26:42 +02:00
while (QLI_line) {
if (c = *QLI_line->line_ptr++)
return c;
next_line(eof_ok);
}
return 0;
}
static void next_line(const bool eof_ok)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* n e x t _ l i n e
*
**************************************
*
* Functional description
* Get the next line from the input stream.
*
**************************************/
TEXT *p, *q;
2001-05-23 15:26:42 +02:00
while (QLI_line) {
bool flag = false;
2001-05-23 15:26:42 +02:00
// Get next line from where ever. If it comes from either the terminal
// or command file, check for another command file.
2001-05-23 15:26:42 +02:00
if (QLI_line->line_type == line_blob) {
// If the current blob segment contains another line, use it
2001-05-23 15:26:42 +02:00
if ((p = QLI_line->line_ptr) != QLI_line->line_data
&& p[-1] == '\n' && *p)
2003-09-10 19:52:12 +02:00
flag = true;
2001-05-23 15:26:42 +02:00
else {
// Initialize line block for retrieval
2001-05-23 15:26:42 +02:00
p = QLI_line->line_data;
QLI_line->line_ptr = QLI_line->line_data;
2003-09-10 19:52:12 +02:00
flag = PRO_get_line(QLI_line->line_source, p,
2001-05-23 15:26:42 +02:00
QLI_line->line_size);
if (flag && QLI_echo)
ib_printf("%s", QLI_line->line_data);
}
}
else {
// Initialize line block for retrieval
2001-05-23 15:26:42 +02:00
QLI_line->line_ptr = QLI_line->line_data;
p = QLI_line->line_data;
if (QLI_line->line_type == line_stdin)
flag = LEX_get_line(QLI_prompt, p, (int) QLI_line->line_size);
else if (QLI_line->line_type == line_file) {
2003-09-10 19:52:12 +02:00
flag = get_line((FILE*) QLI_line->line_source, p, QLI_line->line_size);
2001-05-23 15:26:42 +02:00
if (QLI_echo)
ib_printf("%s", QLI_line->line_data);
}
if (flag) {
for (q = p; classes[*q] & CHR_white; q++);
if (*q == '@') {
TEXT filename[MAXPATHLEN];
2001-05-23 15:26:42 +02:00
for (p = q + 1, q = filename; *p && *p != '\n';)
*q++ = *p++;
*q = 0;
QLI_line->line_ptr = p;
2003-09-10 19:52:12 +02:00
LEX_push_file(filename, true);
2001-05-23 15:26:42 +02:00
continue;
}
}
}
// If get got a line, we're almost done
2001-05-23 15:26:42 +02:00
if (flag)
break;
// We hit end of file. Either complain about the circumstances
// or just close out the current input source. Don't close the
// input source if it's the terminal and we're at a continuation
// prompt.
2001-05-23 15:26:42 +02:00
if (eof_ok && (QLI_line->line_next || QLI_prompt != QLI_cont_string)) {
LEX_pop_line();
return;
}
// this is an unexpected end of file
2001-05-23 15:26:42 +02:00
if (QLI_line->line_type == line_blob)
ERRQ_print_error(64, QLI_line->line_source_name, NULL, NULL, NULL,
NULL);
// Msg 64 unexpected end of procedure in procedure %s
2001-05-23 15:26:42 +02:00
else if (QLI_line->line_type == line_file)
ERRQ_print_error(65, QLI_line->line_source_name, NULL, NULL, NULL,
NULL);
// Msg 65 unexpected end of file in file %s
2001-05-23 15:26:42 +02:00
else {
if (QLI_line->line_type == line_string)
LEX_pop_line();
IBERROR(66); // Msg 66 unexpected eof
2001-05-23 15:26:42 +02:00
}
}
if (!QLI_line)
return;
QLI_line->line_position = QLI_position;
// Dump output to the trace file
2001-05-23 15:26:42 +02:00
if (QLI_line->line_type == line_blob)
while (*p)
p++;
else {
while (*p)
ib_putc(*p++, trace_file);
QLI_position += (TEXT *) p - QLI_line->line_data;
#ifdef PC_FILE_SEEK
// account for the extra line-feed on OS/2 and Windows NT
// to determine file position
2001-05-23 15:26:42 +02:00
QLI_position++;
#endif
}
QLI_line->line_length = (TEXT *) p - QLI_line->line_data;
}
static void retchar(const SSHORT c) // unused, now checked in DEBUG mode.
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* r e t c h a r
*
**************************************
*
* Functional description
* Return a character to the input stream.
*
**************************************/
// CVC: Too naive implementation: what if the pointer is at the beginning?
// What if the character being returned isn't the character that was gotten?
fb_assert(QLI_line && QLI_line->line_ptr[-1] == c);
2001-05-23 15:26:42 +02:00
--QLI_line->line_ptr;
}
2003-09-10 19:52:12 +02:00
static bool scan_number(SSHORT c,
TEXT ** ptr)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s c a n _ n u m b e r
*
**************************************
*
* Functional description
* Pass on a possible numeric literal.
*
**************************************/
2003-09-10 19:52:12 +02:00
bool dot = false;
2001-05-23 15:26:42 +02:00
TEXT* p = *ptr;
2001-05-23 15:26:42 +02:00
// If this is a leading decimal point, check that the next
// character is really a digit, otherwise backout
2001-05-23 15:26:42 +02:00
if (c == '.') {
2003-09-10 19:52:12 +02:00
retchar(c = nextchar(true));
2001-05-23 15:26:42 +02:00
if (!(classes[c] & CHR_digit))
2003-09-10 19:52:12 +02:00
return false;
dot = true;
2001-05-23 15:26:42 +02:00
}
// Gobble up digits up to a single decimal point
2001-05-23 15:26:42 +02:00
for (;;) {
2003-09-10 19:52:12 +02:00
c = nextchar(true);
2001-05-23 15:26:42 +02:00
if (classes[c] & CHR_digit)
*p++ = c;
else if (!dot && c == '.') {
*p++ = c;
2003-09-10 19:52:12 +02:00
dot = true;
2001-05-23 15:26:42 +02:00
}
else
break;
}
// If this is an exponential, eat the exponent sign and digits
2001-05-23 15:26:42 +02:00
if (UPPER(c) == 'E') {
*p++ = c;
2003-09-10 19:52:12 +02:00
c = nextchar(true);
2001-05-23 15:26:42 +02:00
if (c == '+' || c == '-') {
*p++ = c;
2003-09-10 19:52:12 +02:00
c = nextchar(true);
2001-05-23 15:26:42 +02:00
}
while (classes[c] & CHR_digit) {
*p++ = c;
2003-09-10 19:52:12 +02:00
c = nextchar(true);
2001-05-23 15:26:42 +02:00
}
}
retchar(c);
*ptr = p;
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
static int skip_white(void)
{
/**************************************
*
* s k i p _ w h i t e
*
**************************************
*
* Functional description
* Skip over while space and comments in input stream
*
**************************************/
SSHORT c;
2001-05-23 15:26:42 +02:00
2003-09-10 19:52:12 +02:00
while (true) {
c = nextchar(true);
const char char_class = classes[c];
if (char_class & CHR_white)
2001-05-23 15:26:42 +02:00
continue;
if (c == '/') {
SSHORT next;
2003-09-10 19:52:12 +02:00
if ((next = nextchar(true)) != '*') {
2001-05-23 15:26:42 +02:00
retchar(next);
return c;
}
2003-09-10 19:52:12 +02:00
c = nextchar(false);
while ((next = nextchar(false)) && !(c == '*' && next == '/'))
2001-05-23 15:26:42 +02:00
c = next;
continue;
}
break;
}
return c;
}