gambas-source-code/main/gbc/gbc_header.c
Benoît Minisini d3501bf140 [DEVELOPMENT ENVIRONMENT]
* NEW: Work continues on integrating the database manager.
* NEW: Some cosmetic changes in the way controls are drawing on the form
  editor.
* NEW: Panels with Border property set to None are now drawn with a light 
  border.
* BUG: Fix the "Show tab" button and menu.

[INTERPRETER]
* NEW: _attach is a new dynamic special method that is called when an 
  object is attached to or detached from its event observer. The first 
  argument of this method is the event observer, and the second argument 
  the event handler prefix.

[COMPILER]
* NEW: An expression can be a NEW instruction now. Beware that it does not 
  work inside braces.

[GB.DB]
* BUG: Fix an error message in the sqlite handler.

[GB.DB.FORM]
* NEW: DataSource.Table can now be any SQL query. The Filter property is 
  ignored in that case.
* BUG: Setting DataSource.Table to NULL correctly resets the DataSource and
  its children.
* NEW: DataView automatically adjusts the height of its rows to the 
  contents.
* NEW: DataSource.CacheSize is a new property to set the number of rows 
  stored in the internal DataSource cache. When this property is set to 
  zero, the cache size takes its default value (64 rows).

[GB.DB.SQLITE2]
* BUG: Fix a crash in datatype mapping.

[GB.DB.SQLITE3]
* BUG: Fix a crash in datatype mapping.

[GB.QT4]
* BUG: Window.AutoResize property works as expected now.
* OPT: Some optimizations in GridView.
* NEW: GridView.Rows[].Visible returns if a specific row is visible.
* NEW: GridView.Rows[].EnsureVisible ensures that a specific row is 
  visible.
* BUG: Draw.Style.Panel draws the same thing as a panel border now.
* BUG: Window.Closed always returns the accurate value now.


git-svn-id: svn://localhost/gambas/trunk@2108 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2009-07-12 21:49:13 +00:00

919 lines
19 KiB
C
Raw Blame History

