summaryrefslogtreecommitdiff
path: root/gcc/fortran/matchexp.c
diff options
context:
space:
mode:
authordnovillo <dnovillo@138bc75d-0d04-0410-961f-82ee72b054a4>2004-05-13 06:41:07 +0000
committerdnovillo <dnovillo@138bc75d-0d04-0410-961f-82ee72b054a4>2004-05-13 06:41:07 +0000
commit4ee9c6840ad3fc92a9034343278a1e476ad6872a (patch)
treea2568888a519c077427b133de9ece5879a8484a5 /gcc/fortran/matchexp.c
parentebb338380ab170c91e64d38038e6b5ce930d69a1 (diff)
downloadgcc-4ee9c6840ad3fc92a9034343278a1e476ad6872a.tar.gz
Merge tree-ssa-20020619-branch into mainline.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@81764 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/fortran/matchexp.c')
-rw-r--r--gcc/fortran/matchexp.c776
1 files changed, 776 insertions, 0 deletions
diff --git a/gcc/fortran/matchexp.c b/gcc/fortran/matchexp.c
new file mode 100644
index 00000000000..4acd98e66fc
--- /dev/null
+++ b/gcc/fortran/matchexp.c
@@ -0,0 +1,776 @@
+/* Expression parser.
+ Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
+ Contributed by Andy Vaught
+
+This file is part of GNU G95.
+
+GNU G95 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.
+
+GNU G95 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 GNU G95; see the file COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+
+#include "config.h"
+#include <string.h>
+#include "gfortran.h"
+#include "arith.h"
+#include "match.h"
+
+static char expression_syntax[] = "Syntax error in expression at %C";
+
+
+/* Match a user-defined operator name. This is a normal name with a
+ few restrictions. The error_flag controls whether an error is
+ raised if 'true' or 'false' are used or not. */
+
+match
+gfc_match_defined_op_name (char *result, int error_flag)
+{
+ static const char * const badops[] = {
+ "and", "or", "not", "eqv", "neqv", "eq", "ne", "ge", "le", "lt", "gt",
+ NULL
+ };
+
+ char name[GFC_MAX_SYMBOL_LEN + 1];
+ locus old_loc;
+ match m;
+ int i;
+
+ old_loc = *gfc_current_locus ();
+
+ m = gfc_match (" . %n .", name);
+ if (m != MATCH_YES)
+ return m;
+
+ /* .true. and .false. have interpretations as constants. Trying to
+ use these as operators will fail at a later time. */
+
+ if (strcmp (name, "true") == 0 || strcmp (name, "false") == 0)
+ {
+ if (error_flag)
+ goto error;
+ gfc_set_locus (&old_loc);
+ return MATCH_NO;
+ }
+
+ for (i = 0; badops[i]; i++)
+ if (strcmp (badops[i], name) == 0)
+ goto error;
+
+ for (i = 0; name[i]; i++)
+ if (!ISALPHA (name[i]))
+ {
+ gfc_error ("Bad character '%c' in OPERATOR name at %C", name[i]);
+ return MATCH_ERROR;
+ }
+
+ strcpy (result, name);
+ return MATCH_YES;
+
+error:
+ gfc_error ("The name '%s' cannot be used as a defined operator at %C",
+ name);
+
+ gfc_set_locus (&old_loc);
+ return MATCH_ERROR;
+}
+
+
+/* Match a user defined operator. The symbol found must be an
+ operator already. */
+
+static match
+match_defined_operator (gfc_user_op ** result)
+{
+ char name[GFC_MAX_SYMBOL_LEN + 1];
+ match m;
+
+ m = gfc_match_defined_op_name (name, 0);
+ if (m != MATCH_YES)
+ return m;
+
+ *result = gfc_get_uop (name);
+ return MATCH_YES;
+}
+
+
+/* Check to see if the given operator is next on the input. If this
+ is not the case, the parse pointer remains where it was. */
+
+static int
+next_operator (gfc_intrinsic_op t)
+{
+ gfc_intrinsic_op u;
+ locus old_loc;
+
+ old_loc = *gfc_current_locus ();
+ if (gfc_match_intrinsic_op (&u) == MATCH_YES && t == u)
+ return 1;
+
+ gfc_set_locus (&old_loc);
+ return 0;
+}
+
+
+/* Match a primary expression. */
+
+static match
+match_primary (gfc_expr ** result)
+{
+ match m;
+
+ m = gfc_match_literal_constant (result, 0);
+ if (m != MATCH_NO)
+ return m;
+
+ m = gfc_match_array_constructor (result);
+ if (m != MATCH_NO)
+ return m;
+
+ m = gfc_match_rvalue (result);
+ if (m != MATCH_NO)
+ return m;
+
+ /* Match an expression in parenthesis. */
+ if (gfc_match_char ('(') != MATCH_YES)
+ return MATCH_NO;
+
+ m = gfc_match_expr (result);
+ if (m == MATCH_NO)
+ goto syntax;
+ if (m == MATCH_ERROR)
+ return m;
+
+ m = gfc_match_char (')');
+ if (m == MATCH_NO)
+ gfc_error ("Expected a right parenthesis in expression at %C");
+
+ if (m != MATCH_YES)
+ {
+ gfc_free_expr (*result);
+ return MATCH_ERROR;
+ }
+
+ return MATCH_YES;
+
+syntax:
+ gfc_error (expression_syntax);
+ return MATCH_ERROR;
+}
+
+
+/* Build an operator expression node. */
+
+static gfc_expr *
+build_node (gfc_intrinsic_op operator, locus * where,
+ gfc_expr * op1, gfc_expr * op2)
+{
+ gfc_expr *new;
+
+ new = gfc_get_expr ();
+ new->expr_type = EXPR_OP;
+ new->operator = operator;
+ new->where = *where;
+
+ new->op1 = op1;
+ new->op2 = op2;
+
+ return new;
+}
+
+
+/* Match a level 1 expression. */
+
+static match
+match_level_1 (gfc_expr ** result)
+{
+ gfc_user_op *uop;
+ gfc_expr *e, *f;
+ locus where;
+ match m;
+
+ where = *gfc_current_locus ();
+ uop = NULL;
+ m = match_defined_operator (&uop);
+ if (m == MATCH_ERROR)
+ return m;
+
+ m = match_primary (&e);
+ if (m != MATCH_YES)
+ return m;
+
+ if (uop == NULL)
+ *result = e;
+ else
+ {
+ f = build_node (INTRINSIC_USER, &where, e, NULL);
+ f->uop = uop;
+ *result = f;
+ }
+
+ return MATCH_YES;
+}
+
+
+static match
+match_mult_operand (gfc_expr ** result)
+{
+ gfc_expr *e, *exp, *r;
+ locus where;
+ match m;
+
+ m = match_level_1 (&e);
+ if (m != MATCH_YES)
+ return m;
+
+ if (!next_operator (INTRINSIC_POWER))
+ {
+ *result = e;
+ return MATCH_YES;
+ }
+
+ where = *gfc_current_locus ();
+
+ m = match_mult_operand (&exp);
+ if (m == MATCH_NO)
+ gfc_error ("Expected exponent in expression at %C");
+ if (m != MATCH_YES)
+ {
+ gfc_free_expr (e);
+ return MATCH_ERROR;
+ }
+
+ r = gfc_power (e, exp);
+ if (r == NULL)
+ {
+ gfc_free_expr (e);
+ gfc_free_expr (exp);
+ return MATCH_ERROR;
+ }
+
+ r->where = where;
+ *result = r;
+
+ return MATCH_YES;
+}
+
+
+static match
+match_add_operand (gfc_expr ** result)
+{
+ gfc_expr *all, *e, *total;
+ locus where, old_loc;
+ match m;
+ gfc_intrinsic_op i;
+
+ m = match_mult_operand (&all);
+ if (m != MATCH_YES)
+ return m;
+
+ for (;;)
+ {
+ /* Build up a string of products or quotients. */
+
+ old_loc = *gfc_current_locus ();
+
+ if (next_operator (INTRINSIC_TIMES))
+ i = INTRINSIC_TIMES;
+ else
+ {
+ if (next_operator (INTRINSIC_DIVIDE))
+ i = INTRINSIC_DIVIDE;
+ else
+ break;
+ }
+
+ where = *gfc_current_locus ();
+
+ m = match_mult_operand (&e);
+ if (m == MATCH_NO)
+ {
+ gfc_set_locus (&old_loc);
+ break;
+ }
+
+ if (m == MATCH_ERROR)
+ {
+ gfc_free_expr (all);
+ return MATCH_ERROR;
+ }
+
+ if (i == INTRINSIC_TIMES)
+ total = gfc_multiply (all, e);
+ else
+ total = gfc_divide (all, e);
+
+ if (total == NULL)
+ {
+ gfc_free_expr (all);
+ gfc_free_expr (e);
+ return MATCH_ERROR;
+ }
+
+ all = total;
+ all->where = where;
+ }
+
+ *result = all;
+ return MATCH_YES;
+}
+
+
+static int
+match_add_op (void)
+{
+
+ if (next_operator (INTRINSIC_MINUS))
+ return -1;
+ if (next_operator (INTRINSIC_PLUS))
+ return 1;
+ return 0;
+}
+
+
+/* Match a level 2 expression. */
+
+static match
+match_level_2 (gfc_expr ** result)
+{
+ gfc_expr *all, *e, *total;
+ locus where;
+ match m;
+ int i;
+
+ where = *gfc_current_locus ();
+ i = match_add_op ();
+
+ m = match_add_operand (&e);
+ if (i != 0 && m == MATCH_NO)
+ {
+ gfc_error (expression_syntax);
+ m = MATCH_ERROR;
+ }
+
+ if (m != MATCH_YES)
+ return m;
+
+ if (i == 0)
+ all = e;
+ else
+ {
+ if (i == -1)
+ all = gfc_uminus (e);
+ else
+ all = gfc_uplus (e);
+
+ if (all == NULL)
+ {
+ gfc_free_expr (e);
+ return MATCH_ERROR;
+ }
+ }
+
+ all->where = where;
+
+/* Append add-operands to the sum */
+
+ for (;;)
+ {
+ where = *gfc_current_locus ();
+ i = match_add_op ();
+ if (i == 0)
+ break;
+
+ m = match_add_operand (&e);
+ if (m == MATCH_NO)
+ gfc_error (expression_syntax);
+ if (m != MATCH_YES)
+ {
+ gfc_free_expr (all);
+ return MATCH_ERROR;
+ }
+
+ if (i == -1)
+ total = gfc_subtract (all, e);
+ else
+ total = gfc_add (all, e);
+
+ if (total == NULL)
+ {
+ gfc_free_expr (all);
+ gfc_free_expr (e);
+ return MATCH_ERROR;
+ }
+
+ all = total;
+ all->where = where;
+ }
+
+ *result = all;
+ return MATCH_YES;
+}
+
+
+/* Match a level three expression. */
+
+static match
+match_level_3 (gfc_expr ** result)
+{
+ gfc_expr *all, *e, *total;
+ locus where;
+ match m;
+
+ m = match_level_2 (&all);
+ if (m != MATCH_YES)
+ return m;
+
+ for (;;)
+ {
+ if (!next_operator (INTRINSIC_CONCAT))
+ break;
+
+ where = *gfc_current_locus ();
+
+ m = match_level_2 (&e);
+ if (m == MATCH_NO)
+ {
+ gfc_error (expression_syntax);
+ gfc_free_expr (all);
+ }
+ if (m != MATCH_YES)
+ return MATCH_ERROR;
+
+ total = gfc_concat (all, e);
+ if (total == NULL)
+ {
+ gfc_free_expr (all);
+ gfc_free_expr (e);
+ return MATCH_ERROR;
+ }
+
+ all = total;
+ all->where = where;
+ }
+
+ *result = all;
+ return MATCH_YES;
+}
+
+
+/* Match a level 4 expression. */
+
+static match
+match_level_4 (gfc_expr ** result)
+{
+ gfc_expr *left, *right, *r;
+ gfc_intrinsic_op i;
+ locus old_loc;
+ locus where;
+ match m;
+
+ m = match_level_3 (&left);
+ if (m != MATCH_YES)
+ return m;
+
+ old_loc = *gfc_current_locus ();
+
+ if (gfc_match_intrinsic_op (&i) != MATCH_YES)
+ {
+ *result = left;
+ return MATCH_YES;
+ }
+
+ if (i != INTRINSIC_EQ && i != INTRINSIC_NE && i != INTRINSIC_GE
+ && i != INTRINSIC_LE && i != INTRINSIC_LT && i != INTRINSIC_GT)
+ {
+ gfc_set_locus (&old_loc);
+ *result = left;
+ return MATCH_YES;
+ }
+
+ where = *gfc_current_locus ();
+
+ m = match_level_3 (&right);
+ if (m == MATCH_NO)
+ gfc_error (expression_syntax);
+ if (m != MATCH_YES)
+ {
+ gfc_free_expr (left);
+ return MATCH_ERROR;
+ }
+
+ switch (i)
+ {
+ case INTRINSIC_EQ:
+ r = gfc_eq (left, right);
+ break;
+
+ case INTRINSIC_NE:
+ r = gfc_ne (left, right);
+ break;
+
+ case INTRINSIC_LT:
+ r = gfc_lt (left, right);
+ break;
+
+ case INTRINSIC_LE:
+ r = gfc_le (left, right);
+ break;
+
+ case INTRINSIC_GT:
+ r = gfc_gt (left, right);
+ break;
+
+ case INTRINSIC_GE:
+ r = gfc_ge (left, right);
+ break;
+
+ default:
+ gfc_internal_error ("match_level_4(): Bad operator");
+ }
+
+ if (r == NULL)
+ {
+ gfc_free_expr (left);
+ gfc_free_expr (right);
+ return MATCH_ERROR;
+ }
+
+ r->where = where;
+ *result = r;
+
+ return MATCH_YES;
+}
+
+
+static match
+match_and_operand (gfc_expr ** result)
+{
+ gfc_expr *e, *r;
+ locus where;
+ match m;
+ int i;
+
+ i = next_operator (INTRINSIC_NOT);
+ where = *gfc_current_locus ();
+
+ m = match_level_4 (&e);
+ if (m != MATCH_YES)
+ return m;
+
+ r = e;
+ if (i)
+ {
+ r = gfc_not (e);
+ if (r == NULL)
+ {
+ gfc_free_expr (e);
+ return MATCH_ERROR;
+ }
+ }
+
+ r->where = where;
+ *result = r;
+
+ return MATCH_YES;
+}
+
+
+static match
+match_or_operand (gfc_expr ** result)
+{
+ gfc_expr *all, *e, *total;
+ locus where;
+ match m;
+
+ m = match_and_operand (&all);
+ if (m != MATCH_YES)
+ return m;
+
+ for (;;)
+ {
+ if (!next_operator (INTRINSIC_AND))
+ break;
+ where = *gfc_current_locus ();
+
+ m = match_and_operand (&e);
+ if (m == MATCH_NO)
+ gfc_error (expression_syntax);
+ if (m != MATCH_YES)
+ {
+ gfc_free_expr (all);
+ return MATCH_ERROR;
+ }
+
+ total = gfc_and (all, e);
+ if (total == NULL)
+ {
+ gfc_free_expr (all);
+ gfc_free_expr (e);
+ return MATCH_ERROR;
+ }
+
+ all = total;
+ all->where = where;
+ }
+
+ *result = all;
+ return MATCH_YES;
+}
+
+
+static match
+match_equiv_operand (gfc_expr ** result)
+{
+ gfc_expr *all, *e, *total;
+ locus where;
+ match m;
+
+ m = match_or_operand (&all);
+ if (m != MATCH_YES)
+ return m;
+
+ for (;;)
+ {
+ if (!next_operator (INTRINSIC_OR))
+ break;
+ where = *gfc_current_locus ();
+
+ m = match_or_operand (&e);
+ if (m == MATCH_NO)
+ gfc_error (expression_syntax);
+ if (m != MATCH_YES)
+ {
+ gfc_free_expr (all);
+ return MATCH_ERROR;
+ }
+
+ total = gfc_or (all, e);
+ if (total == NULL)
+ {
+ gfc_free_expr (all);
+ gfc_free_expr (e);
+ return MATCH_ERROR;
+ }
+
+ all = total;
+ all->where = where;
+ }
+
+ *result = all;
+ return MATCH_YES;
+}
+
+
+/* Match a level 5 expression. */
+
+static match
+match_level_5 (gfc_expr ** result)
+{
+ gfc_expr *all, *e, *total;
+ locus where;
+ match m;
+ gfc_intrinsic_op i;
+
+ m = match_equiv_operand (&all);
+ if (m != MATCH_YES)
+ return m;
+
+ for (;;)
+ {
+ if (next_operator (INTRINSIC_EQV))
+ i = INTRINSIC_EQV;
+ else
+ {
+ if (next_operator (INTRINSIC_NEQV))
+ i = INTRINSIC_NEQV;
+ else
+ break;
+ }
+
+ where = *gfc_current_locus ();
+
+ m = match_equiv_operand (&e);
+ if (m == MATCH_NO)
+ gfc_error (expression_syntax);
+ if (m != MATCH_YES)
+ {
+ gfc_free_expr (all);
+ return MATCH_ERROR;
+ }
+
+ if (i == INTRINSIC_EQV)
+ total = gfc_eqv (all, e);
+ else
+ total = gfc_neqv (all, e);
+
+ if (total == NULL)
+ {
+ gfc_free_expr (all);
+ gfc_free_expr (e);
+ return MATCH_ERROR;
+ }
+
+ all = total;
+ all->where = where;
+ }
+
+ *result = all;
+ return MATCH_YES;
+}
+
+
+/* Match an expression. At this level, we are stringing together
+ level 5 expressions separated by binary operators. */
+
+match
+gfc_match_expr (gfc_expr ** result)
+{
+ gfc_expr *all, *e;
+ gfc_user_op *uop;
+ locus where;
+ match m;
+
+ m = match_level_5 (&all);
+ if (m != MATCH_YES)
+ return m;
+
+ for (;;)
+ {
+ m = match_defined_operator (&uop);
+ if (m == MATCH_NO)
+ break;
+ if (m == MATCH_ERROR)
+ {
+ gfc_free_expr (all);
+ return MATCH_ERROR;
+ }
+
+ where = *gfc_current_locus ();
+
+ m = match_level_5 (&e);
+ if (m == MATCH_NO)
+ gfc_error (expression_syntax);
+ if (m != MATCH_YES)
+ {
+ gfc_free_expr (all);
+ return MATCH_ERROR;
+ }
+
+ all = build_node (INTRINSIC_USER, &where, all, e);
+ all->uop = uop;
+ }
+
+ *result = all;
+ return MATCH_YES;
+}