/* if.c - If statement parsing execution.
 *  Copyright (C) 2022 by Jed Marti
 *   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 https://www.gnu.org/licenses/.
 */


// Syntax is:
//    if (expression) something; [ else something; ]
// Lots of fiddling and look ahead for extra semicolons.

#include <C8051F380.h>
#include <stdint.h>

#include "lfxmit.h"
#include "serial.h"
#include "storage.h"


extern __xdata floatOrInt IACC;
extern __bit              errorThrown;
extern uint16_t           DREG;
extern __xdata uint8_t    D[];

extern __xdata uint16_t   PC;
extern __bit doit;



void runIf(void)
// We're at the IF word at start. Do the expression (if we're meant to). If non-zero, then
// do the follow on statement skipping th ELSE if any. If  we're not supposed to execute (nested
// IF's and so on), then just go through the motions.
{
  incPC();

  if (getPByte() != LPAREN)
  {
    putstring("if missing (\n");
    errorThrown = 1;
    return;
  }

  // Get the expression value. Punt on syntax error.
  parseExpression();
  if (errorThrown) return;

  // If we're executing, then check the value.
  if (doit)
  {
    DREG = popST();
    if (tag() != DESC_INT)
    {
      putstring("if expression not integer\n");
      errorThrown = 1;
      return;
    }

    // Check value. If zero, then skip the following expression by executing but not executing.
    getIACC();
    if (IACC.i == 0)
    {
      doit = 0;
      runStatement();
      doit = 1;
      // putstring("  Test fails at "); putuint16(PC); terpri();
    }

    // Test was true so do statement, but skip any ELSE present.
    else
    {
      runStatement();
      if (errorThrown) return;
      if (getPByte() == SEMICOLON && getPByte1() == ELSE)
      {
        doit = 0;
        incPC();
        runStatement();
        doit = 1;
      }
      return;
    }

    // Expression as false, watch for ELSE. Note that the IF clause should not be followed
    // by a semicolon if there's an ELSE.
    //    putstring("  Check "); putuint16(PC); putstring(" 0x"); puthexbyte(getPByte()); terpri();
    //    if (getPByte() == SEMICOLON) incPC();

    // Check for an ELSE. If found, then execute that statement.
    if (getPByte() == ELSE)
    {
      incPC();
      runStatement();
    }

    // Now we're done. We should be at the character following the if.
    //putstring("  IF at "); putuint16(PC); terpri();
    return;
  }

  // We're not executing so just go through the motions. Note that here, doit == 0.
  runStatement();
  if (getPByte() == SEMICOLON) incPC();
  if (getPByte() == ELSE)
  {
    incPC();
    runStatement();
  }
  return;
}


// EOF
