/*{{{}}}*/
/*{{{  #includes*/
#define _POSIX_SOURCE   1
#define _POSIX_C_SOURCE 2

#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "cat.h"
#include "default.h"
#include "misc.h"
#include "scanner.h"
/*}}}  */

/*{{{  string    -- match quoted string and return token*/
static Token *string(const char **s)
{
  const char *r;

  r=*s;
  if (**s=='"')
  {
    ++(*s);
    while (**s!='\0' && **s!='"') if (**s=='\\' && *((*s)+1)!='\0') (*s)+=2; else ++(*s);
    if (**s=='\0') { *s=r; return 0; }
    else 
    {
      Token *n;
      char *t;

      ++(*s);
      n=malloc(sizeof(Token));
      n->type=STRING;
      t=n->u.string=malloc((size_t)(*s-r));
      /* Clean string of quotes.  This may waste a few bytes, so? */
      ++r;
      while (r<(*s-1)) if (*r=='\\') { *t++=*(r+1); r+=2; } else *t++=*r++;
      *t='\0';
      return n;
    }
  }
  else return (Token*)0;
}
/*}}}  */
/*{{{  integer   -- match an unsigned integer and return token*/
static Token *integer(const char **s)
{
  const char *r;
  long i;

  r=*s;
  i=posnumber(r,s);
  if (*s!=r && **s!='.' && **s!='e')
  {
    Token *n;

    n=malloc(sizeof(Token));
    n->type=INT;
    n->u.integer=i;
    return n;
  }
  else { *s=r; return (Token*)0; }
}
/*}}}  */
/*{{{  flt       -- match a floating point number*/
static Token *flt(const char **s)
{
  /*{{{  variables*/
  const char *t;
  Token *n;
  double x;
  /*}}}  */

  t=*s;
  x=strtod(t,(char**)s);
  if (t!=*s && isfinite(x)==(const char*)0)
  {
    n=malloc(sizeof(Token));
    n->type=FLOAT;
    n->u.flt=x;
    return n;
  }
  else
  {
    *s=t;
    return (Token*)0;
  }
}
/*}}}  */
/*{{{  operator  -- match an operator and return token*/
static Token *operator(const char **s)
{
  Token *n;
  Operator op;

  switch (**s)
  {
    case '+': op=PLUS; break;
    case '-': op=MINUS; break;
    case '*': op=MUL; break;
    case '/': op=DIV; break;
    case '(': op=OP; break;
    case ')': op=CP; break;
    case ',': op=COMMA; break;
    case '^': op=POW; break;
    case '<': if (*(*s+1)=='=') { ++(*s); op=LE; } else op=LT; break;
    case '>': if (*(*s+1)=='=') { ++(*s); op=GE; } else op=GT; break;
    case '=': if (*(*s+1)=='=') { ++(*s); op=EQ; } else return (Token*)0; break;
    case '!': if (*(*s+1)=='=') { ++(*s); op=NE; } else return (Token*)0; break;
    default: return (Token*)0;
  }
  n=malloc(sizeof(Token));
  n->type=OPERATOR;
  n->u.operator=op;
  ++(*s);
  return n;
}
/*}}}  */
/*{{{  ident     -- match an identifier and return token*/
static Token *ident(const char **s)
{
  const char *begin;
  Token *result;
  
  if (isalpha(**s) || **s=='_' || **s=='@' || **s=='&' || **s=='.')
  {
    begin=*s; ++(*s);
    while (isalpha(**s) || **s=='_' || **s=='@' || **s=='&' || **s=='.' || isdigit(**s)) ++(*s);
    result=malloc(sizeof(Token));
    result->type=IDENT;
    result->u.ident=malloc((size_t)(*s-begin+1));
    strncpy(result->u.ident,begin,(size_t)(*s-begin)); result->u.ident[*s-begin]='\0';
    return result;
  }
  return (Token*)0;
}
/*}}}  */