/***************************************************************************
header.c
Analyzing class description
(c) 2000-2007 Benoit Minisini <gambas@users.sourceforge.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***************************************************************************/
#define _TRANS_HEADER_C
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "gb_common.h"
#include "gb_error.h"
#include "gb_str.h"
#include "gb_file.h"
#include "gb_table.h"
#include "gbc_compile.h"
#include "gbc_trans.h"
#include "gbc_header.h"
/*#define DEBUG*/
static void analyze_function_desc(TRANS_FUNC *func, int flag)
{
PATTERN *look = JOB->current;
TRANS_PARAM *param;
bool is_output;
bool is_optional = FALSE;
TRANS_DECL ttyp;
uint64_t byref_mask = 1;
if (!PATTERN_is_identifier(*look))
THROW("Syntax error. Invalid identifier in function name");
func->index = PATTERN_index(*look);
look++;
if (flag & HF_EVENT)
TABLE_copy_symbol_with_prefix(JOB->class->table, func->index, ':', NULL, &func->index);
func->nparam = 0;
func->byref = 0;
func->vararg = FALSE;
if ((flag & HF_VOID) && PATTERN_is_newline(*look))
{
JOB->current = ++look;
return;
}
if (!PATTERN_is(*look, RS_LBRA))
THROW(E_UNEXPECTED, READ_get_pattern(look));
look++;
for(;;)
{
param = &func->param[func->nparam];
if (PATTERN_is(*look, RS_RBRA))
{
look++;
break;
}
if (func->nparam > 0)
{
if (!PATTERN_is(*look, RS_COMMA))
THROW("Syntax error. Missing ',' or ')'");
look++;
}
if (PATTERN_is(*look, RS_3PTS))
{
look++;
if (!PATTERN_is(*look, RS_RBRA))
THROW("Syntax error. '...' must be the last argument"); //, get_num_desc(func->nparam + 1));
look++;
func->vararg = TRUE;
break;
}
is_output = FALSE;
if (!(flag & HF_NO_OPT))
{
if (PATTERN_is(*look, RS_OPTIONAL))
{
look++;
is_optional = TRUE;
}
}
if (PATTERN_is(*look, RS_AT) || PATTERN_is(*look, RS_BYREF))
{
func->byref |= byref_mask;
look++;
}
if (!PATTERN_is_identifier(*look))
THROW("Syntax error. The &1 argument is not a valid identifier", TRANS_get_num_desc(func->nparam + 1));
param->index = PATTERN_index(*look);
look++;
JOB->current = look;
if (!TRANS_type(TT_NOTHING, &ttyp))
THROW("Syntax error. Invalid type description of &1 argument", TRANS_get_num_desc(func->nparam + 1));
param->type = ttyp.type;
/*
if (is_output)
TYPE_set_flag(&param->type, TF_OUTPUT);
*/
look = JOB->current;
if (is_optional)
{
param->optional = look;
for(;;)
{
if (PATTERN_is(*look, RS_COMMA) || PATTERN_is(*look, RS_RBRA) || PATTERN_is_newline(*look))
break;
look++;
}
JOB->current = look;
}
func->nparam++;
byref_mask <<= 1;
}
JOB->current = look;
}
static void header_module_type(void)
{
const char *ext;
/*JOB->class->name = STR_copy(FILE_get_name(JOB->name));*/
ext = FILE_get_ext(JOB->name);
if (strcasecmp(ext, "module") == 0)
{
JOB->is_module = TRUE;
JOB->is_form = FALSE;
}
else if (strcasecmp(ext, "class") == 0)
{
JOB->is_module = FALSE;
JOB->is_form = FALSE;
}
else if (strcasecmp(ext, "form") == 0)
{
JOB->is_module = FALSE;
JOB->is_form = TRUE;
}
else
THROW("Unknown file extension");
JOB->declared = TRUE;
}
static bool header_event(TRANS_EVENT *event)
{
PATTERN *look = JOB->current;
TRANS_DECL ttyp;
if (!PATTERN_is(*look, RS_EVENT))
return FALSE;
CLEAR(event);
if (JOB->is_module)
THROW("A module cannot raise events");
JOB->current++;
analyze_function_desc((TRANS_FUNC *)event, HF_VOID);
if (PATTERN_is(*JOB->current, RS_AS))
{
if (!TRANS_type(TT_CAN_SQUARE, &ttyp))
THROW("Syntax error in return type");
event->type = ttyp.type;
}
TYPE_set_kind(&event->type, TK_EVENT);
TYPE_set_flag(&event->type, TF_PUBLIC);
return TRUE;
}
static bool header_property(TRANS_PROPERTY *prop)
{
TRANS_DECL ptype;
PATTERN *look = JOB->current;
bool is_static = FALSE;
bool is_public = TRUE;
CLEAR(prop);
/* static */
if (JOB->is_module)
{
is_static = TRUE;
}
else if (PATTERN_is(*look, RS_STATIC))
{
is_static = TRUE;
look++;
}
/* public */
if (PATTERN_is(*look, RS_PUBLIC) || PATTERN_is(*look, RS_PRIVATE))
{
is_public = PATTERN_is(*look, RS_PUBLIC);
look++;
}
if (!PATTERN_is(*look, RS_PROPERTY))
return FALSE;
look++;
JOB->current = look;
if (!is_public)
THROW("A property must be public");
/* read-only property */
if (PATTERN_is(*JOB->current, RS_READ))
{
prop->read = TRUE;
JOB->current++;
}
else
prop->read = FALSE;
/* property name */
if (!PATTERN_is_identifier(*JOB->current))
THROW("Syntax error. Invalid identifier in property name");
prop->index = PATTERN_index(*JOB->current);
JOB->current++;
if (!TRANS_type(TT_CAN_SQUARE, &ptype))
THROW("Syntax error. Bad property type");
prop->type = ptype.type;
prop->line = JOB->line;
TYPE_set_kind(&prop->type, TK_PROPERTY);
if (is_static)
TYPE_set_flag(&prop->type, TF_STATIC);
TYPE_set_flag(&prop->type, TF_PUBLIC);
if (PATTERN_is_string(*JOB->current))
{
prop->comment = PATTERN_index(*JOB->current);
JOB->current++;
}
else
prop->comment = NO_SYMBOL;
return TRUE;
}
static bool header_extern(TRANS_EXTERN *trans)
{
PATTERN *look = JOB->current;
TRANS_DECL ttyp;
int index;
bool is_public;
/* public ? */
is_public = JOB->is_module && JOB->public_module;
if (PATTERN_is(*look, RS_PUBLIC))
{
is_public = TRUE;
look++;
}
else if (PATTERN_is(*look, RS_PRIVATE))
{
is_public = FALSE;
look++;
}
if (!PATTERN_is(*look, RS_EXTERN))
return FALSE;
look++;
CLEAR(trans);
JOB->current = look;
analyze_function_desc((TRANS_FUNC *)trans, HF_NO_3PTS);
if (PATTERN_is(*JOB->current, RS_AS))
{
if (!TRANS_type(TT_CAN_SQUARE, &ttyp))
THROW("Syntax error in return type");
trans->type = ttyp.type;
}
if (TRANS_is(RS_IN))
{
if (!PATTERN_is_string(*JOB->current))
THROW("Library name must be a string");
index = PATTERN_index(*JOB->current);
JOB->current++;
}
else
{
if (JOB->default_library == NO_SYMBOL)
THROW("IN missing");
index = JOB->default_library;
}
trans->library = index;
if (TRANS_is(RS_EXEC))
{
if (!PATTERN_is_string(*JOB->current))
THROW("Alias name must be a string");
trans->alias = PATTERN_index(*JOB->current);
JOB->current++;
}
else
trans->alias = NO_SYMBOL;
TYPE_set_kind(&trans->type, TK_EXTERN);
TYPE_set_flag(&trans->type, TF_STATIC);
if (is_public) TYPE_set_flag(&trans->type, TF_PUBLIC);
return TRUE;
}
static bool header_class(TRANS_DECL *decl)
{
if (!PATTERN_is(*JOB->current, RS_CLASS))
return FALSE;
JOB->current++;
if (!PATTERN_is_identifier(*JOB->current))
THROW("Syntax error. CLASS needs an identifier");
CLEAR(decl);
decl->index = PATTERN_index(*JOB->current);
JOB->current++;
return TRUE;
}
static bool header_declaration(TRANS_DECL *decl)
{
PATTERN *look = JOB->current;
PATTERN *save;
bool is_static = FALSE;
bool is_public = FALSE;
bool is_const = FALSE;
/*bool other = FALSE;*/
/* static ! */
if (JOB->is_module)
{
is_static = TRUE;
}
else if (PATTERN_is(*look, RS_STATIC))
{
is_static = TRUE;
look++;
}
/* public or private ? */
is_public = JOB->is_module && JOB->public_module;
if (PATTERN_is(*look, RS_PUBLIC))
{
is_public = TRUE;
look++;
}
else if (PATTERN_is(*look, RS_PRIVATE))
{
is_public = FALSE;
look++;
}
/* const ? */
is_const = FALSE;
if (PATTERN_is(*look, RS_CONST))
{
/* static const <=> const */
/*
if (is_static)
return FALSE;
*/
is_const = TRUE;
look++;
}
if (!PATTERN_is_identifier(*look))
return FALSE;
CLEAR(decl);
decl->index = PATTERN_index(*look);
look++;
save = JOB->current;
JOB->current = look;
if (!TRANS_type(((!is_const && !is_public) ? (TT_CAN_SQUARE | TT_CAN_ARRAY) : 0) | TT_CAN_NEW, decl))
{
JOB->current = save;
return FALSE;
}
if (is_static) TYPE_set_flag(&decl->type, TF_STATIC);
if (is_public) TYPE_set_flag(&decl->type, TF_PUBLIC);
if (is_const)
TYPE_set_kind(&decl->type, TK_CONST);
else
TYPE_set_kind(&decl->type, TK_VARIABLE);
if (is_const)
{
if (!decl->init)
THROW("Syntax error. Missing '='");
JOB->current = decl->init;
JOB->current = TRANS_get_constant_value(decl, JOB->current);
}
//JOB->current = look;
return TRUE;
}
static bool header_function(TRANS_FUNC *func)
{
static HEADER_SPECIAL spec[] =
{
{ "_init", HS_PUBLIC + HS_STATIC + HS_PROCEDURE + HS_NOPARAM },
{ "_exit", HS_PUBLIC + HS_STATIC + HS_PROCEDURE + HS_NOPARAM},
{ "_new", HS_PUBLIC + HS_DYNAMIC + HS_PROCEDURE },
{ "_free", HS_PUBLIC + HS_DYNAMIC + HS_PROCEDURE + HS_NOPARAM },
{ "_call", HS_PUBLIC },
{ "_get", HS_PUBLIC + HS_FUNCTION },
{ "_put", HS_PUBLIC + HS_PROCEDURE + HS_PUT },
{ "_next", HS_PUBLIC + HS_NOPARAM },
{ "_unknown", HS_PUBLIC + HS_UNKNOWN },
{ "_compare", HS_PUBLIC + HS_DYNAMIC + HS_FUNCTION + HS_COMPARE },
{ "_attach", HS_PUBLIC + HS_DYNAMIC + HS_PROCEDURE + HS_ATTACH },
{ NULL, 0 }
};
PATTERN *look = JOB->current;
PATTERN pat;
TRANS_DECL ttyp;
SYMBOL *sym;
HEADER_SPECIAL *hsp;
bool is_proc = FALSE;
bool is_static = FALSE;
bool is_public = FALSE;
/* static ? */
if (JOB->is_module)
{
is_static = TRUE;
}
else if (PATTERN_is(*look, RS_STATIC))
{
is_static = TRUE;
look++;
}
/* public ou static ? */
is_public = JOB->is_module && JOB->public_module;
if (PATTERN_is(*look, RS_PUBLIC))
{
is_public = TRUE;
look++;
}
else if (PATTERN_is(*look, RS_PRIVATE))
{
is_public = FALSE;
look++;
}
if (PATTERN_is(*look, RS_PROCEDURE) || PATTERN_is(*look, RS_SUB))
is_proc = TRUE;
else if (!PATTERN_is(*look, RS_FUNCTION))
return FALSE;
look++;
CLEAR(func);
JOB->current = look;
analyze_function_desc(func, HF_NORMAL);
//if (!is_proc)
if (PATTERN_is(*JOB->current, RS_AS))
{
if (!TRANS_type(TT_CAN_SQUARE, &ttyp))
THROW("Syntax error. Invalid return type");
func->type = ttyp.type;
is_proc = FALSE;
}
TYPE_set_kind(&func->type, TK_FUNCTION);
if (is_static) TYPE_set_flag(&func->type, TF_STATIC);
if (is_public) TYPE_set_flag(&func->type, TF_PUBLIC);
// Check special methods
sym = TABLE_get_symbol(JOB->class->table, func->index);
if (*sym->name == '_')
{
for (hsp = spec; hsp->name; hsp++)
{
if (sym->len != strlen(hsp->name))
continue;
if (strncmp(sym->name, hsp->name, sym->len))
continue;
if (hsp->flag == HS_ERROR)
THROW("The special method &1 cannot be implemented", hsp->name);
if ((hsp->flag & HS_PUBLIC) && !is_public)
THROW("The special method &1 must be public", hsp->name);
if ((hsp->flag & HS_STATIC) && !is_static)
THROW("The special method &1 must be static", hsp->name);
if ((hsp->flag & HS_DYNAMIC) && is_static)
THROW("The special method &1 cannot be static", hsp->name);
if ((hsp->flag & HS_PROCEDURE) && !is_proc)
THROW("The special method &1 cannot be a function", hsp->name);
if ((hsp->flag & HS_FUNCTION) && is_proc)
THROW("The special method &1 must be a function", hsp->name);
if ((hsp->flag & HS_NOPARAM) && func->nparam > 0)
THROW("The special method &1 takes no arguments", hsp->name);
if (hsp->flag & HS_PUT)
{
if (func->nparam < 1)
THROW("The special method &1 must take at least one argument", hsp->name);
}
if (hsp->flag & HS_UNKNOWN)
{
if (func->nparam > 0 || !func->vararg)
THROW("The special method &1 must take a variable number of arguments only", hsp->name);
}
if (hsp->flag & HS_COMPARE)
{
if (func->type.t.id != T_INTEGER)
THROW("The special method must return an integer");
if (func->nparam != 1)
THROW("The special method must take exactly one argument");
}
if (hsp->flag & HS_ATTACH)
{
if (func->nparam != 2)
THROW("The special method must take exactly two arguments");
if (func->param[0].type.t.id != T_OBJECT || func->param[1].type.t.id != T_STRING)
THROW("The special method signature is incorrect");
}
break;
}
}
/* on saute le corps de la fonction */
if (!PATTERN_is_newline(*(JOB->current)))
THROW("Syntax error at function declaration");
func->line = PATTERN_index(*(JOB->current)) + 1;
func->start = JOB->current + 1;
for(;;)
{
pat = *JOB->current;
if (PATTERN_is_end(pat) || PATTERN_is_command(pat))
{
THROW("Missing End");
}
if (PATTERN_is_newline(pat))
{
JOB->line = PATTERN_index(pat) + 1;
if (PATTERN_is(JOB->current[1], RS_END))
{
if (PATTERN_is_newline(JOB->current[2]))
{
JOB->current += 2;
break;
}
if (TRANS_is_end_function(is_proc, &JOB->current[2]))
{
JOB->current += 3;
break;
}
else
{
if (is_proc && PATTERN_is(JOB->current[2], RS_FUNCTION))
THROW("End Sub expected");
else if (!is_proc && PATTERN_is(JOB->current[2], RS_SUB))
THROW("End Function expected");
}
}
}
JOB->current++;
}
return TRUE;
}
#if 0
static bool header_structure(TRANS_STRUCTURE *structure)
{
TRANS_PARAM *field;
TRANS_DECL ttyp;
if (!PATTERN_is(*JOB->current, RS_STRUCT))
return FALSE;
JOB->current++;
if (!PATTERN_is_identifier(*JOB->current))
THROW("Syntax error. STRUCT needs an identifier");
structure->index = PATTERN_index(*JOB->current);
JOB->current++;
if (!TRANS_newline())
THROW("Syntax error at structure declaration");
structure->nfield = 0;
for(;;)
{
do
{
if (PATTERN_is_end(*JOB->current) || PATTERN_is_command(*JOB->current))
THROW("Missing END STRUCT");
}
while (TRANS_newline());
if (PATTERN_is(*JOB->current, RS_END) && PATTERN_is(JOB->current[1], RS_STRUCT))
{
if (structure->nfield == 0)
THROW("Syntax error. A structure must have one field at least.");
JOB->current += 2;
if (!TRANS_newline())
THROW("Syntax error. End of line expected.");
break;
}
if (structure->nfield >= MAX_STRUCT_FIELD)
THROW("Too many fields in structure declaration.");
field = &structure->field[structure->nfield];
if (!PATTERN_is_identifier(*JOB->current))
THROW("Syntax error. The &1 field is not a valid identifier", TRANS_get_num_desc(structure->nfield + 1));
field->index = PATTERN_index(*JOB->current);
JOB->current++;
if (!TRANS_type(TT_NOTHING, &ttyp))
THROW("Syntax error. Invalid type description of &1 field", TRANS_get_num_desc(structure->nfield + 1));
field->type = ttyp.type;
if (!TRANS_newline())
THROW("Syntax error. End of line expected.");
structure->nfield++;
}
return TRUE;
}
#endif
static bool header_inherits(void)
{
int index;
if (!PATTERN_is(*JOB->current, RS_INHERITS))
return FALSE;
/*{
if (!(PATTERN_is(JOB->current[0], RS_CLASS)
&& PATTERN_is(JOB->current[1], RS_INHERITS)))
return FALSE;
JOB->current++;
}*/
JOB->current++;
if (!PATTERN_is_class(*JOB->current))
THROW("Syntax error. Inherits needs a class name");
if (JOB->class->parent != NO_SYMBOL)
THROW("Cannot inherit twice");
index = PATTERN_index(*JOB->current);
JOB->class->parent = CLASS_add_class(JOB->class, index);
/*printf("JOB->class->parent = %d\n", JOB->class->parent);*/
JOB->current++;
return TRUE;
}
static bool header_option(void)
{
if (PATTERN_is(JOB->current[0], RS_EXPORT))
{
JOB->current++;
JOB->class->exported = TRUE;
if (PATTERN_is(JOB->current[0], RS_OPTIONAL))
{
JOB->current++;
JOB->class->optional = TRUE;
}
return TRUE;
}
if (PATTERN_is(JOB->current[0], RS_CREATE))
{
JOB->current++;
if (PATTERN_is_newline(JOB->current[0]) || PATTERN_is(JOB->current[0], RS_STATIC))
{
JOB->class->autocreate = TRUE;
if (PATTERN_is(JOB->current[0], RS_STATIC))
JOB->current++;
return TRUE;
}
else if (PATTERN_is(JOB->current[0], RS_PRIVATE))
{
JOB->class->nocreate = TRUE;
JOB->current++;
return TRUE;
}
}
return FALSE;
}
static bool header_library(void)
{
if (!TRANS_is(RS_LIBRARY))
return FALSE;
if (!PATTERN_is_string(*JOB->current))
THROW("Library name must be a string");
JOB->default_library = PATTERN_index(*JOB->current);
JOB->current++;
return TRUE;
}
void HEADER_do(void)
{
union {
TRANS_DECL decl;
TRANS_FUNC func;
TRANS_EVENT event;
TRANS_EXTERN ext;
TRANS_PROPERTY prop;
TRANS_STRUCTURE structure;
} trans;
TRANS_reset();
header_module_type();
while (JOB->current < JOB->end)
{
if (PATTERN_is_end(*JOB->current))
break;
if (TRANS_newline())
continue;
if (header_function(&trans.func))
{
CLASS_add_function(JOB->class, &trans.func);
continue;
}
if (header_event(&trans.event))
{
CLASS_add_event(JOB->class, &trans.event);
continue;
}
if (header_property(&trans.prop))
{
CLASS_add_property(JOB->class, &trans.prop);
continue;
}
if (header_extern(&trans.ext))
{
CLASS_add_extern(JOB->class, &trans.ext);
continue;
}
if (header_declaration(&trans.decl))
{
CLASS_add_declaration(JOB->class, &trans.decl);
continue;
}
/*if (header_structure(&trans.structure))
{
//CLASS_add_structure(JOB->class, &trans.structure);
continue;
}*/
if (header_class(&trans.decl))
{
CLASS_add_class_exported(JOB->class, trans.decl.index);
continue;
}
if (header_inherits())
continue;
if (header_option())
continue;
if (header_library())
continue;
if (PATTERN_is_command(*JOB->current))
{
JOB->current++;
continue;
}
THROW(E_UNEXPECTED, READ_get_pattern(JOB->current));
}
/* R<>rganisation des variables statiques et dynamiques
pour <20>iter les probl<62>es d'alignement
*/
CLASS_sort_declaration(JOB->class);
if (JOB->verbose)
CLASS_dump();
}