From 3d6b6a9075f0a0c84c342453889582df637d6b5c Mon Sep 17 00:00:00 2001 From: John Gilmore Date: Thu, 19 Sep 1991 10:31:00 +0000 Subject: [PATCH] * parse.c: New file with the common code remains of expread.y. * expread.y, expread.tab.c: Remove. * parser-defs.h: New file with common declarations from expread.y. * c-exp.y: New file with the C parser from expread.y. * m2-exp.y: New file with the Modula-2 parser. --- gdb/c-exp.y | 1513 +++++++++++++++++++++++++++++++++++++++++++++ gdb/m2-exp.y | 1215 ++++++++++++++++++++++++++++++++++++ gdb/parse.c | 628 +++++++++++++++++++ gdb/parser-defs.h | 162 +++++ 4 files changed, 3518 insertions(+) create mode 100644 gdb/c-exp.y create mode 100644 gdb/m2-exp.y create mode 100644 gdb/parse.c create mode 100644 gdb/parser-defs.h diff --git a/gdb/c-exp.y b/gdb/c-exp.y new file mode 100644 index 00000000000..93a2a04fc2a --- /dev/null +++ b/gdb/c-exp.y @@ -0,0 +1,1513 @@ +/* YACC parser for C expressions, for GDB. + Copyright (C) 1986, 1989, 1990, 1991 Free Software Foundation, Inc. + +This file is part of GDB. + +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 of the License, 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. */ + +/* Parse a C expression from text in a string, + and return the result as a struct expression pointer. + That structure contains arithmetic operations in reverse polish, + with constants represented by operations that are followed by special data. + See expression.h for the details of the format. + What is important here is that it can be built up sequentially + during the process of parsing; the lower levels of the tree always + come first in the result. */ + +%{ + +#include +#include +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" +#include "expression.h" +#include "parser-defs.h" +#include "value.h" +#include "language.h" + +/* These MUST be included in any grammar file!!!! + Please choose unique names! */ +#define yyparse c_parse +#define yylex c_lex +#define yyerror c_error +#define yylval c_lval +#define yychar c_char +#define yydebug c_debug +#define yypact c_pact +#define yyr1 c_r1 +#define yyr2 c_r2 +#define yydef c_def +#define yychk c_chk +#define yypgo c_pgo +#define yyact c_act +#define yyexca c_exca + +void yyerror (); + +/* #define YYDEBUG 1 */ + +%} + +/* Although the yacc "value" of an expression is not used, + since the result is stored in the structure being created, + other node types do have values. */ + +%union + { + LONGEST lval; + unsigned LONGEST ulval; + double dval; + struct symbol *sym; + struct type *tval; + struct stoken sval; + struct ttype tsym; + struct symtoken ssym; + int voidval; + struct block *bval; + enum exp_opcode opcode; + struct internalvar *ivar; + + struct type **tvec; + int *ivec; + } + +%type exp exp1 type_exp start variable +%type type typebase +%type nonempty_typelist +/* %type block */ + +/* Fancy type parsing. */ +%type func_mod direct_abs_decl abs_decl +%type ptype +%type array_mod + +%token INT CHAR +%token UINT +%token FLOAT + +/* Both NAME and TYPENAME tokens represent symbols in the input, + and both convey their data as strings. + But a TYPENAME is a string that happens to be defined as a typedef + or builtin type name (such as int or char) + and a NAME is any other symbol. + Contexts where this distinction is not important can use the + nonterminal "name", which matches either NAME or TYPENAME. */ + +%token STRING +%token NAME /* BLOCKNAME defined below to give it higher precedence. */ +%token TYPENAME +%type name +%type name_not_typename +%type typename + +/* A NAME_OR_INT is a symbol which is not known in the symbol table, + but which would parse as a valid number in the current input radix. + E.g. "c" when input_radix==16. Depending on the parse, it will be + turned into a name or into a number. NAME_OR_UINT ditto. */ + +%token NAME_OR_INT NAME_OR_UINT + +%token STRUCT UNION ENUM SIZEOF UNSIGNED COLONCOLON +%token ERROR + +/* Special type cases, put in to allow the parser to distinguish different + legal basetypes. */ +%token SIGNED LONG SHORT INT_KEYWORD + +%token LAST REGNAME + +%token VARIABLE + +%token ASSIGN_MODIFY + +/* C++ */ +%token THIS + +%left ',' +%left ABOVE_COMMA +%right '=' ASSIGN_MODIFY +%right '?' +%left OR +%left AND +%left '|' +%left '^' +%left '&' +%left EQUAL NOTEQUAL +%left '<' '>' LEQ GEQ +%left LSH RSH +%left '@' +%left '+' '-' +%left '*' '/' '%' +%right UNARY INCREMENT DECREMENT +%right ARROW '.' '[' '(' +%token BLOCKNAME +%type block +%left COLONCOLON + +%% + +start : exp1 + | type_exp + ; + +type_exp: type + { write_exp_elt_opcode(OP_TYPE); + write_exp_elt_type($1); + write_exp_elt_opcode(OP_TYPE);} + ; + +/* Expressions, including the comma operator. */ +exp1 : exp + | exp1 ',' exp + { write_exp_elt_opcode (BINOP_COMMA); } + ; + +/* Expressions, not including the comma operator. */ +exp : '*' exp %prec UNARY + { write_exp_elt_opcode (UNOP_IND); } + +exp : '&' exp %prec UNARY + { write_exp_elt_opcode (UNOP_ADDR); } + +exp : '-' exp %prec UNARY + { write_exp_elt_opcode (UNOP_NEG); } + ; + +exp : '!' exp %prec UNARY + { write_exp_elt_opcode (UNOP_ZEROP); } + ; + +exp : '~' exp %prec UNARY + { write_exp_elt_opcode (UNOP_LOGNOT); } + ; + +exp : INCREMENT exp %prec UNARY + { write_exp_elt_opcode (UNOP_PREINCREMENT); } + ; + +exp : DECREMENT exp %prec UNARY + { write_exp_elt_opcode (UNOP_PREDECREMENT); } + ; + +exp : exp INCREMENT %prec UNARY + { write_exp_elt_opcode (UNOP_POSTINCREMENT); } + ; + +exp : exp DECREMENT %prec UNARY + { write_exp_elt_opcode (UNOP_POSTDECREMENT); } + ; + +exp : SIZEOF exp %prec UNARY + { write_exp_elt_opcode (UNOP_SIZEOF); } + ; + +exp : exp ARROW name + { write_exp_elt_opcode (STRUCTOP_PTR); + write_exp_string ($3); + write_exp_elt_opcode (STRUCTOP_PTR); } + ; + +exp : exp ARROW '*' exp + { write_exp_elt_opcode (STRUCTOP_MPTR); } + ; + +exp : exp '.' name + { write_exp_elt_opcode (STRUCTOP_STRUCT); + write_exp_string ($3); + write_exp_elt_opcode (STRUCTOP_STRUCT); } + ; + +exp : exp '.' '*' exp + { write_exp_elt_opcode (STRUCTOP_MEMBER); } + ; + +exp : exp '[' exp1 ']' + { write_exp_elt_opcode (BINOP_SUBSCRIPT); } + ; + +exp : exp '(' + /* This is to save the value of arglist_len + being accumulated by an outer function call. */ + { start_arglist (); } + arglist ')' %prec ARROW + { write_exp_elt_opcode (OP_FUNCALL); + write_exp_elt_longcst ((LONGEST) end_arglist ()); + write_exp_elt_opcode (OP_FUNCALL); } + ; + +arglist : + ; + +arglist : exp + { arglist_len = 1; } + ; + +arglist : arglist ',' exp %prec ABOVE_COMMA + { arglist_len++; } + ; + +exp : '{' type '}' exp %prec UNARY + { write_exp_elt_opcode (UNOP_MEMVAL); + write_exp_elt_type ($2); + write_exp_elt_opcode (UNOP_MEMVAL); } + ; + +exp : '(' type ')' exp %prec UNARY + { write_exp_elt_opcode (UNOP_CAST); + write_exp_elt_type ($2); + write_exp_elt_opcode (UNOP_CAST); } + ; + +exp : '(' exp1 ')' + { } + ; + +/* Binary operators in order of decreasing precedence. */ + +exp : exp '@' exp + { write_exp_elt_opcode (BINOP_REPEAT); } + ; + +exp : exp '*' exp + { write_exp_elt_opcode (BINOP_MUL); } + ; + +exp : exp '/' exp + { write_exp_elt_opcode (BINOP_DIV); } + ; + +exp : exp '%' exp + { write_exp_elt_opcode (BINOP_REM); } + ; + +exp : exp '+' exp + { write_exp_elt_opcode (BINOP_ADD); } + ; + +exp : exp '-' exp + { write_exp_elt_opcode (BINOP_SUB); } + ; + +exp : exp LSH exp + { write_exp_elt_opcode (BINOP_LSH); } + ; + +exp : exp RSH exp + { write_exp_elt_opcode (BINOP_RSH); } + ; + +exp : exp EQUAL exp + { write_exp_elt_opcode (BINOP_EQUAL); } + ; + +exp : exp NOTEQUAL exp + { write_exp_elt_opcode (BINOP_NOTEQUAL); } + ; + +exp : exp LEQ exp + { write_exp_elt_opcode (BINOP_LEQ); } + ; + +exp : exp GEQ exp + { write_exp_elt_opcode (BINOP_GEQ); } + ; + +exp : exp '<' exp + { write_exp_elt_opcode (BINOP_LESS); } + ; + +exp : exp '>' exp + { write_exp_elt_opcode (BINOP_GTR); } + ; + +exp : exp '&' exp + { write_exp_elt_opcode (BINOP_LOGAND); } + ; + +exp : exp '^' exp + { write_exp_elt_opcode (BINOP_LOGXOR); } + ; + +exp : exp '|' exp + { write_exp_elt_opcode (BINOP_LOGIOR); } + ; + +exp : exp AND exp + { write_exp_elt_opcode (BINOP_AND); } + ; + +exp : exp OR exp + { write_exp_elt_opcode (BINOP_OR); } + ; + +exp : exp '?' exp ':' exp %prec '?' + { write_exp_elt_opcode (TERNOP_COND); } + ; + +exp : exp '=' exp + { write_exp_elt_opcode (BINOP_ASSIGN); } + ; + +exp : exp ASSIGN_MODIFY exp + { write_exp_elt_opcode (BINOP_ASSIGN_MODIFY); + write_exp_elt_opcode ($2); + write_exp_elt_opcode (BINOP_ASSIGN_MODIFY); } + ; + +exp : INT + { write_exp_elt_opcode (OP_LONG); + if ($1 == (int) $1 || $1 == (unsigned int) $1) + write_exp_elt_type (builtin_type_int); + else + write_exp_elt_type (BUILTIN_TYPE_LONGEST); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_LONG); } + ; + +exp : NAME_OR_INT + { YYSTYPE val; + parse_number ($1.stoken.ptr, $1.stoken.length, 0, &val); + write_exp_elt_opcode (OP_LONG); + if (val.lval == (int) val.lval || + val.lval == (unsigned int) val.lval) + write_exp_elt_type (builtin_type_int); + else + write_exp_elt_type (BUILTIN_TYPE_LONGEST); + write_exp_elt_longcst (val.lval); + write_exp_elt_opcode (OP_LONG); } + ; + +exp : UINT + { + write_exp_elt_opcode (OP_LONG); + if ($1 == (unsigned int) $1) + write_exp_elt_type (builtin_type_unsigned_int); + else + write_exp_elt_type (BUILTIN_TYPE_UNSIGNED_LONGEST); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_LONG); + } + ; + +exp : NAME_OR_UINT + { YYSTYPE val; + parse_number ($1.stoken.ptr, $1.stoken.length, 0, &val); + write_exp_elt_opcode (OP_LONG); + if (val.ulval == (unsigned int) val.ulval) + write_exp_elt_type (builtin_type_unsigned_int); + else + write_exp_elt_type (BUILTIN_TYPE_UNSIGNED_LONGEST); + write_exp_elt_longcst ((LONGEST)val.ulval); + write_exp_elt_opcode (OP_LONG); + } + ; + +exp : CHAR + { write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type_char); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_LONG); } + ; + +exp : FLOAT + { write_exp_elt_opcode (OP_DOUBLE); + write_exp_elt_type (builtin_type_double); + write_exp_elt_dblcst ($1); + write_exp_elt_opcode (OP_DOUBLE); } + ; + +exp : variable + ; + +exp : LAST + { write_exp_elt_opcode (OP_LAST); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_LAST); } + ; + +exp : REGNAME + { write_exp_elt_opcode (OP_REGISTER); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_REGISTER); } + ; + +exp : VARIABLE + { write_exp_elt_opcode (OP_INTERNALVAR); + write_exp_elt_intern ($1); + write_exp_elt_opcode (OP_INTERNALVAR); } + ; + +exp : SIZEOF '(' type ')' %prec UNARY + { write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type_int); + write_exp_elt_longcst ((LONGEST) TYPE_LENGTH ($3)); + write_exp_elt_opcode (OP_LONG); } + ; + +exp : STRING + { write_exp_elt_opcode (OP_STRING); + write_exp_string ($1); + write_exp_elt_opcode (OP_STRING); } + ; + +/* C++. */ +exp : THIS + { write_exp_elt_opcode (OP_THIS); + write_exp_elt_opcode (OP_THIS); } + ; + +/* end of C++. */ + +block : BLOCKNAME + { + if ($1.sym != 0) + $$ = SYMBOL_BLOCK_VALUE ($1.sym); + else + { + struct symtab *tem = + lookup_symtab (copy_name ($1.stoken)); + if (tem) + $$ = BLOCKVECTOR_BLOCK + (BLOCKVECTOR (tem), STATIC_BLOCK); + else + error ("No file or function \"%s\".", + copy_name ($1.stoken)); + } + } + ; + +block : block COLONCOLON name + { struct symbol *tem + = lookup_symbol (copy_name ($3), $1, + VAR_NAMESPACE, 0, NULL); + if (!tem || SYMBOL_CLASS (tem) != LOC_BLOCK) + error ("No function \"%s\" in specified context.", + copy_name ($3)); + $$ = SYMBOL_BLOCK_VALUE (tem); } + ; + +variable: block COLONCOLON name + { struct symbol *sym; + sym = lookup_symbol (copy_name ($3), $1, + VAR_NAMESPACE, 0, NULL); + if (sym == 0) + error ("No symbol \"%s\" in specified context.", + copy_name ($3)); + + write_exp_elt_opcode (OP_VAR_VALUE); + write_exp_elt_sym (sym); + write_exp_elt_opcode (OP_VAR_VALUE); } + ; + +variable: typebase COLONCOLON name + { + struct type *type = $1; + if (TYPE_CODE (type) != TYPE_CODE_STRUCT + && TYPE_CODE (type) != TYPE_CODE_UNION) + error ("`%s' is not defined as an aggregate type.", + TYPE_NAME (type)); + + write_exp_elt_opcode (OP_SCOPE); + write_exp_elt_type (type); + write_exp_string ($3); + write_exp_elt_opcode (OP_SCOPE); + } + | typebase COLONCOLON '~' name + { + struct type *type = $1; + if (TYPE_CODE (type) != TYPE_CODE_STRUCT + && TYPE_CODE (type) != TYPE_CODE_UNION) + error ("`%s' is not defined as an aggregate type.", + TYPE_NAME (type)); + + if (strcmp (type_name_no_tag (type), $4.ptr)) + error ("invalid destructor `%s::~%s'", + type_name_no_tag (type), $4.ptr); + + write_exp_elt_opcode (OP_SCOPE); + write_exp_elt_type (type); + write_exp_string ($4); + write_exp_elt_opcode (OP_SCOPE); + write_exp_elt_opcode (UNOP_LOGNOT); + } + | COLONCOLON name + { + char *name = copy_name ($2); + struct symbol *sym; + int i; + + sym = + lookup_symbol (name, 0, VAR_NAMESPACE, 0, NULL); + if (sym) + { + write_exp_elt_opcode (OP_VAR_VALUE); + write_exp_elt_sym (sym); + write_exp_elt_opcode (OP_VAR_VALUE); + break; + } + for (i = 0; i < misc_function_count; i++) + if (!strcmp (misc_function_vector[i].name, name)) + break; + + if (i < misc_function_count) + { + enum misc_function_type mft = + misc_function_vector[i].type; + + write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type_int); + write_exp_elt_longcst ((LONGEST) misc_function_vector[i].address); + write_exp_elt_opcode (OP_LONG); + write_exp_elt_opcode (UNOP_MEMVAL); + if (mft == mf_data || mft == mf_bss) + write_exp_elt_type (builtin_type_int); + else if (mft == mf_text) + write_exp_elt_type (lookup_function_type (builtin_type_int)); + else + write_exp_elt_type (builtin_type_char); + write_exp_elt_opcode (UNOP_MEMVAL); + } + else + if (symtab_list == 0 + && partial_symtab_list == 0) + error ("No symbol table is loaded. Use the \"file\" command."); + else + error ("No symbol \"%s\" in current context.", name); + } + ; + +variable: name_not_typename + { struct symbol *sym = $1.sym; + + if (sym) + { + switch (sym->class) + { + case LOC_REGISTER: + case LOC_ARG: + case LOC_REF_ARG: + case LOC_REGPARM: + case LOC_LOCAL: + case LOC_LOCAL_ARG: + if (innermost_block == 0 || + contained_in (block_found, + innermost_block)) + innermost_block = block_found; + case LOC_UNDEF: + case LOC_CONST: + case LOC_STATIC: + case LOC_TYPEDEF: + case LOC_LABEL: + case LOC_BLOCK: + case LOC_CONST_BYTES: + + /* In this case the expression can + be evaluated regardless of what + frame we are in, so there is no + need to check for the + innermost_block. These cases are + listed so that gcc -Wall will + report types that may not have + been considered. */ + + break; + } + write_exp_elt_opcode (OP_VAR_VALUE); + write_exp_elt_sym (sym); + write_exp_elt_opcode (OP_VAR_VALUE); + } + else if ($1.is_a_field_of_this) + { + /* C++: it hangs off of `this'. Must + not inadvertently convert from a method call + to data ref. */ + if (innermost_block == 0 || + contained_in (block_found, innermost_block)) + innermost_block = block_found; + write_exp_elt_opcode (OP_THIS); + write_exp_elt_opcode (OP_THIS); + write_exp_elt_opcode (STRUCTOP_PTR); + write_exp_string ($1.stoken); + write_exp_elt_opcode (STRUCTOP_PTR); + } + else + { + register int i; + register char *arg = copy_name ($1.stoken); + + /* FIXME, this search is linear! At least + optimize the strcmp with a 1-char cmp... */ + for (i = 0; i < misc_function_count; i++) + if (!strcmp (misc_function_vector[i].name, arg)) + break; + + if (i < misc_function_count) + { + enum misc_function_type mft = + misc_function_vector[i].type; + + write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type_int); + write_exp_elt_longcst ((LONGEST) misc_function_vector[i].address); + write_exp_elt_opcode (OP_LONG); + write_exp_elt_opcode (UNOP_MEMVAL); + if (mft == mf_data || mft == mf_bss) + write_exp_elt_type (builtin_type_int); + else if (mft == mf_text) + write_exp_elt_type (lookup_function_type (builtin_type_int)); + else + write_exp_elt_type (builtin_type_char); + write_exp_elt_opcode (UNOP_MEMVAL); + } + else if (symtab_list == 0 + && partial_symtab_list == 0) + error ("No symbol table is loaded. Use the \"file\" command."); + else + error ("No symbol \"%s\" in current context.", + copy_name ($1.stoken)); + } + } + ; + + +ptype : typebase + | typebase abs_decl + { + /* This is where the interesting stuff happens. */ + int done = 0; + int array_size; + struct type *follow_type = $1; + + while (!done) + switch (pop_type ()) + { + case tp_end: + done = 1; + break; + case tp_pointer: + follow_type = lookup_pointer_type (follow_type); + break; + case tp_reference: + follow_type = lookup_reference_type (follow_type); + break; + case tp_array: + array_size = pop_type_int (); + if (array_size != -1) + follow_type = create_array_type (follow_type, + array_size); + else + follow_type = lookup_pointer_type (follow_type); + break; + case tp_function: + follow_type = lookup_function_type (follow_type); + break; + } + $$ = follow_type; + } + ; + +abs_decl: '*' + { push_type (tp_pointer); $$ = 0; } + | '*' abs_decl + { push_type (tp_pointer); $$ = $2; } + | '&' + { push_type (tp_reference); $$ = 0; } + | '&' abs_decl + { push_type (tp_reference); $$ = $2; } + | direct_abs_decl + ; + +direct_abs_decl: '(' abs_decl ')' + { $$ = $2; } + | direct_abs_decl array_mod + { + push_type_int ($2); + push_type (tp_array); + } + | array_mod + { + push_type_int ($1); + push_type (tp_array); + $$ = 0; + } + | direct_abs_decl func_mod + { push_type (tp_function); } + | func_mod + { push_type (tp_function); } + ; + +array_mod: '[' ']' + { $$ = -1; } + | '[' INT ']' + { $$ = $2; } + ; + +func_mod: '(' ')' + { $$ = 0; } + ; + +type : ptype + | typebase COLONCOLON '*' + { $$ = lookup_member_type (builtin_type_int, $1); } + | type '(' typebase COLONCOLON '*' ')' + { $$ = lookup_member_type ($1, $3); } + | type '(' typebase COLONCOLON '*' ')' '(' ')' + { $$ = lookup_member_type + (lookup_function_type ($1), $3); } + | type '(' typebase COLONCOLON '*' ')' '(' nonempty_typelist ')' + { $$ = lookup_member_type + (lookup_function_type ($1), $3); + free ($8); } + ; + +typebase + : TYPENAME + { $$ = $1.type; } + | INT_KEYWORD + { $$ = builtin_type_int; } + | LONG + { $$ = builtin_type_long; } + | SHORT + { $$ = builtin_type_short; } + | LONG INT_KEYWORD + { $$ = builtin_type_long; } + | UNSIGNED LONG INT_KEYWORD + { $$ = builtin_type_unsigned_long; } + | LONG LONG + { $$ = builtin_type_long_long; } + | LONG LONG INT_KEYWORD + { $$ = builtin_type_long_long; } + | UNSIGNED LONG LONG + { $$ = builtin_type_unsigned_long_long; } + | UNSIGNED LONG LONG INT_KEYWORD + { $$ = builtin_type_unsigned_long_long; } + | SHORT INT_KEYWORD + { $$ = builtin_type_short; } + | UNSIGNED SHORT INT_KEYWORD + { $$ = builtin_type_unsigned_short; } + | STRUCT name + { $$ = lookup_struct (copy_name ($2), + expression_context_block); } + | UNION name + { $$ = lookup_union (copy_name ($2), + expression_context_block); } + | ENUM name + { $$ = lookup_enum (copy_name ($2), + expression_context_block); } + | UNSIGNED typename + { $$ = lookup_unsigned_typename (TYPE_NAME($2.type)); } + | UNSIGNED + { $$ = builtin_type_unsigned_int; } + | SIGNED typename + { $$ = $2.type; } + | SIGNED + { $$ = builtin_type_int; } + ; + +typename: TYPENAME + | INT_KEYWORD + { + $$.stoken.ptr = "int"; + $$.stoken.length = 3; + $$.type = builtin_type_int; + } + | LONG + { + $$.stoken.ptr = "long"; + $$.stoken.length = 4; + $$.type = builtin_type_long; + } + | SHORT + { + $$.stoken.ptr = "short"; + $$.stoken.length = 5; + $$.type = builtin_type_short; + } + ; + +nonempty_typelist + : type + { $$ = (struct type **)xmalloc (sizeof (struct type *) * 2); + $$[0] = (struct type *)0; + $$[1] = $1; + } + | nonempty_typelist ',' type + { int len = sizeof (struct type *) * ++($1[0]); + $$ = (struct type **)xrealloc ($1, len); + $$[$$[0]] = $3; + } + ; + +name : NAME { $$ = $1.stoken; } + | BLOCKNAME { $$ = $1.stoken; } + | TYPENAME { $$ = $1.stoken; } + | NAME_OR_INT { $$ = $1.stoken; } + | NAME_OR_UINT { $$ = $1.stoken; } + ; + +name_not_typename : NAME + | BLOCKNAME +/* These would be useful if name_not_typename was useful, but it is just + a fake for "variable", so these cause reduce/reduce conflicts because + the parser can't tell whether NAME_OR_INT is a name_not_typename (=variable, + =exp) or just an exp. If name_not_typename was ever used in an lvalue + context where only a name could occur, this might be useful. + | NAME_OR_INT + | NAME_OR_UINT + */ + ; + +%% + +/* Take care of parsing a number (anything that starts with a digit). + Set yylval and return the token type; update lexptr. + LEN is the number of characters in it. */ + +/*** Needs some error checking for the float case ***/ + +static int +parse_number (p, len, parsed_float, putithere) + register char *p; + register int len; + int parsed_float; + YYSTYPE *putithere; +{ + register LONGEST n = 0; + register LONGEST prevn = 0; + register int i; + register int c; + register int base = input_radix; + int unsigned_p = 0; + + extern double atof (); + + if (parsed_float) + { + /* It's a float since it contains a point or an exponent. */ + putithere->dval = atof (p); + return FLOAT; + } + + /* Handle base-switching prefixes 0x, 0t, 0d, 0 */ + if (p[0] == '0') + switch (p[1]) + { + case 'x': + case 'X': + if (len >= 3) + { + p += 2; + base = 16; + len -= 2; + } + break; + + case 't': + case 'T': + case 'd': + case 'D': + if (len >= 3) + { + p += 2; + base = 10; + len -= 2; + } + break; + + default: + base = 8; + break; + } + + while (len-- > 0) + { + c = *p++; + if (c >= 'A' && c <= 'Z') + c += 'a' - 'A'; + if (c != 'l' && c != 'u') + n *= base; + if (c >= '0' && c <= '9') + n += i = c - '0'; + else + { + if (base > 10 && c >= 'a' && c <= 'f') + n += i = c - 'a' + 10; + else if (len == 0 && c == 'l') + ; + else if (len == 0 && c == 'u') + unsigned_p = 1; + else + return ERROR; /* Char not a digit */ + } + if (i >= base) + return ERROR; /* Invalid digit in this base */ + if(!unsigned_p && (prevn >= n)) + unsigned_p=1; /* Try something unsigned */ + /* Don't do the range check if n==i and i==0, since that special + case will give an overflow error. */ + if(RANGE_CHECK && n!=0) + { + if((unsigned_p && (unsigned)prevn >= (unsigned)n)) + range_error("Overflow on numeric constant."); + } + prevn=n; + } + + if (unsigned_p) + { + putithere->ulval = n; + return UINT; + } + else + { + putithere->lval = n; + return INT; + } +} + +struct token +{ + char *operator; + int token; + enum exp_opcode opcode; +}; + +const static struct token tokentab3[] = + { + {">>=", ASSIGN_MODIFY, BINOP_RSH}, + {"<<=", ASSIGN_MODIFY, BINOP_LSH} + }; + +const static struct token tokentab2[] = + { + {"+=", ASSIGN_MODIFY, BINOP_ADD}, + {"-=", ASSIGN_MODIFY, BINOP_SUB}, + {"*=", ASSIGN_MODIFY, BINOP_MUL}, + {"/=", ASSIGN_MODIFY, BINOP_DIV}, + {"%=", ASSIGN_MODIFY, BINOP_REM}, + {"|=", ASSIGN_MODIFY, BINOP_LOGIOR}, + {"&=", ASSIGN_MODIFY, BINOP_LOGAND}, + {"^=", ASSIGN_MODIFY, BINOP_LOGXOR}, + {"++", INCREMENT, BINOP_END}, + {"--", DECREMENT, BINOP_END}, + {"->", ARROW, BINOP_END}, + {"&&", AND, BINOP_END}, + {"||", OR, BINOP_END}, + {"::", COLONCOLON, BINOP_END}, + {"<<", LSH, BINOP_END}, + {">>", RSH, BINOP_END}, + {"==", EQUAL, BINOP_END}, + {"!=", NOTEQUAL, BINOP_END}, + {"<=", LEQ, BINOP_END}, + {">=", GEQ, BINOP_END} + }; + +/* Read one token, getting characters through lexptr. */ + +int +yylex () +{ + register int c; + register int namelen; + register unsigned i; + register char *tokstart; + + retry: + + tokstart = lexptr; + /* See if it is a special token of length 3. */ + for (i = 0; i < sizeof tokentab3 / sizeof tokentab3[0]; i++) + if (!strncmp (tokstart, tokentab3[i].operator, 3)) + { + lexptr += 3; + yylval.opcode = tokentab3[i].opcode; + return tokentab3[i].token; + } + + /* See if it is a special token of length 2. */ + for (i = 0; i < sizeof tokentab2 / sizeof tokentab2[0]; i++) + if (!strncmp (tokstart, tokentab2[i].operator, 2)) + { + lexptr += 2; + yylval.opcode = tokentab2[i].opcode; + return tokentab2[i].token; + } + + switch (c = *tokstart) + { + case 0: + return 0; + + case ' ': + case '\t': + case '\n': + lexptr++; + goto retry; + + case '\'': + lexptr++; + c = *lexptr++; + if (c == '\\') + c = parse_escape (&lexptr); + yylval.lval = c; + c = *lexptr++; + if (c != '\'') + error ("Invalid character constant."); + return CHAR; + + case '(': + paren_depth++; + lexptr++; + return c; + + case ')': + if (paren_depth == 0) + return 0; + paren_depth--; + lexptr++; + return c; + + case ',': + if (comma_terminates && paren_depth == 0) + return 0; + lexptr++; + return c; + + case '.': + /* Might be a floating point number. */ + if (lexptr[1] < '0' || lexptr[1] > '9') + goto symbol; /* Nope, must be a symbol. */ + /* FALL THRU into number case. */ + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + /* It's a number. */ + int got_dot = 0, got_e = 0, toktype; + register char *p = tokstart; + int hex = input_radix > 10; + + if (c == '0' && (p[1] == 'x' || p[1] == 'X')) + { + p += 2; + hex = 1; + } + else if (c == '0' && (p[1]=='t' || p[1]=='T' || p[1]=='d' || p[1]=='D')) + { + p += 2; + hex = 0; + } + + for (;; ++p) + { + if (!hex && !got_e && (*p == 'e' || *p == 'E')) + got_dot = got_e = 1; + else if (!hex && !got_dot && *p == '.') + got_dot = 1; + else if (got_e && (p[-1] == 'e' || p[-1] == 'E') + && (*p == '-' || *p == '+')) + /* This is the sign of the exponent, not the end of the + number. */ + continue; + /* We will take any letters or digits. parse_number will + complain if past the radix, or if L or U are not final. */ + else if ((*p < '0' || *p > '9') + && ((*p < 'a' || *p > 'z') + && (*p < 'A' || *p > 'Z'))) + break; + } + toktype = parse_number (tokstart, p - tokstart, got_dot|got_e, &yylval); + if (toktype == ERROR) + { + char *err_copy = (char *) alloca (p - tokstart + 1); + + bcopy (tokstart, err_copy, p - tokstart); + err_copy[p - tokstart] = 0; + error ("Invalid number \"%s\".", err_copy); + } + lexptr = p; + return toktype; + } + + case '+': + case '-': + case '*': + case '/': + case '%': + case '|': + case '&': + case '^': + case '~': + case '!': + case '@': + case '<': + case '>': + case '[': + case ']': + case '?': + case ':': + case '=': + case '{': + case '}': + symbol: + lexptr++; + return c; + + case '"': + for (namelen = 1; (c = tokstart[namelen]) != '"'; namelen++) + if (c == '\\') + { + c = tokstart[++namelen]; + if (c >= '0' && c <= '9') + { + c = tokstart[++namelen]; + if (c >= '0' && c <= '9') + c = tokstart[++namelen]; + } + } + yylval.sval.ptr = tokstart + 1; + yylval.sval.length = namelen - 1; + lexptr += namelen + 1; + return STRING; + } + + if (!(c == '_' || c == '$' + || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) + /* We must have come across a bad character (e.g. ';'). */ + error ("Invalid character '%c' in expression.", c); + + /* It's a name. See how long it is. */ + namelen = 0; + for (c = tokstart[namelen]; + (c == '_' || c == '$' || (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); + c = tokstart[++namelen]) + ; + + /* The token "if" terminates the expression and is NOT + removed from the input stream. */ + if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f') + { + return 0; + } + + lexptr += namelen; + + /* Handle the tokens $digits; also $ (short for $0) and $$ (short for $$1) + and $$digits (equivalent to $<-digits> if you could type that). + Make token type LAST, and put the number (the digits) in yylval. */ + + if (*tokstart == '$') + { + register int negate = 0; + c = 1; + /* Double dollar means negate the number and add -1 as well. + Thus $$ alone means -1. */ + if (namelen >= 2 && tokstart[1] == '$') + { + negate = 1; + c = 2; + } + if (c == namelen) + { + /* Just dollars (one or two) */ + yylval.lval = - negate; + return LAST; + } + /* Is the rest of the token digits? */ + for (; c < namelen; c++) + if (!(tokstart[c] >= '0' && tokstart[c] <= '9')) + break; + if (c == namelen) + { + yylval.lval = atoi (tokstart + 1 + negate); + if (negate) + yylval.lval = - yylval.lval; + return LAST; + } + } + + /* Handle tokens that refer to machine registers: + $ followed by a register name. */ + + if (*tokstart == '$') { + for (c = 0; c < NUM_REGS; c++) + if (namelen - 1 == strlen (reg_names[c]) + && !strncmp (tokstart + 1, reg_names[c], namelen - 1)) + { + yylval.lval = c; + return REGNAME; + } + for (c = 0; c < num_std_regs; c++) + if (namelen - 1 == strlen (std_regs[c].name) + && !strncmp (tokstart + 1, std_regs[c].name, namelen - 1)) + { + yylval.lval = std_regs[c].regnum; + return REGNAME; + } + } + /* Catch specific keywords. Should be done with a data structure. */ + switch (namelen) + { + case 8: + if (!strncmp (tokstart, "unsigned", 8)) + return UNSIGNED; + break; + case 6: + if (!strncmp (tokstart, "struct", 6)) + return STRUCT; + if (!strncmp (tokstart, "signed", 6)) + return SIGNED; + if (!strncmp (tokstart, "sizeof", 6)) + return SIZEOF; + break; + case 5: + if (!strncmp (tokstart, "union", 5)) + return UNION; + if (!strncmp (tokstart, "short", 5)) + return SHORT; + break; + case 4: + if (!strncmp (tokstart, "enum", 4)) + return ENUM; + if (!strncmp (tokstart, "long", 4)) + return LONG; + if (!strncmp (tokstart, "this", 4)) + { + static const char this_name[] = + { CPLUS_MARKER, 't', 'h', 'i', 's', '\0' }; + + if (lookup_symbol (this_name, expression_context_block, + VAR_NAMESPACE, 0, NULL)) + return THIS; + } + break; + case 3: + if (!strncmp (tokstart, "int", 3)) + return INT_KEYWORD; + break; + default: + break; + } + + yylval.sval.ptr = tokstart; + yylval.sval.length = namelen; + + /* Any other names starting in $ are debugger internal variables. */ + + if (*tokstart == '$') + { + yylval.ivar = lookup_internalvar (copy_name (yylval.sval) + 1); + return VARIABLE; + } + + /* Use token-type BLOCKNAME for symbols that happen to be defined as + functions or symtabs. If this is not so, then ... + Use token-type TYPENAME for symbols that happen to be defined + currently as names of types; NAME for other symbols. + The caller is not constrained to care about the distinction. */ + { + char *tmp = copy_name (yylval.sval); + struct symbol *sym; + int is_a_field_of_this = 0; + int hextype; + + sym = lookup_symbol (tmp, expression_context_block, + VAR_NAMESPACE, &is_a_field_of_this, NULL); + if ((sym && SYMBOL_CLASS (sym) == LOC_BLOCK) || + lookup_partial_symtab (tmp)) + { + yylval.ssym.sym = sym; + yylval.ssym.is_a_field_of_this = is_a_field_of_this; + return BLOCKNAME; + } + if (sym && SYMBOL_CLASS (sym) == LOC_TYPEDEF) + { + yylval.tsym.type = SYMBOL_TYPE (sym); + return TYPENAME; + } + if ((yylval.tsym.type = lookup_primitive_typename (tmp)) != 0) + return TYPENAME; + + /* Input names that aren't symbols but ARE valid hex numbers, + when the input radix permits them, can be names or numbers + depending on the parse. Note we support radixes > 16 here. */ + if (!sym && + ((tokstart[0] >= 'a' && tokstart[0] < 'a' + input_radix - 10) || + (tokstart[0] >= 'A' && tokstart[0] < 'A' + input_radix - 10))) + { + YYSTYPE newlval; /* Its value is ignored. */ + hextype = parse_number (tokstart, namelen, 0, &newlval); + if (hextype == INT) + { + yylval.ssym.sym = sym; + yylval.ssym.is_a_field_of_this = is_a_field_of_this; + return NAME_OR_INT; + } + if (hextype == UINT) + { + yylval.ssym.sym = sym; + yylval.ssym.is_a_field_of_this = is_a_field_of_this; + return NAME_OR_UINT; + } + } + + /* Any other kind of symbol */ + yylval.ssym.sym = sym; + yylval.ssym.is_a_field_of_this = is_a_field_of_this; + return NAME; + } +} + +void +yyerror (msg) + char *msg; +{ + error ("Invalid syntax in expression."); +} + +/* Table mapping opcodes into strings for printing operators + and precedences of the operators. */ + +const static struct op_print c_op_print_tab[] = + { + {",", BINOP_COMMA, PREC_COMMA, 0}, + {"=", BINOP_ASSIGN, PREC_ASSIGN, 1}, + {"||", BINOP_OR, PREC_OR, 0}, + {"&&", BINOP_AND, PREC_AND, 0}, + {"|", BINOP_LOGIOR, PREC_LOGIOR, 0}, + {"&", BINOP_LOGAND, PREC_LOGAND, 0}, + {"^", BINOP_LOGXOR, PREC_LOGXOR, 0}, + {"==", BINOP_EQUAL, PREC_EQUAL, 0}, + {"!=", BINOP_NOTEQUAL, PREC_EQUAL, 0}, + {"<=", BINOP_LEQ, PREC_ORDER, 0}, + {">=", BINOP_GEQ, PREC_ORDER, 0}, + {">", BINOP_GTR, PREC_ORDER, 0}, + {"<", BINOP_LESS, PREC_ORDER, 0}, + {">>", BINOP_RSH, PREC_SHIFT, 0}, + {"<<", BINOP_LSH, PREC_SHIFT, 0}, + {"+", BINOP_ADD, PREC_ADD, 0}, + {"-", BINOP_SUB, PREC_ADD, 0}, + {"*", BINOP_MUL, PREC_MUL, 0}, + {"/", BINOP_DIV, PREC_MUL, 0}, + {"%", BINOP_REM, PREC_MUL, 0}, + {"@", BINOP_REPEAT, PREC_REPEAT, 0}, + {"-", UNOP_NEG, PREC_PREFIX, 0}, + {"!", UNOP_ZEROP, PREC_PREFIX, 0}, + {"~", UNOP_LOGNOT, PREC_PREFIX, 0}, + {"*", UNOP_IND, PREC_PREFIX, 0}, + {"&", UNOP_ADDR, PREC_PREFIX, 0}, + {"sizeof ", UNOP_SIZEOF, PREC_PREFIX, 0}, + {"++", UNOP_PREINCREMENT, PREC_PREFIX, 0}, + {"--", UNOP_PREDECREMENT, PREC_PREFIX, 0}, + /* C++ */ + {"::", BINOP_SCOPE, PREC_PREFIX, 0}, +}; + +/* These variables point to the objects + representing the predefined C data types. */ + +struct type *builtin_type_void; +struct type *builtin_type_char; +struct type *builtin_type_short; +struct type *builtin_type_int; +struct type *builtin_type_long; +struct type *builtin_type_long_long; +struct type *builtin_type_unsigned_char; +struct type *builtin_type_unsigned_short; +struct type *builtin_type_unsigned_int; +struct type *builtin_type_unsigned_long; +struct type *builtin_type_unsigned_long_long; +struct type *builtin_type_float; +struct type *builtin_type_double; + +struct type **(c_builtin_types[]) = +{ + &builtin_type_int, + &builtin_type_long, + &builtin_type_short, + &builtin_type_char, + &builtin_type_float, + &builtin_type_double, + &builtin_type_void, + &builtin_type_long_long, + &builtin_type_unsigned_char, + &builtin_type_unsigned_short, + &builtin_type_unsigned_int, + &builtin_type_unsigned_long, + &builtin_type_unsigned_long_long, + 0 +}; + +/* FIXME: Eventually do a separate defintion for C++. */ + +struct language_defn c_language_defn = { + "c", /* Language name */ + language_c, + c_builtin_types, + range_check_off, + type_check_off, + c_parse, + c_error, + &BUILTIN_TYPE_LONGEST, /* longest signed integral type */ + &BUILTIN_TYPE_UNSIGNED_LONGEST,/* longest unsigned integral type */ + &builtin_type_double, /* longest floating point type */ + "0x%x", "0x%", "x", /* Hex format, prefix, suffix */ + "0%o", "0%", "o", /* Octal format, prefix, suffix */ + c_op_print_tab, /* expression operators for printing */ + LANG_MAGIC +}; + +void +_initialize_c_exp () +{ + /* FIXME: The code below assumes that the sizes of the basic data + types are the same on the host and target machines!!! */ + + builtin_type_void = init_type (TYPE_CODE_VOID, 1, 0, "void"); + + builtin_type_float = init_type (TYPE_CODE_FLT, sizeof (float), 0, "float"); + builtin_type_double = init_type (TYPE_CODE_FLT, sizeof (double), 0, "double"); + + builtin_type_char = init_type (TYPE_CODE_INT, sizeof (char), 0, "char"); + builtin_type_short = init_type (TYPE_CODE_INT, sizeof (short), 0, "short"); + builtin_type_long = init_type (TYPE_CODE_INT, sizeof (long), 0, "long"); + builtin_type_int = init_type (TYPE_CODE_INT, sizeof (int), 0, "int"); + + builtin_type_unsigned_char = init_type (TYPE_CODE_INT, sizeof (char), 1, "unsigned char"); + builtin_type_unsigned_short = init_type (TYPE_CODE_INT, sizeof (short), 1, "unsigned short"); + builtin_type_unsigned_long = init_type (TYPE_CODE_INT, sizeof (long), 1, "unsigned long"); + builtin_type_unsigned_int = init_type (TYPE_CODE_INT, sizeof (int), 1, "unsigned int"); + + builtin_type_long_long = + init_type (TYPE_CODE_INT, TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT, + 0, "long long"); + builtin_type_unsigned_long_long = + init_type (TYPE_CODE_INT, TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT, + 1, "unsigned long long"); + + add_language (&c_language_defn); + set_language (language_c); /* Make C the default language */ +} diff --git a/gdb/m2-exp.y b/gdb/m2-exp.y new file mode 100644 index 00000000000..1a5edacbb61 --- /dev/null +++ b/gdb/m2-exp.y @@ -0,0 +1,1215 @@ +/* YACC grammar for Modula-2 expressions, for GDB. + Copyright (C) 1986, 1989, 1990, 1991 Free Software Foundation, Inc. + Generated from expread.y (now c-exp.y) and contributed by the Department + of Computer Science at the State University of New York at Buffalo, 1991. + +This file is part of GDB. + +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 of the License, 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. */ + +/* Parse a Modula-2 expression from text in a string, + and return the result as a struct expression pointer. + That structure contains arithmetic operations in reverse polish, + with constants represented by operations that are followed by special data. + See expression.h for the details of the format. + What is important here is that it can be built up sequentially + during the process of parsing; the lower levels of the tree always + come first in the result. */ + +%{ +#include +#include +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" +#include "expression.h" +#include "language.h" +#include "parser-defs.h" + +/* These MUST be included in any grammar file!!!! + Please choose unique names! */ +#define yyparse m2_parse +#define yylex m2_lex +#define yyerror m2_error +#define yylval m2_lval +#define yychar m2_char +#define yydebug m2_debug +#define yypact m2_pact +#define yyr1 m2_r1 +#define yyr2 m2_r2 +#define yydef m2_def +#define yychk m2_chk +#define yypgo m2_pgo +#define yyact m2_act +#define yyexca m2_exca + +void yyerror (); + +/* The sign of the number being parsed. */ +int number_sign = 1; + +/* The block that the module specified by the qualifer on an identifer is + contained in, */ +struct block *modblock=0; + +char *make_qualname(); + +/* #define YYDEBUG 1 */ + +%} + +/* Although the yacc "value" of an expression is not used, + since the result is stored in the structure being created, + other node types do have values. */ + +%union + { + LONGEST lval; + unsigned LONGEST ulval; + double dval; + struct symbol *sym; + struct type *tval; + struct stoken sval; + int voidval; + struct block *bval; + enum exp_opcode opcode; + struct internalvar *ivar; + + struct type **tvec; + int *ivec; + } + +%type exp type_exp start set +%type variable +%type type +%type block +%type fblock + +%token INT HEX ERROR +%token UINT TRUE FALSE CHAR +%token FLOAT + +/* Both NAME and TYPENAME tokens represent symbols in the input, + and both convey their data as strings. + But a TYPENAME is a string that happens to be defined as a typedef + or builtin type name (such as int or char) + and a NAME is any other symbol. + + Contexts where this distinction is not important can use the + nonterminal "name", which matches either NAME or TYPENAME. */ + +%token STRING +%token NAME BLOCKNAME IDENT CONST VARNAME +%token TYPENAME + +%token SIZE CAP ORD HIGH ABS MIN MAX FLOAT_FUNC VAL CHR ODD TRUNC +%token INC DEC INCL EXCL + +/* The GDB scope operator */ +%token COLONCOLON + +%token LAST REGNAME + +%token INTERNAL_VAR + +/* M2 tokens */ +%left ',' +%left ABOVE_COMMA +%nonassoc ASSIGN +%left '<' '>' LEQ GEQ '=' NOTEQUAL '#' IN +%left OR +%left AND '&' +%left '@' +%left '+' '-' +%left '*' '/' DIV MOD +%right UNARY +%right '^' DOT '[' '(' +%right NOT '~' +%left COLONCOLON QID +/* This is not an actual token ; it is used for precedence. +%right QID +*/ +%% + +start : exp + | type_exp + ; + +type_exp: type + { write_exp_elt_opcode(OP_TYPE); + write_exp_elt_type($1); + write_exp_elt_opcode(OP_TYPE); + } + ; + +/* Expressions */ + +exp : exp '^' %prec UNARY + { write_exp_elt_opcode (UNOP_IND); } + +exp : '-' + { number_sign = -1; } + exp %prec UNARY + { number_sign = 1; + write_exp_elt_opcode (UNOP_NEG); } + ; + +exp : '+' exp %prec UNARY + { write_exp_elt_opcode(UNOP_PLUS); } + ; + +exp : not_exp exp %prec UNARY + { write_exp_elt_opcode (UNOP_ZEROP); } + ; + +not_exp : NOT + | '~' + ; + +exp : CAP '(' exp ')' + { write_exp_elt_opcode (UNOP_CAP); } + ; + +exp : ORD '(' exp ')' + { write_exp_elt_opcode (UNOP_ORD); } + ; + +exp : ABS '(' exp ')' + { write_exp_elt_opcode (UNOP_ABS); } + ; + +exp : HIGH '(' exp ')' + { write_exp_elt_opcode (UNOP_HIGH); } + ; + +exp : MIN '(' type ')' + { write_exp_elt_opcode (UNOP_MIN); + write_exp_elt_type ($3); + write_exp_elt_opcode (UNOP_MIN); } + ; + +exp : MAX '(' type ')' + { write_exp_elt_opcode (UNOP_MAX); + write_exp_elt_type ($3); + write_exp_elt_opcode (UNOP_MIN); } + ; + +exp : FLOAT_FUNC '(' exp ')' + { write_exp_elt_opcode (UNOP_FLOAT); } + ; + +exp : VAL '(' type ',' exp ')' + { write_exp_elt_opcode (BINOP_VAL); + write_exp_elt_type ($3); + write_exp_elt_opcode (BINOP_VAL); } + ; + +exp : CHR '(' exp ')' + { write_exp_elt_opcode (UNOP_CHR); } + ; + +exp : ODD '(' exp ')' + { write_exp_elt_opcode (UNOP_ODD); } + ; + +exp : TRUNC '(' exp ')' + { write_exp_elt_opcode (UNOP_TRUNC); } + ; + +exp : SIZE exp %prec UNARY + { write_exp_elt_opcode (UNOP_SIZEOF); } + ; + + +exp : INC '(' exp ')' + { write_exp_elt_opcode(UNOP_PREINCREMENT); } + ; + +exp : INC '(' exp ',' exp ')' + { write_exp_elt_opcode(BINOP_ASSIGN_MODIFY); + write_exp_elt_opcode(BINOP_ADD); + write_exp_elt_opcode(BINOP_ASSIGN_MODIFY); } + ; + +exp : DEC '(' exp ')' + { write_exp_elt_opcode(UNOP_PREDECREMENT);} + ; + +exp : DEC '(' exp ',' exp ')' + { write_exp_elt_opcode(BINOP_ASSIGN_MODIFY); + write_exp_elt_opcode(BINOP_SUB); + write_exp_elt_opcode(BINOP_ASSIGN_MODIFY); } + ; + +exp : exp DOT NAME + { write_exp_elt_opcode (STRUCTOP_STRUCT); + write_exp_string ($3); + write_exp_elt_opcode (STRUCTOP_STRUCT); } + ; + +exp : set + ; + +exp : exp IN set + { error("Sets are not implemented.");} + ; + +exp : INCL '(' exp ',' exp ')' + { error("Sets are not implemented.");} + ; + +exp : EXCL '(' exp ',' exp ')' + { error("Sets are not implemented.");} + +set : '{' arglist '}' + { error("Sets are not implemented.");} + | type '{' arglist '}' + { error("Sets are not implemented.");} + ; + + +/* Modula-2 array subscript notation [a,b,c...] */ +exp : exp '[' + /* This function just saves the number of arguments + that follow in the list. It is *not* specific to + function types */ + { start_arglist(); } + non_empty_arglist ']' %prec DOT + { write_exp_elt_opcode (BINOP_MULTI_SUBSCRIPT); + write_exp_elt_longcst ((LONGEST) end_arglist()); + write_exp_elt_opcode (BINOP_MULTI_SUBSCRIPT); } + ; + +exp : exp '(' + /* This is to save the value of arglist_len + being accumulated by an outer function call. */ + { start_arglist (); } + arglist ')' %prec DOT + { write_exp_elt_opcode (OP_FUNCALL); + write_exp_elt_longcst ((LONGEST) end_arglist ()); + write_exp_elt_opcode (OP_FUNCALL); } + ; + +arglist : + ; + +arglist : exp + { arglist_len = 1; } + ; + +arglist : arglist ',' exp %prec ABOVE_COMMA + { arglist_len++; } + ; + +non_empty_arglist + : exp + { arglist_len = 1; } + ; + +non_empty_arglist + : non_empty_arglist ',' exp %prec ABOVE_COMMA + { arglist_len++; } + ; + +/* GDB construct */ +exp : '{' type '}' exp %prec UNARY + { write_exp_elt_opcode (UNOP_MEMVAL); + write_exp_elt_type ($2); + write_exp_elt_opcode (UNOP_MEMVAL); } + ; + +exp : type '(' exp ')' %prec UNARY + { write_exp_elt_opcode (UNOP_CAST); + write_exp_elt_type ($1); + write_exp_elt_opcode (UNOP_CAST); } + ; + +exp : '(' exp ')' + { } + ; + +/* Binary operators in order of decreasing precedence. Note that some + of these operators are overloaded! (ie. sets) */ + +/* GDB construct */ +exp : exp '@' exp + { write_exp_elt_opcode (BINOP_REPEAT); } + ; + +exp : exp '*' exp + { write_exp_elt_opcode (BINOP_MUL); } + ; + +exp : exp '/' exp + { write_exp_elt_opcode (BINOP_DIV); } + ; + +exp : exp DIV exp + { write_exp_elt_opcode (BINOP_INTDIV); } + ; + +exp : exp MOD exp + { write_exp_elt_opcode (BINOP_REM); } + ; + +exp : exp '+' exp + { write_exp_elt_opcode (BINOP_ADD); } + ; + +exp : exp '-' exp + { write_exp_elt_opcode (BINOP_SUB); } + ; + +exp : exp '=' exp + { write_exp_elt_opcode (BINOP_EQUAL); } + ; + +exp : exp NOTEQUAL exp + { write_exp_elt_opcode (BINOP_NOTEQUAL); } + | exp '#' exp + { write_exp_elt_opcode (BINOP_NOTEQUAL); } + ; + +exp : exp LEQ exp + { write_exp_elt_opcode (BINOP_LEQ); } + ; + +exp : exp GEQ exp + { write_exp_elt_opcode (BINOP_GEQ); } + ; + +exp : exp '<' exp + { write_exp_elt_opcode (BINOP_LESS); } + ; + +exp : exp '>' exp + { write_exp_elt_opcode (BINOP_GTR); } + ; + +exp : exp AND exp + { write_exp_elt_opcode (BINOP_AND); } + ; + +exp : exp '&' exp + { write_exp_elt_opcode (BINOP_AND); } + ; + +exp : exp OR exp + { write_exp_elt_opcode (BINOP_OR); } + ; + +exp : exp ASSIGN exp + { write_exp_elt_opcode (BINOP_ASSIGN); } + ; + + +/* Constants */ + +exp : TRUE + { write_exp_elt_opcode (OP_BOOL); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_BOOL); } + ; + +exp : FALSE + { write_exp_elt_opcode (OP_BOOL); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_BOOL); } + ; + +exp : INT + { write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type_m2_int); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_LONG); } + ; + +exp : UINT + { + write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type_m2_card); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_LONG); + } + ; + +exp : CHAR + { write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type_m2_char); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_LONG); } + ; + + +exp : FLOAT + { write_exp_elt_opcode (OP_DOUBLE); + write_exp_elt_type (builtin_type_m2_real); + write_exp_elt_dblcst ($1); + write_exp_elt_opcode (OP_DOUBLE); } + ; + +exp : variable + ; + +/* The GDB internal variable $$, et al. */ +exp : LAST + { write_exp_elt_opcode (OP_LAST); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_LAST); } + ; + +exp : REGNAME + { write_exp_elt_opcode (OP_REGISTER); + write_exp_elt_longcst ((LONGEST) $1); + write_exp_elt_opcode (OP_REGISTER); } + ; + +exp : SIZE '(' type ')' %prec UNARY + { write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type_int); + write_exp_elt_longcst ((LONGEST) TYPE_LENGTH ($3)); + write_exp_elt_opcode (OP_LONG); } + ; + +exp : STRING + { write_exp_elt_opcode (OP_M2_STRING); + write_exp_string ($1); + write_exp_elt_opcode (OP_M2_STRING); } + ; + +/* This will be used for extensions later. Like adding modules. */ +block : fblock + { $$ = SYMBOL_BLOCK_VALUE($1); } + ; + +fblock : BLOCKNAME + { struct symbol *sym + = lookup_symbol (copy_name ($1), expression_context_block, + VAR_NAMESPACE, 0, NULL); + $$ = sym;} + ; + + +/* GDB scope operator */ +fblock : block COLONCOLON BLOCKNAME + { struct symbol *tem + = lookup_symbol (copy_name ($3), $1, + VAR_NAMESPACE, 0, NULL); + if (!tem || SYMBOL_CLASS (tem) != LOC_BLOCK) + error ("No function \"%s\" in specified context.", + copy_name ($3)); + $$ = tem; + } + ; + +/* Useful for assigning to PROCEDURE variables */ +variable: fblock + { write_exp_elt_opcode(OP_VAR_VALUE); + write_exp_elt_sym ($1); + write_exp_elt_opcode (OP_VAR_VALUE); } + ; + +/* GDB internal ($foo) variable */ +variable: INTERNAL_VAR + { write_exp_elt_opcode (OP_INTERNALVAR); + write_exp_elt_intern ($1); + write_exp_elt_opcode (OP_INTERNALVAR); } + ; + +/* GDB scope operator */ +variable: block COLONCOLON NAME + { struct symbol *sym; + sym = lookup_symbol (copy_name ($3), $1, + VAR_NAMESPACE, 0, NULL); + if (sym == 0) + error ("No symbol \"%s\" in specified context.", + copy_name ($3)); + + write_exp_elt_opcode (OP_VAR_VALUE); + write_exp_elt_sym (sym); + write_exp_elt_opcode (OP_VAR_VALUE); } + ; + +/* Base case for variables. */ +variable: NAME + { struct symbol *sym; + int is_a_field_of_this; + + sym = lookup_symbol (copy_name ($1), + expression_context_block, + VAR_NAMESPACE, + &is_a_field_of_this, + NULL); + if (sym) + { + switch (sym->class) + { + case LOC_REGISTER: + case LOC_ARG: + case LOC_LOCAL: + if (innermost_block == 0 || + contained_in (block_found, + innermost_block)) + innermost_block = block_found; + } + write_exp_elt_opcode (OP_VAR_VALUE); + write_exp_elt_sym (sym); + write_exp_elt_opcode (OP_VAR_VALUE); + } + else + { + register int i; + register char *arg = copy_name ($1); + + for (i = 0; i < misc_function_count; i++) + if (!strcmp (misc_function_vector[i].name, arg)) + break; + + if (i < misc_function_count) + { + enum misc_function_type mft = + (enum misc_function_type) + misc_function_vector[i].type; + + write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type_int); + write_exp_elt_longcst ((LONGEST) misc_function_vector[i].address); + write_exp_elt_opcode (OP_LONG); + write_exp_elt_opcode (UNOP_MEMVAL); + if (mft == mf_data || mft == mf_bss) + write_exp_elt_type (builtin_type_int); + else if (mft == mf_text) + write_exp_elt_type (lookup_function_type (builtin_type_int)); + else + write_exp_elt_type (builtin_type_char); + write_exp_elt_opcode (UNOP_MEMVAL); + } + else if (symtab_list == 0 + && partial_symtab_list == 0) + error ("No symbol table is loaded. Use the \"symbol-file\" command."); + else + error ("No symbol \"%s\" in current context.", + copy_name ($1)); + } + } + ; + +type + : TYPENAME + { $$ = lookup_typename (copy_name ($1), + expression_context_block, 0); } + + ; + +%% + +#if 0 /* FIXME! */ +int +overflow(a,b) + long a,b; +{ + return (MAX_OF_TYPE(builtin_type_m2_int) - b) < a; +} + +int +uoverflow(a,b) + unsigned long a,b; +{ + return (MAX_OF_TYPE(builtin_type_m2_card) - b) < a; +} +#endif /* FIXME */ + +/* Take care of parsing a number (anything that starts with a digit). + Set yylval and return the token type; update lexptr. + LEN is the number of characters in it. */ + +/*** Needs some error checking for the float case ***/ + +static int +parse_number (olen) + int olen; +{ + register char *p = lexptr; + register LONGEST n = 0; + register LONGEST prevn = 0; + register int c,i,ischar=0; + register int base = input_radix; + register int len = olen; + char *err_copy; + int unsigned_p = number_sign == 1 ? 1 : 0; + + extern double atof (); + + if(p[len-1] == 'H') + { + base = 16; + len--; + } + else if(p[len-1] == 'C' || p[len-1] == 'B') + { + base = 8; + ischar = p[len-1] == 'C'; + len--; + } + + /* Scan the number */ + for (c = 0; c < len; c++) + { + if (p[c] == '.' && base == 10) + { + /* It's a float since it contains a point. */ + yylval.dval = atof (p); + lexptr += len; + return FLOAT; + } + if (p[c] == '.' && base != 10) + error("Floating point numbers must be base 10."); + if (base == 10 && (p[c] < '0' || p[c] > '9')) + error("Invalid digit \'%c\' in number.",p[c]); + } + + while (len-- > 0) + { + c = *p++; + n *= base; + if( base == 8 && (c == '8' || c == '9')) + error("Invalid digit \'%c\' in octal number.",c); + if (c >= '0' && c <= '9') + i = c - '0'; + else + { + if (base == 16 && c >= 'A' && c <= 'F') + i = c - 'A' + 10; + else + return ERROR; + } + n+=i; + if(i >= base) + return ERROR; + if(!unsigned_p && number_sign == 1 && (prevn >= n)) + unsigned_p=1; /* Try something unsigned */ + /* Don't do the range check if n==i and i==0, since that special + case will give an overflow error. */ + if(RANGE_CHECK && n!=i && i) + { + if((unsigned_p && (unsigned)prevn >= (unsigned)n) || + ((!unsigned_p && number_sign==-1) && -prevn <= -n)) + range_error("Overflow on numeric constant."); + } + prevn=n; + } + + lexptr = p; + if(*p == 'B' || *p == 'C' || *p == 'H') + lexptr++; /* Advance past B,C or H */ + + if (ischar) + { + yylval.ulval = n; + return CHAR; + } + else if ( unsigned_p && number_sign == 1) + { + yylval.ulval = n; + return UINT; + } + else if((unsigned_p && (n<0))) + range_error("Overflow on numeric constant -- number too large."); + else + { + yylval.lval = n; + return INT; + } +} + + +/* Some tokens */ + +static struct +{ + char name[2]; + int token; +} tokentab2[] = +{ + {"<>", NOTEQUAL }, + {":=", ASSIGN }, + {"<=", LEQ }, + {">=", GEQ }, + {"::", COLONCOLON }, + +}; + +/* Some specific keywords */ + +struct keyword { + char keyw[10]; + int token; +}; + +static struct keyword keytab[] = +{ + {"OR" , OR }, + {"IN", IN },/* Note space after IN */ + {"AND", AND }, + {"ABS", ABS }, + {"CHR", CHR }, + {"DEC", DEC }, + {"NOT", NOT }, + {"DIV", DIV }, + {"INC", INC }, + {"MAX", MAX }, + {"MIN", MIN }, + {"MOD", MOD }, + {"ODD", ODD }, + {"CAP", CAP }, + {"ORD", ORD }, + {"VAL", VAL }, + {"EXCL", EXCL }, + {"HIGH", HIGH }, + {"INCL", INCL }, + {"SIZE", SIZE }, + {"FLOAT", FLOAT_FUNC }, + {"TRUNC", TRUNC }, +}; + + +/* Read one token, getting characters through lexptr. */ + +/* This is where we will check to make sure that the language and the operators used are + compatible */ + +static int +yylex () +{ + register int c; + register int namelen; + register int i; + register char *tokstart; + register char quote; + + retry: + + tokstart = lexptr; + + + /* See if it is a special token of length 2 */ + for( i = 0 ; i < sizeof tokentab2 / sizeof tokentab2[0] ; i++) + if(!strncmp(tokentab2[i].name, tokstart, 2)) + { + lexptr += 2; + return tokentab2[i].token; + } + + switch (c = *tokstart) + { + case 0: + return 0; + + case ' ': + case '\t': + case '\n': + lexptr++; + goto retry; + + case '(': + paren_depth++; + lexptr++; + return c; + + case ')': + if (paren_depth == 0) + return 0; + paren_depth--; + lexptr++; + return c; + + case ',': + if (comma_terminates && paren_depth == 0) + return 0; + lexptr++; + return c; + + case '.': + /* Might be a floating point number. */ + if (lexptr[1] >= '0' && lexptr[1] <= '9') + break; /* Falls into number code. */ + else + { + lexptr++; + return DOT; + } + +/* These are character tokens that appear as-is in the YACC grammar */ + case '+': + case '-': + case '*': + case '/': + case '^': + case '<': + case '>': + case '[': + case ']': + case '=': + case '{': + case '}': + case '#': + case '@': + case '~': + case '&': + lexptr++; + return c; + + case '\'' : + case '"': + quote = c; + for (namelen = 1; (c = tokstart[namelen]) != quote && c != '\0'; namelen++) + if (c == '\\') + { + c = tokstart[++namelen]; + if (c >= '0' && c <= '9') + { + c = tokstart[++namelen]; + if (c >= '0' && c <= '9') + c = tokstart[++namelen]; + } + } + if(c != quote) + error("Unterminated string or character constant."); + yylval.sval.ptr = tokstart + 1; + yylval.sval.length = namelen - 1; + lexptr += namelen + 1; + + if(namelen == 2) /* Single character */ + { + yylval.ulval = tokstart[1]; + return CHAR; + } + else + return STRING; + } + + /* Is it a number? */ + /* Note: We have already dealt with the case of the token '.'. + See case '.' above. */ + if ((c >= '0' && c <= '9')) + { + /* It's a number. */ + int got_dot = 0, got_e = 0; + register char *p = tokstart; + int toktype; + + for (++p ;; ++p) + { + if (!got_e && (*p == 'e' || *p == 'E')) + got_dot = got_e = 1; + else if (!got_dot && *p == '.') + got_dot = 1; + else if (got_e && (p[-1] == 'e' || p[-1] == 'E') + && (*p == '-' || *p == '+')) + /* This is the sign of the exponent, not the end of the + number. */ + continue; + else if ((*p < '0' || *p > '9') && + (*p < 'A' || *p > 'F') && + (*p != 'H')) /* Modula-2 hexadecimal number */ + break; + } + toktype = parse_number (p - tokstart); + if (toktype == ERROR) + { + char *err_copy = (char *) alloca (p - tokstart + 1); + + bcopy (tokstart, err_copy, p - tokstart); + err_copy[p - tokstart] = 0; + error ("Invalid number \"%s\".", err_copy); + } + lexptr = p; + return toktype; + } + + if (!(c == '_' || c == '$' + || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) + /* We must have come across a bad character (e.g. ';'). */ + error ("Invalid character '%c' in expression.", c); + + /* It's a name. See how long it is. */ + namelen = 0; + for (c = tokstart[namelen]; + (c == '_' || c == '$' || (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); + c = tokstart[++namelen]) + ; + + /* The token "if" terminates the expression and is NOT + removed from the input stream. */ + if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f') + { + return 0; + } + + lexptr += namelen; + + /* Handle the tokens $digits; also $ (short for $0) and $$ (short for $$1) + and $$digits (equivalent to $<-digits> if you could type that). + Make token type LAST, and put the number (the digits) in yylval. */ + + if (*tokstart == '$') + { + register int negate = 0; + c = 1; + /* Double dollar means negate the number and add -1 as well. + Thus $$ alone means -1. */ + if (namelen >= 2 && tokstart[1] == '$') + { + negate = 1; + c = 2; + } + if (c == namelen) + { + /* Just dollars (one or two) */ + yylval.lval = - negate; + return LAST; + } + /* Is the rest of the token digits? */ + for (; c < namelen; c++) + if (!(tokstart[c] >= '0' && tokstart[c] <= '9')) + break; + if (c == namelen) + { + yylval.lval = atoi (tokstart + 1 + negate); + if (negate) + yylval.lval = - yylval.lval; + return LAST; + } + } + + /* Handle tokens that refer to machine registers: + $ followed by a register name. */ + + if (*tokstart == '$') { + for (c = 0; c < NUM_REGS; c++) + if (namelen - 1 == strlen (reg_names[c]) + && !strncmp (tokstart + 1, reg_names[c], namelen - 1)) + { + yylval.lval = c; + return REGNAME; + } + for (c = 0; c < num_std_regs; c++) + if (namelen - 1 == strlen (std_regs[c].name) + && !strncmp (tokstart + 1, std_regs[c].name, namelen - 1)) + { + yylval.lval = std_regs[c].regnum; + return REGNAME; + } + } + + + /* Lookup special keywords */ + for(i = 0 ; i < sizeof(keytab) / sizeof(keytab[0]) ; i++) + if(namelen == strlen(keytab[i].keyw) && !strncmp(tokstart,keytab[i].keyw,namelen)) + return keytab[i].token; + + yylval.sval.ptr = tokstart; + yylval.sval.length = namelen; + + /* Any other names starting in $ are debugger internal variables. */ + + if (*tokstart == '$') + { + yylval.ivar = (struct internalvar *) lookup_internalvar (copy_name (yylval.sval) + 1); + return INTERNAL_VAR; + } + + + /* Use token-type BLOCKNAME for symbols that happen to be defined as + functions. If this is not so, then ... + Use token-type TYPENAME for symbols that happen to be defined + currently as names of types; NAME for other symbols. + The caller is not constrained to care about the distinction. */ + { + + + char *tmp = copy_name (yylval.sval); + struct symbol *sym; + + if (lookup_partial_symtab (tmp)) + return BLOCKNAME; + sym = lookup_symbol (tmp, expression_context_block, + VAR_NAMESPACE, 0, NULL); + if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK) + return BLOCKNAME; + if (lookup_typename (copy_name (yylval.sval), expression_context_block, 1)) + return TYPENAME; + + if(sym) + { + switch(sym->class) + { + case LOC_STATIC: + case LOC_REGISTER: + case LOC_ARG: + case LOC_REF_ARG: + case LOC_REGPARM: + case LOC_LOCAL: + case LOC_LOCAL_ARG: + case LOC_CONST: + case LOC_CONST_BYTES: + return NAME; + + case LOC_TYPEDEF: + return TYPENAME; + + case LOC_BLOCK: + return BLOCKNAME; + + case LOC_UNDEF: + error("internal: Undefined class in m2lex()"); + + case LOC_LABEL: + error("internal: Unforseen case in m2lex()"); + } + } + else + { + /* Built-in BOOLEAN type. This is sort of a hack. */ + if(!strncmp(tokstart,"TRUE",4)) + { + yylval.ulval = 1; + return TRUE; + } + else if(!strncmp(tokstart,"FALSE",5)) + { + yylval.ulval = 0; + return FALSE; + } + } + + /* Must be another type of name... */ + return NAME; + } +} + +char * +make_qualname(mod,ident) + char *mod, *ident; +{ + char *new = xmalloc(strlen(mod)+strlen(ident)+2); + + strcpy(new,mod); + strcat(new,"."); + strcat(new,ident); + return new; +} + + +void +yyerror() +{ + printf("Parsing: %s\n",lexptr); + if (yychar < 256) + error("Invalid syntax in expression near character '%c'.",yychar); + else + error("Invalid syntax in expression near a '%s'.", + yytname[yychar-255]); +} + +/* Table of operators and their precedences for printing expressions. */ + +const static struct op_print m2_op_print_tab[] = { + {"+", BINOP_ADD, PREC_ADD, 0}, + {"+", UNOP_PLUS, PREC_PREFIX, 0}, + {"-", BINOP_SUB, PREC_ADD, 0}, + {"-", UNOP_NEG, PREC_PREFIX, 0}, + {"*", BINOP_MUL, PREC_MUL, 0}, + {"/", BINOP_DIV, PREC_MUL, 0}, + {"DIV", BINOP_INTDIV, PREC_MUL, 0}, + {"MOD", BINOP_REM, PREC_MUL, 0}, + {":=", BINOP_ASSIGN, PREC_ASSIGN, 1}, + {"OR", BINOP_OR, PREC_OR, 0}, + {"AND", BINOP_AND, PREC_AND, 0}, + {"NOT", UNOP_ZEROP, PREC_PREFIX, 0}, + {"=", BINOP_EQUAL, PREC_EQUAL, 0}, + {"<>", BINOP_NOTEQUAL, PREC_EQUAL, 0}, + {"<=", BINOP_LEQ, PREC_ORDER, 0}, + {">=", BINOP_GEQ, PREC_ORDER, 0}, + {">", BINOP_GTR, PREC_ORDER, 0}, + {"<", BINOP_LESS, PREC_ORDER, 0}, + {"^", UNOP_IND, PREC_PREFIX, 0}, + {"@", BINOP_REPEAT, PREC_REPEAT, 0}, +}; + +/* The built-in types of Modula-2. */ + +struct type *builtin_type_m2_char; +struct type *builtin_type_m2_int; +struct type *builtin_type_m2_card; +struct type *builtin_type_m2_real; +struct type *builtin_type_m2_bool; + +struct type **(m2_builtin_types[]) = +{ + &builtin_type_m2_char, + &builtin_type_m2_int, + &builtin_type_m2_card, + &builtin_type_m2_real, + &builtin_type_m2_bool, + 0 +}; + +struct language_defn m2_language_defn = { + "modula-2", + language_m2, + &m2_builtin_types[0], + range_check_on, + type_check_on, + m2_parse, /* parser */ + m2_error, /* parser error function */ + &builtin_type_m2_int, /* longest signed integral type */ + &builtin_type_m2_card, /* longest unsigned integral type */ + &builtin_type_m2_real, /* longest floating point type */ + "0%XH", "0%", "XH", /* Hex format string, prefix, suffix */ + "%oB", "%", "oB", /* Octal format string, prefix, suffix */ + m2_op_print_tab, /* expression operators for printing */ + LANG_MAGIC +}; + +/* Initialization for Modula-2 */ + +void +_initialize_m2_exp () +{ + /* FIXME: The code below assumes that the sizes of the basic data + types are the same on the host and target machines!!! */ + + /* Modula-2 "pervasive" types. NOTE: these can be redefined!!! */ + builtin_type_m2_int = init_type (TYPE_CODE_INT, sizeof(int), 0, "INTEGER"); + builtin_type_m2_card = init_type (TYPE_CODE_INT, sizeof(int), 1, "CARDINAL"); + builtin_type_m2_real = init_type (TYPE_CODE_FLT, sizeof(float), 0, "REAL"); + builtin_type_m2_char = init_type (TYPE_CODE_CHAR, sizeof(char), 1, "CHAR"); + + builtin_type_m2_bool = init_type (TYPE_CODE_BOOL, sizeof(int), 1, "BOOLEAN"); + TYPE_NFIELDS(builtin_type_m2_bool) = 2; + TYPE_FIELDS(builtin_type_m2_bool) = + (struct field *) malloc (sizeof (struct field) * 2); + TYPE_FIELD_BITPOS(builtin_type_m2_bool,0) = 0; + TYPE_FIELD_NAME(builtin_type_m2_bool,0) = (char *)malloc(6); + strcpy(TYPE_FIELD_NAME(builtin_type_m2_bool,0),"FALSE"); + TYPE_FIELD_BITPOS(builtin_type_m2_bool,1) = 1; + TYPE_FIELD_NAME(builtin_type_m2_bool,1) = (char *)malloc(5); + strcpy(TYPE_FIELD_NAME(builtin_type_m2_bool,1),"TRUE"); + + add_language (&m2_language_defn); +} diff --git a/gdb/parse.c b/gdb/parse.c new file mode 100644 index 00000000000..214df03b6b5 --- /dev/null +++ b/gdb/parse.c @@ -0,0 +1,628 @@ +/* Parse expressions for GDB. + Copyright (C) 1986, 1989, 1990, 1991 Free Software Foundation, Inc. + Modified from expread.y by the Department of Computer Science at the + State University of New York at Buffalo, 1991. + +This file is part of GDB. + +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 of the License, 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. */ + +/* Parse an expression from text in a string, + and return the result as a struct expression pointer. + That structure contains arithmetic operations in reverse polish, + with constants represented by operations that are followed by special data. + See expression.h for the details of the format. + What is important here is that it can be built up sequentially + during the process of parsing; the lower levels of the tree always + come first in the result. */ + +#include +#include "defs.h" +#include "param.h" +#include "symtab.h" +#include "frame.h" +#include "expression.h" +#include "value.h" +#include "command.h" +#include "language.h" +#include "parser-defs.h" + +/* Assign machine-independent names to certain registers + (unless overridden by the REGISTER_NAMES table) */ + +struct std_regs std_regs[] = { +#ifdef PC_REGNUM + { "pc", PC_REGNUM }, +#endif +#ifdef FP_REGNUM + { "fp", FP_REGNUM }, +#endif +#ifdef SP_REGNUM + { "sp", SP_REGNUM }, +#endif +#ifdef PS_REGNUM + { "ps", PS_REGNUM }, +#endif +}; + +unsigned num_std_regs = (sizeof std_regs / sizeof std_regs[0]); + + +/* Begin counting arguments for a function call, + saving the data about any containing call. */ + +void +start_arglist () +{ + register struct funcall *new = (struct funcall *) xmalloc (sizeof (struct funcall)); + + new->next = funcall_chain; + new->arglist_len = arglist_len; + arglist_len = 0; + funcall_chain = new; +} + +/* Return the number of arguments in a function call just terminated, + and restore the data for the containing function call. */ + +int +end_arglist () +{ + register int val = arglist_len; + register struct funcall *call = funcall_chain; + funcall_chain = call->next; + arglist_len = call->arglist_len; + free (call); + return val; +} + +/* Free everything in the funcall chain. + Used when there is an error inside parsing. */ + +void +free_funcalls () +{ + register struct funcall *call, *next; + + for (call = funcall_chain; call; call = next) + { + next = call->next; + free (call); + } +} + +/* This page contains the functions for adding data to the struct expression + being constructed. */ + +/* Add one element to the end of the expression. */ + +/* To avoid a bug in the Sun 4 compiler, we pass things that can fit into + a register through here */ + +void +write_exp_elt (expelt) + union exp_element expelt; +{ + if (expout_ptr >= expout_size) + { + expout_size *= 2; + expout = (struct expression *) xrealloc (expout, + sizeof (struct expression) + + expout_size * sizeof (union exp_element)); + } + expout->elts[expout_ptr++] = expelt; +} + +void +write_exp_elt_opcode (expelt) + enum exp_opcode expelt; +{ + union exp_element tmp; + + tmp.opcode = expelt; + + write_exp_elt (tmp); +} + +void +write_exp_elt_sym (expelt) + struct symbol *expelt; +{ + union exp_element tmp; + + tmp.symbol = expelt; + + write_exp_elt (tmp); +} + +void +write_exp_elt_longcst (expelt) + LONGEST expelt; +{ + union exp_element tmp; + + tmp.longconst = expelt; + + write_exp_elt (tmp); +} + +void +write_exp_elt_dblcst (expelt) + double expelt; +{ + union exp_element tmp; + + tmp.doubleconst = expelt; + + write_exp_elt (tmp); +} + +void +write_exp_elt_type (expelt) + struct type *expelt; +{ + union exp_element tmp; + + tmp.type = expelt; + + write_exp_elt (tmp); +} + +void +write_exp_elt_intern (expelt) + struct internalvar *expelt; +{ + union exp_element tmp; + + tmp.internalvar = expelt; + + write_exp_elt (tmp); +} + +/* Add a string constant to the end of the expression. + Follow it by its length in bytes, as a separate exp_element. */ + +void +write_exp_string (str) + struct stoken str; +{ + register int len = str.length; + register int lenelt + = (len + sizeof (union exp_element)) / sizeof (union exp_element); + + expout_ptr += lenelt; + + if (expout_ptr >= expout_size) + { + expout_size = max (expout_size * 2, expout_ptr + 10); + expout = (struct expression *) + xrealloc (expout, (sizeof (struct expression) + + (expout_size * sizeof (union exp_element)))); + } + bcopy (str.ptr, (char *) &expout->elts[expout_ptr - lenelt], len); + ((char *) &expout->elts[expout_ptr - lenelt])[len] = 0; + write_exp_elt_longcst ((LONGEST) len); +} + +/* Return a null-terminated temporary copy of the name + of a string token. */ + +char * +copy_name (token) + struct stoken token; +{ + bcopy (token.ptr, namecopy, token.length); + namecopy[token.length] = 0; + return namecopy; +} + +/* Reverse an expression from suffix form (in which it is constructed) + to prefix form (in which we can conveniently print or execute it). */ + +static void prefixify_subexp (); + +void +prefixify_expression (expr) + register struct expression *expr; +{ + register int len = sizeof (struct expression) + + expr->nelts * sizeof (union exp_element); + register struct expression *temp; + register int inpos = expr->nelts, outpos = 0; + + temp = (struct expression *) alloca (len); + + /* Copy the original expression into temp. */ + bcopy (expr, temp, len); + + prefixify_subexp (temp, expr, inpos, outpos); +} + +/* Return the number of exp_elements in the subexpression of EXPR + whose last exp_element is at index ENDPOS - 1 in EXPR. */ + +int +length_of_subexp (expr, endpos) + register struct expression *expr; + register int endpos; +{ + register int oplen = 1; + register int args = 0; + register int i; + + if (endpos < 0) + error ("?error in length_of_subexp"); + + i = (int) expr->elts[endpos - 1].opcode; + + switch (i) + { + /* C++ */ + case OP_SCOPE: + oplen = 4 + ((expr->elts[endpos - 2].longconst + + sizeof (union exp_element)) + / sizeof (union exp_element)); + break; + + case OP_LONG: + case OP_DOUBLE: + oplen = 4; + break; + + case OP_TYPE: + case OP_BOOL: + case OP_VAR_VALUE: + case OP_LAST: + case OP_REGISTER: + case OP_INTERNALVAR: + oplen = 3; + break; + + case OP_FUNCALL: + oplen = 3; + args = 1 + expr->elts[endpos - 2].longconst; + break; + + case UNOP_MAX: + case UNOP_MIN: + oplen = 3; + args = 0; + break; + + case BINOP_VAL: + case UNOP_CAST: + case UNOP_MEMVAL: + oplen = 3; + args = 1; + break; + + case UNOP_ABS: + case UNOP_CAP: + case UNOP_CHR: + case UNOP_FLOAT: + case UNOP_HIGH: + case UNOP_ODD: + case UNOP_ORD: + case UNOP_TRUNC: + oplen = 1; + args = 1; + break; + + case STRUCTOP_STRUCT: + case STRUCTOP_PTR: + args = 1; + case OP_M2_STRING: + case OP_STRING: + oplen = 3 + ((expr->elts[endpos - 2].longconst + + sizeof (union exp_element)) + / sizeof (union exp_element)); + break; + + case TERNOP_COND: + args = 3; + break; + + /* Modula-2 */ + case BINOP_MULTI_SUBSCRIPT: + oplen=3; + args = 1 + expr->elts[endpos- 2].longconst; + break; + + case BINOP_ASSIGN_MODIFY: + oplen = 3; + args = 2; + break; + + /* C++ */ + case OP_THIS: + oplen = 2; + break; + + default: + args = 1 + (i < (int) BINOP_END); + } + + while (args > 0) + { + oplen += length_of_subexp (expr, endpos - oplen); + args--; + } + + return oplen; +} + +/* Copy the subexpression ending just before index INEND in INEXPR + into OUTEXPR, starting at index OUTBEG. + In the process, convert it from suffix to prefix form. */ + +static void +prefixify_subexp (inexpr, outexpr, inend, outbeg) + register struct expression *inexpr; + struct expression *outexpr; + register int inend; + int outbeg; +{ + register int oplen = 1; + register int args = 0; + register int i; + int *arglens; + enum exp_opcode opcode; + + /* Compute how long the last operation is (in OPLEN), + and also how many preceding subexpressions serve as + arguments for it (in ARGS). */ + + opcode = inexpr->elts[inend - 1].opcode; + switch (opcode) + { + /* C++ */ + case OP_SCOPE: + oplen = 4 + ((inexpr->elts[inend - 2].longconst + + sizeof (union exp_element)) + / sizeof (union exp_element)); + break; + + case OP_LONG: + case OP_DOUBLE: + oplen = 4; + break; + + case OP_TYPE: + case OP_BOOL: + case OP_VAR_VALUE: + case OP_LAST: + case OP_REGISTER: + case OP_INTERNALVAR: + oplen = 3; + break; + + case OP_FUNCALL: + oplen = 3; + args = 1 + inexpr->elts[inend - 2].longconst; + break; + + case UNOP_MIN: + case UNOP_MAX: + oplen = 3; + args = 0; + break; + + case UNOP_CAST: + case UNOP_MEMVAL: + oplen = 3; + args = 1; + break; + + case UNOP_ABS: + case UNOP_CAP: + case UNOP_CHR: + case UNOP_FLOAT: + case UNOP_HIGH: + case UNOP_ODD: + case UNOP_ORD: + case UNOP_TRUNC: + oplen=1; + args=1; + break; + + case STRUCTOP_STRUCT: + case STRUCTOP_PTR: + args = 1; + case OP_M2_STRING: + case OP_STRING: + oplen = 3 + ((inexpr->elts[inend - 2].longconst + + sizeof (union exp_element)) + / sizeof (union exp_element)); + + break; + + case TERNOP_COND: + args = 3; + break; + + case BINOP_ASSIGN_MODIFY: + oplen = 3; + args = 2; + break; + + /* Modula-2 */ + case BINOP_MULTI_SUBSCRIPT: + oplen=3; + args = 1 + inexpr->elts[inend - 2].longconst; + break; + + /* C++ */ + case OP_THIS: + oplen = 2; + break; + + default: + args = 1 + ((int) opcode < (int) BINOP_END); + } + + /* Copy the final operator itself, from the end of the input + to the beginning of the output. */ + inend -= oplen; + bcopy (&inexpr->elts[inend], &outexpr->elts[outbeg], + oplen * sizeof (union exp_element)); + outbeg += oplen; + + /* Find the lengths of the arg subexpressions. */ + arglens = (int *) alloca (args * sizeof (int)); + for (i = args - 1; i >= 0; i--) + { + oplen = length_of_subexp (inexpr, inend); + arglens[i] = oplen; + inend -= oplen; + } + + /* Now copy each subexpression, preserving the order of + the subexpressions, but prefixifying each one. + In this loop, inend starts at the beginning of + the expression this level is working on + and marches forward over the arguments. + outbeg does similarly in the output. */ + for (i = 0; i < args; i++) + { + oplen = arglens[i]; + inend += oplen; + prefixify_subexp (inexpr, outexpr, inend, outbeg); + outbeg += oplen; + } +} + +/* This page contains the two entry points to this file. */ + +/* Read an expression from the string *STRINGPTR points to, + parse it, and return a pointer to a struct expression that we malloc. + Use block BLOCK as the lexical context for variable names; + if BLOCK is zero, use the block of the selected stack frame. + Meanwhile, advance *STRINGPTR to point after the expression, + at the first nonwhite character that is not part of the expression + (possibly a null character). + + If COMMA is nonzero, stop if a comma is reached. */ + +struct expression * +parse_exp_1 (stringptr, block, comma) + char **stringptr; + struct block *block; + int comma; +{ + struct cleanup *old_chain; + + lexptr = *stringptr; + + paren_depth = 0; + type_stack_depth = 0; + + comma_terminates = comma; + + if (lexptr == 0 || *lexptr == 0) + error_no_arg ("expression to compute"); + + old_chain = make_cleanup (free_funcalls, 0); + funcall_chain = 0; + + expression_context_block = block ? block : get_selected_block (); + + namecopy = (char *) alloca (strlen (lexptr) + 1); + expout_size = 10; + expout_ptr = 0; + expout = (struct expression *) + xmalloc (sizeof (struct expression) + + expout_size * sizeof (union exp_element)); + expout->language_defn = current_language; + make_cleanup (free_current_contents, &expout); + + if (current_language->la_parser ()) + current_language->la_error (NULL); + + discard_cleanups (old_chain); + expout->nelts = expout_ptr; + expout = (struct expression *) + xrealloc (expout, + sizeof (struct expression) + + expout_ptr * sizeof (union exp_element)); + prefixify_expression (expout); + *stringptr = lexptr; + return expout; +} + +/* Parse STRING as an expression, and complain if this fails + to use up all of the contents of STRING. */ + +struct expression * +parse_expression (string) + char *string; +{ + register struct expression *exp; + exp = parse_exp_1 (&string, 0, 0); + if (*string) + error ("Junk after end of expression."); + return exp; +} + +void +push_type (tp) + enum type_pieces tp; +{ + if (type_stack_depth == type_stack_size) + { + type_stack_size *= 2; + type_stack = (union type_stack_elt *) + xrealloc (type_stack, type_stack_size * sizeof (*type_stack)); + } + type_stack[type_stack_depth++].piece = tp; +} + +void +push_type_int (n) + int n; +{ + if (type_stack_depth == type_stack_size) + { + type_stack_size *= 2; + type_stack = (union type_stack_elt *) + xrealloc (type_stack, type_stack_size * sizeof (*type_stack)); + } + type_stack[type_stack_depth++].int_val = n; +} + +enum type_pieces +pop_type () +{ + if (type_stack_depth) + return type_stack[--type_stack_depth].piece; + return tp_end; +} + +int +pop_type_int () +{ + if (type_stack_depth) + return type_stack[--type_stack_depth].int_val; + /* "Can't happen". */ + return 0; +} + +void +_initialize_parse () +{ + type_stack_size = 80; + type_stack_depth = 0; + type_stack = (union type_stack_elt *) + xmalloc (type_stack_size * sizeof (*type_stack)); +} diff --git a/gdb/parser-defs.h b/gdb/parser-defs.h new file mode 100644 index 00000000000..c5c8077d219 --- /dev/null +++ b/gdb/parser-defs.h @@ -0,0 +1,162 @@ +/* Parser definitions for GDB. + Copyright (C) 1986, 1989, 1990, 1991 Free Software Foundation, Inc. + Modified from expread.y by the Department of Computer Science at the + State University of New York at Buffalo. + +This file is part of GDB. + +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 of the License, 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. */ + +struct std_regs { + char *name; + int regnum; +}; + +extern struct std_regs std_regs[]; +extern unsigned num_std_regs; + +struct expression *expout; +int expout_size; +int expout_ptr; + +extern struct type *init_type (); + +void write_exp_elt (); +void write_exp_elt_opcode (); +void write_exp_elt_sym (); +void write_exp_elt_longcst (); +void write_exp_elt_dblcst (); +void write_exp_elt_type (); +void write_exp_elt_intern (); +void write_exp_string (); +void start_arglist (); +int end_arglist (); +void free_funcalls (); +char *copy_name (); + +/* If this is nonzero, this block is used as the lexical context + for symbol names. */ + +struct block *expression_context_block; + +/* The innermost context required by the stack and register variables + we've encountered so far. */ +struct block *innermost_block; + +/* The block in which the most recently discovered symbol was found. */ +struct block *block_found; + +/* Number of arguments seen so far in innermost function call. */ +int arglist_len; + +/* Data structure for saving values of arglist_len + for function calls whose arguments contain other function calls. */ + +struct funcall + { + struct funcall *next; + int arglist_len; + }; + +struct funcall *funcall_chain; + +/* This kind of datum is used to represent the name + of a symbol token. */ + +struct stoken + { + char *ptr; + int length; + }; + +struct ttype + { + struct stoken stoken; + struct type *type; + }; + +struct symtoken + { + struct stoken stoken; + struct symbol *sym; + int is_a_field_of_this; + }; + +/* For parsing of complicated types. + An array should be preceded in the list by the size of the array. */ +enum type_pieces + {tp_end = -1, tp_pointer, tp_reference, tp_array, tp_function}; +/* The stack can contain either an enum type_pieces or an int. */ +union type_stack_elt { + enum type_pieces piece; + int int_val; +}; +union type_stack_elt *type_stack; +int type_stack_depth, type_stack_size; + +void push_type (); +void push_type_int (); +enum type_pieces pop_type (); +int pop_type_int (); + +/* During parsing of a C expression, the pointer to the next character + is in this variable. */ + +char *lexptr; + +/* Tokens that refer to names do so with explicit pointer and length, + so they can share the storage that lexptr is parsing. + + When it is necessary to pass a name to a function that expects + a null-terminated string, the substring is copied out + into a block of storage that namecopy points to. + + namecopy is allocated once, guaranteed big enough, for each parsing. */ + +char *namecopy; + +/* Current depth in parentheses within the expression. */ + +int paren_depth; + +/* Nonzero means stop parsing on first comma (if not within parentheses). */ + +int comma_terminates; + +/* These codes indicate operator precedences for expression printing, + least tightly binding first. */ +/* Adding 1 to a precedence value is done for binary operators, + on the operand which is more tightly bound, so that operators + of equal precedence within that operand will get parentheses. */ +/* PREC_HYPER and PREC_ABOVE_COMMA are not the precedence of any operator; + they are used as the "surrounding precedence" to force + various kinds of things to be parenthesized. */ +enum precedence +{ PREC_NULL, PREC_COMMA, PREC_ABOVE_COMMA, PREC_ASSIGN, PREC_OR, PREC_AND, + PREC_LOGIOR, PREC_LOGAND, PREC_LOGXOR, PREC_EQUAL, PREC_ORDER, + PREC_SHIFT, PREC_ADD, PREC_MUL, PREC_REPEAT, + PREC_HYPER, PREC_PREFIX, PREC_SUFFIX }; + +/* Table mapping opcodes into strings for printing operators + and precedences of the operators. */ + +struct op_print +{ + char *string; + enum exp_opcode opcode; + /* Precedence of operator. These values are used only by comparisons. */ + enum precedence precedence; + int right_assoc; +}; -- 2.30.2