/* parse.c - parse arithmetic expressions.
 *  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/.
 */

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

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


// The program space (see run.c).
extern __xdata uint8_t  I[];
extern __xdata uint16_t PMAX;
extern __xdata uint16_t PC;

// The evaluation/parsing stack.
__xdata uint16_t ST[PSTACK];
uint8_t          SPTR;

// The symbol table.
extern __xdata SymtabEntry S[];
extern __xdata uint8_t     SINDEX;

extern __bit errorThrown;

// Some constants.
extern __xdata uint16_t iZERO;
extern __xdata uint16_t iONE;

// Accumulators.
extern __xdata floatOrInt IACC;
extern __xdata floatOrInt FACC;

// Whether to execute or just skip.
extern __bit doit;

// See morse.c
extern __xdata uint32_t ditMilliseconds;
extern __xdata uint8_t  spaceDits;



// Forward references.
void parseExpression(void);

void initParse(void)
{
  SPTR = 0;
}



void pushST(uint16_t v)
// If we're executing, then push a value onto the evaluation stack checking for overflow. If
// not, then just continue on.
{

  if (doit)
  {
    if (SPTR >= PSTACK)
    {
      putstring("Stack overflow\n");
      errorThrown = 1;
    }
    else
      ST[SPTR++] = v;
  }
}



uint16_t popST(void)
// If we're executing, then pop something from the evaluation stack. Otherwise, just ignore.
// Check for stack underflow. Returns 0 for error and sets the error bit.
{
  if (doit)
  {
    if (SPTR < 1)
    {
      putstring("Stack underflow\n");
      errorThrown = 1;
      return 0;
    }
    return ST[--SPTR];
  }
  else return 0;
}



uint8_t getPByte(void)
// Check for program overflow and return STOP if so. Otherwise return the byte at the current
// program counter. 
{
  if (PC >= ISIZE)
  {
    putstring("PC = 0x");
    puthex16(PC);
    putstring(" out of bounds\n");
    errorThrown = 1;
    return STOP;
  }
  return I[PC];
}



uint8_t getPByte1(void)
// Get one ahead.
{
  if ((PC + 1) >= ISIZE)
  {
    putstring("PC = 0x");
    puthex16(PC);
    putstring(" out of bounds\n");
    errorThrown = 1;
    return STOP;
  }
  return I[PC + 1];
}



void incPC(void)
// It takes 12 bytes to do this which is a lot more than the 3 byte call. We don't test here
// because its usually done immediately after by getPByte above.
{
  ++PC;
}



static void I2IACC(void)
// Get the next 4 bytes into IACC. The floating point method is a bit more efficient.
{
  incPC();
  IACC.i8.b8a = getPByte();
  incPC();
  IACC.i8.b8b = getPByte();
  incPC();
  IACC.i8.b8c = getPByte();
  incPC();
  IACC.i8.b8d = getPByte();
  pushST(newInt(IACC.i));
}




static void parseFnCall(void)
//     Parse a function call leaving its argument on the stack. The function needs to check
// for execution and error flags. (See functions.c).
{
  incPC();
  if (getPByte() != LPAREN)
  {
    putstring("Function call missing (\n");
    errorThrown = 1;
    return;
  }
  incPC();
  parseExpression();
  if (errorThrown) return;
  
  if (getPByte() != RPAREN)
  {
    putstring("Function call missing )\n");
    errorThrown = 1;
    return;
  }

  // Move beyoind the parentheses. Note: caller does this.
  //  incPC();
}

  
  
static void parsePrimary(void)
//     Lowest level expression.
//   <primary> ::= 0 | 1 | <integer> | <float> | <string> | <variable> |
//          <functioncall> [(<expression>)] | (<expression>)
// Errors work back up the chain.
{
  uint8_t c;

  c = getPByte();
  if (errorThrown) return;

  if (c == ZERO) pushST(iZERO);
  else if (c == ONE) pushST(iONE);

  // We have INT32 bl b2 b2 bh
  else if (c == INT32) I2IACC();

  // We have FLOAT fl f1 f2 fh
  else if (c == FLOAT)
  {
    incPC();
    pushST(newFloatAddr(&I[PC]));
    PC += 3;    // We add 1 below.
  }

  // A string.
  else if (c == STRING)
  {
    incPC();
    pushST(newStringI());
    while (getPByte() != 0) incPC();
  }

  // Unary -.
  else if (c == DIFFERENCE)
  {
    incPC();
    parsePrimary();
    if (doit) minus();
    return;
  }

  // We have VARIABLE id-number  or VARIABLE id-number [ expression ]
  else if (c == VARIABLE)
  {
    incPC();
    c = getPByte();
    incPC();
    if (getPByte() == LSQB)
    {
      incPC();
      parseExpression();
      if (getPByte() != RSQB)
      {
        putstring("Vector missing ]\n");
        errorThrown = 1;
        return;
      }
      incPC();
      getVector(c);
    }
    
    else if (doit)
      {
        //        putstring("  symbol "); putbyte(c); putstring("  "); puthex16(S[c].addr);terpri();
        pushST(S[c].addr);
      }
    return;
  }

  // Function calls that need an argument.
  else switch (c) {
    case DIG0: readDIG0(); break;
    case DIG1: readDIG1(); break;
    case ANYCHAR: anyChar(); break;
    case RED: readRed(); break;
    case YELLOW: readYellow(); break;
    case GREEN: readGreen(); break;
    case BLUE: readBlue(); break;
    case DITMS: pushST(newInt(ditMilliseconds)); break;
    case SPACEDITS: pushST(newInt((int32_t) spaceDits)); break;
    case A2DC0: readAD0(); break;
    case A2DC1: readAD1(); break;
    case RAWTEMP: readRawTemp(); break;
    case TEMP: readTemp(); break;
    case ISINT: parseFnCall(); isInt(); break;
    case ISFLOAT: parseFnCall(); isFloat(); break;
    case ISSTRING: parseFnCall(); isString(); break;
    case ISBYTEVECTOR: parseFnCall(); isByteVector(); break;
    case ISFLOATVECTOR: parseFnCall(); isFloatVector(); break;
    case SQRT: parseFnCall(); doSqrt(); break;
    case ROUND: parseFnCall(); doRound(); break;
    case ABS: parseFnCall(); doAbs(); break;
    case READCH: parseFnCall(); doReadch(); break;
      
    case LPAREN:
      incPC();
      parseExpression();
      if (errorThrown) return;
      if (getPByte() != RPAREN)
      {
        putstring("Unbalanced paren\n");
        errorThrown = 1;
        return;
      }
      break;
     
    default:
      putstring("Expression syntax\n");
      errorThrown = 1;
      return;
    }
  incPC();
  return;
}



