8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 17:23:03 +01:00
firebird-mirror/src/gpre/par.cpp

3222 lines
80 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
//____________________________________________________________
//
// PROGRAM: C preprocessor
// MODULE: par.cpp
// DESCRIPTION: Parser
//
// 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): ______________________________________.
2005-01-05 05:02:19 +01:00
2001-05-23 15:26:42 +02:00
// Revision 1.2 2000/11/27 09:26:13 fsg
// Fixed bugs in gpre to handle PYXIS forms
// and allow edit.e and fred.e to go through
// gpre without errors (and correct result).
//
// This is a partial fix until all
// PYXIS datatypes are adjusted in frm_trn.c
//
// removed some compiler warnings too
//
// TMN (Mike Nordell) 11.APR.2001 - Reduce compiler warnings
// TMN (Mike Nordell) APR-MAY.2001 - Conversion to C++
//
//
//____________________________________________________________
//
//
#include "firebird.h"
2001-05-23 15:26:42 +02:00
#include <setjmp.h>
#include <stdlib.h>
#include <string.h>
2003-11-08 17:40:17 +01:00
#include "../jrd/ibase.h"
2001-05-23 15:26:42 +02:00
#include "../gpre/gpre.h"
#include "../gpre/cmp_proto.h"
#include "../gpre/exp_proto.h"
#include "../gpre/gpre_proto.h"
#include "../gpre/hsh_proto.h"
#include "../gpre/gpre_meta.h"
#include "../gpre/msc_proto.h"
#include "../gpre/par_proto.h"
#include "../gpre/sql_proto.h"
#include "../common/utils_proto.h"
2001-05-23 15:26:42 +02:00
static jmp_buf* PAR_jmp_buf;
#ifdef FTN_BLK_DATA
2005-01-07 05:00:19 +01:00
static void block_data_list(const dbb*);
#endif
static bool match_parentheses();
static act* par_any();
static act* par_array_element();
static act* par_at();
static act* par_based();
static act* par_begin();
static blb* par_blob();
static act* par_blob_action(ACT_T);
static act* par_blob_field();
static act* par_case();
static act* par_clear_handles();
static act* par_derived_from();
static act* par_end_block();
static act* par_end_error();
static act* par_end_fetch();
static act* par_end_for();
static act* par_end_modify();
static act* par_end_stream();
static act* par_end_store(bool);
static act* par_erase();
static act* par_fetch();
static act* par_finish();
static act* par_for();
static act* par_function();
static act* par_left_brace();
static act* par_modify();
static act* par_on();
static act* par_on_error();
static act* par_open_blob(ACT_T, gpre_sym*);
static bool par_options(gpre_req*, bool);
static act* par_procedure();
static act* par_ready();
static act* par_returning_values();
static act* par_right_brace();
static act* par_release();
static act* par_slice(ACT_T);
static act* par_store();
static act* par_start_stream();
static act* par_start_transaction();
static act* par_subroutine();
static act* par_trans(ACT_T);
static act* par_type();
static act* par_variable();
static act* scan_routine_header();
2001-05-23 15:26:42 +02:00
static void set_external_flag();
static bool terminator();
2001-05-23 15:26:42 +02:00
static int brace_count;
static bool routine_decl;
static act* cur_statement;
static act* cur_item;
2004-02-02 12:02:12 +01:00
static gpre_lls* cur_for;
static gpre_lls* cur_store;
static gpre_lls* cur_fetch;
static gpre_lls* cur_modify;
static gpre_lls* cur_error;
static gpre_lls* routine_stack;
static gpre_fld* flag_field;
2001-05-23 15:26:42 +02:00
//____________________________________________________________
//
// We have a token with a symbolic meaning. If appropriate,
// parse an action segment. If not, return NULL.
//
act* PAR_action(const TEXT* base_dir)
2001-05-23 15:26:42 +02:00
{
jmp_buf env;
gpre_sym* symbol = gpreGlob.token_global.tok_symbol;
2001-05-23 15:26:42 +02:00
if (!symbol) {
cur_statement = NULL;
return NULL;
}
if ((USHORT) gpreGlob.token_global.tok_keyword >= (USHORT) KW_start_actions &&
(USHORT) gpreGlob.token_global.tok_keyword <= (USHORT) KW_end_actions)
2001-05-23 15:26:42 +02:00
{
const enum kwwords keyword = gpreGlob.token_global.tok_keyword;
2001-05-23 15:26:42 +02:00
switch (keyword)
{
case KW_READY:
case KW_START_TRANSACTION:
case KW_FINISH:
case KW_COMMIT:
case KW_PREPARE:
case KW_RELEASE_REQUESTS:
case KW_ROLLBACK:
case KW_FUNCTION:
case KW_SAVE:
case KW_SUB:
case KW_SUBROUTINE:
CPR_eol_token();
break;
case KW_EXTERNAL:
set_external_flag();
return NULL;
case KW_FOR:
/** Get the next token as it is without upcasing **/
gpreGlob.override_case = true;
2001-05-23 15:26:42 +02:00
CPR_token();
break;
default:
CPR_token();
}
2001-12-24 03:51:06 +01:00
try {
2001-05-23 15:26:42 +02:00
PAR_jmp_buf = &env;
switch (keyword)
{
case KW_INT:
case KW_LONG:
case KW_SHORT:
case KW_CHAR:
case KW_FLOAT:
case KW_DOUBLE:
// ***
// par_var_c (keyword);
//**
return NULL;
case KW_ANY:
return par_any();
case KW_AT:
return par_at();
case KW_BASED:
return par_based();
case KW_CLEAR_HANDLES:
return par_clear_handles();
case KW_DATABASE:
return PAR_database(false, base_dir);
2001-05-23 15:26:42 +02:00
case KW_DERIVED_FROM:
return par_derived_from();
case KW_END_ERROR:
return par_end_error();
case KW_END_FOR:
return cur_statement = par_end_for();
case KW_END_MODIFY:
return cur_statement = par_end_modify();
case KW_END_STREAM:
return cur_statement = par_end_stream();
case KW_END_STORE:
return cur_statement = par_end_store(false);
case KW_END_STORE_SPECIAL:
return cur_statement = par_end_store(true);
2001-05-23 15:26:42 +02:00
case KW_ELEMENT:
return par_array_element();
case KW_ERASE:
return cur_statement = par_erase();
case KW_EVENT_INIT:
return cur_statement = PAR_event_init(false);
2001-05-23 15:26:42 +02:00
case KW_EVENT_WAIT:
return cur_statement = PAR_event_wait(false);
2001-05-23 15:26:42 +02:00
case KW_FETCH:
return cur_statement = par_fetch();
case KW_FINISH:
return cur_statement = par_finish();
case KW_FOR:
return par_for();
case KW_END_FETCH:
return cur_statement = par_end_fetch();
case KW_MODIFY:
return par_modify();
case KW_ON:
return par_on();
case KW_ON_ERROR:
return par_on_error();
case KW_READY:
return cur_statement = par_ready();
case KW_RELEASE_REQUESTS:
return cur_statement = par_release();
case KW_RETURNING:
return par_returning_values();
case KW_START_STREAM:
return cur_statement = par_start_stream();
case KW_STORE:
return par_store();
case KW_START_TRANSACTION:
return cur_statement = par_start_transaction();
case KW_FUNCTION:
return par_function();
case KW_PROCEDURE:
return par_procedure();
case KW_PROC:
break;
case KW_SUBROUTINE:
return par_subroutine();
case KW_SUB:
break;
case KW_OPEN_BLOB:
return cur_statement = par_open_blob(ACT_blob_open, 0);
case KW_CREATE_BLOB:
return cur_statement = par_open_blob(ACT_blob_create, 0);
case KW_GET_SLICE:
return cur_statement = par_slice(ACT_get_slice);
case KW_PUT_SLICE:
return cur_statement = par_slice(ACT_put_slice);
case KW_GET_SEGMENT:
return cur_statement = par_blob_action(ACT_get_segment);
case KW_PUT_SEGMENT:
return cur_statement = par_blob_action(ACT_put_segment);
case KW_CLOSE_BLOB:
return cur_statement = par_blob_action(ACT_blob_close);
case KW_CANCEL_BLOB:
return cur_statement = par_blob_action(ACT_blob_cancel);
case KW_COMMIT:
return cur_statement = par_trans(ACT_commit);
case KW_SAVE:
return cur_statement = par_trans(ACT_commit_retain_context);
case KW_ROLLBACK:
return cur_statement = par_trans(ACT_rollback);
case KW_PREPARE:
return cur_statement = par_trans(ACT_prepare);
case KW_L_BRACE:
return par_left_brace();
case KW_R_BRACE:
return par_right_brace();
case KW_END:
return par_end_block();
case KW_BEGIN:
return par_begin();
case KW_CASE:
return par_case();
case KW_EXEC:
{
if (!MSC_match(KW_SQL))
break;
gpreGlob.sw_sql = true;
act* action = SQL_action(base_dir);
gpreGlob.sw_sql = false;
return action;
}
2001-05-23 15:26:42 +02:00
default:
break;
}
2001-12-24 03:51:06 +01:00
} // try
catch (const gpre_exception&) {
throw;
}
catch (const Firebird::fatal_exception&)
{
// CVC: a fatal exception should be propagated.
// For example, a failure in our runtime.
throw;
}
2003-02-13 13:01:28 +01:00
catch (const std::exception&) {
gpreGlob.sw_sql = false;
2001-12-24 03:51:06 +01:00
/* This is to force GPRE to get the next symbol. Fix for bug #274. DROOT */
gpreGlob.token_global.tok_symbol = NULL;
2001-12-24 03:51:06 +01:00
return NULL;
}
2001-05-23 15:26:42 +02:00
cur_statement = NULL;
return NULL;
}
for (; symbol; symbol = symbol->sym_homonym)
{
switch (symbol->sym_type)
{
case SYM_context:
2001-12-24 03:51:06 +01:00
try {
PAR_jmp_buf = &env;
cur_statement = NULL;
return par_variable();
}
catch (const gpre_exception&) {
throw;
}
catch (const Firebird::fatal_exception&)
{
// CVC: a fatal exception should be propagated.
throw;
}
2003-02-13 13:01:28 +01:00
catch (const std::exception&) {
2001-12-24 03:51:06 +01:00
return 0;
}
2001-05-23 15:26:42 +02:00
case SYM_blob:
2001-12-24 03:51:06 +01:00
try {
PAR_jmp_buf = &env;
cur_statement = NULL;
return par_blob_field();
}
catch (const gpre_exception&) {
throw;
}
catch (const Firebird::fatal_exception&)
{
// CVC: a fatal exception should be propagated.
throw;
}
2003-02-13 13:01:28 +01:00
catch (const std::exception&) {
2001-12-24 03:51:06 +01:00
return 0;
}
2001-05-23 15:26:42 +02:00
case SYM_relation:
2001-12-24 03:51:06 +01:00
try {
PAR_jmp_buf = &env;
cur_statement = NULL;
return par_type();
}
catch (const gpre_exception&) {
throw;
}
catch (const Firebird::fatal_exception&)
{
// CVC: a fatal exception should be propagated.
throw;
}
2003-02-13 13:01:28 +01:00
catch (const std::exception&) {
2001-12-24 03:51:06 +01:00
return 0;
}
2001-05-23 15:26:42 +02:00
default:
break;
}
}
cur_statement = NULL;
CPR_token();
return NULL;
}
//____________________________________________________________
//
// Parse a blob subtype -- either a signed number or a symbolic name.
//
2003-09-05 16:55:59 +02:00
SSHORT PAR_blob_subtype(DBB db)
2001-05-23 15:26:42 +02:00
{
// Check for symbol type name
2001-05-23 15:26:42 +02:00
if (gpreGlob.token_global.tok_type == tok_ident) {
gpre_rel* relation;
gpre_fld* field;
2003-09-05 16:55:59 +02:00
if (!(relation = MET_get_relation(db, "RDB$FIELDS", "")) ||
2001-05-23 15:26:42 +02:00
!(field = MET_field(relation, "RDB$FIELD_SUB_TYPE")))
2003-09-05 16:55:59 +02:00
{
2001-05-23 15:26:42 +02:00
PAR_error("error during BLOB SUB_TYPE lookup");
2003-09-05 16:55:59 +02:00
}
SSHORT const_subtype;
if (!MET_type(field, gpreGlob.token_global.tok_string, &const_subtype))
2003-10-15 00:22:32 +02:00
CPR_s_error("blob sub_type");
PAR_get_token();
2001-05-23 15:26:42 +02:00
return const_subtype;
}
return EXP_SSHORT_ordinal(true);
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
// Parse a DATABASE declaration. If successful, return
// an action block.
//
act* PAR_database(bool sql, const TEXT* base_directory)
2001-05-23 15:26:42 +02:00
{
if (gpreGlob.token_global.tok_length >= NAME_SIZE)
PAR_error("Database alias too long");
TEXT s[MAXPATHLEN << 1];
2001-05-23 15:26:42 +02:00
act* action = MSC_action(0, ACT_database);
DBB db = (DBB) MSC_alloc(DBB_LEN);
2001-05-23 15:26:42 +02:00
// Get handle name token, make symbol for handle, and
// insert symbol into hash table
gpre_sym* symbol = PAR_symbol(SYM_dummy);
2001-05-23 15:26:42 +02:00
db->dbb_name = symbol;
symbol->sym_type = SYM_database;
symbol->sym_object = (gpre_ctx*) db;
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_EQUALS))
CPR_s_error("\"=\" in database declaration");
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_STATIC))
2001-05-23 15:26:42 +02:00
db->dbb_scope = DBB_STATIC;
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_EXTERN))
2001-05-23 15:26:42 +02:00
db->dbb_scope = DBB_EXTERN;
2003-10-15 00:22:32 +02:00
MSC_match(KW_COMPILETIME);
2001-05-23 15:26:42 +02:00
// parse the compiletime options
TEXT* string;
2001-05-23 15:26:42 +02:00
for (;;) {
if (MSC_match(KW_FILENAME) && (!isQuoted(gpreGlob.token_global.tok_type)))
2003-10-15 00:22:32 +02:00
CPR_s_error("quoted file name");
2001-05-23 15:26:42 +02:00
if (isQuoted(gpreGlob.token_global.tok_type)) {
if (base_directory) {
db->dbb_filename = string = (TEXT *) MSC_alloc(gpreGlob.token_global.tok_length +
strlen(base_directory) + 1);
2003-10-15 03:18:01 +02:00
MSC_copy_cat(base_directory, strlen(base_directory),
gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length, string);
}
else {
db->dbb_filename = string = (TEXT *) MSC_alloc(gpreGlob.token_global.tok_length + 1);
MSC_copy(gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length, string);
}
gpreGlob.token_global.tok_length += 2;
2001-05-23 15:26:42 +02:00
}
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_PASSWORD)) {
if (!isQuoted(gpreGlob.token_global.tok_type))
2003-10-15 00:22:32 +02:00
CPR_s_error("quoted password");
2001-05-23 15:26:42 +02:00
db->dbb_c_password = string =
(TEXT *) MSC_alloc(gpreGlob.token_global.tok_length + 1);
MSC_copy(gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length, string);
2001-05-23 15:26:42 +02:00
}
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_USER)) {
if (!isQuoted(gpreGlob.token_global.tok_type))
2003-10-15 00:22:32 +02:00
CPR_s_error("quoted user name");
db->dbb_c_user = string = (TEXT *) MSC_alloc(gpreGlob.token_global.tok_length + 1);
MSC_copy(gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length, string);
2001-05-23 15:26:42 +02:00
}
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_LC_MESSAGES)) {
if (!isQuoted(gpreGlob.token_global.tok_type))
2003-10-15 00:22:32 +02:00
CPR_s_error("quoted language name");
2001-05-23 15:26:42 +02:00
db->dbb_c_lc_messages = string =
(TEXT *) MSC_alloc(gpreGlob.token_global.tok_length + 1);
MSC_copy(gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length, string);
2001-05-23 15:26:42 +02:00
}
2003-10-15 00:22:32 +02:00
else if (!sql && MSC_match(KW_LC_CTYPE)) {
if (!isQuoted(gpreGlob.token_global.tok_type))
2003-10-15 00:22:32 +02:00
CPR_s_error("quoted character set name");
2001-05-23 15:26:42 +02:00
db->dbb_c_lc_ctype = string =
(TEXT *) MSC_alloc(gpreGlob.token_global.tok_length + 1);
MSC_copy(gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length, string);
2001-05-23 15:26:42 +02:00
}
else
break;
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
}
if ((gpreGlob.sw_auto) && (db->dbb_c_password || db->dbb_c_user || db->dbb_c_lc_ctype
2003-09-05 16:55:59 +02:00
|| db->dbb_c_lc_messages))
{
2001-05-23 15:26:42 +02:00
CPR_warn
("PASSWORD, USER and NAMES options require -manual switch to gpre.");
2003-09-05 16:55:59 +02:00
}
2001-05-23 15:26:42 +02:00
if (!db->dbb_filename)
2003-10-15 00:22:32 +02:00
CPR_s_error("quoted file name");
2001-05-23 15:26:42 +02:00
if (gpreGlob.default_user && !db->dbb_c_user)
db->dbb_c_user = gpreGlob.default_user;
2001-05-23 15:26:42 +02:00
if (gpreGlob.default_password && !db->dbb_c_password)
db->dbb_c_password = gpreGlob.default_password;
2001-05-23 15:26:42 +02:00
if (gpreGlob.module_lc_ctype && !db->dbb_c_lc_ctype)
db->dbb_c_lc_ctype = gpreGlob.module_lc_ctype;
2001-05-23 15:26:42 +02:00
if (gpreGlob.default_lc_ctype && !db->dbb_c_lc_ctype)
db->dbb_c_lc_ctype = gpreGlob.default_lc_ctype;
2001-05-23 15:26:42 +02:00
// parse the runtime options
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_RUNTIME)) {
if (MSC_match(KW_FILENAME))
db->dbb_runtime = sql ? SQL_var_or_string(false)
: PAR_native_value(false, false);
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_PASSWORD))
db->dbb_r_password = sql ? SQL_var_or_string(false)
: PAR_native_value(false, false);
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_USER))
db->dbb_r_user = sql ? SQL_var_or_string(false)
: PAR_native_value(false, false);
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_LC_MESSAGES))
db->dbb_r_lc_messages = sql ? SQL_var_or_string(false)
: PAR_native_value(false, false);
2003-10-15 00:22:32 +02:00
else if (!sql && MSC_match(KW_LC_CTYPE))
db->dbb_r_lc_ctype = sql ? SQL_var_or_string(false)
: PAR_native_value(false, false);
2001-05-23 15:26:42 +02:00
else
db->dbb_runtime = sql ? SQL_var_or_string(false)
: PAR_native_value(false, false);
2001-05-23 15:26:42 +02:00
}
if ((gpreGlob.sw_language == lang_ada) && (gpreGlob.token_global.tok_keyword == KW_HANDLES)) {
2003-10-15 00:22:32 +02:00
PAR_get_token();
if (isQuoted(gpreGlob.token_global.tok_type))
2003-10-15 00:22:32 +02:00
CPR_s_error("quoted file name");
2005-01-07 05:00:19 +01:00
int len = gpreGlob.token_global.tok_length;
if (len > MAXPATHLEN - 2)
len = MAXPATHLEN - 2;
MSC_copy(gpreGlob.token_global.tok_string, len, s);
s[len] = 0;
2001-05-23 15:26:42 +02:00
strcat(s, ".");
if (!gpreGlob.ada_package[0] || !strcmp(gpreGlob.ada_package, s))
2005-01-07 05:00:19 +01:00
{
strncpy(gpreGlob.ada_package, s, MAXPATHLEN);
gpreGlob.ada_package[MAXPATHLEN - 1] = 0;
}
2001-05-23 15:26:42 +02:00
else {
2005-01-07 05:00:19 +01:00
fb_utils::snprintf(s, sizeof(s),
2001-05-23 15:26:42 +02:00
"Ada handle package \"%s\" already in use, ignoring package %s",
gpreGlob.ada_package, gpreGlob.token_global.tok_string);
2001-05-23 15:26:42 +02:00
CPR_warn(s);
}
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
}
if (gpreGlob.sw_language != lang_fortran)
2003-10-15 00:22:32 +02:00
MSC_match(KW_SEMI_COLON);
2001-05-23 15:26:42 +02:00
bool found_error = false;
try {
if (!MET_database(db, true))
found_error = true;
}
// CVC: It avoids countless errors if the db can't be loaded.
catch (const Firebird::status_exception& exc)
{
found_error = true;
// CVC: Print the low level error. The lack of this caused me a lot of problems.
// Granted, status_exception doesn't help, but fatal_exception carries a
// meaningful message.
CPR_error(exc.what());
}
if (found_error)
{
fb_utils::snprintf(s, sizeof(s), "Couldn't access database %s = '%s'",
2001-05-23 15:26:42 +02:00
db->dbb_name->sym_string, db->dbb_filename);
CPR_error(s);
CPR_abort();
}
db->dbb_next = gpreGlob.isc_databases;
gpreGlob.isc_databases = db;
2001-05-23 15:26:42 +02:00
HSH_insert(symbol);
// Load up the symbol (hash) table with relation names from this databases.
MET_load_hash_table(db);
#ifdef FTN_BLK_DATA
if ((gpreGlob.sw_language == lang_fortran) && (db->dbb_scope != DBB_EXTERN))
2001-05-23 15:26:42 +02:00
block_data_list(db);
#endif
// Since we have a real DATABASE statement, get rid of any artificial
// databases that were created because of an INCLUDE SQLCA statement.
for (DBB* db_ptr = &gpreGlob.isc_databases; *db_ptr;)
2001-05-23 15:26:42 +02:00
if ((*db_ptr)->dbb_flags & DBB_sqlca)
*db_ptr = (*db_ptr)->dbb_next;
else
db_ptr = &(*db_ptr)->dbb_next;
return action;
}
//____________________________________________________________
//
// Parse end of statement. All languages except ADA leave
// the trailing semi-colon dangling. ADA, however, must
// eat the semi-colon as part of the statement. In any case,
// return TRUE is a semi-colon is/was there, otherwise return
// FALSE.
//
2003-09-11 04:13:46 +02:00
bool PAR_end()
2001-05-23 15:26:42 +02:00
{
if ((gpreGlob.sw_language == lang_ada) || (gpreGlob.sw_language == lang_c) ||
(isLangCpp(gpreGlob.sw_language)))
2003-09-11 04:13:46 +02:00
{
2003-10-15 00:22:32 +02:00
return (MSC_match(KW_SEMI_COLON));
2003-09-11 04:13:46 +02:00
}
2001-05-23 15:26:42 +02:00
return (gpreGlob.token_global.tok_keyword == KW_SEMI_COLON);
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
// Report an error during parse and unwind.
//
void PAR_error(const TEXT* string)
2001-05-23 15:26:42 +02:00
{
2003-10-15 00:22:32 +02:00
CPR_error(string);
2001-05-23 15:26:42 +02:00
PAR_unwind();
}
//____________________________________________________________
//
// Parse an event init statement, preparing
// to wait on a number of named gpreGlob.events.
2001-05-23 15:26:42 +02:00
//
act* PAR_event_init(bool sql)
2001-05-23 15:26:42 +02:00
{
// char req_name[128];
2001-05-23 15:26:42 +02:00
// make up statement node
SQL_resolve_identifier("<identifier>", NULL, MAX_EVENT_SIZE);
//SQL_resolve_identifier("<identifier>", req_name, sizeof(req_name));
//strcpy(gpreGlob.token_global.tok_string, req_name); Why? It's already done.
gpre_nod* init = MSC_node(nod_event_init, 4);
2002-11-11 20:19:43 +01:00
init->nod_arg[0] = (GPRE_NOD) PAR_symbol(SYM_dummy);
init->nod_arg[3] = (GPRE_NOD) gpreGlob.isc_databases;
2001-05-23 15:26:42 +02:00
act* action = MSC_action(0, ACT_event_init);
2001-05-23 15:26:42 +02:00
action->act_object = (REF) init;
// parse optional database handle
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_LEFT_PAREN)) {
gpre_sym* symbol = gpreGlob.token_global.tok_symbol;
if (symbol && (symbol->sym_type == SYM_database))
2002-11-11 20:19:43 +01:00
init->nod_arg[3] = (GPRE_NOD) symbol->sym_object;
2001-05-23 15:26:42 +02:00
else
2003-10-15 00:22:32 +02:00
CPR_s_error("left parenthesis or database handle");
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
PAR_get_token();
if (!MSC_match(KW_LEFT_PAREN))
CPR_s_error("left parenthesis");
2001-05-23 15:26:42 +02:00
}
// eat any number of event strings until a right paren is found,
// pushing the gpreGlob.events onto a stack
2001-05-23 15:26:42 +02:00
GPRE_NOD node;
gpre_lls* stack = NULL;
int count = 0;
while (true) {
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_RIGHT_PAREN))
2001-05-23 15:26:42 +02:00
break;
const gpre_sym* symbol;
if (!sql && (symbol = gpreGlob.token_global.tok_symbol)
&& symbol->sym_type == SYM_context)
{
gpre_ctx* context;
gpre_fld* field = EXP_field(&context);
ref* reference = EXP_post_field(field, context, false);
2003-10-15 00:22:32 +02:00
node = MSC_node(nod_field, 1);
2002-11-11 20:19:43 +01:00
node->nod_arg[0] = (GPRE_NOD) reference;
2001-05-23 15:26:42 +02:00
}
else {
2003-10-15 00:22:32 +02:00
node = MSC_node(nod_null, 1);
2001-05-23 15:26:42 +02:00
if (sql)
node->nod_arg[0] = (GPRE_NOD) SQL_var_or_string(false);
2001-05-23 15:26:42 +02:00
else
node->nod_arg[0] = (GPRE_NOD) PAR_native_value(false, false);
2001-05-23 15:26:42 +02:00
}
2003-10-15 03:18:01 +02:00
MSC_push(node, &stack);
2001-05-23 15:26:42 +02:00
count++;
2003-10-15 00:22:32 +02:00
MSC_match(KW_COMMA);
2001-05-23 15:26:42 +02:00
}
// pop the event strings off the stack
gpre_nod* event_list = init->nod_arg[1] = MSC_node(nod_list, (SSHORT) count);
gpre_nod** ptr = event_list->nod_arg + count;
2001-05-23 15:26:42 +02:00
while (stack)
2003-10-15 03:18:01 +02:00
*--ptr = (GPRE_NOD) MSC_pop(&stack);
2001-05-23 15:26:42 +02:00
MSC_push((GPRE_NOD) action, &gpreGlob.events);
2001-05-23 15:26:42 +02:00
if (!sql)
PAR_end();
return action;
}
//____________________________________________________________
//
// Parse an event wait statement, preparing
// to wait on a number of named gpreGlob.events.
2001-05-23 15:26:42 +02:00
//
act* PAR_event_wait(bool sql)
2001-05-23 15:26:42 +02:00
{
// char req_name[132];
2001-05-23 15:26:42 +02:00
// this is a simple statement, just add a handle
act* action = MSC_action(0, ACT_event_wait);
SQL_resolve_identifier("<identifier>", NULL, MAX_EVENT_SIZE);
//SQL_resolve_identifier("<identifier>", req_name, sizeof(req_name));
//strcpy(gpreGlob.token_global.tok_string, req_name); redundant
2001-05-23 15:26:42 +02:00
action->act_object = (REF) PAR_symbol(SYM_dummy);
if (!sql)
PAR_end();
return action;
}
//____________________________________________________________
//
// Perform any last minute stuff necessary at the end of pass1.
//
void PAR_fini()
{
if (cur_for)
2003-10-15 00:22:32 +02:00
CPR_error("unterminated FOR statement");
2001-05-23 15:26:42 +02:00
if (cur_modify)
2003-10-15 00:22:32 +02:00
CPR_error("unterminated MODIFY statement");
2001-05-23 15:26:42 +02:00
if (cur_store)
2003-10-15 00:22:32 +02:00
CPR_error("unterminated STORE statement");
2001-05-23 15:26:42 +02:00
if (cur_error)
2003-10-15 00:22:32 +02:00
CPR_error("unterminated ON_ERROR clause");
2001-05-23 15:26:42 +02:00
if (cur_item)
2003-10-15 00:22:32 +02:00
CPR_error("unterminated ITEM statement");
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
// Get a token or unwind the parse
// if we hit end of file
//
TOK PAR_get_token()
{
if (CPR_token() == NULL) {
CPR_error("unexpected EOF");
PAR_unwind();
}
return NULL;
}
//____________________________________________________________
//
// Do any initialization necessary.
// For one thing, set all current indicators
// to null, since nothing is current. Also,
// set up a block to hold the current routine,
//
// (The 'routine' indicator tells the code
// generator where to put ports to support
// recursive routines and Fortran's strange idea
// of separate sub-modules. For PASCAL only, we
// keep a stack of routines, and pay special attention
// to the main routine.)
//
void PAR_init()
{
SQL_init();
2003-09-11 04:13:46 +02:00
cur_error = cur_fetch = cur_for = cur_modify = cur_store = NULL;
cur_statement = cur_item = NULL;
2001-05-23 15:26:42 +02:00
gpreGlob.cur_routine = MSC_action(0, ACT_routine);
gpreGlob.cur_routine->act_flags |= ACT_main;
MSC_push((GPRE_NOD) gpreGlob.cur_routine, &routine_stack);
routine_decl = true;
2001-05-23 15:26:42 +02:00
flag_field = NULL;
brace_count = 0;
}
static inline void gobble(SCHAR*& string)
2003-10-16 10:51:06 +02:00
{
const SCHAR* s1 = gpreGlob.token_global.tok_string;
2003-10-16 10:51:06 +02:00
while (*s1)
2003-10-15 00:22:32 +02:00
*string++ = *s1++;
PAR_get_token();
}
2001-05-23 15:26:42 +02:00
//____________________________________________________________
//
// Parse a native expression as a string.
//
TEXT* PAR_native_value(bool array_ref,
bool handle_ref)
2001-05-23 15:26:42 +02:00
{
SCHAR buffer[512];
2001-05-23 15:26:42 +02:00
SCHAR* string = buffer;
2001-05-23 15:26:42 +02:00
while (true) {
2001-05-23 15:26:42 +02:00
/** PAR_native_values copies the string constants. These are
passed to api calls. Make sure to enclose these with
double quotes.
**/
if (gpreGlob.sw_sql_dialect == 1) {
if (isQuoted(gpreGlob.token_global.tok_type)) {
//const enum tok_t typ = gpreGlob.token_global.tok_type;
gpreGlob.token_global.tok_length += 2;
2001-05-23 15:26:42 +02:00
*string++ = '\"';
gobble(string);
2001-05-23 15:26:42 +02:00
*string++ = '\"';
break;
}
}
else if (gpreGlob.sw_sql_dialect == 2) {
if (gpreGlob.token_global.tok_type == tok_dblquoted)
2001-05-23 15:26:42 +02:00
PAR_error("Ambiguous use of double quotes in dialect 2");
else if (gpreGlob.token_global.tok_type == tok_sglquoted) {
gpreGlob.token_global.tok_length += 2;
2001-05-23 15:26:42 +02:00
*string++ = '\"';
gobble(string);
2001-05-23 15:26:42 +02:00
*string++ = '\"';
break;
}
}
else if (gpreGlob.sw_sql_dialect == 3) {
if (gpreGlob.token_global.tok_type == tok_sglquoted) {
gpreGlob.token_global.tok_length += 2;
2001-05-23 15:26:42 +02:00
*string++ = '\"';
gobble(string);
2001-05-23 15:26:42 +02:00
*string++ = '\"';
break;
}
}
if (gpreGlob.token_global.tok_keyword == KW_AMPERSAND || gpreGlob.token_global.tok_keyword == KW_ASTERISK)
gobble(string);
if (gpreGlob.token_global.tok_type != tok_ident)
2003-10-15 00:22:32 +02:00
CPR_s_error("identifier");
gobble(string);
2001-05-23 15:26:42 +02:00
/* For ADA, gobble '<attribute> */
if ((gpreGlob.sw_language == lang_ada) && (gpreGlob.token_global.tok_string[0] == '\'')) {
gobble(string);
2001-05-23 15:26:42 +02:00
}
enum kwwords keyword = gpreGlob.token_global.tok_keyword;
2001-05-23 15:26:42 +02:00
if (keyword == KW_LEFT_PAREN) {
int parens = 1;
2001-05-23 15:26:42 +02:00
while (parens) {
const enum tok_t typ = gpreGlob.token_global.tok_type;
2003-10-15 00:22:32 +02:00
if (isQuoted(typ))
*string++ = (typ == tok_sglquoted) ? '\'' : '\"';
gobble(string);
2003-10-15 00:22:32 +02:00
if (isQuoted(typ))
*string++ = (typ == tok_sglquoted) ? '\'' : '\"';
keyword = gpreGlob.token_global.tok_keyword;
2001-05-23 15:26:42 +02:00
if (keyword == KW_RIGHT_PAREN)
parens--;
else if (keyword == KW_LEFT_PAREN)
parens++;
}
gobble(string);
keyword = gpreGlob.token_global.tok_keyword;
2001-05-23 15:26:42 +02:00
}
while (keyword == KW_L_BRCKET) {
int brackets = 1;
2001-05-23 15:26:42 +02:00
while (brackets) {
gobble(string);
keyword = gpreGlob.token_global.tok_keyword;
2001-05-23 15:26:42 +02:00
if (keyword == KW_R_BRCKET)
brackets--;
else if (keyword == KW_L_BRCKET)
brackets++;
}
gobble(string);
keyword = gpreGlob.token_global.tok_keyword;
2001-05-23 15:26:42 +02:00
}
while ((keyword == KW_CARAT) && (gpreGlob.sw_language == lang_pascal)) {
gobble(string);
keyword = gpreGlob.token_global.tok_keyword;
2001-05-23 15:26:42 +02:00
}
if (
(keyword == KW_DOT
&& (!handle_ref || gpreGlob.sw_language == lang_c
|| gpreGlob.sw_language == lang_ada)) || keyword == KW_POINTS
2004-05-29 06:54:20 +02:00
|| (keyword == KW_COLON && !gpreGlob.sw_sql && !array_ref))
{
gobble(string);
2001-05-23 15:26:42 +02:00
}
else
break;
}
int length = string - buffer;
SCHAR* const s2 = string = (SCHAR *) MSC_alloc(length + 1);
const SCHAR* s1 = buffer;
2001-05-23 15:26:42 +02:00
if (length) {
do {
2001-05-23 15:26:42 +02:00
*string++ = *s1++;
} while (--length);
}
2001-05-23 15:26:42 +02:00
return s2;
}
//____________________________________________________________
//
// Find a pseudo-field for null. If there isn't one,
// make one.
//
gpre_fld* PAR_null_field()
2001-05-23 15:26:42 +02:00
{
if (flag_field)
return flag_field;
flag_field = MET_make_field("gds__null_flag", dtype_short, sizeof(SSHORT),
false);
2001-05-23 15:26:42 +02:00
return flag_field;
}
//____________________________________________________________
//
// Parse the RESERVING clause of the start_transaction & set transaction
// statements, creating a partial TPB in the process. The
// TPB just hangs off the end of the transaction block.
//
void PAR_reserving( USHORT flags, bool parse_sql)
2001-05-23 15:26:42 +02:00
{
DBB database;
while (true) {
// find a relation name, or maybe a list of them
2001-05-23 15:26:42 +02:00
if ((!parse_sql) && terminator())
break;
do {
2004-05-29 06:54:20 +02:00
gpre_rel* relation = EXP_relation();
if (!relation)
2003-10-15 00:22:32 +02:00
CPR_s_error("relation name");
2001-05-23 15:26:42 +02:00
database = relation->rel_database;
rrl* lock_block = (rrl*) MSC_alloc(RRL_LEN);
2001-05-23 15:26:42 +02:00
lock_block->rrl_next = database->dbb_rrls;
lock_block->rrl_relation = relation;
database->dbb_rrls = lock_block;
2003-10-15 00:22:32 +02:00
} while (MSC_match(KW_COMMA));
2001-05-23 15:26:42 +02:00
/*
* get the lock level and mode and apply them to all the
* relations in the list
*/
2003-10-15 00:22:32 +02:00
MSC_match(KW_FOR);
USHORT lock_level = (flags & TRA_con) ? isc_tpb_protected : isc_tpb_shared;
USHORT lock_mode = isc_tpb_lock_read;
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_PROTECTED))
2003-11-08 17:40:17 +01:00
lock_level = isc_tpb_protected;
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_EXCLUSIVE))
2003-11-08 17:40:17 +01:00
lock_level = isc_tpb_exclusive;
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_SHARED))
2003-11-08 17:40:17 +01:00
lock_level = isc_tpb_shared;
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_WRITE)) {
2001-05-23 15:26:42 +02:00
if (flags & TRA_ro)
2003-10-15 00:22:32 +02:00
CPR_error("write lock requested for a read_only transaction");
2003-11-08 17:40:17 +01:00
lock_mode = isc_tpb_lock_write;
2001-05-23 15:26:42 +02:00
}
else
2003-10-15 00:22:32 +02:00
MSC_match(KW_READ);
2001-05-23 15:26:42 +02:00
for (database = gpreGlob.isc_databases; database; database = database->dbb_next)
{
for (rrl* lock_block = database->dbb_rrls; lock_block;
lock_block = lock_block->rrl_next)
{
if (!lock_block->rrl_lock_level)
2001-05-23 15:26:42 +02:00
{
2003-11-04 00:59:24 +01:00
fb_assert(lock_level <= MAX_UCHAR);
fb_assert(lock_mode <= MAX_UCHAR);
2001-05-23 15:26:42 +02:00
lock_block->rrl_lock_level = (UCHAR) lock_level;
lock_block->rrl_lock_mode = (UCHAR) lock_mode;
}
}
}
2003-10-15 00:22:32 +02:00
if (!(MSC_match(KW_COMMA)))
2001-05-23 15:26:42 +02:00
break;
}
}
//____________________________________________________________
//
// Initialize the request and the ready.
//
gpre_req* PAR_set_up_dpb_info(rdy* ready, act* action, USHORT buffercount)
2001-05-23 15:26:42 +02:00
{
ready->rdy_database->dbb_buffercount = buffercount;
gpre_req* request = MSC_request(REQ_ready);
2001-05-23 15:26:42 +02:00
request->req_database = ready->rdy_database;
request->req_actions = action;
ready->rdy_request = request;
return request;
}
//____________________________________________________________
//
// Make a symbol from the current token, and advance
// to the next token. If a symbol type other than
// SYM_dummy, the symbol can be overloaded, but not
// redefined.
//
gpre_sym* PAR_symbol(enum sym_t type)
2001-05-23 15:26:42 +02:00
{
gpre_sym* symbol;
2001-05-23 15:26:42 +02:00
for (symbol = gpreGlob.token_global.tok_symbol; symbol; symbol = symbol->sym_homonym)
2001-05-23 15:26:42 +02:00
if (type == SYM_dummy || symbol->sym_type == type) {
2005-01-07 05:00:19 +01:00
TEXT s[ERROR_LENGTH];
fb_utils::snprintf(s, sizeof(s),
"symbol %s is already in use", gpreGlob.token_global.tok_string);
2001-05-23 15:26:42 +02:00
PAR_error(s);
}
symbol = MSC_symbol(SYM_cursor, gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length, 0);
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
return symbol;
}
//____________________________________________________________
//
// There's been a parse error, so unwind out.
//
void PAR_unwind()
{
2004-03-01 05:57:43 +01:00
throw std::exception();
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
// mark databases specified in start_transaction and set transaction
// statements.
//
void PAR_using_db()
{
while (true) {
gpre_sym* symbol = MSC_find_symbol(gpreGlob.token_global.tok_symbol, SYM_database);
if (symbol) {
DBB db = (DBB) symbol->sym_object;
2001-05-23 15:26:42 +02:00
db->dbb_flags |= DBB_in_trans;
}
else
2003-10-15 00:22:32 +02:00
CPR_s_error("database handle");
PAR_get_token();
if (!MSC_match(KW_COMMA))
2001-05-23 15:26:42 +02:00
break;
}
}
#ifdef FTN_BLK_DATA
//____________________________________________________________
//
//
// Damn fortran sometimes only allows global
// initializations in block data. This collects
// names of dbs to be so handled.
//
2005-01-07 05:00:19 +01:00
static void block_data_list( const dbb* db)
2001-05-23 15:26:42 +02:00
{
if (db->dbb_scope == DBB_EXTERN)
return;
const TEXT* name = db->dbb_name->sym_string;
dbd* list = gpreGlob.global_db_list;
2001-05-23 15:26:42 +02:00
if (gpreGlob.global_db_count)
2005-01-07 05:00:19 +01:00
if (gpreGlob.global_db_count > MAX_DATABASES) {
2001-05-23 15:26:42 +02:00
PAR_error
("Database limit exceeded: 32 databases per source file.");
}
else {
for (const dbd* const end = gpreGlob.global_db_list + gpreGlob.global_db_count;
list < end; list++)
{
2001-05-23 15:26:42 +02:00
if (!(strcmp(name, list->dbb_name)))
return;
}
}
2001-05-23 15:26:42 +02:00
2005-01-07 05:00:19 +01:00
if (gpreGlob.global_db_count >= MAX_DATABASES)
2001-05-23 15:26:42 +02:00
return;
strcpy(list->dbb_name, name);
gpreGlob.global_db_count++;
2001-05-23 15:26:42 +02:00
}
#endif
//____________________________________________________________
//
//
// For reasons best left unnamed, we need
// to skip the contents of a parenthesized
// list
//
static bool match_parentheses()
2001-05-23 15:26:42 +02:00
{
USHORT paren_count = 0;
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_LEFT_PAREN)) {
2001-05-23 15:26:42 +02:00
paren_count++;
while (paren_count) {
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_RIGHT_PAREN))
2001-05-23 15:26:42 +02:00
paren_count--;
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_LEFT_PAREN))
2001-05-23 15:26:42 +02:00
paren_count++;
else
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
}
return true;
2001-05-23 15:26:42 +02:00
}
else
return false;
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
// Parse a free standing ANY expression.
//
static act* par_any()
2001-05-23 15:26:42 +02:00
{
// For time being flag as an error
2001-05-23 15:26:42 +02:00
PAR_error("Free standing any not supported");
gpre_sym* symbol = NULL;
2001-05-23 15:26:42 +02:00
// Make up request block. Since this might not be a database statement,
// stay ready to back out if necessay.
gpre_req* request = MSC_request(REQ_any);
2001-05-23 15:26:42 +02:00
par_options(request, true);
gpre_rse* rec_expr = EXP_rse(request, symbol);
2001-05-23 15:26:42 +02:00
EXP_rse_cleanup(rec_expr);
act* action = MSC_action(request, ACT_any);
2001-05-23 15:26:42 +02:00
request->req_rse = rec_expr;
gpre_ctx* context = rec_expr->rse_context[0];
gpre_rel* relation = context->ctx_relation;
2001-05-23 15:26:42 +02:00
request->req_database = relation->rel_database;
act* function = MSC_action(0, ACT_function);
2001-05-23 15:26:42 +02:00
function->act_object = (REF) action;
function->act_next = gpreGlob.global_functions;
gpreGlob.global_functions = function;
2001-05-23 15:26:42 +02:00
return action;
}
//____________________________________________________________
//
// Parse a free reference to a database field in general
// program context. If the next keyword isn't a context
// varying, this isn't an array element reference.
//
static act* par_array_element()
2001-05-23 15:26:42 +02:00
{
if (!MSC_find_symbol(gpreGlob.token_global.tok_symbol, SYM_context))
2001-05-23 15:26:42 +02:00
return NULL;
gpre_ctx* context;
gpre_fld* field = EXP_field(&context);
gpre_req* request = context->ctx_request;
gpre_nod* node = EXP_array(request, field, false, false);
ref* reference = MSC_reference(&request->req_references);
2001-05-23 15:26:42 +02:00
reference->ref_expr = node;
gpre_fld* element = field->fld_array;
reference->ref_field = element;
2001-05-23 15:26:42 +02:00
element->fld_symbol = field->fld_symbol;
reference->ref_context = context;
2002-11-11 20:19:43 +01:00
node->nod_arg[0] = (GPRE_NOD) reference;
2001-05-23 15:26:42 +02:00
act* action = MSC_action(request, ACT_variable);
2001-05-23 15:26:42 +02:00
action->act_object = reference;
return action;
}
//____________________________________________________________
//
// Parse an AT END clause.
//
static act* par_at()
2001-05-23 15:26:42 +02:00
{
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_END) || !cur_fetch)
2001-05-23 15:26:42 +02:00
return NULL;
act* action = (act*) cur_fetch->lls_object;
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
return MSC_action(action->act_request, ACT_at_end);
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
// Parse a BASED ON clause. If this
// is fortran and we don't have a database
// declared yet, don't parse it completely.
// If this is PLI look for a left paren or
// a semi colon to avoid stomping a
// DECLARE i FIXED BIN BASED (X);
// or DECLARE LIST (10) FIXED BINARY BASED;
//
static act* par_based()
2001-05-23 15:26:42 +02:00
{
bool notSegment = false; // a COBOL specific patch
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
MSC_match(KW_ON);
act* action = MSC_action(0, ACT_basedon);
bas* based_on = (bas*) MSC_alloc(BAS_LEN);
2001-05-23 15:26:42 +02:00
action->act_object = (REF) based_on;
2005-01-07 05:00:19 +01:00
if ((gpreGlob.sw_language != lang_fortran) || gpreGlob.isc_databases)
{
TEXT s[ERROR_LENGTH];
gpre_rel* relation = EXP_relation();
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_DOT))
CPR_s_error("dot in qualified field reference");
SQL_resolve_identifier("<fieldname>", NULL, NAME_SIZE + 1);
if (gpreGlob.token_global.tok_length >= NAME_SIZE)
PAR_error("Field length too long");
gpre_fld* field = MET_field(relation, gpreGlob.token_global.tok_string);
if (!field) {
2005-01-07 05:00:19 +01:00
fb_utils::snprintf(s, sizeof(s),
"undefined field %s", gpreGlob.token_global.tok_string);
2001-05-23 15:26:42 +02:00
PAR_error(s);
}
if (SQL_DIALECT_V5 == gpreGlob.sw_sql_dialect) {
2004-05-29 06:54:20 +02:00
const USHORT field_dtype = field->fld_dtype;
2001-05-23 15:26:42 +02:00
if ((dtype_sql_date == field_dtype) ||
(dtype_sql_time == field_dtype) ||
2004-05-29 06:54:20 +02:00
(dtype_int64 == field_dtype))
{
2001-05-23 15:26:42 +02:00
PAR_error
("BASED ON impermissible datatype for a dialect-1 program");
}
}
2003-10-15 00:22:32 +02:00
PAR_get_token();
2005-01-07 05:00:19 +01:00
char tmpChar[2]; // a COBOL specific patch
if (gpreGlob.sw_language == lang_cobol && gpreGlob.token_global.tok_keyword == KW_DOT) {
strcpy(tmpChar, gpreGlob.token_global.tok_string);
2001-05-23 15:26:42 +02:00
}
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_DOT)) {
if (!MSC_match(KW_SEGMENT)) {
if (gpreGlob.sw_language != lang_cobol)
2001-05-23 15:26:42 +02:00
PAR_error
("only .SEGMENT allowed after qualified field name");
else {
strcpy(based_on->bas_terminator, tmpChar);
notSegment = true;
2001-05-23 15:26:42 +02:00
}
}
else if (!(field->fld_flags & FLD_blob)) {
2005-01-07 05:00:19 +01:00
fb_utils::snprintf(s, sizeof(s), "field %s is not a blob",
2001-05-23 15:26:42 +02:00
field->fld_symbol->sym_string);
PAR_error(s);
}
if (!notSegment) /* this is flag is to solve KW_DOT problem
in COBOL. should be false for all other lang */
2001-05-23 15:26:42 +02:00
based_on->bas_flags |= BAS_segment;
}
based_on->bas_field = field;
}
else {
based_on->bas_rel_name = (STR) MSC_alloc(gpreGlob.token_global.tok_length + 1);
MSC_copy(gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length,
based_on->bas_rel_name->str_string);
2003-10-15 00:22:32 +02:00
PAR_get_token();
if (!MSC_match(KW_DOT))
2001-05-23 15:26:42 +02:00
PAR_error("expected qualified field name");
else {
based_on->bas_fld_name = (STR) MSC_alloc(gpreGlob.token_global.tok_length + 1);
MSC_copy(gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length,
based_on->bas_fld_name->str_string);
bool ambiguous_flag = false;
2003-10-15 00:22:32 +02:00
PAR_get_token();
if (MSC_match(KW_DOT)) {
2001-05-23 15:26:42 +02:00
based_on->bas_db_name = based_on->bas_rel_name;
based_on->bas_rel_name = based_on->bas_fld_name;
based_on->bas_fld_name = (STR) MSC_alloc(gpreGlob.token_global.tok_length + 1);
MSC_copy(gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length,
based_on->bas_fld_name->str_string);
if (gpreGlob.token_global.tok_keyword == KW_SEGMENT)
ambiguous_flag = true;
2003-10-15 00:22:32 +02:00
PAR_get_token();
if (MSC_match(KW_DOT)) {
if (!MSC_match(KW_SEGMENT))
2001-05-23 15:26:42 +02:00
PAR_error("too many qualifiers on field name");
based_on->bas_flags |= BAS_segment;
ambiguous_flag = false;
2001-05-23 15:26:42 +02:00
}
}
if (ambiguous_flag)
based_on->bas_flags |= BAS_ambiguous;
}
}
switch (gpreGlob.sw_language) {
2001-05-23 15:26:42 +02:00
case lang_internal:
case lang_fortran:
case lang_epascal:
case lang_c:
case lang_cxx:
do {
2003-10-15 03:18:01 +02:00
MSC_push((GPRE_NOD) PAR_native_value(false, false),
2001-05-23 15:26:42 +02:00
&based_on->bas_variables);
2003-10-15 00:22:32 +02:00
} while (MSC_match(KW_COMMA));
2001-05-23 15:26:42 +02:00
/*
** bug_4031. based_on->bas_variables are now in reverse order.
** we must reverse the order so we can output them to the .c
** file correctly.
*/
if (based_on->bas_variables->lls_next) {
2004-02-02 12:02:12 +01:00
gpre_lls* t1 = based_on->bas_variables; // last one in the old list
gpre_lls* t2 = NULL; // last one in the new list
gpre_lls* hold = t2; // beginning of new list
2001-05-23 15:26:42 +02:00
// while we still have a next one, keep going thru
2001-05-23 15:26:42 +02:00
while (t1->lls_next) {
// now find the last one in the list
2001-05-23 15:26:42 +02:00
while (t1->lls_next->lls_next)
t1 = t1->lls_next;
// if this is the first time thru, set hold
2001-05-23 15:26:42 +02:00
if (hold == NULL) {
hold = t1->lls_next;
t2 = hold;
}
else {
/* not first time thru, add this one to the end
** of the new list */
t2->lls_next = t1->lls_next;
t2 = t2->lls_next;
}
// now null out the last one, and start again
2001-05-23 15:26:42 +02:00
t1->lls_next = NULL;
t1 = based_on->bas_variables;
}
/* ok, we're done, tack the original lls onto the very
** end of the new list. */
t2->lls_next = t1;
if (hold)
based_on->bas_variables = hold;
}
default:
break;
}
if (notSegment)
return action;
if (gpreGlob.token_global.tok_keyword == KW_SEMI_COLON ||
2004-05-29 06:54:20 +02:00
(gpreGlob.sw_language == lang_cobol && gpreGlob.token_global.tok_keyword == KW_DOT))
{
strcpy(based_on->bas_terminator, gpreGlob.token_global.tok_string);
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
}
return action;
}
//____________________________________________________________
//
// If this is a PASCAL program, and we're
// in a code block, then increment the
// brace count. If we're in a routine
// declaration, then we've reached the start
// of the code block and should mark it as
// a new routine.
//
static act* par_begin()
2001-05-23 15:26:42 +02:00
{
if (gpreGlob.sw_language == lang_pascal) {
routine_decl = false;
gpreGlob.cur_routine->act_count++;
2001-05-23 15:26:42 +02:00
}
return NULL;
}
//____________________________________________________________
//
// Parse a blob handle and return the blob.
//
static blb* par_blob()
2001-05-23 15:26:42 +02:00
{
2004-05-29 06:54:20 +02:00
gpre_sym* symbol = MSC_find_symbol(gpreGlob.token_global.tok_symbol, SYM_blob);
2001-05-23 15:26:42 +02:00
2004-05-29 06:54:20 +02:00
if (!symbol)
2003-10-15 00:22:32 +02:00
CPR_s_error("blob handle");
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
return (blb*) symbol->sym_object;
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
// Parse a GET_SEGMENT, PUT_SEGMENT, CLOSE_BLOB or CANCEL_BLOB.
//
static act* par_blob_action( ACT_T type)
2001-05-23 15:26:42 +02:00
{
blb* blob = par_blob();
act* action = MSC_action(blob->blb_request, type);
2001-05-23 15:26:42 +02:00
action->act_object = (REF) blob;
// Need to eat the semicolon if present
if (gpreGlob.sw_language == lang_c)
2003-10-15 00:22:32 +02:00
MSC_match(KW_SEMI_COLON);
2001-05-23 15:26:42 +02:00
else
PAR_end();
return action;
}
//____________________________________________________________
//
// Parse a blob segment or blob field reference.
//
static act* par_blob_field()
2001-05-23 15:26:42 +02:00
{
const SSHORT first = gpreGlob.token_global.tok_first;
2001-05-23 15:26:42 +02:00
blb* blob = par_blob();
2001-05-23 15:26:42 +02:00
ACT_T type;
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_DOT)) {
if (MSC_match(KW_SEGMENT))
2001-05-23 15:26:42 +02:00
type = ACT_segment;
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_LENGTH))
2001-05-23 15:26:42 +02:00
type = ACT_segment_length;
else
2003-10-15 00:22:32 +02:00
CPR_s_error("SEGMENT or LENGTH");
2001-05-23 15:26:42 +02:00
}
else
type = ACT_blob_handle;
act* action = MSC_action(blob->blb_request, type);
2001-05-23 15:26:42 +02:00
if (first)
action->act_flags |= ACT_first;
action->act_object = (REF) blob;
return action;
}
//____________________________________________________________
//
// If this is a PASCAL program, and we're
// in a code block, then a case statement
// will end with an END, so it adds to the
// begin count.
//
static act* par_case()
2001-05-23 15:26:42 +02:00
{
if ((gpreGlob.sw_language == lang_pascal) && (!routine_decl))
gpreGlob.cur_routine->act_count++;
2001-05-23 15:26:42 +02:00
return NULL;
}
//____________________________________________________________
//
// Parse degenerate CLEAR_HANDLES command.
//
static act* par_clear_handles()
2001-05-23 15:26:42 +02:00
{
2003-10-15 00:22:32 +02:00
return MSC_action(0, ACT_clear_handles);
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
// Parse a DERIVED_FROM clause. Like
// BASED ON but for C/C++ prototypes.
//
static act* par_derived_from()
2001-05-23 15:26:42 +02:00
{
if ((gpreGlob.sw_language != lang_c) && (!isLangCpp(gpreGlob.sw_language))) {
2001-05-23 15:26:42 +02:00
return (NULL);
}
2001-05-23 15:26:42 +02:00
act* action = MSC_action(0, ACT_basedon);
bas* based_on = (bas*) MSC_alloc(BAS_LEN);
2001-05-23 15:26:42 +02:00
action->act_object = (REF) based_on;
gpre_rel* relation = EXP_relation();
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_DOT))
CPR_s_error("dot in qualified field reference");
2005-01-07 05:00:19 +01:00
SQL_resolve_identifier("<Field Name>", NULL, NAME_SIZE);
gpre_fld* field = MET_field(relation, gpreGlob.token_global.tok_string);
if (!field) {
2005-01-07 05:00:19 +01:00
TEXT s[ERROR_LENGTH];
fb_utils::snprintf(s, sizeof(s),
"undefined field %s", gpreGlob.token_global.tok_string);
2001-05-23 15:26:42 +02:00
PAR_error(s);
}
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
based_on->bas_field = field;
2004-02-02 12:02:12 +01:00
based_on->bas_variables = (gpre_lls*) MSC_alloc(LLS_LEN);
2001-05-23 15:26:42 +02:00
based_on->bas_variables->lls_next = NULL;
based_on->bas_variables->lls_object =
(GPRE_NOD) PAR_native_value(false, false);
2001-05-23 15:26:42 +02:00
strcpy(based_on->bas_terminator, gpreGlob.token_global.tok_string);
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
return action;
}
//____________________________________________________________
//
//
// If the language is PASCAL, and if we're
// the body of a routine, every END counts
// against the number of BEGIN's and CASE's
// and when the count comes to zero, we SHOULD
// be at the end of the current routine, so
// pop it off the routine stack.
//
static act* par_end_block()
2001-05-23 15:26:42 +02:00
{
if (gpreGlob.sw_language == lang_pascal &&
!routine_decl && --gpreGlob.cur_routine->act_count == 0 && routine_stack)
2004-05-29 06:54:20 +02:00
{
gpreGlob.cur_routine = (act*) MSC_pop(&routine_stack);
2004-05-29 06:54:20 +02:00
}
2001-05-23 15:26:42 +02:00
return NULL;
}
//____________________________________________________________
//
// Parse an END_ERROR statement. Piece of cake.
//
static act* par_end_error()
2001-05-23 15:26:42 +02:00
{
// avoid parsing an ada exception end_error -
// check for a semicolon
if (!PAR_end() && gpreGlob.sw_language == lang_ada)
2001-05-23 15:26:42 +02:00
return NULL;
if (!cur_error)
PAR_error("END_ERROR used out of context");
if (!((act*) MSC_pop(&cur_error)))
2001-05-23 15:26:42 +02:00
return NULL;
// Need to eat the semicolon for c if present
if (gpreGlob.sw_language == lang_c)
2003-10-15 00:22:32 +02:00
MSC_match(KW_SEMI_COLON);
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
return MSC_action(0, ACT_enderror);
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
// Parse END_FETCH statement (clause?).
//
static act* par_end_fetch()
2001-05-23 15:26:42 +02:00
{
if (!cur_fetch)
PAR_error("END_FETCH used out of context");
act* begin_action = (act*) MSC_pop(&cur_fetch);
2001-05-23 15:26:42 +02:00
act* action = MSC_action(begin_action->act_request, ACT_hctef);
2001-05-23 15:26:42 +02:00
begin_action->act_pair = action;
action->act_pair = begin_action;
PAR_end();
return action;
}
//____________________________________________________________
//
// Parse a FOR loop terminator.
//
static act* par_end_for()
2001-05-23 15:26:42 +02:00
{
if (!cur_for)
PAR_error("unmatched END_FOR");
act* begin_action = (act*) MSC_pop(&cur_for);
if (!begin_action)
2001-05-23 15:26:42 +02:00
return NULL;
PAR_end();
gpre_req* request = begin_action->act_request;
2001-05-23 15:26:42 +02:00
// If the action is a blob for, make up a blob end.
if (begin_action->act_type == ACT_blob_for) {
blb* blob = (blb*) begin_action->act_object;
act* action = MSC_action(request, ACT_endblob);
2001-05-23 15:26:42 +02:00
action->act_object = (REF) blob;
begin_action->act_pair = action;
action->act_pair = begin_action;
HSH_remove(blob->blb_symbol);
blob->blb_flags |= BLB_symbol_released;
return action;
}
// If there isn't a database assigned, the FOR statement itself
// failed. Since an error has been given, just return quietly.
if (!request->req_database)
return NULL;
act* action = MSC_action(request, ACT_endfor);
2001-05-23 15:26:42 +02:00
begin_action->act_pair = action;
action->act_pair = begin_action;
EXP_rse_cleanup(request->req_rse);
for (blb* blob = request->req_blobs; blob; blob = blob->blb_next)
{
2001-05-23 15:26:42 +02:00
if (!(blob->blb_flags & BLB_symbol_released))
HSH_remove(blob->blb_symbol);
}
2001-05-23 15:26:42 +02:00
return action;
}
//____________________________________________________________
//
// Parse and process END_MODIFY. The processing mostly includes
// copying field references to proper context at proper level.
//
static act* par_end_modify()
2001-05-23 15:26:42 +02:00
{
if (!cur_modify)
PAR_error("unmatched END_MODIFY");
PAR_end();
upd* modify = (upd*) MSC_pop(&cur_modify);
2001-05-23 15:26:42 +02:00
if (gpreGlob.errors_global)
2001-05-23 15:26:42 +02:00
return NULL;
gpre_req* request = modify->upd_request;
2001-05-23 15:26:42 +02:00
2004-05-29 06:54:20 +02:00
// used at the end of this function
act* begin_action = request->req_actions;
while ((upd*) begin_action->act_object != modify)
begin_action = begin_action->act_next;
2001-05-23 15:26:42 +02:00
// Build assignments for all fields and null flags referenced
2004-02-02 12:02:12 +01:00
gpre_lls* stack = NULL;
int count = 0;
2001-05-23 15:26:42 +02:00
2004-05-29 06:54:20 +02:00
for (ref* reference = request->req_references; reference;
2001-05-23 15:26:42 +02:00
reference = reference->ref_next)
2004-05-29 06:54:20 +02:00
{
if (reference->ref_context == modify->upd_source &&
reference->ref_level >= modify->upd_level &&
!reference->ref_master)
{
ref* change = MSC_reference(&modify->upd_references);
2001-05-23 15:26:42 +02:00
change->ref_context = modify->upd_update;
change->ref_field = reference->ref_field;
change->ref_source = reference;
change->ref_flags = reference->ref_flags;
2004-05-29 06:54:20 +02:00
gpre_nod* item = MSC_node(nod_assignment, 2);
2002-11-11 20:19:43 +01:00
item->nod_arg[0] = MSC_unary(nod_value, (GPRE_NOD) change);
item->nod_arg[1] = MSC_unary(nod_field, (GPRE_NOD) change);
2003-10-15 03:18:01 +02:00
MSC_push((GPRE_NOD) item, &stack);
2001-05-23 15:26:42 +02:00
count++;
if (reference->ref_null) {
2004-05-29 06:54:20 +02:00
ref* flag = MSC_reference(&modify->upd_references);
2001-05-23 15:26:42 +02:00
flag->ref_context = change->ref_context;
flag->ref_field = flag_field;
flag->ref_master = change;
flag->ref_source = reference->ref_null;
change->ref_null = flag;
2003-10-15 00:22:32 +02:00
item = MSC_node(nod_assignment, 2);
2002-11-11 20:19:43 +01:00
item->nod_arg[0] = MSC_unary(nod_value, (GPRE_NOD) flag);
item->nod_arg[1] = MSC_unary(nod_field, (GPRE_NOD) flag);
2003-10-15 03:18:01 +02:00
MSC_push((GPRE_NOD) item, &stack);
2001-05-23 15:26:42 +02:00
count++;
}
}
2004-05-29 06:54:20 +02:00
}
2001-05-23 15:26:42 +02:00
// Build a list node of the assignments
2004-05-29 06:54:20 +02:00
gpre_nod* assignments = MSC_node(nod_list, (SSHORT) count);
modify->upd_assignments = assignments;
gpre_nod** ptr = assignments->nod_arg + count;
2001-05-23 15:26:42 +02:00
while (stack)
2003-10-15 03:18:01 +02:00
*--ptr = (GPRE_NOD) MSC_pop(&stack);
2001-05-23 15:26:42 +02:00
act* action = MSC_action(request, ACT_endmodify);
2001-05-23 15:26:42 +02:00
action->act_object = (REF) modify;
begin_action->act_pair = action;
action->act_pair = begin_action;
return action;
}
//____________________________________________________________
//
// Parse a stream END statement.
//
static act* par_end_stream()
2001-05-23 15:26:42 +02:00
{
2004-05-29 06:54:20 +02:00
gpre_sym* symbol = gpreGlob.token_global.tok_symbol;
if (!symbol || symbol->sym_type != SYM_stream)
2003-10-15 00:22:32 +02:00
CPR_s_error("stream cursor");
2001-05-23 15:26:42 +02:00
gpre_req* request = (gpre_req*) symbol->sym_object;
2001-05-23 15:26:42 +02:00
HSH_remove(symbol);
EXP_rse_cleanup(request->req_rse);
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
PAR_end();
2003-10-15 00:22:32 +02:00
return MSC_action(request, ACT_s_end);
2001-05-23 15:26:42 +02:00
}
//____________________________________________________________
//
// Process an END_STORE.
//
static act* par_end_store(bool special)
2001-05-23 15:26:42 +02:00
{
REF reference;
2001-05-23 15:26:42 +02:00
if (!cur_store)
PAR_error("unmatched END_STORE");
PAR_end();
act* begin_action = (act*) MSC_pop(&cur_store);
gpre_req* request = begin_action->act_request;
2001-05-23 15:26:42 +02:00
if (request->req_type == REQ_store) {
if (gpreGlob.errors_global)
2001-05-23 15:26:42 +02:00
return NULL;
// Make up an assignment list for all field references
2001-05-23 15:26:42 +02:00
int count = 0;
2001-05-23 15:26:42 +02:00
for (reference = request->req_references; reference;
2003-09-13 14:22:11 +02:00
reference = reference->ref_next)
{
if (!reference->ref_master)
2001-05-23 15:26:42 +02:00
count++;
2003-09-13 14:22:11 +02:00
}
2001-05-23 15:26:42 +02:00
2005-01-07 05:00:19 +01:00
gpre_nod* const assignments = MSC_node(nod_list, (SSHORT) count);
request->req_node = assignments;
2001-05-23 15:26:42 +02:00
count = 0;
for (reference = request->req_references; reference;
reference = reference->ref_next)
{
2001-05-23 15:26:42 +02:00
if (reference->ref_master)
continue;
gpre_nod* item = MSC_node(nod_assignment, 2);
2002-11-11 20:19:43 +01:00
item->nod_arg[0] = MSC_unary(nod_value, (GPRE_NOD) reference);
item->nod_arg[1] = MSC_unary(nod_field, (GPRE_NOD) reference);
2001-05-23 15:26:42 +02:00
assignments->nod_arg[count++] = item;
}
}
else {
/* if the request type is store2, we have store ...returning_values.
* The next action on the cur_store stack points to a upd structure
2001-05-23 15:26:42 +02:00
* which will give us the assignments for this one.
*/
act* action2 = (act*) MSC_pop(&cur_store);
upd* return_values = (upd*) action2->act_object;
2001-05-23 15:26:42 +02:00
// Build assignments for all fields and null flags referenced
2001-05-23 15:26:42 +02:00
gpre_lls* stack = NULL;
int count = 0;
2001-05-23 15:26:42 +02:00
for (reference = request->req_references; reference;
reference = reference->ref_next)
{
if (reference->ref_context == return_values->upd_update &&
2001-05-23 15:26:42 +02:00
reference->ref_level >= return_values->upd_level &&
!reference->ref_master)
{
ref* change = MSC_reference(&return_values->upd_references);
2001-05-23 15:26:42 +02:00
change->ref_context = return_values->upd_update;
change->ref_field = reference->ref_field;
change->ref_source = reference;
change->ref_flags = reference->ref_flags;
gpre_nod* item = MSC_node(nod_assignment, 2);
2002-11-11 20:19:43 +01:00
item->nod_arg[0] = MSC_unary(nod_field, (GPRE_NOD) change);
item->nod_arg[1] = MSC_unary(nod_value, (GPRE_NOD) change);
2003-10-15 03:18:01 +02:00
MSC_push((GPRE_NOD) item, &stack);
2001-05-23 15:26:42 +02:00
count++;
}
}
2001-05-23 15:26:42 +02:00
// Build a list node of the assignments
2001-05-23 15:26:42 +02:00
2005-01-07 05:00:19 +01:00
gpre_nod* const assignments = MSC_node(nod_list, (SSHORT) count);
return_values->upd_assignments = assignments;
gpre_nod** ptr = assignments->nod_arg + count;
2001-05-23 15:26:42 +02:00
while (stack)
2003-10-15 03:18:01 +02:00
*--ptr = (GPRE_NOD) MSC_pop(&stack);
2001-05-23 15:26:42 +02:00
}
gpre_ctx* context = request->req_contexts;
if (context)
2001-05-23 15:26:42 +02:00
HSH_remove(context->ctx_symbol);
act* action;
if (special)
2003-10-15 00:22:32 +02:00
action = MSC_action(request, ACT_endstore_special);
else
2003-10-15 00:22:32 +02:00
action = MSC_action(request, ACT_endstore);
2001-05-23 15:26:42 +02:00
begin_action->act_pair = action;
action->act_pair = begin_action;
return action;
}
//____________________________________________________________
//
// Parse a ERASE statement.
//
static act* par_erase()
2001-05-23 15:26:42 +02:00
{
2004-05-29 06:54:20 +02:00
gpre_sym* symbol = gpreGlob.token_global.tok_symbol;
if (!symbol || symbol->sym_type != SYM_context)
2003-10-15 00:22:32 +02:00
CPR_s_error("context variable");
2001-05-23 15:26:42 +02:00
gpre_ctx* source = symbol->sym_object;
gpre_req* request = source->ctx_request;
2001-05-23 15:26:42 +02:00
if (request->req_type != REQ_for && request->req_type != REQ_cursor)
PAR_error("invalid context for modify");
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
PAR_end();
// Make an update block to hold everything known about the modify
upd* erase = (upd*) MSC_alloc(UPD_LEN);
2001-05-23 15:26:42 +02:00
erase->upd_request = request;
erase->upd_source = source;
act* action = MSC_action(request, ACT_erase);
2001-05-23 15:26:42 +02:00
action->act_object = (REF) erase;
return action;
}
//____________________________________________________________
//
// Parse a stream FETCH statement.
//
static act* par_fetch()
2001-05-23 15:26:42 +02:00
{
2004-05-29 06:54:20 +02:00
gpre_sym* symbol = gpreGlob.token_global.tok_symbol;
if (!symbol || symbol->sym_type != SYM_stream)
2001-05-23 15:26:42 +02:00
return NULL;
gpre_req* request = (gpre_req*) symbol->sym_object;
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
PAR_end();
act* action = MSC_action(request, ACT_s_fetch);
2003-10-15 03:18:01 +02:00
MSC_push((GPRE_NOD) action, &cur_fetch);
2001-05-23 15:26:42 +02:00
return action;
}
//____________________________________________________________
//
// Parse a FINISH statement.
//
static act* par_finish()
2001-05-23 15:26:42 +02:00
{
act* action = MSC_action(0, ACT_finish);
2001-05-23 15:26:42 +02:00
if (!terminator())
while (true) {
2004-05-29 06:54:20 +02:00
gpre_sym* symbol = gpreGlob.token_global.tok_symbol;
if (symbol && (symbol->sym_type == SYM_database))
{
rdy* ready = (rdy*) MSC_alloc(RDY_LEN);
ready->rdy_next = (rdy*) action->act_object;
2001-05-23 15:26:42 +02:00
action->act_object = (REF) ready;
ready->rdy_database = (DBB) symbol->sym_object;
CPR_eol_token();
}
else
2003-10-15 00:22:32 +02:00
CPR_s_error("database handle");
2001-05-23 15:26:42 +02:00
if (terminator())
break;
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_COMMA))
2001-05-23 15:26:42 +02:00
break;
}
if (gpreGlob.sw_language == lang_ada)
2003-10-15 00:22:32 +02:00
MSC_match(KW_SEMI_COLON);
2001-05-23 15:26:42 +02:00
return action;
}
//____________________________________________________________
//
// Parse a FOR clause, returning an action.
// We don't know where we are a host language FOR, a record looping
// FOR, or a blob FOR. Parse a little ahead and try to find out.
// Avoid stepping on user routines that use GDML keywords
//
static act* par_for()
2001-05-23 15:26:42 +02:00
{
TEXT s[ERROR_LENGTH];
2004-05-29 06:54:20 +02:00
gpre_sym* symbol = NULL;
bool dup_symbol = false;
2001-05-23 15:26:42 +02:00
2004-05-29 06:54:20 +02:00
if (!(gpreGlob.token_global.tok_keyword == KW_FIRST) && !(gpreGlob.token_global.tok_keyword == KW_LEFT_PAREN))
{
if (gpreGlob.token_global.tok_symbol)
dup_symbol = true;
2001-05-23 15:26:42 +02:00
symbol = MSC_symbol(SYM_cursor, gpreGlob.token_global.tok_string, gpreGlob.token_global.tok_length, 0);
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_IN)) {
2001-05-23 15:26:42 +02:00
MSC_free((UCHAR *) symbol);
return NULL;
}
if (dup_symbol) {
fb_utils::snprintf(s, sizeof(s),
"symbol %s is already in use", gpreGlob.token_global.tok_string);
2001-05-23 15:26:42 +02:00
PAR_error(s);
}
2004-05-29 06:54:20 +02:00
const gpre_sym* temp = gpreGlob.token_global.tok_symbol;
if (temp && temp->sym_type == SYM_context)
2001-05-23 15:26:42 +02:00
return par_open_blob(ACT_blob_for, symbol);
}
// Make up request block. Since this might not be a database statement,
// stay ready to back out if necessay.
2004-05-29 06:54:20 +02:00
gpre_req* request = MSC_request(REQ_for);
2001-05-23 15:26:42 +02:00
2004-05-29 06:54:20 +02:00
gpre_rse* rec_expr;
if (!par_options(request, true) || !(rec_expr = EXP_rse(request, symbol))) {
2001-05-23 15:26:42 +02:00
MSC_free_request(request);
return NULL;
}
2004-05-29 06:54:20 +02:00
act* action = MSC_action(request, ACT_for);
2003-10-15 03:18:01 +02:00
MSC_push((GPRE_NOD) action, &cur_for);
2001-05-23 15:26:42 +02:00
request->req_rse = rec_expr;
gpre_ctx* context = rec_expr->rse_context[0];
2004-05-29 06:54:20 +02:00
gpre_rel* relation = context->ctx_relation;
2001-05-23 15:26:42 +02:00
request->req_database = relation->rel_database;
gpre_ctx** ptr = rec_expr->rse_context;
for (const gpre_ctx* const* const end = ptr + rec_expr->rse_count;
ptr < end; ptr++)
{
2001-05-23 15:26:42 +02:00
context = *ptr;
context->ctx_next = request->req_contexts;
request->req_contexts = context;
}
return action;
}
//____________________________________________________________
//
// A function declaration is interesting in
// FORTRAN because it starts a new sub-module
// and we have to begin everything all over.
// In PASCAL it's interesting because it may
// indicate a good place to put message declarations.
// Unfortunately that requires a loose parse of the
// routine header, but what the hell...
//
static act* par_function()
2001-05-23 15:26:42 +02:00
{
if (gpreGlob.sw_language == lang_fortran)
2001-05-23 15:26:42 +02:00
return par_subroutine();
if (gpreGlob.sw_language == lang_pascal)
2001-05-23 15:26:42 +02:00
return par_procedure();
return NULL;
}
//____________________________________________________________
//
// Check a left brace (or whatever) for start of a new
// routine.
//
static act* par_left_brace()
2001-05-23 15:26:42 +02:00
{
if (brace_count++ > 0)
return NULL;
act* action = MSC_action(0, ACT_routine);
gpreGlob.cur_routine = action;
2001-05-23 15:26:42 +02:00
action->act_flags |= ACT_mark;
return action;
}
//____________________________________________________________
//
// Parse a MODIFY statement.
//
static act* par_modify()
2001-05-23 15:26:42 +02:00
{
// Set up modify and action blocks. This is done here to leave the
// structure in place to cleanly handle END_MODIFY under error conditions.
upd* modify = (upd*) MSC_alloc(UPD_LEN);
2003-10-15 03:18:01 +02:00
MSC_push((GPRE_NOD) modify, &cur_modify);
2001-05-23 15:26:42 +02:00
// If the next token isn't a context variable, we can't continue
2004-05-29 06:54:20 +02:00
gpre_sym* symbol = gpreGlob.token_global.tok_symbol;
if (!symbol || symbol->sym_type != SYM_context) {
2005-01-07 05:00:19 +01:00
SCHAR s[ERROR_LENGTH];
sprintf(s, "%s is not a valid context variable", gpreGlob.token_global.tok_string);
2001-05-23 15:26:42 +02:00
PAR_error(s);
}
gpre_ctx* source = symbol->sym_object;
gpre_req* request = source->ctx_request;
2001-05-23 15:26:42 +02:00
if (request->req_type != REQ_for && request->req_type != REQ_cursor)
PAR_error("invalid context for modify");
act* action = MSC_action(request, ACT_modify);
2001-05-23 15:26:42 +02:00
action->act_object = (REF) modify;
2003-10-15 00:22:32 +02:00
PAR_get_token();
MSC_match(KW_USING);
2001-05-23 15:26:42 +02:00
// Make an update context by cloning the source context
gpre_ctx* update = MSC_context(request);
2001-05-23 15:26:42 +02:00
update->ctx_symbol = source->ctx_symbol;
update->ctx_relation = source->ctx_relation;
// Make an update block to hold everything known about the modify
modify->upd_request = request;
modify->upd_source = source;
modify->upd_update = update;
modify->upd_level = ++request->req_level;
return action;
}
//____________________________________________________________
//
// This rather degenerate routine exists to allow both:
//
// ON_ERROR
// ON ERROR
//
// so the more dim of our users avoid mistakes.
//
static act* par_on()
2001-05-23 15:26:42 +02:00
{
2003-10-15 00:22:32 +02:00
if (!(MSC_match(KW_ERROR)))
2001-05-23 15:26:42 +02:00
return NULL;
return par_on_error();
}
//____________________________________________________________
//
// Parse a trailing ON_ERROR clause.
//
static act* par_on_error()
2001-05-23 15:26:42 +02:00
{
if (!cur_statement)
PAR_error("ON_ERROR used out of context");
PAR_end();
act* action = MSC_action(0, ACT_on_error);
cur_statement->act_error = action;
2001-05-23 15:26:42 +02:00
action->act_object = (REF) cur_statement;
2003-10-15 03:18:01 +02:00
MSC_push((GPRE_NOD) action, &cur_error);
2001-05-23 15:26:42 +02:00
if (cur_statement->act_pair)
cur_statement->act_pair->act_error = action;
return action;
}
//____________________________________________________________
//
// Parse an "open blob" type statement. These include OPEN_BLOB,
// CREATE_BLOB, and blob FOR.
//
static act* par_open_blob( ACT_T act_op, gpre_sym* symbol)
2001-05-23 15:26:42 +02:00
{
// If somebody hasn't already parsed up a symbol for us, parse the
// symbol and the mandatory IN now.
if (!symbol) {
symbol = PAR_symbol(SYM_dummy);
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_IN))
CPR_s_error("IN");
2001-05-23 15:26:42 +02:00
}
// The next thing we should find is a field reference. Get it.
gpre_ctx* context;
2004-05-29 06:54:20 +02:00
gpre_fld* field = EXP_field(&context);
if (!field)
2001-05-23 15:26:42 +02:00
return NULL;
2005-01-07 05:00:19 +01:00
TEXT s[ERROR_LENGTH];
2001-05-23 15:26:42 +02:00
if (!(field->fld_flags & FLD_blob)) {
2005-01-07 05:00:19 +01:00
fb_utils::snprintf(s, sizeof(s),
"Field %s is not a blob", field->fld_symbol->sym_string);
2001-05-23 15:26:42 +02:00
PAR_error(s);
}
if (field->fld_array_info) {
2005-01-07 05:00:19 +01:00
fb_utils::snprintf(s, sizeof(s),
"Field %s is an array and can not be opened as a blob",
2001-05-23 15:26:42 +02:00
field->fld_symbol->sym_string);
PAR_error(s);
}
gpre_req* request = context->ctx_request;
ref* reference = EXP_post_field(field, context, false);
2001-05-23 15:26:42 +02:00
blb* blob = (blb*) MSC_alloc(BLB_LEN);
2001-05-23 15:26:42 +02:00
blob->blb_symbol = symbol;
blob->blb_reference = reference;
// See if we need a blob filter (do we have a subtype to subtype clause?)
2005-01-07 05:00:19 +01:00
bool filter_is_defined = false;
2001-05-23 15:26:42 +02:00
for (;;)
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_FILTER)) {
blob->blb_const_from_type = (MSC_match(KW_FROM)) ?
2001-05-23 15:26:42 +02:00
PAR_blob_subtype(request->req_database) : field->fld_sub_type;
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_TO))
CPR_s_error("TO");
2001-05-23 15:26:42 +02:00
blob->blb_const_to_type = PAR_blob_subtype(request->req_database);
filter_is_defined = true;
2001-05-23 15:26:42 +02:00
}
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_STREAM))
2003-11-08 17:40:17 +01:00
blob->blb_type = isc_bpb_type_stream;
2001-05-23 15:26:42 +02:00
else
break;
if (!(blob->blb_seg_length = field->fld_seg_length))
blob->blb_seg_length = 512;
blob->blb_request = request;
blob->blb_next = request->req_blobs;
request->req_blobs = blob;
symbol->sym_type = SYM_blob;
symbol->sym_object = (gpre_ctx*) blob;
2001-05-23 15:26:42 +02:00
HSH_insert(symbol);
// ** You just inserted the context variable into the hash table.
//The current token however might be the same context variable.
//If so, get the symbol for it.
//*
if (gpreGlob.token_global.tok_keyword == KW_none)
gpreGlob.token_global.tok_symbol = HSH_lookup(gpreGlob.token_global.tok_string);
2001-05-23 15:26:42 +02:00
act* action = MSC_action(request, act_op);
2001-05-23 15:26:42 +02:00
action->act_object = (REF) blob;
if (act_op == ACT_blob_for)
2003-10-15 03:18:01 +02:00
MSC_push((GPRE_NOD) action, &cur_for);
2001-05-23 15:26:42 +02:00
// Need to eat the semicolon if present
if (gpreGlob.sw_language == lang_c)
2003-10-15 00:22:32 +02:00
MSC_match(KW_SEMI_COLON);
2001-05-23 15:26:42 +02:00
else
PAR_end();
return action;
}
//____________________________________________________________
//
// Parse request options. Return true if successful, otherwise
// false. If a flag is set, don't give an error on false.
2001-05-23 15:26:42 +02:00
//
static bool par_options(gpre_req* request,
bool flag)
2001-05-23 15:26:42 +02:00
{
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_LEFT_PAREN))
return true;
2001-05-23 15:26:42 +02:00
while (true) {
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_RIGHT_PAREN))
return true;
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_REQUEST_HANDLE)) {
request->req_handle = PAR_native_value(false, true);
2001-05-23 15:26:42 +02:00
request->req_flags |= REQ_exp_hand;
}
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_TRANSACTION_HANDLE))
request->req_trans = PAR_native_value(false, true);
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_LEVEL))
request->req_request_level = PAR_native_value(false, false);
2001-05-23 15:26:42 +02:00
else {
if (!flag)
2003-10-15 00:22:32 +02:00
CPR_s_error("request option");
return false;
2001-05-23 15:26:42 +02:00
}
2003-10-15 00:22:32 +02:00
MSC_match(KW_COMMA);
2001-05-23 15:26:42 +02:00
}
}
//____________________________________________________________
//
// If this is PLI, then we've got a new procedure.
//
// If this is PASCAL, then we've come upon
// a program, module, function, or procedure header.
// Alas and alack, we have to decide if this is
// a real header or a forward/external declaration.
//
// In either case, we make a mark-only action block,
// because that's real cheap. If it's a real routine,
// we make the action the current routine.
//
static act* par_procedure()
2001-05-23 15:26:42 +02:00
{
act* action;
2001-05-23 15:26:42 +02:00
if (gpreGlob.sw_language == lang_pascal) {
routine_decl = true;
2001-05-23 15:26:42 +02:00
action = scan_routine_header();
if (!(action->act_flags & ACT_decl)) {
MSC_push((GPRE_NOD) gpreGlob.cur_routine, &routine_stack);
gpreGlob.cur_routine = action;
2001-05-23 15:26:42 +02:00
}
}
else
action = NULL;
return action;
}
//____________________________________________________________
//
// Parse a READY statement.
//
static act* par_ready()
2001-05-23 15:26:42 +02:00
{
gpre_req* request;
gpre_sym* symbol;
2003-09-05 16:55:59 +02:00
DBB db;
bool need_handle = false;
USHORT default_buffers = 0;
2001-05-23 15:26:42 +02:00
act* action = MSC_action(0, ACT_ready);
2001-05-23 15:26:42 +02:00
if (gpreGlob.token_global.tok_keyword == KW_CACHE)
2003-10-15 00:22:32 +02:00
CPR_s_error("database name or handle");
2001-05-23 15:26:42 +02:00
while (!terminator()) {
/* this default mechanism is left here for backwards
compatibility, but it is no longer documented and
is not something we should maintain for all ready
options since it needlessly complicates the ready
statement without providing any extra functionality */
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_DEFAULT)) {
if (!MSC_match(KW_CACHE))
CPR_s_error("database name or handle");
default_buffers = atoi(gpreGlob.token_global.tok_string);
2001-05-23 15:26:42 +02:00
CPR_eol_token();
2003-10-15 00:22:32 +02:00
MSC_match(KW_BUFFERS);
2001-05-23 15:26:42 +02:00
continue;
}
rdy* ready = (rdy*) MSC_alloc(RDY_LEN);
ready->rdy_next = (rdy*) action->act_object;
2001-05-23 15:26:42 +02:00
action->act_object = (REF) ready;
if (!(symbol = gpreGlob.token_global.tok_symbol) || symbol->sym_type != SYM_database) {
ready->rdy_filename = PAR_native_value(false, false);
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_AS))
need_handle = true;
2001-05-23 15:26:42 +02:00
}
if (!(symbol = gpreGlob.token_global.tok_symbol) || symbol->sym_type != SYM_database) {
if (!gpreGlob.isc_databases || gpreGlob.isc_databases->dbb_next || need_handle) {
need_handle = false;
2003-10-15 00:22:32 +02:00
CPR_s_error("database handle");
2001-05-23 15:26:42 +02:00
}
ready->rdy_database = gpreGlob.isc_databases;
2001-05-23 15:26:42 +02:00
}
need_handle = false;
2001-05-23 15:26:42 +02:00
if (!ready->rdy_database)
ready->rdy_database = (DBB) symbol->sym_object;
if (terminator())
break;
CPR_eol_token();
// pick up the possible parameters, in any order
2001-05-23 15:26:42 +02:00
USHORT buffers = 0;
2003-09-05 16:55:59 +02:00
db = ready->rdy_database;
2001-05-23 15:26:42 +02:00
for (;;) {
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_CACHE)) {
buffers = atoi(gpreGlob.token_global.tok_string);
2001-05-23 15:26:42 +02:00
CPR_eol_token();
2003-10-15 00:22:32 +02:00
MSC_match(KW_BUFFERS);
2001-05-23 15:26:42 +02:00
}
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_USER))
db->dbb_r_user = PAR_native_value(false, false);
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_PASSWORD))
db->dbb_r_password = PAR_native_value(false, false);
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_LC_MESSAGES))
db->dbb_r_lc_messages = PAR_native_value(false, false);
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_LC_CTYPE)) {
db->dbb_r_lc_ctype = PAR_native_value(false, false);
2003-09-05 16:55:59 +02:00
db->dbb_know_subtype = 2;
2001-05-23 15:26:42 +02:00
}
else
break;
}
request = NULL;
if (buffers)
request = PAR_set_up_dpb_info(ready, action, buffers);
/* if there are any options that take host variables as arguments,
make sure that we generate variables for the request so that the
dpb can be extended at runtime */
2003-09-05 16:55:59 +02:00
if (db->dbb_r_user || db->dbb_r_password ||
db->dbb_r_lc_messages || db->dbb_r_lc_ctype)
{
2001-05-23 15:26:42 +02:00
if (!request)
request = PAR_set_up_dpb_info(ready, action, default_buffers);
request->req_flags |= REQ_extend_dpb;
}
/* ...and if there are compile time user or password specified,
make sure there will be a dpb generated for them */
2003-09-05 16:55:59 +02:00
if (!request && (db->dbb_c_user || db->dbb_c_password ||
db->dbb_c_lc_messages || db->dbb_c_lc_ctype))
{
2001-05-23 15:26:42 +02:00
request = PAR_set_up_dpb_info(ready, action, default_buffers);
2003-09-05 16:55:59 +02:00
}
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
MSC_match(KW_COMMA);
2001-05-23 15:26:42 +02:00
}
PAR_end();
if (action->act_object) {
if (default_buffers)
for (rdy* ready = (rdy*) action->act_object; ready;
2003-09-13 14:22:11 +02:00
ready = ready->rdy_next)
{
if (!ready->rdy_request)
request = PAR_set_up_dpb_info(ready, action, default_buffers);
}
2001-05-23 15:26:42 +02:00
return action;
}
// No explicit databases -- pick up all known
for (db = gpreGlob.isc_databases; db; db = db->dbb_next)
2003-09-05 16:55:59 +02:00
if (db->dbb_runtime || !(db->dbb_flags & DBB_sqlca)) {
rdy* ready = (rdy*) MSC_alloc(RDY_LEN);
ready->rdy_next = (rdy*) action->act_object;
2001-05-23 15:26:42 +02:00
action->act_object = (REF) ready;
2003-09-05 16:55:59 +02:00
ready->rdy_database = db;
2001-05-23 15:26:42 +02:00
}
if (!action->act_object)
PAR_error("no database available to READY");
else
for (rdy* ready = (rdy*) action->act_object; ready; ready = ready->rdy_next) {
2001-05-23 15:26:42 +02:00
request = ready->rdy_request;
if (default_buffers && !ready->rdy_request)
request = PAR_set_up_dpb_info(ready, action, default_buffers);
/* if there are any options that take host variables as arguments,
make sure that we generate variables for the request so that the
dpb can be extended at runtime */
2003-09-05 16:55:59 +02:00
db = ready->rdy_database;
if (db->dbb_r_user || db->dbb_r_password ||
db->dbb_r_lc_messages || db->dbb_r_lc_ctype)
{
2001-05-23 15:26:42 +02:00
if (!request)
2003-09-05 16:55:59 +02:00
request = PAR_set_up_dpb_info(ready, action, default_buffers);
2001-05-23 15:26:42 +02:00
request->req_flags |= REQ_extend_dpb;
}
/* ...and if there are compile time user or password specified,
make sure there will be a dpb generated for them */
2003-09-05 16:55:59 +02:00
if (!request && (db->dbb_c_user || db->dbb_c_password ||
db->dbb_c_lc_messages || db->dbb_c_lc_ctype))
{
2001-05-23 15:26:42 +02:00
request = PAR_set_up_dpb_info(ready, action, default_buffers);
2003-09-05 16:55:59 +02:00
}
2001-05-23 15:26:42 +02:00
}
return action;
}
//____________________________________________________________
//
// Parse a returning values clause in a STORE
// returning an action.
// Act as if we were at end_store, then set up
// for a further set of fields for returned values.
//
static act* par_returning_values()
2001-05-23 15:26:42 +02:00
{
REF reference;
2001-05-23 15:26:42 +02:00
if (!cur_store)
PAR_error("STORE must precede RETURNING_VALUES");
act* begin_action = (act*) MSC_pop(&cur_store);
gpre_req* request = begin_action->act_request;
2001-05-23 15:26:42 +02:00
// First take care of the impending store:
// Make up an assignment list for all field references and
// clone the references while we are at it
int count = 0;
2001-05-23 15:26:42 +02:00
for (reference = request->req_references; reference;
2003-09-13 14:22:11 +02:00
reference = reference->ref_next)
{
if (!reference->ref_master)
2001-05-23 15:26:42 +02:00
count++;
2003-09-13 14:22:11 +02:00
}
2001-05-23 15:26:42 +02:00
GPRE_NOD assignments = MSC_node(nod_list, (SSHORT) count);
request->req_node = assignments;
2001-05-23 15:26:42 +02:00
count = 0;
for (reference = request->req_references; reference;
reference = reference->ref_next)
{
2003-10-15 00:22:32 +02:00
REF save_ref = MSC_reference(&begin_action->act_object);
2001-05-23 15:26:42 +02:00
save_ref->ref_context = reference->ref_context;
save_ref->ref_field = reference->ref_field;
save_ref->ref_source = reference;
save_ref->ref_flags = reference->ref_flags;
if (reference->ref_master)
continue;
2003-10-15 00:22:32 +02:00
GPRE_NOD item = MSC_node(nod_assignment, 2);
2002-11-11 20:19:43 +01:00
item->nod_arg[0] = MSC_unary(nod_value, (GPRE_NOD) save_ref);
item->nod_arg[1] = MSC_unary(nod_field, (GPRE_NOD) save_ref);
2001-05-23 15:26:42 +02:00
assignments->nod_arg[count++] = item;
}
// Next make an updated context for post_store actions
upd* new_values = (upd*) MSC_alloc(UPD_LEN);
gpre_ctx* source = request->req_contexts;
2001-05-23 15:26:42 +02:00
request->req_type = REQ_store2;
gpre_ctx* new_ctx = MSC_context(request);
new_ctx->ctx_symbol = source->ctx_symbol;
new_ctx->ctx_relation = source->ctx_relation;
new_ctx->ctx_symbol->sym_object = new_ctx; // pointing to itself?
2001-05-23 15:26:42 +02:00
// make an update block to hold everything known about referenced
// fields
act* action = MSC_action(request, ACT_store2);
2001-05-23 15:26:42 +02:00
action->act_object = (REF) new_values;
new_values->upd_request = request;
new_values->upd_source = source;
new_values->upd_update = new_ctx;
2001-05-23 15:26:42 +02:00
new_values->upd_level = ++request->req_level;
// both actions go on the cur_store stack, the store topmost
2003-10-15 03:18:01 +02:00
MSC_push((GPRE_NOD) action, &cur_store);
MSC_push((GPRE_NOD) begin_action, &cur_store);
2001-05-23 15:26:42 +02:00
return action;
}
//____________________________________________________________
//
// Do something about a right brace.
//
static act* par_right_brace()
2001-05-23 15:26:42 +02:00
{
if (--brace_count < 0)
brace_count = 0;
return NULL;
}
//____________________________________________________________
//
// Parse a RELEASE_REQUEST statement.
//
static act* par_release()
2001-05-23 15:26:42 +02:00
{
act* action = MSC_action(0, ACT_release);
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
MSC_match(KW_FOR);
2001-05-23 15:26:42 +02:00
2004-05-29 06:54:20 +02:00
gpre_sym* symbol = gpreGlob.token_global.tok_symbol;
if (symbol && (symbol->sym_type == SYM_database)) {
2001-05-23 15:26:42 +02:00
action->act_object = (REF) symbol->sym_object;
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
}
PAR_end();
return action;
}
//____________________________________________________________
//
// Handle a GET_SLICE or PUT_SLICE statement.
//
static act* par_slice( ACT_T type)
2001-05-23 15:26:42 +02:00
{
gpre_ctx* context;
gpre_fld* field = EXP_field(&context);
2001-05-23 15:26:42 +02:00
ary* info = field->fld_array_info;
if (!info)
2003-10-15 00:22:32 +02:00
CPR_s_error("array field");
2001-05-23 15:26:42 +02:00
gpre_req* request = MSC_request(REQ_slice);
slc* slice = (slc*) MSC_alloc(SLC_LEN(info->ary_dimension_count));
request->req_slice = slice;
2001-05-23 15:26:42 +02:00
slice->slc_dimensions = info->ary_dimension_count;
slice->slc_field = field;
slice->slc_field_ref = EXP_post_field(field, context, false);
2001-05-23 15:26:42 +02:00
slice->slc_parent_request = context->ctx_request;
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_L_BRCKET))
CPR_s_error("left bracket");
2001-05-23 15:26:42 +02:00
USHORT n = 0;
for (slc::slc_repeat* tail = slice->slc_rpt; ++n <= slice->slc_dimensions;
++tail)
{
2001-05-23 15:26:42 +02:00
tail->slc_lower = tail->slc_upper = EXP_subscript(request);
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_COLON))
2001-05-23 15:26:42 +02:00
tail->slc_upper = EXP_subscript(request);
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_COMMA))
2001-05-23 15:26:42 +02:00
break;
}
if (n != slice->slc_dimensions)
PAR_error("subscript count mismatch");
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_R_BRCKET))
CPR_s_error("right bracket");
2001-05-23 15:26:42 +02:00
if (type == ACT_get_slice) {
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_INTO))
CPR_s_error("INTO");
2001-05-23 15:26:42 +02:00
}
2003-10-15 00:22:32 +02:00
else if (!MSC_match(KW_FROM))
CPR_s_error("FROM");
2001-05-23 15:26:42 +02:00
slice->slc_array = EXP_subscript(0);
act* action = MSC_action(request, type);
2001-05-23 15:26:42 +02:00
action->act_object = (REF) slice;
if (gpreGlob.sw_language == lang_c)
2003-10-15 00:22:32 +02:00
MSC_match(KW_SEMI_COLON);
2001-05-23 15:26:42 +02:00
else
PAR_end();
return action;
}
//____________________________________________________________
//
// Parse a STORE clause, returning an action.
//
static act* par_store()
2001-05-23 15:26:42 +02:00
{
gpre_req* request = MSC_request(REQ_store);
par_options(request, false);
act* action = MSC_action(request, ACT_store);
2003-10-15 03:18:01 +02:00
MSC_push((GPRE_NOD) action, &cur_store);
2001-05-23 15:26:42 +02:00
gpre_ctx* context = EXP_context(request, 0);
gpre_rel* relation = context->ctx_relation;
2001-05-23 15:26:42 +02:00
request->req_database = relation->rel_database;
HSH_insert(context->ctx_symbol);
// ** You just inserted the context variable into the hash table.
//The current token however might be the same context variable.
//If so, get the symbol for it.
//*
if (gpreGlob.token_global.tok_keyword == KW_none)
gpreGlob.token_global.tok_symbol = HSH_lookup(gpreGlob.token_global.tok_string);
2003-10-15 00:22:32 +02:00
MSC_match(KW_USING);
2001-05-23 15:26:42 +02:00
return action;
}
//____________________________________________________________
//
// Parse a start stream statement.
//
static act* par_start_stream()
2001-05-23 15:26:42 +02:00
{
gpre_req* request = MSC_request(REQ_cursor);
par_options(request, false);
act* action = MSC_action(request, ACT_s_start);
2001-05-23 15:26:42 +02:00
gpre_sym* cursor = PAR_symbol(SYM_dummy);
2001-05-23 15:26:42 +02:00
cursor->sym_type = SYM_stream;
cursor->sym_object = (gpre_ctx*) request;
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
MSC_match(KW_USING);
gpre_rse* rec_expr = EXP_rse(request, 0);
2001-05-23 15:26:42 +02:00
request->req_rse = rec_expr;
gpre_ctx* context = rec_expr->rse_context[0];
gpre_rel* relation = context->ctx_relation;
2001-05-23 15:26:42 +02:00
request->req_database = relation->rel_database;
gpre_ctx** ptr = rec_expr->rse_context;
for (const gpre_ctx* const* const end = ptr + rec_expr->rse_count;
ptr < end; ptr++)
{
2001-05-23 15:26:42 +02:00
context = *ptr;
context->ctx_next = request->req_contexts;
request->req_contexts = context;
}
HSH_insert(cursor);
PAR_end();
return action;
}
//____________________________________________________________
//
// Parse a START_TRANSACTION statement, including
// transaction handle, transaction options, and
// reserving list.
//
static act* par_start_transaction()
2001-05-23 15:26:42 +02:00
{
act* action = MSC_action(0, ACT_start);
2001-05-23 15:26:42 +02:00
if (terminator()) {
PAR_end();
return action;
}
gpre_tra* trans = (gpre_tra*) MSC_alloc(TRA_LEN);
2001-05-23 15:26:42 +02:00
// get the transaction handle
if (!gpreGlob.token_global.tok_symbol)
trans->tra_handle = PAR_native_value(false, true);
2001-05-23 15:26:42 +02:00
// loop reading the various transaction options
while (!(gpreGlob.token_global.tok_keyword == KW_RESERVING) && !(gpreGlob.token_global.tok_keyword == KW_USING) &&
2003-10-15 00:22:32 +02:00
!terminator())
{
if (MSC_match(KW_READ_ONLY)) {
2001-05-23 15:26:42 +02:00
trans->tra_flags |= TRA_ro;
continue;
}
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_READ_WRITE))
2001-05-23 15:26:42 +02:00
continue;
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_CONSISTENCY)) {
2001-05-23 15:26:42 +02:00
trans->tra_flags |= TRA_con;
continue;
}
2003-10-15 00:22:32 +02:00
// *** else if (MSC_match (KW_READ_COMMITTED))
2001-05-23 15:26:42 +02:00
// {
// trans->tra_flags |= TRA_read_committed;
// continue;
// } **
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_CONCURRENCY))
2001-05-23 15:26:42 +02:00
continue;
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_NO_WAIT)) {
2001-05-23 15:26:42 +02:00
trans->tra_flags |= TRA_nw;
continue;
}
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_WAIT))
2001-05-23 15:26:42 +02:00
continue;
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_AUTOCOMMIT)) {
2001-05-23 15:26:42 +02:00
trans->tra_flags |= TRA_autocommit;
continue;
}
if (gpreGlob.sw_language == lang_cobol || gpreGlob.sw_language == lang_fortran)
2001-05-23 15:26:42 +02:00
break;
else
2003-10-15 00:22:32 +02:00
CPR_s_error("transaction keyword");
2001-05-23 15:26:42 +02:00
}
// send out for the list of reserved relations
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_RESERVING)) {
2001-05-23 15:26:42 +02:00
trans->tra_flags |= TRA_rrl;
PAR_reserving(trans->tra_flags, false);
2001-05-23 15:26:42 +02:00
}
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_USING)) {
2001-05-23 15:26:42 +02:00
trans->tra_flags |= TRA_inc;
PAR_using_db();
}
PAR_end();
CMP_t_start(trans);
action->act_object = (REF) trans;
return action;
}
//____________________________________________________________
//
// We have hit either a function or subroutine declaration.
// If the language is fortran, make the position with a break.
//
static act* par_subroutine()
2001-05-23 15:26:42 +02:00
{
if (gpreGlob.sw_language != lang_fortran)
2001-05-23 15:26:42 +02:00
return NULL;
act* action = MSC_action(0, ACT_routine);
2001-05-23 15:26:42 +02:00
action->act_flags |= ACT_mark | ACT_break;
gpreGlob.cur_routine = action;
2001-05-23 15:26:42 +02:00
return action;
}
//____________________________________________________________
//
// Parse a transaction termination statement: commit,
// prepare, rollback, or save (commit retaining context).
//
static act* par_trans( ACT_T act_op)
2001-05-23 15:26:42 +02:00
{
act* action = MSC_action(0, act_op);
2001-05-23 15:26:42 +02:00
if (!terminator()) {
2004-05-29 06:54:20 +02:00
const bool parens = MSC_match(KW_LEFT_PAREN);
if ((gpreGlob.sw_language == lang_fortran)
2004-05-29 06:54:20 +02:00
&& (act_op == ACT_commit_retain_context))
{
2003-10-15 00:22:32 +02:00
if (!(MSC_match(KW_TRANSACTION_HANDLE)))
2001-05-23 15:26:42 +02:00
return NULL;
}
else
2003-10-15 00:22:32 +02:00
MSC_match(KW_TRANSACTION_HANDLE);
action->act_object = (REF) PAR_native_value(false, true);
2001-05-23 15:26:42 +02:00
if (parens)
EXP_match_paren();
}
if ((gpreGlob.sw_language != lang_fortran) && (gpreGlob.sw_language != lang_pascal))
2003-10-15 00:22:32 +02:00
MSC_match(KW_SEMI_COLON);
2001-05-23 15:26:42 +02:00
return action;
}
//____________________________________________________________
//
// Parse something of the form:
//
// <relation> . <field> . <something>
//
// where <something> is currently an enumerated type.
//
static act* par_type()
2001-05-23 15:26:42 +02:00
{
// Pick up relation
2001-05-23 15:26:42 +02:00
// ***
//gpre_sym* symbol;
//symbol = gpreGlob.token_global.tok_symbol;
//relation = (gpre_rel*) symbol->sym_object;
2003-10-15 00:22:32 +02:00
//PAR_get_token();
2001-05-23 15:26:42 +02:00
//**
gpre_rel* relation = EXP_relation();
2001-05-23 15:26:42 +02:00
// No dot and we give up
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_DOT))
2001-05-23 15:26:42 +02:00
return NULL;
2005-01-07 05:00:19 +01:00
// Look for field name. No field name, punt
2001-05-23 15:26:42 +02:00
SQL_resolve_identifier("<Field Name>", NULL, NAME_SIZE);
2004-05-29 06:54:20 +02:00
gpre_fld* field = MET_field(relation, gpreGlob.token_global.tok_string);
if (!field)
2001-05-23 15:26:42 +02:00
return NULL;
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
if (!MSC_match(KW_DOT))
CPR_s_error("period");
2001-05-23 15:26:42 +02:00
// Lookup type. If we can't find it, complain bitterly
SSHORT type;
if (!MET_type(field, gpreGlob.token_global.tok_string, &type)) {
2005-01-07 05:00:19 +01:00
TEXT s[ERROR_LENGTH];
fb_utils::snprintf(s, sizeof(s),
"undefined type %s", gpreGlob.token_global.tok_string);
2001-05-23 15:26:42 +02:00
PAR_error(s);
}
2003-10-15 00:22:32 +02:00
PAR_get_token();
act* action = MSC_action(0, ACT_type_number);
action->act_object = (REF) (IPTR) type;
2001-05-23 15:26:42 +02:00
return action;
}
//____________________________________________________________
//
// Parse a free reference to a database field in general
// program context.
//
static act* par_variable()
2001-05-23 15:26:42 +02:00
{
//
2001-05-23 15:26:42 +02:00
// Since fortran is fussy about continuations and the like,
// see if this variable token is the first thing in a statement.
//
const USHORT first = gpreGlob.token_global.tok_first;
gpre_ctx* context;
gpre_fld* field = EXP_field(&context);
2001-05-23 15:26:42 +02:00
gpre_fld* cast;
2004-05-29 06:54:20 +02:00
bool dot = MSC_match(KW_DOT);
if (dot && (cast = EXP_cast(field))) {
2001-05-23 15:26:42 +02:00
field = cast;
2003-10-15 00:22:32 +02:00
dot = MSC_match(KW_DOT);
2001-05-23 15:26:42 +02:00
}
bool is_null = false;
2003-10-15 00:22:32 +02:00
if (dot && MSC_match(KW_NULL)) {
is_null = true;
2003-09-11 04:13:46 +02:00
dot = false;
2001-05-23 15:26:42 +02:00
}
gpre_req* request = context->ctx_request;
ref* reference = EXP_post_field(field, context, is_null);
2001-05-23 15:26:42 +02:00
if (field->fld_array)
EXP_post_array(reference);
act* action = MSC_action(request, ACT_variable);
2001-05-23 15:26:42 +02:00
if (first)
action->act_flags |= ACT_first;
if (dot)
action->act_flags |= ACT_back_token;
action->act_object = reference;
if (!is_null)
return action;
// We've got a explicit null flag referernce rather than a field
// reference. If there's already a null reference for the field,
// use it; otherwise make one up.
if (reference->ref_null) {
action->act_object = reference->ref_null;
return action;
}
// Check to see if the flag field has been allocated. If not, sigh, allocate it
ref* flag = MSC_reference(&request->req_references);
2001-05-23 15:26:42 +02:00
flag->ref_context = reference->ref_context;
flag->ref_field = PAR_null_field();
flag->ref_level = request->req_level;
flag->ref_master = reference;
reference->ref_null = flag;
action->act_object = flag;
return action;
}
//____________________________________________________________
//
// This is PASCAL, and we've got a function, or procedure header.
// Alas and alack, we have to decide if this is a real header or
// a forward/external declaration.
//
// Basically we scan the thing, skipping parenthesized bits,
// looking for a semi-colon. We look at the next token, which may
// be OPTIONS followed by a parenthesized list of options, or it
// may be just some options, or it may be nothing. If the options
// are EXTERN or FORWARD, we've got a reference, otherwise its a real
// routine (or possibly program or module).
//
// Fortunately all of these are of the form:
// <keyword> <name> [( blah, blah )] [: type] ; [<options>;]
//
//
static act* scan_routine_header()
2001-05-23 15:26:42 +02:00
{
act* action = MSC_action(0, ACT_routine);
2001-05-23 15:26:42 +02:00
action->act_flags |= ACT_mark;
2003-10-15 00:22:32 +02:00
while (!(MSC_match(KW_SEMI_COLON)))
2001-05-23 15:26:42 +02:00
if (!(match_parentheses()))
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_OPTIONS) && MSC_match(KW_LEFT_PAREN)) {
while (!(MSC_match(KW_RIGHT_PAREN))) {
if (MSC_match(KW_EXTERN) || MSC_match(KW_FORWARD))
2001-05-23 15:26:42 +02:00
action->act_flags |= ACT_decl;
else
2003-10-15 00:22:32 +02:00
PAR_get_token();
2001-05-23 15:26:42 +02:00
}
2003-10-15 00:22:32 +02:00
MSC_match(KW_SEMI_COLON);
2001-05-23 15:26:42 +02:00
}
else
for (;;) {
2003-10-15 00:22:32 +02:00
if (MSC_match(KW_EXTERN) || MSC_match(KW_FORWARD)) {
2001-05-23 15:26:42 +02:00
action->act_flags |= ACT_decl;
2003-10-15 00:22:32 +02:00
MSC_match(KW_SEMI_COLON);
2001-05-23 15:26:42 +02:00
}
2003-10-15 00:22:32 +02:00
else if (MSC_match(KW_INTERNAL) || MSC_match(KW_ABNORMAL) ||
MSC_match(KW_VARIABLE) || MSC_match(KW_VAL_PARAM))
2003-09-11 04:13:46 +02:00
{
2003-10-15 00:22:32 +02:00
MSC_match(KW_SEMI_COLON);
2003-09-11 04:13:46 +02:00
}
2001-05-23 15:26:42 +02:00
else
break;
}
return action;
}
//____________________________________________________________
//
// If this is a external declaration in
// a BASIC program, set a flag to indicate
// the situation.
//
static void set_external_flag()
{
CPR_token();
}
//____________________________________________________________
//
// Check the current token for a logical terminator. Terminators
// are semi-colon, ELSE, or ON_ERROR.
//
static bool terminator()
2001-05-23 15:26:42 +02:00
{
2003-10-15 00:22:32 +02:00
// For C, changed keyword (KW_SEMICOLON) to MSC_match (KW_SEMICOLON) to eat a
2001-05-23 15:26:42 +02:00
// semicolon if it is present so as to allow it to be there or not be there.
// Bug#833. mao 6/21/89
// For C, right brace ("}") must also be a terminator.
if (gpreGlob.sw_language == lang_c) {
if (MSC_match(KW_SEMI_COLON) || gpreGlob.token_global.tok_keyword == KW_ELSE ||
gpreGlob.token_global.tok_keyword == KW_ON_ERROR || gpreGlob.token_global.tok_keyword == KW_R_BRACE)
{
return true;
}
2001-05-23 15:26:42 +02:00
}
else if (gpreGlob.sw_language == lang_ada) {
if (MSC_match(KW_SEMI_COLON) || gpreGlob.token_global.tok_keyword == KW_ELSE ||
gpreGlob.token_global.tok_keyword == KW_ON_ERROR)
2003-10-15 00:22:32 +02:00
{
return true;
2003-10-15 00:22:32 +02:00
}
2001-05-23 15:26:42 +02:00
}
else {
if (gpreGlob.token_global.tok_keyword == KW_SEMI_COLON || gpreGlob.token_global.tok_keyword == KW_ELSE ||
gpreGlob.token_global.tok_keyword == KW_ON_ERROR ||
(gpreGlob.sw_language == lang_cobol && gpreGlob.token_global.tok_keyword == KW_DOT))
{
return true;
}
2001-05-23 15:26:42 +02:00
}
return false;
2001-05-23 15:26:42 +02:00
}