TinyMAZE/
TinyMAZE/config/
TinyMAZE/doc/
TinyMAZE/run/msgs/
TinyMAZE/src/
TinyMAZE/src/db/
TinyMAZE/src/ident/
TinyMAZE/src/io/
TinyMAZE/src/prog/
TinyMAZE/src/softcode/
TinyMAZE/src/util/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "db.h"
#include "externs.h"
#include "softcode.h"

static void add_if(PROG *program, int test)
{
  SCIF *newif;
  PROGLINE *p, *pelse = NULL;
  int deep = 0;
  char *command;

  if(test == -1)
  {
    syntax_error();
    return;
  }

  newif = (SCIF *)stack_alloc(sizeof(SCIF), 1, 0);
  newif->test = test;

  for(p = code_ptr->pline->next;p;p = p->next)
  {
    command = get_command(p->code);

    if(!string_compare(command, "for"))
      deep++;
    else if(!string_compare(command, "next"))
      deep--;
    if(!string_compare(command, "if"))
      deep++;
    else if(!string_compare(command, "endif"))
    {
      if(!deep--)
        break;
    }
    else if(!string_compare(command, "else"))
      if(!deep)
      {
        if(pelse)
        {
          notify(code_ptr->executor,
            tprintf("|+R|Too many ELSE statements in |+Y|%d|+R|.",
            code_ptr->pline->line));
          notify(code_ptr->executor, tprintf("%d %s",
            code_ptr->pline->line, code_ptr->pline->code));
          notify(code_ptr->executor,
            tprintf("|+R|First ELSE in |+Y|%d|+R|.",
            pelse->line));
          notify(code_ptr->executor, tprintf("%d %s",
            pelse->line, pelse->code));
          end_program(code_ptr->program, 0);
          return;
        }
        else
          pelse = p;
      }
  }

  if(!p)
  {
    notify(code_ptr->executor, tprintf("|+R|IF with no ENDIF in %d",
      code_ptr->pline->line));
    notify(code_ptr->executor, tprintf("%d %s",
      code_ptr->pline->line, code_ptr->pline->code));
    end_program(code_ptr->program, 0);
    return;
  }

  newif->endif = p;

  if(!test)
  {
    if(pelse)
      code_ptr->pline = pelse;
    else
      code_ptr->pline = p;
  }

  newif->next = code_ptr->iflist;
  code_ptr->iflist = newif;
}

static int test_if(char *ifstr)
{
  char buf[4096];
  char left[4096], op[4096], right[4096];
  char *p = NULL;
  int leftmath, rightmath;
  int i, val;
  char *operators[] = { ">", "<", ">=", "<=", "!=", "=" };

  for(i = 0;i < 6;++i)
    if((p = strstr(ifstr, operators[i])))
      break;

  if(p)
  {
    strncpy(buf, ifstr, p-ifstr);
    *(buf+(p-ifstr)) = '\0';
    if((leftmath = parse_output(buf, left)) == -1)
      return(-1);
    strncpy(op, p, strlen(operators[i]));
    *(op+strlen(operators[i])) = '\0';
    if((rightmath = parse_output(p+strlen(operators[i]), right)) == -1)
      return(-1);

    if(!*right)
      return(-1);
  }
  else
  {
    if((leftmath = parse_output(ifstr, left)) == -1)
      return(-1);

    if(leftmath)
      return(atoi(left));

    return(*left);
  }

  if((leftmath && !rightmath) || (!leftmath && rightmath))
  {
    notify(code_ptr->executor,
      tprintf("|+R|Type Mismatch in |+Y|%d|+R|.",
      code_ptr->pline->line));
    notify(code_ptr->executor, tprintf("%d %s",
      code_ptr->pline->line, code_ptr->pline->code));
    end_program(code_ptr->program, 0);
    return(-1);
  }

  if(!leftmath)
  {
    val = strcmp(left, right);

    if(!strcmp(op, "<"))
      return((val < 0));
    if(!strcmp(op, ">"))
      return((val > 0));
    if(!strcmp(op, "="))
      return(!val);
    if(!strcmp(op, ">="))
      return((val >= 0));
    if(!strcmp(op, "<="))
      return((val <= 0));
    return(val);             /* != */
  }

  if(!strcmp(op, "<"))
    return((atoi(left) < atoi(right)));
  if(!strcmp(op, ">"))
    return((atoi(left) > atoi(right)));
  if(!strcmp(op, "="))
    return((atoi(left) == atoi(right)));
  if(!strcmp(op, "<="))
    return((atoi(left) <= atoi(right)));
  if(!strcmp(op, ">="))
    return((atoi(left) >= atoi(right)));
  return((atoi(left) != atoi(right)));    /* != */
}