/*{{{  scan      -- scan string into tokens*/
Token **scan(const char **s)
{
  /*{{{  variables*/
  Token **na,*n;
  const char *r;
  int i,j;
  /*}}}  */

  /*{{{  compute number of tokens*/
  r=*s;
  while (*r==' ') ++r;
  for (i=0; *r!='\0'; ++i)
  {
    const char *or;

    or=r;
    while (*r==' ') ++r;  
    n=string(&r);
    if (n==(Token*)0) n=operator(&r);
    if (n==(Token*)0) n=integer(&r);
    if (n==(Token*)0) n=flt(&r);
    if (n==(Token*)0) n=ident(&r);
    if (or==r) { *s=r; return (Token**)0; }
  }
  /*}}}  */
  /*{{{  allocate token space*/
  na=malloc(sizeof(Token*)*(i+1));
  /*}}}  */
  /*{{{  store tokens*/
  r=*s;
  while (*r==' ') ++r;
  for (j=0; j<i; ++j) 
  {
    while (*r==' ') ++r;    
    n=string(&r);
    if (n==(Token*)0) n=operator(&r);  
    if (n==(Token*)0) n=integer(&r);
    if (n==(Token*)0) n=flt(&r);
    if (n==(Token*)0) n=ident(&r);
    na[j]=n;
  }
  na[j]=(Token*)0;
  /*}}}  */
  return na;
}
/*}}}  */
/*{{{  print     -- print token sequence*/
void print(char *s, size_t size, int star, int quote, int scientific, int precision, Token **n)
{
  int cur;
  
  cur=0;
  if (n!=(Token**)0) for (; cur<size-1 && (*n)!=(Token*)0; ++n) switch ((*n)->type)
  {
    /*{{{  EMPTY*/
    case EMPTY: assert(cur==0); *(s+cur)='\0'; ++cur; break;
    /*}}}  */
    /*{{{  STRING*/
    case STRING:
    {
      char *str=(*n)->u.string;

      if (quote)
      {
        *(s+cur)='"';
        ++cur;
      }
      while (cur<size-1 && *str!='\0') 
      {
        if (quote) if (*str=='"' || *str=='\\') *(s+cur++)='\\';
        if (cur<(size-1)) *(s+cur++)=*str;
        ++str;
      }
      if (quote)
      {
        if (cur<(size-1)) *(s+cur)='"';
        ++cur;
      }
      break;
    }
    /*}}}  */
    /*{{{  INT*/
    case INT:
    {
      char buf[20];

      sprintf(buf,"%ld",(*n)->u.integer);
      assert(strlen(buf)<sizeof(buf));
      strncpy(s+cur,buf,size-cur-1);
      cur+=strlen(buf);
      break;
    }  
    /*}}}  */
    /*{{{  FLOAT*/
    case FLOAT:
    {
      /*{{{  variables*/
      char buf[1024],*p;
      /*}}}  */

      sprintf(buf,scientific ? "%.*e" : "%.*f",precision==-1 ? DEF_REALPRECISION : precision, (*n)->u.flt);
      assert(strlen(buf)<sizeof(buf));
      if (!scientific && precision==-1)
      {
        p=&buf[strlen(buf)-1];
        while (p>buf && *p=='0' && *(p-1)!='.') { *p='\0'; --p; }
      }
      p=buf+strlen(buf);
      while (*--p==' ') *p='\0';
      strncpy(s+cur,buf,size-cur-1);
      cur+=strlen(buf);
      break;
    }
    /*}}}  */
    /*{{{  OPERATOR*/
    case OPERATOR:
    {
      static const char *ops[]={ "+", "-", "*", "/", "(", ")", ",", "<", "<=", ">=", ">", "==", "!=", "^" };
      
      strncpy(s+cur,ops[(*n)->u.operator],size-cur-1);
      cur+=strlen(ops[(*n)->u.operator]);
      break;
    }
    /*}}}  */
    /*{{{  IDENT*/
    case IDENT:
    {
      strncpy(s+cur,(*n)->u.ident,size-cur-1);  
      cur+=strlen((*n)->u.ident);
      break;
    }
    /*}}}  */
    /*{{{  LOCATION*/
    case LOCATION:
    {
      char buf[60];

      sprintf(buf,"&(%d,%d,%d)",(*n)->u.location[0],(*n)->u.location[1],(*n)->u.location[2]);
      strncpy(s+cur,buf,size-cur-1);
      cur+=strlen(buf);
      break;
    }
    /*}}}  */
    /*{{{  EEK*/
    case EEK:
    {
      strncpy(s+cur,OHWELL,size-cur-1);
      cur+=5;
      break;
    }
    /*}}}  */
    /*{{{  default*/
    default: assert(0);
    /*}}}  */
  }
  if (cur<size) *(s+cur)='\0';
  else
  {
    if (star) for (cur=0; cur<(size-1); ++cur) *(s+cur)='*';
    *(s+size-1)='\0';
  }
}
/*}}}  */