static void parseTerm1(void)
//     Left associative *, /, %. See arithmetic.c for the actual calculations.
// <term1> ::= [* <primary> <term1> | / <primary> <term1> | % <primary> <term1>]*
{
 loop:
  if (getPByte() == TIMES)
  {
    incPC();
    parsePrimary();
    if (errorThrown) return;
    times2();
    goto loop;
  }

  else if (getPByte() == QUOTIENT)
  {
    incPC();
    parsePrimary();
    if (errorThrown) return;
    quotient();
    goto loop;
  }

  else if (getPByte() == REMAINDER)
  {
    incPC();
    parsePrimary();
    if (errorThrown) return;
    remainder();
    goto loop;
  }
}
    


static void parseTerm(void)
//     High precedence diadic *, /, % functionality.
// <term> ::= <primary> <term1>
{
  parsePrimary();
  if (errorThrown) return;
  parseTerm1();
}
    


static void parseSum1(void)
//     left associative + and - diadic operations.
// <sum1> ::= [+ <term> <sum1>] | - <term> <sum1>]*
{
 loop:
  if (getPByte() == PLUS)
  {
    incPC();
    parseTerm();
    if (errorThrown) return;
    plus2();
    goto loop;
  }
  else if (getPByte() == DIFFERENCE)
  {
    incPC();
    parseTerm();
    if (errorThrown) return;
    difference();
    goto loop;
  }
}

 

static void parseSum(void)
//     Check for unary minus then the left associative sum.
// <sum> ::= [- <sum> ] <term> <sum1>
{
  /*  if (getPByte() == DIFFERENCE)
  {
    incPC();
    parseSum();
    if (errorThrown) return;
    minus();
    return;
  }
  */
  parseTerm();
  if (errorThrown) return;
  parseSum1();
}



static void parseBitAndOr1(void)
//     Parse and execute bit and/or. Probably should add ^ some day. Left associative.
// <bitandor1> ::= [<& <sum> <bitandor1> | | <sum> <bitandor1>]*
{
 loop:
  if (getPByte() == BAND)
  {
    incPC();
    parseSum();
    bitand();
    goto loop;
  }
  else if (getPByte() == BOR)
  {
    incPC();
    parseSum();
    bitor();
    goto loop;
  }
}



static void parseBitAndOr(void)
//    Parse bit and/or functionality.
//  <bitandor> ::= <sum> <bitandor1>
{
  parseSum();
  parseBitAndOr1();
}



void parseComplement(void)
//     Parse and evaluate bit 1's complement.
//   <complement> ::= [~ <bitandor> ] | <bitandor>
{
  if (getPByte() == BNOT)
  {
    incPC();
    parseBitAndOr();
    bitnot();
    return;
  }
  parseBitAndOr();
}

  

static void parseRelation(void)
//     Parse and evalaute the diadic relations.
// <relation> ::= <ccmplement> [< <complement> | <= <complement> | > <complement> |
//     >= <complement> | == <complement>  | != <complement> ]
{
  parseComplement();
  if (getPByte() == LESSP)
  {
    incPC();
    parseComplement();
    lessp();
    return;
  }
  else if (getPByte() == LEQ)
  {
    incPC();
    parseComplement();
    leq();
    return;
  }
  else if (getPByte() == GREATERP)
  {
    incPC();
    parseComplement();
    greaterp();
    return;
  }
  else if (getPByte() == GEQ)
  {
    incPC();
    parseComplement();
    geq();
    return;
  }
  else if (getPByte() == EQUAL)
  {
    incPC();
    parseComplement();
    equal();
    return;
  }
  else if (getPByte() == NEQ)
  {
    incPC();
    parseComplement();
    neq();
    return;
  }
}


static void parseNegate(void)
//     Parse and evaluate logical negation.
// <negate> ::= ! <relation> | <relation>
{
  if (getPByte() == NOT)
  {
    incPC();
    parseRelation();
    not();
    return;
  }
  parseRelation();
}



static void parseLogical(void)
//     Parse logical and/or
// <logical1> ::= [&& <negate> | || <negate> ]*
// Note that both arguments are evaluated so things like v >= 0 && sqrt(v) will result in an error
// if v < 0.
{
 loop:
  if (getPByte() == AND)
  {
    incPC();
    parseNegate();
    and();
    goto loop;
  }
  else if (getPByte() == OR)
  {
    incPC();
    parseNegate();
    or();
    goto loop;
  }

  
}
      
    
    

void parseExpression(void)
//     Top level expression parsing/evaluation.
// <expression> ::= <negate> <logical>
{
  parseNegate();
  parseLogical();
}



// EOF