void sc_do_if(char *arg1, char *arg2)
{
  char ifstr[4096];

  if(*arg2)
    sprintf(ifstr, "%s=%s", arg1, arg2);
  else
    strcpy(ifstr, arg1);

  add_if(code_ptr->program, test_if(ifstr));
}

void sc_do_else()
{
  if(!code_ptr->iflist)
  {
    notify(code_ptr->executor,
      tprintf("|+R|ELSE statement not within IF in |+Y|%d|+R|.",
      code_ptr->pline->line));
    notify(code_ptr->executor, tprintf("%d %s",
      code_ptr->pline->line, code_ptr->pline->code));
    end_program(code_ptr->program, 0);
    return;
  }

  if(code_ptr->iflist->test)
    code_ptr->pline = code_ptr->iflist->endif;
  else
    return;
}

void sc_do_endif()
{
  SCIF *freeme;

  if(!code_ptr->iflist)
  {
    notify(code_ptr->executor,
      tprintf("|+R|ENDIF statement not within IF in |+Y|%d|+R|.",
      code_ptr->pline->line));
    notify(code_ptr->executor, tprintf("%d %s",
      code_ptr->pline->line, code_ptr->pline->code));
    end_program(code_ptr->program, 0);
    return;
  }

  freeme = code_ptr->iflist;
  code_ptr->iflist = code_ptr->iflist->next;
  stack_free(freeme);
}

static void add_for(int max, int step, PROGLINE *pnext, SCVAR *var)
{
  SCFOR *newfor;

  newfor = (SCFOR *)stack_alloc(sizeof(SCFOR), 1, 0);

  newfor->top = code_ptr->pline;
  newfor->max = max;
  newfor->step = step;
  newfor->pnext = pnext;
  newfor->var = var;

  newfor->next = code_ptr->forlist;
  code_ptr->forlist = newfor;
}

void sc_do_for(char *arg1, char *arg2)
{
  PROGLINE *pline;
  char *p, *b;
  char buf[4096], buf2[4096];
  int max;
  int step;
  int deep;
  SCVAR *var;

  if(*arg1 != '~' || *last_char(arg1) == '$')
  {
    syntax_error();
    return;
  }

  ivar_val(arg1+1); /* This will create the var if it doesn't exist */

  if(!code_ptr)        /* Illegal var name */
    return;

  var = find_var(arg1+1, VAR_NUM);

  for(p = arg2, b = buf;*p && !isspace(*p);++p, ++b)
    *b = *p;
  *b = '\0';

  if(!*p || parse_output(buf, buf2) != 1)
  {
    syntax_error();
    return;
  }

  var->nvalue = atoi(buf2);
  p++;

  strcpy(buf, "TO");
  for(b = buf;*b;++p, ++b)
  {
    if(toupper(*p) != *b)
    {
      syntax_error();
      return;
    }
  }
  if(!isspace(*p++))
  {
    syntax_error();
    return;
  }

  for(b = buf;*p && !isspace(*p);p++, b++)
    *b = *p;
  *b = '\0';

  if(parse_output(buf, buf2) != 1)
  {
    syntax_error();
    return;
  }

  max = atoi(buf2);

  while(*p && !isspace(*p))
    p++;

  if(!*p)
    step = 1;
  else
  {
    p++;
    strcpy(buf, "STEP");
    for(b = buf;*b;p++, b++)
    {
      if(toupper(*p) != *b)
      {
        syntax_error();
        return;
      }
    }
    if(!isspace(*p++))
    {
      syntax_error();
      return;
    }
    if(parse_output(p, buf) != 1)
    {
      syntax_error();
      return;
    }
    step = atoi(buf);
  }

  deep = 0;

  for(pline = code_ptr->pline->next;pline;pline = pline->next)
  {
    if((p = strchr(pline->code, ' ')))
    {
      strncpy(buf, pline->code, p-pline->code);
      *(buf+(p-pline->code)) = '\0';
    }
    else
      strcpy(buf, pline->code);

    if(!string_compare(buf, "if"))
      deep++;
    else if(!string_compare(buf, "endif"))
      deep--;
    else if(!string_compare(buf, "for"))
      deep++;
    else if(!string_compare(buf, "next"))
      if(!deep--)
        break;
  }

  if(!pline)
  {
    notify(code_ptr->executor,
      tprintf("|+R|FOR without NEXT in |+Y|%d|+R|.",
      code_ptr->pline->line));
    notify(code_ptr->executor, tprintf("%d %s",
      code_ptr->pline->line, code_ptr->pline->code));
    end_program(code_ptr->program, 0);
    return;
  }

  add_for(max, step, pline, var);
}

