mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 12:00:11 -05:00
This commit adds support for modules publicizing the exports of other
modules. For example:
module GRAMP =
access pub GRAMP_Symbol,
pub GRAMP_Word,
pub GRAMP_Alphabet
def pub string libgramp_version = "1.0";
skip
fed
Signed-off-by: Jose E. Marchesi <jemarch@gnu.org>
gcc/algol68/ChangeLog
* a68-parser-taxes.cc (tax_module_dec): Do not handle
DEFINING_MODULE_INDICANT.
* a68-exports.cc (a68_add_module_to_moif): Do not mangle module
names in module extracts.
(add_pub_revelations_to_moif): New function.
(a68_do_exports): Simplify and call add_pub_revelations_to_moif.
* a68-imports.cc (a68_decode_moifs): Add all decoded moifs to the
global list TOP_MOIF.
* a68-parser-extract.cc (extract_revelation): Recurse to import
extracts from publicized modules.
(a68_extract_indicants): Do not add symbol table entries for
defining modules.
* a68-types.h (struct TAG_T): Remove field EXPORTED.
(EXPORTED): Remove macro.
(TOP_MOIF): Define.
* a68-parser.cc (a68_parser): Initialize global list of moifs.
(a68_new_tag): Do not initialize EXPORTED.
gcc/testsuite/ChangeLog
* algol68/execute/modules/module22bar.a68: New test.
* algol68/execute/modules/module22foo.a68: Likewise.
* algol68/execute/modules/program-22.a68: Likewise.
* algol68/compile/modules/program-11.a68: Adjust test to
publicized modules.
* algol68/compile/modules/program-error-multiple-delaration-module-1.a68:
Likewise.
1179 lines
27 KiB
C++
1179 lines
27 KiB
C++
/* ALGOL 68 parser.
|
|
Copyright (C) 2001-2023 J. Marcel van der Veer.
|
|
Copyright (C) 2025 Jose E. Marchesi.
|
|
|
|
Original implementation by J. Marcel van der Veer.
|
|
Adapted for GCC by Jose E. Marchesi.
|
|
|
|
GCC 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 3, or (at your option)
|
|
any later version.
|
|
|
|
GCC 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 GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
/*
|
|
This is a Mailloux-type parser driver.
|
|
|
|
The Algol 68 grammar is a two level (Van Wijngaarden, "VW") grammar
|
|
that incorporates, as syntactical rules, the semantical rules in
|
|
other languages. Examples are correct use of symbols, modes and
|
|
scope.
|
|
|
|
This code constitutes an effective "VW Algol 68 parser". A
|
|
pragmatic approach was chosen since in the early days of Algol 68,
|
|
many "ab initio" implementations failed, probably because
|
|
techniques to parse a language like Algol 68 had yet to be
|
|
invented.
|
|
|
|
This is a Mailloux-type parser, in the sense that it scans a
|
|
"phrase" for definitions needed for parsing. Algol 68 allows for
|
|
tags to be used before they are defined, which gives freedom in
|
|
top-down programming.
|
|
|
|
B. J. Mailloux. On the implementation of Algol 68.
|
|
Thesis, Universiteit van Amsterdam (Mathematisch Centrum) [1968].
|
|
|
|
Technically, Mailloux's approach renders the two-level grammar
|
|
LALR.
|
|
|
|
First part of the parser is the scanner. The source file is read, is
|
|
tokenised. The result is a linear list of tokens that is input for the
|
|
parser, that will transform the linear list into a syntax tree.
|
|
|
|
This front-end tokenises all symbols before the bottom-up parser is invoked.
|
|
This means that scanning does not use information from the parser. The
|
|
scanner does of course some rudimentary parsing.
|
|
|
|
The scanner supports two stropping regimes: "bold" (or "upper") and
|
|
"quote". Examples of both:
|
|
|
|
bold stropping: BEGIN INT i = 1, j = 1; print (i + j) END
|
|
|
|
quote stropping: 'BEGIN' 'INT' I = 1, J = 1; PRINT (I + J) 'END'
|
|
|
|
Quote stropping was used frequently in the (excusez-le-mot)
|
|
punch-card age. Hence, bold stropping is the default. There also
|
|
existed point stropping, but that has not been implemented here.
|
|
|
|
Next part of the parser is a recursive-descent type to check
|
|
parenthesis. Also a first set-up is made of symbol tables, needed
|
|
by the bottom-up parser. Next part is the bottom-up parser, that
|
|
parses without knowing modes while parsing and reducing. It can
|
|
therefore not exchange "[]" with "()" as was blessed by the Revised
|
|
Report. This is solved by treating CALL and SLICE as equivalent for
|
|
the moment and letting the mode checker sort it out later.
|
|
|
|
Parsing progresses in various phases to avoid spurious diagnostics
|
|
from a recovering parser. Every phase "tightens" the grammar more.
|
|
An error in any phase makes the parser quit when that phase ends.
|
|
The parser is forgiving in case of superfluous semicolons.
|
|
|
|
These are the parser phases:
|
|
|
|
(1) Parenthesis are checked to see whether they match. Then, a top-down
|
|
parser determines the basic-block structure of the program
|
|
so symbol tables can be set up that the bottom-up parser will consult
|
|
as you can define things before they are applied.
|
|
|
|
(2) A bottom-up parser resolves the structure of the program.
|
|
|
|
(3) After the symbol tables have been finalised, a small rearrangement of the
|
|
tree may be required where JUMPs have no GOTO. This leads to the
|
|
non-standard situation that JUMPs without GOTO can have the syntactic
|
|
position of a PRIMARY, SECONDARY or TERTIARY. The bottom-up parser also
|
|
does not check VICTAL correctness of declarers. This is done separately.
|
|
|
|
The parser sets up symbol tables and populates them as far as needed to parse
|
|
the source. After the bottom-up parser terminates succesfully, the symbol tables
|
|
are completed.
|
|
|
|
(4) Next, modes are collected and rules for well-formedness and structural
|
|
equivalence are applied. Then the symbol-table is completed now moids are
|
|
all known.
|
|
|
|
(5) Next phases are the mode checker and coercion inserter. The syntax tree is
|
|
traversed to determine and check all modes, and to select operators. Then
|
|
the tree is traversed again to insert coercions.
|
|
|
|
(6) A static scope checker detects where objects are transported out of scope.
|
|
At run time, a dynamic scope checker will check that what the static scope
|
|
checker cannot see.
|
|
|
|
(7) A serial-clause dynamic stack allocation (DSA) phase annotates the
|
|
serial clauses that contain phrases whose elaboration may result in
|
|
dynamic stack adjustments.
|
|
*/
|
|
|
|
#define INCLUDE_MEMORY
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "diagnostic.h"
|
|
#include "tree.h"
|
|
|
|
#include "a68.h"
|
|
|
|
/* Global state kept by the parser. */
|
|
|
|
PARSER_T a68_parser_state;
|
|
|
|
/* A few forward declarations of functions defined below. */
|
|
|
|
static void make_special_mode (MOID_T ** n, int m);
|
|
static void tie_label_to_serial (NODE_T *p);
|
|
static void tie_label_to_unit (NODE_T *p);
|
|
|
|
/* Is_ref_refety_flex. */
|
|
|
|
bool
|
|
a68_is_ref_refety_flex (MOID_T *m)
|
|
{
|
|
if (IS_REF_FLEX (m))
|
|
return true;
|
|
else if (IS_REF (m))
|
|
return a68_is_ref_refety_flex (SUB (m));
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/* Count number of operands in operator parameter list. */
|
|
|
|
int
|
|
a68_count_operands (NODE_T *p)
|
|
{
|
|
if (p != NO_NODE)
|
|
{
|
|
if (IS (p, DECLARER))
|
|
return a68_count_operands (NEXT (p));
|
|
else if (IS (p, COMMA_SYMBOL))
|
|
return 1 + a68_count_operands (NEXT (p));
|
|
else
|
|
return a68_count_operands (NEXT (p)) + a68_count_operands (SUB (p));
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Count formal bounds in declarer in tree. */
|
|
|
|
int
|
|
a68_count_formal_bounds (NODE_T * p)
|
|
{
|
|
if (p == NO_NODE)
|
|
return 0;
|
|
else
|
|
{
|
|
if (IS (p, COMMA_SYMBOL))
|
|
return 1;
|
|
else
|
|
return a68_count_formal_bounds (NEXT (p)) + a68_count_formal_bounds (SUB (p));
|
|
}
|
|
}
|
|
|
|
/* Count pictures. */
|
|
|
|
void
|
|
a68_count_pictures (NODE_T *p, int *k)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (IS (p, PICTURE))
|
|
(*k)++;
|
|
a68_count_pictures (SUB (p), k);
|
|
}
|
|
}
|
|
|
|
/* Whether token cannot follow semicolon or EXIT. */
|
|
|
|
bool
|
|
a68_is_semicolon_less (NODE_T *p)
|
|
{
|
|
switch (ATTRIBUTE (p))
|
|
{
|
|
case BUS_SYMBOL:
|
|
case CLOSE_SYMBOL:
|
|
case END_SYMBOL:
|
|
case SEMI_SYMBOL:
|
|
case EXIT_SYMBOL:
|
|
case THEN_BAR_SYMBOL:
|
|
case ELSE_BAR_SYMBOL:
|
|
case THEN_SYMBOL:
|
|
case ELIF_SYMBOL:
|
|
case ELSE_SYMBOL:
|
|
case FI_SYMBOL:
|
|
case IN_SYMBOL:
|
|
case OUT_SYMBOL:
|
|
case OUSE_SYMBOL:
|
|
case ESAC_SYMBOL:
|
|
case OD_SYMBOL:
|
|
case FED_SYMBOL:
|
|
case POSTLUDE_SYMBOL:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Whether formal bounds. */
|
|
|
|
bool
|
|
a68_is_formal_bounds (NODE_T *p)
|
|
{
|
|
if (p == NO_NODE)
|
|
return true;
|
|
|
|
switch (ATTRIBUTE (p))
|
|
{
|
|
case OPEN_SYMBOL:
|
|
case CLOSE_SYMBOL:
|
|
case SUB_SYMBOL:
|
|
case BUS_SYMBOL:
|
|
case COMMA_SYMBOL:
|
|
case COLON_SYMBOL:
|
|
case INT_DENOTATION:
|
|
case IDENTIFIER:
|
|
case OPERATOR:
|
|
return (a68_is_formal_bounds (SUB (p))
|
|
&& a68_is_formal_bounds (NEXT (p)));
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Whether token terminates a unit. */
|
|
|
|
bool
|
|
a68_is_unit_terminator (NODE_T *p)
|
|
{
|
|
switch (ATTRIBUTE (p))
|
|
{
|
|
case BUS_SYMBOL:
|
|
case CLOSE_SYMBOL:
|
|
case END_SYMBOL:
|
|
case SEMI_SYMBOL:
|
|
case EXIT_SYMBOL:
|
|
case COMMA_SYMBOL:
|
|
case THEN_BAR_SYMBOL:
|
|
case ELSE_BAR_SYMBOL:
|
|
case THEN_SYMBOL:
|
|
case ELIF_SYMBOL:
|
|
case ELSE_SYMBOL:
|
|
case FI_SYMBOL:
|
|
case IN_SYMBOL:
|
|
case OUT_SYMBOL:
|
|
case OUSE_SYMBOL:
|
|
case ESAC_SYMBOL:
|
|
case FED_SYMBOL:
|
|
case POSTLUDE_SYMBOL:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Whether token is a unit-terminator in a loop clause. */
|
|
|
|
bool
|
|
a68_is_loop_keyword (NODE_T *p)
|
|
{
|
|
switch (ATTRIBUTE (p))
|
|
{
|
|
case FOR_SYMBOL:
|
|
case FROM_SYMBOL:
|
|
case BY_SYMBOL:
|
|
case TO_SYMBOL:
|
|
case WHILE_SYMBOL:
|
|
case DO_SYMBOL:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Get good attribute. */
|
|
|
|
enum a68_attribute
|
|
a68_get_good_attribute (NODE_T *p)
|
|
{
|
|
switch (ATTRIBUTE (p))
|
|
{
|
|
case UNIT:
|
|
case TERTIARY:
|
|
case SECONDARY:
|
|
case PRIMARY:
|
|
return a68_get_good_attribute (SUB (p));
|
|
default:
|
|
return ATTRIBUTE (p);
|
|
}
|
|
}
|
|
|
|
/* Preferably don't put intelligible diagnostic here. */
|
|
|
|
bool
|
|
a68_dont_mark_here (NODE_T *p)
|
|
{
|
|
switch (ATTRIBUTE (p))
|
|
{
|
|
case ALT_DO_SYMBOL:
|
|
case ALT_EQUALS_SYMBOL:
|
|
case ANDF_SYMBOL:
|
|
case ASSERT_SYMBOL:
|
|
case ASSIGN_SYMBOL:
|
|
case ASSIGN_TO_SYMBOL:
|
|
case AT_SYMBOL:
|
|
case BEGIN_SYMBOL:
|
|
case BITS_SYMBOL:
|
|
case BOLD_COMMENT_SYMBOL:
|
|
case BOLD_PRAGMAT_SYMBOL:
|
|
case BOLD_COMMENT_BEGIN_SYMBOL:
|
|
case BOLD_COMMENT_END_SYMBOL:
|
|
case BOOL_SYMBOL:
|
|
case BUS_SYMBOL:
|
|
case BY_SYMBOL:
|
|
case BYTES_SYMBOL:
|
|
case CASE_SYMBOL:
|
|
case CHANNEL_SYMBOL:
|
|
case CHAR_SYMBOL:
|
|
case CLOSE_SYMBOL:
|
|
case COLON_SYMBOL:
|
|
case COMMA_SYMBOL:
|
|
case COMPLEX_SYMBOL:
|
|
case COMPL_SYMBOL:
|
|
case DO_SYMBOL:
|
|
case ELIF_SYMBOL:
|
|
case ELSE_BAR_SYMBOL:
|
|
case ELSE_SYMBOL:
|
|
case EMPTY_SYMBOL:
|
|
case END_SYMBOL:
|
|
case EQUALS_SYMBOL:
|
|
case ESAC_SYMBOL:
|
|
case EXIT_SYMBOL:
|
|
case FALSE_SYMBOL:
|
|
case FILE_SYMBOL:
|
|
case FI_SYMBOL:
|
|
case FLEX_SYMBOL:
|
|
case FOR_SYMBOL:
|
|
case FROM_SYMBOL:
|
|
case GO_SYMBOL:
|
|
case GOTO_SYMBOL:
|
|
case HEAP_SYMBOL:
|
|
case IF_SYMBOL:
|
|
case IN_SYMBOL:
|
|
case INT_SYMBOL:
|
|
case ISNT_SYMBOL:
|
|
case IS_SYMBOL:
|
|
case LOC_SYMBOL:
|
|
case LONG_SYMBOL:
|
|
case MAIN_SYMBOL:
|
|
case MODE_SYMBOL:
|
|
case NIL_SYMBOL:
|
|
case OD_SYMBOL:
|
|
case OF_SYMBOL:
|
|
case OPEN_SYMBOL:
|
|
case OP_SYMBOL:
|
|
case ORF_SYMBOL:
|
|
case OUSE_SYMBOL:
|
|
case OUT_SYMBOL:
|
|
case PAR_SYMBOL:
|
|
case POINT_SYMBOL:
|
|
case PRIO_SYMBOL:
|
|
case PROC_SYMBOL:
|
|
case REAL_SYMBOL:
|
|
case REF_SYMBOL:
|
|
case ROWS_SYMBOL:
|
|
case ROW_SYMBOL:
|
|
case SEMA_SYMBOL:
|
|
case SEMI_SYMBOL:
|
|
case SHORT_SYMBOL:
|
|
case SKIP_SYMBOL:
|
|
case STRING_SYMBOL:
|
|
case STRUCT_SYMBOL:
|
|
case STYLE_I_COMMENT_SYMBOL:
|
|
case STYLE_II_COMMENT_SYMBOL:
|
|
case STYLE_I_PRAGMAT_SYMBOL:
|
|
case SUB_SYMBOL:
|
|
case THEN_BAR_SYMBOL:
|
|
case THEN_SYMBOL:
|
|
case TO_SYMBOL:
|
|
case TRUE_SYMBOL:
|
|
case UNION_SYMBOL:
|
|
case VOID_SYMBOL:
|
|
case WHILE_SYMBOL:
|
|
case SERIAL_CLAUSE:
|
|
case ENQUIRY_CLAUSE:
|
|
case INITIALISER_SERIES:
|
|
case DECLARATION_LIST:
|
|
case DEF_SYMBOL:
|
|
case FED_SYMBOL:
|
|
case POSTLUDE_SYMBOL:
|
|
case ACCESS_SYMBOL:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Renumber nodes in the given subtree P, starting with number N. */
|
|
|
|
static void
|
|
renumber_nodes (NODE_T *p, int *n)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
NUMBER (p) = (*n)++;
|
|
renumber_nodes (SUB (p), n);
|
|
}
|
|
}
|
|
|
|
/* Parse an ALGOL 68 source file. */
|
|
|
|
void
|
|
a68_parser (const char *filename)
|
|
{
|
|
int renum = 0;
|
|
|
|
/* Initialisation. */
|
|
A68 (top_keyword) = NO_KEYWORD;
|
|
A68 (top_token) = NO_TOKEN;
|
|
A68_PARSER (error_tag) = (TAG_T *) a68_new_tag ();
|
|
TOP_NODE (&A68_JOB) = NO_NODE;
|
|
TOP_MOID (&A68_JOB) = NO_MOID;
|
|
TOP_MOIF (&A68_JOB) = NO_MOIF;
|
|
TOP_LINE (&A68_JOB) = NO_LINE;
|
|
STANDENV_MOID (&A68_JOB) = NO_MOID;
|
|
a68_set_up_tables ();
|
|
ERROR_COUNT (&A68_JOB) = WARNING_COUNT (&A68_JOB) = 0;
|
|
|
|
/* Tokeniser. */
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
{
|
|
bool empty_program;
|
|
bool ok = a68_lexical_analyser (filename, &empty_program);
|
|
|
|
if (!ok)
|
|
return;
|
|
|
|
if (empty_program)
|
|
a68_error (NO_NODE,
|
|
"particular program or prelude packet not found in source file");
|
|
|
|
TREE_LISTING_SAFE (&A68_JOB) = true;
|
|
renum = 0;
|
|
renumber_nodes (TOP_NODE (&A68_JOB), &renum);
|
|
}
|
|
|
|
/* Final initialisations. */
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
{
|
|
A68_STANDENV = NO_TABLE;
|
|
a68_init_postulates ();
|
|
A68 (mode_count) = 0;
|
|
make_special_mode (&M_HIP, A68 (mode_count)++);
|
|
make_special_mode (&M_UNDEFINED, A68 (mode_count)++);
|
|
make_special_mode (&M_ERROR, A68 (mode_count)++);
|
|
make_special_mode (&M_VACUUM, A68 (mode_count)++);
|
|
make_special_mode (&M_C_STRING, A68 (mode_count)++);
|
|
make_special_mode (&M_COLLITEM, A68 (mode_count)++);
|
|
}
|
|
|
|
/* Handle pragmats. */
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
a68_handle_pragmats (TOP_NODE (&A68_JOB));
|
|
|
|
/* Top-down parser. */
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
{
|
|
a68_check_parenthesis (TOP_NODE (&A68_JOB));
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
{
|
|
if (OPTION_BRACKETS (&A68_JOB))
|
|
a68_substitute_brackets (TOP_NODE (&A68_JOB));
|
|
A68 (symbol_table_count) = 0;
|
|
A68_STANDENV = a68_new_symbol_table (NO_TABLE);
|
|
LEVEL (A68_STANDENV) = 0;
|
|
a68_top_down_parser (TOP_NODE (&A68_JOB));
|
|
// printf ("AFTER TOP-DOWN\n");
|
|
// a68_dump_parse_tree (TOP_NODE (&A68_JOB));
|
|
}
|
|
|
|
renum = 0;
|
|
renumber_nodes (TOP_NODE (&A68_JOB), &renum);
|
|
}
|
|
|
|
/* Standard environment builder. */
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
{
|
|
TABLE (TOP_NODE (&A68_JOB)) = a68_new_symbol_table (A68_STANDENV);
|
|
a68_make_standard_environ ();
|
|
STANDENV_MOID (&A68_JOB) = TOP_MOID (&A68_JOB);
|
|
}
|
|
|
|
/* Bottom-up parser. */
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
{
|
|
a68_preliminary_symbol_table_setup (TOP_NODE (&A68_JOB));
|
|
// printf ("AFTER PRELIMINARY SYMBOL TABLE SETUP\n");
|
|
// a68_dump_parse_tree (TOP_NODE (&A68_JOB), true);
|
|
a68_bottom_up_parser (TOP_NODE (&A68_JOB));
|
|
renum = 0;
|
|
renumber_nodes (TOP_NODE (&A68_JOB), &renum);
|
|
}
|
|
|
|
// printf ("AFTER BOTTOM-UP\n");
|
|
// a68_dump_parse_tree (TOP_NODE (&A68_JOB), true);
|
|
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
{
|
|
a68_bottom_up_error_check (TOP_NODE (&A68_JOB));
|
|
a68_victal_checker (TOP_NODE (&A68_JOB));
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
{
|
|
a68_finalise_symbol_table_setup (TOP_NODE (&A68_JOB), 1);
|
|
// printf ("AFTER FINALISE SYMBOL TABLE SETUP\n");
|
|
// a68_dump_parse_tree (TOP_NODE (&A68_JOB), true, true);
|
|
NEST (TABLE (TOP_NODE (&A68_JOB))) = A68 (symbol_table_count) = 3;
|
|
a68_reset_symbol_table_nest_count (TOP_NODE (&A68_JOB));
|
|
a68_fill_symbol_table_outer (TOP_NODE (&A68_JOB), TABLE (TOP_NODE (&A68_JOB)));
|
|
a68_set_nest (TOP_NODE (&A68_JOB), NO_NODE);
|
|
a68_set_proc_level (TOP_NODE (&A68_JOB), 1);
|
|
}
|
|
renum = 0;
|
|
renumber_nodes (TOP_NODE (&A68_JOB), &renum);
|
|
}
|
|
|
|
/* Mode table builder. */
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
a68_make_moid_list (&A68_JOB);
|
|
CROSS_REFERENCE_SAFE (&A68_JOB) = true;
|
|
|
|
/* Symbol table builder. */
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
a68_collect_taxes (TOP_NODE (&A68_JOB));
|
|
|
|
/* Post parser. */
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
a68_rearrange_goto_less_jumps (TOP_NODE (&A68_JOB));
|
|
|
|
/* Mode checker. */
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
a68_mode_checker (TOP_NODE (&A68_JOB));
|
|
|
|
/* Coercion inserter. */
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
{
|
|
a68_coercion_inserter (TOP_NODE (&A68_JOB));
|
|
renum = 0;
|
|
renumber_nodes (TOP_NODE (&A68_JOB), &renum);
|
|
}
|
|
|
|
/* Application checker. */
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
{
|
|
a68_mark_moids (TOP_NODE (&A68_JOB));
|
|
a68_mark_auxilliary (TOP_NODE (&A68_JOB));
|
|
a68_jumps_from_procs (TOP_NODE (&A68_JOB));
|
|
a68_warn_for_unused_tags (TOP_NODE (&A68_JOB));
|
|
}
|
|
|
|
/* Static scope checker. */
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
{
|
|
tie_label_to_serial (TOP_NODE (&A68_JOB));
|
|
tie_label_to_unit (TOP_NODE (&A68_JOB));
|
|
a68_bind_routine_tags_to_tree (TOP_NODE (&A68_JOB));
|
|
a68_scope_checker (TOP_NODE (&A68_JOB));
|
|
}
|
|
|
|
/* Serial dynamic stack allocation checker. */
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
{
|
|
a68_serial_dsa (TOP_NODE (&A68_JOB));
|
|
}
|
|
|
|
/* Finalise syntax tree. */
|
|
if (ERROR_COUNT (&A68_JOB) == 0)
|
|
{
|
|
int num = 0;
|
|
renumber_nodes (TOP_NODE (&A68_JOB), &num);
|
|
NEST (TABLE (TOP_NODE (&A68_JOB))) = A68 (symbol_table_count) = 3;
|
|
a68_reset_symbol_table_nest_count (TOP_NODE (&A68_JOB));
|
|
}
|
|
}
|
|
|
|
/* New_node_info. */
|
|
|
|
NODE_INFO_T *
|
|
a68_new_node_info (void)
|
|
{
|
|
NODE_INFO_T *z = ggc_cleared_alloc<NODE_INFO_T> ();
|
|
|
|
A68 (new_node_infos)++;
|
|
PROCEDURE_LEVEL (z) = 0;
|
|
CHAR_IN_LINE (z) = NO_TEXT;
|
|
SYMBOL (z) = NO_TEXT;
|
|
PRAGMAT (z) = NO_TEXT;
|
|
PRAGMAT_TYPE (z) = 0;
|
|
PRAGMAT_LINE (z) = NO_LINE;
|
|
PRAGMAT_CHAR_IN_LINE (z) = NO_TEXT;
|
|
COMMENT (z) = NO_TEXT;
|
|
COMMENT_TYPE (z) = 0;
|
|
COMMENT_LINE (z) = NO_LINE;
|
|
COMMENT_CHAR_IN_LINE (z) = NO_TEXT;
|
|
LINE (z) = NO_LINE;
|
|
return z;
|
|
}
|
|
|
|
/* New_genie_info. */
|
|
|
|
GINFO_T *
|
|
a68_new_genie_info (void)
|
|
{
|
|
GINFO_T *z = ggc_cleared_alloc<GINFO_T> ();
|
|
|
|
A68 (new_genie_infos)++;
|
|
PARTIAL_PROC (z) = NO_MOID;
|
|
PARTIAL_LOCALE (z) = NO_MOID;
|
|
return z;
|
|
}
|
|
|
|
/* Allocate and return a new parse tree node with proper defaults. */
|
|
|
|
NODE_T *
|
|
a68_new_node (void)
|
|
{
|
|
NODE_T *z = ggc_cleared_alloc<NODE_T> ();
|
|
|
|
A68 (new_nodes)++;
|
|
TABLE (z) = NO_TABLE;
|
|
INFO (z) = NO_NINFO;
|
|
GINFO (z) = NO_GINFO;
|
|
ATTRIBUTE (z) = STOP;
|
|
ANNOTATION (z) = STOP;
|
|
MOID (z) = NO_MOID;
|
|
NEXT (z) = NO_NODE;
|
|
PREVIOUS (z) = NO_NODE;
|
|
SUB (z) = NO_NODE;
|
|
NEST (z) = NO_NODE;
|
|
NON_LOCAL (z) = NO_TABLE;
|
|
TAX (z) = NO_TAG;
|
|
SEQUENCE (z) = NO_NODE;
|
|
PACK (z) = NO_PACK;
|
|
CDECL (z) = NULL_TREE;
|
|
DYNAMIC_STACK_ALLOCS (z) = false;
|
|
PUBLICIZED (z) = false;
|
|
return z;
|
|
}
|
|
|
|
/* Some_node. */
|
|
|
|
NODE_T *
|
|
a68_some_node (const char *t)
|
|
{
|
|
NODE_T *z = a68_new_node ();
|
|
INFO (z) = a68_new_node_info ();
|
|
GINFO (z) = a68_new_genie_info ();
|
|
NSYMBOL (z) = t;
|
|
return z;
|
|
}
|
|
|
|
/* New_symbol_table. */
|
|
|
|
TABLE_T *
|
|
a68_new_symbol_table (TABLE_T *p)
|
|
{
|
|
TABLE_T *z = ggc_cleared_alloc<TABLE_T> ();
|
|
|
|
NUM (z) = A68 (symbol_table_count);
|
|
LEVEL (z) = A68 (symbol_table_count)++;
|
|
NEST (z) = A68 (symbol_table_count);
|
|
ATTRIBUTE (z) = 0;
|
|
INITIALISE_FRAME (z) = true;
|
|
PROC_OPS (z) = true;
|
|
INITIALISE_ANON (z) = true;
|
|
PREVIOUS (z) = p;
|
|
OUTER (z) = NO_TABLE;
|
|
IDENTIFIERS (z) = NO_TAG;
|
|
OPERATORS (z) = NO_TAG;
|
|
MODULES (z) = NO_TAG;
|
|
PRIO (z) = NO_TAG;
|
|
INDICANTS (z) = NO_TAG;
|
|
LABELS (z) = NO_TAG;
|
|
ANONYMOUS (z) = NO_TAG;
|
|
JUMP_TO (z) = NO_NODE;
|
|
SEQUENCE (z) = NO_NODE;
|
|
PUBLIC_RANGE (z) = false;
|
|
return z;
|
|
}
|
|
|
|
/* New_moid. */
|
|
|
|
MOID_T *
|
|
a68_new_moid (void)
|
|
{
|
|
MOID_T *z = ggc_cleared_alloc<MOID_T> ();
|
|
|
|
A68 (new_modes)++;
|
|
ATTRIBUTE (z) = 0;
|
|
NUMBER (z) = 0;
|
|
DIM (z) = 0;
|
|
USE (z) = false;
|
|
HAS_ROWS (z) = false;
|
|
PORTABLE (z) = true;
|
|
DERIVATE (z) = false;
|
|
NODE (z) = NO_NODE;
|
|
PACK (z) = NO_PACK;
|
|
SUB (z) = NO_MOID;
|
|
EQUIVALENT_MODE (z) = NO_MOID;
|
|
SLICE (z) = NO_MOID;
|
|
TRIM (z) = NO_MOID;
|
|
DEFLEXED (z) = NO_MOID;
|
|
NAME (z) = NO_MOID;
|
|
MULTIPLE_MODE (z) = NO_MOID;
|
|
NEXT (z) = NO_MOID;
|
|
CTYPE (z) = NULL_TREE;
|
|
ASM_LABEL (z) = NULL;
|
|
return z;
|
|
}
|
|
|
|
/* New_pack. */
|
|
|
|
PACK_T *
|
|
a68_new_pack (void)
|
|
{
|
|
PACK_T *z = ggc_cleared_alloc<PACK_T> ();
|
|
|
|
MOID (z) = NO_MOID;
|
|
TEXT (z) = NO_TEXT;
|
|
NODE (z) = NO_NODE;
|
|
NEXT (z) = NO_PACK;
|
|
PREVIOUS (z) = NO_PACK;
|
|
return z;
|
|
}
|
|
|
|
/* New_tag. */
|
|
|
|
TAG_T *
|
|
a68_new_tag (void)
|
|
{
|
|
TAG_T *z = ggc_cleared_alloc<TAG_T> ();
|
|
|
|
STATUS (z) = NULL_MASK;
|
|
TAG_TABLE (z) = NO_TABLE;
|
|
MOID (z) = NO_MOID;
|
|
NODE (z) = NO_NODE;
|
|
UNIT (z) = NO_NODE;
|
|
VALUE (z) = NO_TEXT;
|
|
SCOPE (z) = PRIMAL_SCOPE;
|
|
SCOPE_ASSIGNED (z) = false;
|
|
PRIO (z) = 0;
|
|
USE (z) = false;
|
|
IN_PROC (z) = false;
|
|
HEAP (z) = false;
|
|
YOUNGEST_ENVIRON (z) = PRIMAL_SCOPE;
|
|
LOC_ASSIGNED (z) = false;
|
|
NEXT (z) = NO_TAG;
|
|
BODY (z) = NO_TAG;
|
|
PORTABLE (z) = true;
|
|
VARIABLE (z) = false;
|
|
IS_RECURSIVE (z) = false;
|
|
PUBLICIZED (z) = true; /* XXX */
|
|
ASCRIBED_ROUTINE_TEXT (z) = false;
|
|
LOWERER (z) = NO_LOWERER;
|
|
TAX_TREE_DECL (z) = NULL_TREE;
|
|
MOIF (z) = NO_MOIF;
|
|
EXTERN_SYMBOL (z) = NO_TEXT;
|
|
NUMBER (z) = ++A68_PARSER (tag_number);
|
|
return z;
|
|
}
|
|
|
|
/* Make special, internal mode. */
|
|
|
|
static void
|
|
make_special_mode (MOID_T ** n, int m)
|
|
{
|
|
(*n) = a68_new_moid ();
|
|
ATTRIBUTE (*n) = 0;
|
|
NUMBER (*n) = m;
|
|
PACK (*n) = NO_PACK;
|
|
SUB (*n) = NO_MOID;
|
|
EQUIVALENT (*n) = NO_MOID;
|
|
DEFLEXED (*n) = NO_MOID;
|
|
NAME (*n) = NO_MOID;
|
|
SLICE (*n) = NO_MOID;
|
|
TRIM (*n) = NO_MOID;
|
|
ROWED (*n) = NO_MOID;
|
|
}
|
|
|
|
/* Whether attributes match in subsequent nodes. */
|
|
|
|
bool
|
|
a68_whether (NODE_T * p, ...)
|
|
{
|
|
va_list vl;
|
|
va_start (vl, p);
|
|
int a;
|
|
while ((a = va_arg (vl, int)) != STOP)
|
|
{
|
|
if (p != NO_NODE && a == WILDCARD)
|
|
FORWARD (p);
|
|
else if (p != NO_NODE && (a == KEYWORD))
|
|
{
|
|
if (a68_find_keyword_from_attribute (A68 (top_keyword), ATTRIBUTE (p)) != NO_KEYWORD)
|
|
FORWARD (p);
|
|
else
|
|
{
|
|
va_end (vl);
|
|
return false;
|
|
}
|
|
}
|
|
else if (p != NO_NODE && (a >= 0 ? a == ATTRIBUTE (p) : (-a) != ATTRIBUTE (p)))
|
|
FORWARD (p);
|
|
else
|
|
{
|
|
va_end (vl);
|
|
return false;
|
|
}
|
|
}
|
|
va_end (vl);
|
|
return true;
|
|
}
|
|
|
|
/* Whether one of a series of attributes matches a node. */
|
|
|
|
bool
|
|
a68_is_one_of (NODE_T *p, ...)
|
|
{
|
|
if (p != NO_NODE)
|
|
{
|
|
bool match = false;
|
|
int a;
|
|
|
|
va_list vl;
|
|
va_start (vl, p);
|
|
while ((a = va_arg (vl, int)) != STOP)
|
|
match = (match | IS (p, a));
|
|
va_end (vl);
|
|
return match;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
|
|
/* Isolate nodes p-q making p a branch to p-q
|
|
|
|
From x - p - a - b - c - q - y
|
|
To x - t - y
|
|
|
|
|
p - a - b - c - q
|
|
*/
|
|
|
|
void
|
|
a68_make_sub (NODE_T *p, NODE_T *q, enum a68_attribute t)
|
|
{
|
|
NODE_T *z = a68_new_node ();
|
|
|
|
gcc_assert (p != NO_NODE && q != NO_NODE);
|
|
*z = *p;
|
|
|
|
if (GINFO (p) != NO_GINFO)
|
|
GINFO (z) = a68_new_genie_info ();
|
|
|
|
PREVIOUS (z) = NO_NODE;
|
|
|
|
if (p == q)
|
|
NEXT (z) = NO_NODE;
|
|
else
|
|
{
|
|
if (NEXT (p) != NO_NODE)
|
|
PREVIOUS (NEXT (p)) = z;
|
|
NEXT (p) = NEXT (q);
|
|
if (NEXT (p) != NO_NODE)
|
|
PREVIOUS (NEXT (p)) = p;
|
|
NEXT (q) = NO_NODE;
|
|
}
|
|
|
|
SUB (p) = z;
|
|
ATTRIBUTE (p) = t;
|
|
}
|
|
|
|
/* Find symbol table at level I. */
|
|
|
|
static TABLE_T *
|
|
find_level (NODE_T *n, int i)
|
|
{
|
|
if (n == NO_NODE)
|
|
return NO_TABLE;
|
|
else
|
|
{
|
|
TABLE_T *s = TABLE (n);
|
|
|
|
if (s != NO_TABLE && LEVEL (s) == i)
|
|
return s;
|
|
else if ((s = find_level (SUB (n), i)) != NO_TABLE)
|
|
return s;
|
|
else if ((s = find_level (NEXT (n), i)) != NO_TABLE)
|
|
return s;
|
|
else
|
|
return NO_TABLE;
|
|
}
|
|
}
|
|
|
|
/* Whether P is top of lexical level. */
|
|
|
|
bool
|
|
a68_is_new_lexical_level (NODE_T *p)
|
|
{
|
|
switch (ATTRIBUTE (p))
|
|
{
|
|
case ALT_DO_PART:
|
|
case BRIEF_ELIF_PART:
|
|
case BRIEF_OUSE_PART:
|
|
case BRIEF_CONFORMITY_OUSE_PART:
|
|
case CHOICE:
|
|
case CLOSED_CLAUSE:
|
|
case CONDITIONAL_CLAUSE:
|
|
case DO_PART:
|
|
case ELIF_PART:
|
|
case ELSE_PART:
|
|
case CASE_CLAUSE:
|
|
case CASE_CHOICE_CLAUSE:
|
|
case CASE_IN_PART:
|
|
case CASE_OUSE_PART:
|
|
case OUT_PART:
|
|
case ROUTINE_TEXT:
|
|
case SPECIFIED_UNIT:
|
|
case THEN_PART:
|
|
case CONFORMITY_CLAUSE:
|
|
case CONFORMITY_CHOICE:
|
|
case CONFORMITY_IN_PART:
|
|
case CONFORMITY_OUSE_PART:
|
|
case WHILE_PART:
|
|
case DEF_PART:
|
|
case POSTLUDE_PART:
|
|
case ACCESS_CLAUSE:
|
|
return true;
|
|
case MODULE_TEXT:
|
|
/* Module texts introduce an additional lexical level encompassing all
|
|
its parts only if it is endowed with revelations. */
|
|
if (SUB (p) != NO_NODE && IS (SUB (p), REVELATION_PART))
|
|
return true;
|
|
else
|
|
return false;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Couple of utility functions.
|
|
*/
|
|
|
|
/* Safely append to buffer. */
|
|
|
|
void
|
|
a68_bufcat (char *dst, const char *src, int len)
|
|
{
|
|
if (src != NO_TEXT) {
|
|
char *d = dst;
|
|
const char *s = src;
|
|
int n = len;
|
|
// Find end of dst and left-adjust; do not go past end
|
|
for (; n-- != 0 && d[0] != '\0'; d++) {
|
|
;
|
|
}
|
|
int dlen = (int) (d - dst);
|
|
n = len - dlen;
|
|
if (n > 0) {
|
|
while (s[0] != '\0') {
|
|
if (n != 1) {
|
|
(d++)[0] = s[0];
|
|
n--;
|
|
}
|
|
s++;
|
|
}
|
|
d[0] = '\0';
|
|
}
|
|
// Better sure than sorry
|
|
dst[len - 1] = '\0';
|
|
}
|
|
}
|
|
|
|
/* Safely copy to buffer. */
|
|
|
|
void
|
|
a68_bufcpy (char *dst, const char *src, int len)
|
|
{
|
|
if (src != NO_TEXT) {
|
|
char *d = dst;
|
|
const char *s = src;
|
|
int n = len;
|
|
// Copy as many as fit
|
|
if (n > 0 && --n > 0) {
|
|
do {
|
|
if (((d++)[0] = (s++)[0]) == '\0') {
|
|
break;
|
|
}
|
|
} while (--n > 0);
|
|
}
|
|
if (n == 0 && len > 0) {
|
|
// Not enough room in dst, so terminate
|
|
d[0] = '\0';
|
|
}
|
|
// Better sure than sorry
|
|
dst[len - 1] = '\0';
|
|
}
|
|
}
|
|
|
|
/* Make a new copy of concatenated strings. */
|
|
|
|
char *
|
|
a68_new_string (const char *t, ...)
|
|
{
|
|
va_list vl;
|
|
va_start (vl, t);
|
|
const char *q = t;
|
|
if (q == NO_TEXT) {
|
|
va_end (vl);
|
|
return NO_TEXT;
|
|
}
|
|
int len = 0;
|
|
while (q != NO_TEXT) {
|
|
len += (int) strlen (q);
|
|
q = va_arg (vl, char *);
|
|
}
|
|
va_end (vl);
|
|
len++;
|
|
char *z = (char *) xmalloc ((size_t) len);
|
|
z[0] = '\0';
|
|
q = t;
|
|
va_start (vl, t);
|
|
while (q != NO_TEXT) {
|
|
a68_bufcat (z, q, len);
|
|
q = va_arg (vl, char *);
|
|
}
|
|
va_end (vl);
|
|
return z;
|
|
}
|
|
|
|
/* Tie label to the clause it is defined in. */
|
|
|
|
static void
|
|
tie_label_to_serial (NODE_T *p)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (IS (p, SERIAL_CLAUSE))
|
|
{
|
|
bool valid_follow;
|
|
|
|
if (NEXT (p) == NO_NODE)
|
|
valid_follow = true;
|
|
else if (IS (NEXT (p), CLOSE_SYMBOL))
|
|
valid_follow = true;
|
|
else if (IS (NEXT (p), END_SYMBOL))
|
|
valid_follow = true;
|
|
else if (IS (NEXT (p), OD_SYMBOL))
|
|
valid_follow = true;
|
|
else
|
|
valid_follow = false;
|
|
|
|
if (valid_follow)
|
|
JUMP_TO (TABLE (SUB (p))) = NO_NODE;
|
|
}
|
|
|
|
tie_label_to_serial (SUB (p));
|
|
}
|
|
}
|
|
|
|
/* Tie label to the clause it is defined in. */
|
|
|
|
static void
|
|
tie_label (NODE_T *p, NODE_T *unit)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (IS (p, DEFINING_IDENTIFIER))
|
|
UNIT (TAX (p)) = unit;
|
|
tie_label (SUB (p), unit);
|
|
}
|
|
}
|
|
|
|
/* Tie label to the clause it is defined in. */
|
|
|
|
static void
|
|
tie_label_to_unit (NODE_T *p)
|
|
{
|
|
for (; p != NO_NODE; FORWARD (p))
|
|
{
|
|
if (IS (p, LABELED_UNIT))
|
|
tie_label (SUB_SUB (p), NEXT_SUB (p));
|
|
tie_label_to_unit (SUB (p));
|
|
}
|
|
}
|
|
|
|
/* Table with attribute names. */
|
|
|
|
static const char *attribute_names[] =
|
|
{
|
|
"STOP",
|
|
#define A68_ATTR(ATTR,DESCR) DESCR,
|
|
#include "a68-parser-attrs.def"
|
|
#undef A68_ATTR
|
|
};
|
|
|
|
/* Get the name of an attribute. */
|
|
|
|
const char *
|
|
a68_attribute_name (enum a68_attribute attr)
|
|
{
|
|
return attribute_names[attr];
|
|
}
|
|
|
|
/* Get the location of node P as a GCC location. */
|
|
|
|
location_t
|
|
a68_get_node_location (NODE_T *p)
|
|
{
|
|
LINE_T *line = LINE (INFO (p));
|
|
|
|
if (line == NO_LINE)
|
|
return UNKNOWN_LOCATION;
|
|
|
|
unsigned line_number = NUMBER (line);
|
|
unsigned column_number = CHAR_IN_LINE (INFO (p)) - STRING (line) + 1;
|
|
const char *filename = FILENAME (line);
|
|
|
|
location_t gcc_location;
|
|
|
|
linemap_add (line_table, LC_ENTER, 0, filename, line_number);
|
|
linemap_line_start (line_table, line_number, 0);
|
|
gcc_location = linemap_position_for_column (line_table, column_number);
|
|
linemap_add (line_table, LC_LEAVE, 0, NULL, 0);
|
|
|
|
return gcc_location;
|
|
}
|
|
|
|
/* Get the location of POS inside LINE as a GCC location. */
|
|
|
|
location_t
|
|
a68_get_line_location (LINE_T *line, const char *pos)
|
|
{
|
|
location_t loc;
|
|
|
|
linemap_add (line_table, LC_ENTER, 0, FILENAME (line), NUMBER (line));
|
|
linemap_line_start (line_table, NUMBER (line), 0);
|
|
loc = linemap_position_for_column (line_table, pos - STRING (line) + 1);
|
|
linemap_add (line_table, LC_LEAVE, 0, NULL, 0);
|
|
return loc;
|
|
}
|