gambas-source-code/main/gbc/gbc_trans_expr.c

757 lines
14 KiB
C
Raw Normal View History

/***************************************************************************
gbc_trans_expr.c
(c) 2000-2012 Benoît 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 2, 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., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
***************************************************************************/
#define _TRANS_EXPR_C
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "gb_common.h"
#include "gb_error.h"
#include "gbc_compile.h"
#include "gbc_trans.h"
#include "gb_code.h"
/*#define DEBUG*/
//static bool _accept_statement = FALSE;
static short get_nparam(PATTERN *tree, int count, int *pindex, uint64_t *byref)
{
PATTERN pattern;
int nparam = 0;
int index = *pindex;
if (index < count)
{
pattern = tree[index + 1];
if (PATTERN_is_param(pattern))
{
index++;
nparam = PATTERN_index(pattern);
}
if (byref)
{
int shift = 0;
*byref = 0;
while (index < count)
{
pattern = tree[index + 1];
if (!PATTERN_is_param(pattern))
break;
index++;
*byref |= (uint64_t)PATTERN_index(pattern) << shift;
shift += 16;
}
}
else
{
while (index < count)
{
pattern = tree[index + 1];
if (!PATTERN_is_param(pattern))
break;
index++;
}
}
*pindex = index;
}
/*
Gère le cas on a codé un subr sans mettre de parenthèses
=> nparam = 0
*/
return (short)nparam;
}
static void push_number(int index)
{
TRANS_NUMBER number;
TRANS_DECL decl;
if (TRANS_get_number(index, &number))
THROW(E_SYNTAX);
if (number.type == T_INTEGER)
{
CODE_push_number(number.ival);
return;
}
CLEAR(&decl);
decl.type = TYPE_make(number.type, 0, 0);
decl.index = NO_SYMBOL;
decl.value = index;
if (number.type == T_LONG)
decl.lvalue = number.lval;
CODE_push_const(CLASS_add_constant(JOB->class, &decl));
}
static void push_string(int index, bool trans)
{
TRANS_DECL decl;
SYMBOL *sym;
int len;
if (index == VOID_STRING)
len = 0;
else
{
sym = TABLE_get_symbol(JOB->class->string, index);
len = sym->len;
}
if (len == 0)
{
CODE_push_void_string();
}
else if (len == 1 && !trans)
{
CODE_push_char(*(sym->name));
}
else
{
//CLEAR(&decl);
if (trans)
decl.type = TYPE_make(T_CSTRING, 0, 0);
else
decl.type = TYPE_make(T_STRING, 0, 0);
decl.index = NO_SYMBOL;
decl.value = index;
CODE_push_const(CLASS_add_constant(JOB->class, &decl));
}
}
void TRANS_class(int index)
{
const char *name;
if (CLASS_exist_class(JOB->class, index))
CODE_push_class(CLASS_add_class(JOB->class, index));
else
{
name = TABLE_get_symbol_name(JOB->class->table, index);
THROW("Unknown identifier: &1", name);
}
}
static void trans_identifier(int index, bool first, bool point, PATTERN next)
{
CLASS_SYMBOL *sym = CLASS_get_symbol(JOB->class, index);
bool is_static;
bool is_func;
int type;
CONSTANT *constant;
if (!TYPE_is_null(sym->local.type) && !point)
{
CODE_push_local(sym->local.value);
}
else if (!TYPE_is_null(sym->global.type) && !point)
{
type = TYPE_get_kind(sym->global.type);
if (type == TK_CONST)
{
if (PATTERN_is_point(next))
goto __CLASS;
constant = &JOB->class->constant[sym->global.value];
type = TYPE_get_id(constant->type);
if (type == T_BOOLEAN)
CODE_push_boolean(constant->value);
else if (type == T_INTEGER)
CODE_push_number(constant->value);
else
CODE_push_const(sym->global.value);
}
else if (type == TK_EXTERN)
{
if (PATTERN_is_point(next))
goto __CLASS;
CODE_push_extern(sym->global.value);
}
/* That breaks some code if the property has the same name as a class!
else if (type == TK_PROPERTY)
{
CODE_push_me(FALSE);
CODE_push_unknown(CLASS_add_unknown(JOB->class, index));
}*/
else if (type == TK_EVENT || type == TK_LABEL || type == TK_PROPERTY)
{
goto __CLASS;
}
else /* TK_FUNCTION || TK_VARIABLE */
{
is_static = TYPE_is_static(sym->global.type);
is_func = type == TK_FUNCTION;
if (!is_static && TYPE_is_static(JOB->func->type))
THROW("Dynamic symbols cannot be used in static function");
if (is_func && PATTERN_is_point(next))
goto __CLASS;
else
CODE_push_global(sym->global.value, is_static, is_func);
}
}
else
{
/*index = CLASS_add_symbol(JOB->class, index);*/
if (point)
CODE_push_unknown(CLASS_add_unknown(JOB->class, index));
/* Class must be declared now ! */
else
goto __CLASS;
}
return;
__CLASS:
TRANS_class(index);
}
static void trans_subr(int subr, short nparam)
{
SUBR_INFO *info = &COMP_subr_info[subr];
if (subr == SUBR_VarPtr)
{
if (CODE_check_varptr())
THROW("VarPtr() argument must be a dynamic, a static or a local variable");
}
if (nparam < info->min_param)
THROW("Not enough arguments to &1()", info->name);
else if (nparam > info->max_param)
THROW("Too many arguments to &1()", info->name);
CODE_subr(info->opcode, nparam, info->optype, FALSE /* output */, (info->max_param == info->min_param));
}
static void trans_operation(short op, short nparam, PATTERN previous)
{
COMP_INFO *info = &COMP_res_info[op];
switch (info->value)
{
case OP_PT:
if (nparam == 0)
TRANS_use_with();
else if (!PATTERN_is_identifier(previous))
THROW(E_SYNTAX);
break;
case OP_EXCL:
if (!PATTERN_is_identifier(previous))
THROW(E_SYNTAX);
break;
case OP_LSQR:
CODE_push_array(nparam);
break;
case OP_RSQR:
TRANS_subr(TS_SUBR_ARRAY, nparam);
break;
case OP_COLON:
TRANS_subr(TS_SUBR_COLLECTION, nparam);
break;
case OP_MINUS:
if (nparam == 1)
CODE_op(C_NEG, 0, nparam, TRUE);
else
CODE_op(info->code, info->subcode, nparam, TRUE);
break;
default:
CODE_op(info->code, info->subcode, nparam, (info->flag != RSF_OPN));
}
}
static void trans_call(short nparam, uint64_t byref)
{
if (!byref)
{
CODE_call(nparam);
return;
}
CODE_call_byref(nparam, byref);
}
static void trans_expr_from_tree(TRANS_TREE *tree, int count)
{
int i, op;
short nparam;
PATTERN pattern, next_pattern, prev_pattern;
uint64_t byref;
count--;
pattern = NULL_PATTERN;
for (i = 0; i <= count; i++)
{
prev_pattern = pattern;
pattern = tree[i];
if (i < count)
next_pattern = tree[i + 1];
else
next_pattern = NULL_PATTERN;
if (PATTERN_is_number(pattern))
push_number(PATTERN_index(pattern));
else if (PATTERN_is_string(pattern))
push_string(PATTERN_index(pattern), FALSE);
else if (PATTERN_is_tstring(pattern))
push_string(PATTERN_index(pattern), TRUE);
else if (PATTERN_is_identifier(pattern))
trans_identifier(PATTERN_index(pattern), PATTERN_is_first(pattern), PATTERN_is_point(pattern), next_pattern);
else if (PATTERN_is_class(pattern))
TRANS_class(PATTERN_index(pattern));
else if (PATTERN_is_subr(pattern))
{
nparam = get_nparam(tree, count, &i, NULL);
trans_subr(PATTERN_index(pattern), nparam);
}
else if (PATTERN_is_reserved(pattern))
{
if (PATTERN_is(pattern, RS_TRUE))
{
CODE_push_boolean(TRUE);
}
else if (PATTERN_is(pattern, RS_FALSE))
{
CODE_push_boolean(FALSE);
}
else if (PATTERN_is(pattern, RS_NULL))
{
CODE_push_null();
}
else if (PATTERN_is(pattern, RS_ME))
{
/*if (FUNCTION_is_static(JOB->func))
THROW("ME cannot be used in a static function");*/
CODE_push_me(FALSE);
}
else if (PATTERN_is(pattern, RS_SUPER))
{
/*if (FUNCTION_is_static(JOB->func))
THROW("ME cannot be used in a static function");*/
CODE_push_super(FALSE);
}
else if (PATTERN_is(pattern, RS_LAST))
{
CODE_push_last();
}
else if (PATTERN_is(pattern, RS_AT))
{
if (!CODE_popify_last())
THROW("This expression cannot be passed by reference");
}
else if (PATTERN_is(pattern, RS_COMMA))
{
CODE_drop();
}
else if (PATTERN_is(pattern, RS_ERROR))
{
TRANS_subr(TS_SUBR_ERROR, 0);
}
else if (PATTERN_is(pattern, RS_OPTIONAL))
{
CODE_push_void();
}
else if (PATTERN_is(pattern, RS_PINF))
{
CODE_push_inf(FALSE);
}
else if (PATTERN_is(pattern, RS_MINF))
{
CODE_push_inf(TRUE);
}
else
{
op = PATTERN_index(pattern);
if (op == RS_LBRA)
{
nparam = get_nparam(tree, count, &i, &byref);
trans_call(nparam, byref);
}
else
{
nparam = get_nparam(tree, count, &i, NULL);
trans_operation((short)op, nparam, prev_pattern);
}
}
}
}
}
void TRANS_new(void)
{
int index;
int i, nparam;
bool array = FALSE;
bool event = FALSE;
bool collection = FALSE;
bool check_param = FALSE;
nparam = 0;
if (PATTERN_is_class(*JOB->current))
{
index = CLASS_add_class(JOB->class, PATTERN_index(*JOB->current));
if (PATTERN_is(JOB->current[1], RS_LSQR))
index = CLASS_get_array_class(JOB->class, T_OBJECT, index);
CODE_push_class(index);
nparam = 1;
}
else if (PATTERN_is_type(*JOB->current))
{
if (!PATTERN_is(JOB->current[1], RS_LSQR))
THROW("Cannot instantiate native types");
//CODE_push_number(RES_get_type(PATTERN_index(*JOB->current)));
CODE_push_class(CLASS_get_array_class(JOB->class, RES_get_type(PATTERN_index(*JOB->current)), -1));
nparam = 1;
}
else if (PATTERN_is(*JOB->current, RS_LBRA))
{
/* NEW ("Class", ...) */
nparam = 0;
JOB->current--;
check_param = TRUE;
}
else
THROW(E_SYNTAX);
JOB->current++;
if (TRANS_is(RS_LSQR))
{
if (collection)
THROW("Array declaration is forbidden with typed collection");
if (!PATTERN_is(*JOB->current, RS_RSQR))
{
for (i = 0;; i++)
{
if (i > MAX_ARRAY_DIM)
THROW("Too many dimensions");
TRANS_expression(FALSE);
nparam++;
if (PATTERN_is(*JOB->current, RS_RSQR))
break;
if (!PATTERN_is(*JOB->current, RS_COMMA))
THROW(E_MISSING, "']'");
JOB->current++;
}
}
JOB->current++;
array = TRUE;
}
else
{
if (TRANS_is(RS_LBRA))
{
for(;;)
{
if (nparam > MAX_PARAM_FUNC)
THROW("Too many arguments");
if (PATTERN_is(*JOB->current, RS_AT) || PATTERN_is(*JOB->current, RS_BYREF))
THROW("NEW cannot have arguments passed by reference");
TRANS_expression(FALSE);
nparam++;
if (PATTERN_is(*JOB->current, RS_RBRA))
break;
if (!PATTERN_is(*JOB->current, RS_COMMA))
THROW(E_MISSING, "')'");
JOB->current++;
}
JOB->current++;
if (check_param && nparam == 0)
THROW("Not enough argument to New()");
}
if (TRANS_is(RS_AS))
{
TRANS_expression(FALSE);
nparam++;
event = TRUE;
}
/*
CODE_call(nparam, FALSE);
CODE_drop();
*/
}
if (collection)
CODE_new(nparam, TRUE, event);
else
CODE_new(nparam, array, event);
}
void TRANS_expression(bool check_statement)
{
TRANS_TREE *tree;
int tree_length;
[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 23:49:13 +02:00
if (TRANS_is(RS_NEW))
{
TRANS_new();
return;
}
else if (TRANS_is(RS_READ))
{
TRANS_read();
return;
}
[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 23:49:13 +02:00
TRANS_tree(check_statement, &tree, &tree_length);
trans_expr_from_tree(tree, tree_length);
FREE(&tree, "TRANS_expression");
if (check_statement)
{
/*
if (!CODE_check_statement_last())
THROW("This expression cannot be a statement");
*/
CODE_drop();
}
}
void TRANS_ignore_expression()
{
TRANS_tree(FALSE, NULL, NULL);
}
TYPE TRANS_variable_get_type()
{
TYPE type = TYPE_make(T_VOID, 0, 0);
TRANS_TREE *tree;
int count;
int index;
CLASS_SYMBOL *sym;
TRANS_tree(FALSE, &tree, &count);
if (count == 1 && PATTERN_is_identifier(*tree))
{
index = PATTERN_index(*tree);
sym = CLASS_get_symbol(JOB->class, index);
if (!TYPE_is_null(sym->local.type))
{
return sym->local.type;
}
else if (!TYPE_is_null(sym->global.type))
{
int type = TYPE_get_kind(sym->global.type);
if (type == TK_VARIABLE)
return sym->global.type;
}
}
FREE(&tree, "TRANS_variable_get_type");
return type;
}
void TRANS_reference(void)
{
TRANS_expression(FALSE);
if (!CODE_popify_last())
THROW("Invalid assignment");
}
bool TRANS_affectation(bool dup)
{
static TRANS_STATEMENT statement[] = {
//{ RS_NEW, TRANS_new },
{ RS_OPEN, TRANS_open },
{ RS_SHELL, TRANS_shell },
{ RS_EXEC, TRANS_exec },
{ RS_RAISE, TRANS_raise },
{ RS_PIPE, TRANS_pipe },
{ RS_LOCK, TRANS_lock },
{ RS_MEMORY, TRANS_memory },
//{ RS_READ, TRANS_read },
{ RS_NONE, NULL }
};
TRANS_STATEMENT *st;
PATTERN *look = JOB->current;
PATTERN *left, *expr, *after;
int niv = 0;
bool equal = FALSE;
bool stat = FALSE;
int op;
for(;;)
{
if (PATTERN_is_newline(*look)) // || PATTERN_is_end(*look))
break;
if (PATTERN_is(*look, RS_LBRA) || PATTERN_is(*look, RS_LSQR))
{
niv++;
}
else if (PATTERN_is(*look, RS_RBRA) || PATTERN_is(*look, RS_RSQR))
{
if (niv > 0)
niv--;
}
else if (niv == 0)
{
if (PATTERN_is(*look, RS_EQUAL))
{
equal = TRUE;
op = RS_NONE;
break;
}
else if (PATTERN_is_reserved(*look) && RES_is_assignment(PATTERN_index(*look)))
{
equal = TRUE;
op = RES_get_assignment_operator(PATTERN_index(*look));
break;
}
}
look++;
}
if (!equal)
return FALSE;
left = JOB->current;
*look++ = PATTERN_make(RT_NEWLINE, 0);
expr = look;
JOB->current = expr;
if (op == RS_NONE && (PATTERN_is_reserved(*JOB->current)))
{
if (TRANS_is(RS_NEW))
{
TRANS_in_affectation++;
TRANS_new();
TRANS_in_affectation--;
stat = TRUE;
}
else
{
for (st = statement; st->id; st++)
{
if (TRANS_is(st->id))
{
TRANS_in_affectation++;
(*st->func)();
TRANS_in_affectation--;
stat = TRUE;
}
}
}
}
if (!stat)
{
if (op != RS_NONE)
{
JOB->current = left;
TRANS_expression(FALSE);
}
JOB->current = expr;
TRANS_expression(FALSE);
after = JOB->current;
if (op != RS_NONE)
trans_operation(op, 2, NULL_PATTERN);
}
after = JOB->current;
if (dup)
CODE_dup();
JOB->current = left;
TRANS_reference();
JOB->current = after;
return TRUE;
}