//! @file parser-annotate.c
//! @author J. Marcel van der Veer

//! @section Copyright
//!
//! This file is part of Algol68G - an Algol 68 compiler-interpreter.
//! Copyright 2001-2025 J. Marcel van der Veer [algol68g@xs4all.nl].

//! @section License
//!
//! 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 3 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, see [http://www.gnu.org/licenses/].

//! @section Synopsis
//!
//! Annotate syntax tree.

#include "a68g.h"
#include "a68g-defines.h"
#include "a68g-parser.h"
#include "a68g-prelude.h"

// The A68G GC cannot walk the expression stack, which holds no mode info.
// En lieu, all units yielding a value with mode that needs colouring
// copy the top-of-stack to an anonymous variable in the frame stack.
//
// TODO: Optimize this code as some tags are placed without need. 
//       For instance, not all REFs point to the heap.

//! @brief Test whether unit is an identifier (primary).

BOOL_T identifier_gc (NODE_T *p)
{
  switch (ATTRIBUTE (p))
  {
    case UNIT:
    case TERTIARY:
    case SECONDARY:
    case PRIMARY:
      return identifier_gc (SUB (p));
    case IDENTIFIER:
      return TAX (p) != NO_TAG;
    default:
      return A68G_FALSE;
  }
}

//! @brief Annotate syntax tree for GC.

void annotate_gc_identity_declaration (NODE_T *p)
{
  if (p != NO_NODE) {
    switch (ATTRIBUTE (p)) {
    case DECLARER: {
        annotate_gc (SUB (p));
        annotate_gc_identity_declaration (NEXT (p));
        return;
      }
    case DEFINING_IDENTIFIER: {
// Do not tag the unit, value will already be in the frame stack.
        annotate_gc (SUB (NEXT_NEXT (p)));
        return;
      }
    }
  }
}

//! @brief Annotate syntax tree for GC.

void annotate_gc_variable_declaration (NODE_T * p)
{
  if (p != NO_NODE) {
    switch (ATTRIBUTE (p)) {
    case DECLARER: {
        annotate_gc (SUB (p));
        annotate_gc_variable_declaration (NEXT (p));
        return;
      }
    case DEFINING_IDENTIFIER: {
        if (whether (p, DEFINING_IDENTIFIER, ASSIGN_SYMBOL, UNIT, STOP)) {
// Do not tag the unit, value will already be in the frame stack.
          annotate_gc (SUB (NEXT_NEXT (p)));
          return;
        }
      }
    }
  }
}

//! @brief Annotate syntax tree for GC.

void annotate_gc (NODE_T *p)
{
  for (; p != NO_NODE; FORWARD (p)) {
    if (ATTRIBUTE (p) == UNIT) {
      MOID_T *m = MOID (p);
      if (identifier_gc (p)) {
        /* Value already in the frame stack */;
      } else if (moid_needs_colouring (m)) {
        TAG_T *z = add_tag (TABLE (p), ANONYMOUS, p, MOID (p), UNIT);
        TAX_GC (p) = z;
        HEAP (z) = LOC_SYMBOL;
        USE (z) = A68G_TRUE;
      }
      annotate_gc (SUB (p));
    } else if (ATTRIBUTE (p) == IDENTITY_DECLARATION) {
      annotate_gc_identity_declaration (SUB (p));
    } else if (ATTRIBUTE (p) == VARIABLE_DECLARATION) {
      annotate_gc_variable_declaration (SUB (p));
    } else if (SUB (p) != NO_NODE) {
      annotate_gc (SUB (p));
    }
  }
}