void sc_do_next()
{
  SCFOR *freeme;

  if(!code_ptr->forlist)
  {
    notify(code_ptr->executor,
      tprintf("|+R|NEXT without FOR in |+Y|%d|+R|.",
      code_ptr->pline->line));
    notify(code_ptr->executor, tprintf("%d %s",
      code_ptr->pline->line, code_ptr->pline->code));
    end_program(code_ptr->program, 0);
    return;
  }

  code_ptr->forlist->var->nvalue += code_ptr->forlist->step;

  if(code_ptr->forlist->var->nvalue > code_ptr->forlist->max)
  {
    code_ptr->pline = code_ptr->forlist->pnext;

    freeme = code_ptr->forlist;
    code_ptr->forlist = code_ptr->forlist->next;
    stack_free(freeme);

    return;
  }

  code_ptr->pline = code_ptr->forlist->top;
}

void sc_do_wait(char *arg)
{
  int sec;

  if(!*arg)
  {
    syntax_error();
    return;
  }

  sec = atoi(arg);

  if(sec < 1 || sec > 65535)
  {
    syntax_error();
    return;
  }

  code_ptr->wait = now+sec;
}

void sc_do_end()
{
  end_program(code_ptr->program, 1);
}

void sc_do_goto(char *arg)
{
  PROGLINE *pline;
  int linenum;

  linenum = atoi(arg);

  for(pline = code_ptr->program->program;pline;pline = pline->next)
    if(pline->line == linenum)
      break;

  if(!pline)
  {
    notify(code_ptr->executor,
      tprintf("|+R|Invalid line reference in |+Y|%d|+R|.",
      code_ptr->pline->line));
    notify(code_ptr->executor, tprintf("%d %s",
      code_ptr->pline->line, code_ptr->pline->code));
    end_program(code_ptr->program, 0);
    return;
  }

  code_ptr->pline = pline;
  code_ptr->advance = 0;
}

void sc_do_gosub(char *arg)
{
  SCGOSUB *newgosub;
  PROGLINE *pline, *pline2;
  int linenum;
  int deep = 0;
  char *command;

  linenum = atoi(arg);

  for(pline = code_ptr->program->program;pline;pline = pline->next)
    if(pline->line == linenum)
      break;

  if(!pline)
  {
    notify(code_ptr->executor,
      tprintf("|+R|Invalid line reference in |+Y|%d|+R|.",
      code_ptr->pline->line));
    notify(code_ptr->executor, tprintf("%d %s",
      code_ptr->pline->line, code_ptr->pline->code));
    end_program(code_ptr->program, 0);
    return;
  }

  for(pline2 = code_ptr->pline->next;pline2;pline2 = pline2->next)
  {
    command = get_command(pline2->code);

    if(!string_compare(command, "gosub"))
      deep++;
    else if(!string_compare(command, "return"))
      if(!deep--)
        break;
  }

  if(!pline2)
  {
    notify(code_ptr->executor,
      tprintf("|+R|GOSUB without RETURN in |+Y|%d|+R|.",
      code_ptr->pline->line));
    notify(code_ptr->executor, tprintf("%d %s",
      code_ptr->pline->line, code_ptr->pline->code));
    end_program(code_ptr->program, 0);
    return;
  }

  newgosub = (SCGOSUB *)stack_alloc(sizeof(SCGOSUB), 1, 0);

  newgosub->top = code_ptr->pline;
  newgosub->next = code_ptr->gosublist;
  code_ptr->gosublist = newgosub;
  code_ptr->pline = pline;
  code_ptr->advance = 0;
}

void sc_do_return()
{
  SCGOSUB *freeme;

  if(!code_ptr->gosublist)
  {
    notify(code_ptr->executor,
      tprintf("|+R|RETURN not within GOSUB in |+Y|%d|+R|.",
      code_ptr->pline->line));
    notify(code_ptr->executor, tprintf("%d %s",
      code_ptr->pline->line, code_ptr->pline->code));
    end_program(code_ptr->program, 0);
    return;
  }

  code_ptr->pline = code_ptr->gosublist->top;
  freeme = code_ptr->gosublist;
  code_ptr->gosublist = code_ptr->gosublist->next;
  stack_free(freeme);
}