/* This file is part of the Project Athena Zephyr Notification System.
 * Created by: Mark W. Eichin <eichin@athena.mit.edu>
 * $Source: /mit/zephyr/src/zwgc/RCS/parse.c,v $
 * $Author: eichin $
 *
 *	Copyright (c) 1988 by the Massachusetts Institute of Technology.
 *	For copying and distribution information, see the file
 *	"mit-copyright.h". 
 */
#include <zephyr/mit-copyright.h>
#ifndef lint
static char rcsid[] = "$Header: parse.c,v 2.8 88/08/03 18:07:31 eichin Exp $";
#endif lint

/*
 * parse.c
 */
#include "ropes.h"
#include "support.h"
#include "comp_types.h"
  
parse_err(descr, tok, toklen)
     char *descr, *tok;
     int toklen;
{
  char tokcp[BUFSIZ];
  strncpy(tokcp, tok, toklen);
  tokcp[toklen] = '\0';
  printf("zwgc description file error (%s:%s)\n",descr, tokcp);
}

rope *parselit_raw();

rope *parselit(arglen, args)
     int arglen;
     char *args;
{
  return(parselit_raw(ropinit(ROPE_SIZE), arglen, args));
}

rope *parselit_raw(arg_ro, arglen, args)
     rope *arg_ro;
     int arglen;
     char *args;
{
  char *last_arg = args;
  int is_variable=0;
  rope loc_ro;
  
  while(arglen)
    {
      if(is_blank(*args))
	{
	  last_arg = args;
	  while(arglen && is_blank(*args))
	    {
	      args++;
	      arglen--;
	    }
	  loc_ro = tokenize_raw(last_arg, args-last_arg);
	  ropappend(arg_ro, loc_ro, ROPE_SIZE);
	}
      if(is_magic(*args))
	{
	  switch(*args)
	    {
	    case '$':
	      is_variable = TRUE;
	      break;
	    default:
	      printf("zwgc:parse Unknown magic char:<%c>\n",*args);
	      abort();
	    }
	}
      last_arg = args;
      while(arglen && !is_blank(*args)
	    && (args==last_arg || !is_magic(*args))
	    && ((args==last_arg) || /* the $ is ok... */
		(!is_variable) || /* must restrict what are variable names */
		(((args-last_arg)>1)?isalnum(*args):is_symchar(*args)))
	    )
	{
	  args++;
	  arglen--;
	}
      if(arglen || (last_arg != args))
	{
	  loc_ro = is_variable?
	    tokenize_variable(last_arg, args-last_arg)
	      :tokenize_raw(last_arg, args-last_arg);
	  ropappend(arg_ro, loc_ro, ROPE_SIZE);
	  is_variable = 0;
	}
    }
  return(arg_ro);
}

rope *parsefields(arglen, args)
     int arglen;
     char *args;
{
  char *last_arg = args;
  rope loc_ro, *arg_ro;
  
  arg_ro = ropinit(ROPE_SIZE);
  for(;arglen--;args++)
    {
      if(arglen && !is_blank(*args)) continue;
      
      if(is_blank(*args) || (!arglen && args++))
	{			/* use , instead? */
	  if(last_arg != args)
	    {
	      loc_ro = tokenize_variable(last_arg, args-last_arg);
	      ropappend(arg_ro, loc_ro, ROPE_SIZE);
	    }
	  last_arg=args+1;	/* skip the blank... */
	}	      
    }
  return(arg_ro);
}
rope *parseset(arglen, args)
     int arglen;
     char *args;
{
  /* this one is like fields and lit: get one field, the rest is lit. */
  char *last_arg = args;
  rope loc_ro, *arg_ro;
  
  arg_ro = ropinit(ROPE_SIZE);
  /* first get one variable name (the destination of assignment) */
  for(;arglen--;args++)
    {
      if(arglen && !is_blank(*args)) continue;
      
      if(is_blank(*args) || (!arglen && args++))
	{			/* use , instead? */
	  if(last_arg != args)
	    {
	      loc_ro = tokenize_variable(last_arg, args-last_arg);
	      ropappend(arg_ro, loc_ro, ROPE_SIZE);
	      args++;		/* since we are getting out */
	      break;
	    }
	  last_arg=args;
	}	      
    }
  /* now get the literal value of the assignment */
  arg_ro = parselit_raw(arg_ro, arglen, args);
  return(arg_ro);
}

rope *parse_clause();

rope *parse_me(inv)
     char **inv;
{
  return(parse_clause(ropinit(ROPE_SIZE), inv));
}

rope *parse_clause(linerope,inv)
     rope *linerope;
     char **inv;
{
  /*
   * rope *parse_string()
   *
   * returns a rope for the grammar. The grammar is:
   * <DOES>	:: Does <expr> \n <lines\n> enddoes \n
   * 		;; (CMD.DOES <expr> <lines>)
   * <MATCH>	:: match <expr> \n <lines\n> endmatch \n
   * 		;; (CMD.MATCH <expr> <lines>)
   * <lines>	:: [<line>\n]+
   * 		;; <line> <line> ... <line>
   * <line>	:: <DOES> | <MATCH> | <SHOW>
   * 		:: <SUMM> | <FIELDS> | <SET>
   * 		:: <BUTTON> | <EXEC> | <EXEC>
   * 		;; (The ``lisp'' should be straightforward...)
   * <SHOW>	:: show\n <showexpr> endshow\n
   * 
   */
#define LEXIT_EOF   0
#define LEXIT_OK    1
#define LEXIT_SLURP 2

  char *str = *inv;
  char *cmnd, *args;
  int cmndlen, arglen;
  /*   rope *linerope; */
  rope cmnd_ro, *arg_ro;
  int stat;

  /*   linerope = ropinit(ROPE_SIZE); */
  
  while((stat = lexit(&str, &cmnd, &cmndlen, &args, &arglen)))
    {
      if(stat == LEXIT_SLURP) continue;
      /* ok, we have a line now... */
      cmnd_ro = tokenize_command(cmnd, cmndlen);
/* new arg procs here */
      switch(cmnd_ro.ind.ex)
	{
	  /* First, those with *no* arguments */
	case CMD_SHOW:
	case CMD_EXIT:
	case CMD_ENDSHOW:
	case CMD_ENDDOES:
	case CMD_ENDMATCH:
	  arg_ro = NULL;
	  break;
	  /* next, those with literal like arguments */
	case CMD_SUMMARY:
	case CMD_EXEC:
	case CMD_DOES:
	case CMD_MATCH:
	  arg_ro = parselit(arglen, args);
	  break;
	  /* button is special */
	case CMD_BUTTON:
	  arg_ro = NULL;
	  break;
	  /* fields is all variables */
	case CMD_FIELDS:
	  arg_ro = parsefields(arglen, args);
	  break;
	  /* set is var, then literal... */
	case CMD_SET:
	  arg_ro = parseset(arglen, args);
	  break;
	case CMD_BEEP:
	  arg_ro = NULL;	/* parse an arg eventually... */
	  break;
	default:
	  parse_err("Invalid Command token", cmnd, cmndlen);
	  *inv = str;
	  ropfree(linerope);	/* another MPROF special */
	  return(NULL);		/* is this right??? */
	}
/* end of new arg procs... */      

      ropappend(linerope, cmnd_ro, ROPE_SIZE);
    
      if(arg_ro)
	{
	  rope tmp_ro;

	  tmp_ro.typ = ROPE;
	  tmp_ro.ind.rope = arg_ro;

	  ropappend(linerope, tmp_ro, ROPE_SIZE);
	}
      switch(cmnd_ro.ind.ex)
	{
	case CMD_SHOW:
	  /* Well, show gets some special processing... */
	  {
	    rope tmp_ro;
	    rope *parse_show();
	
	    tmp_ro.typ = ROPE;
	    tmp_ro.ind.rope = parse_show(&str);

	    ropappend(linerope, tmp_ro, ROPE_SIZE);
	  }
	/* explicitly fall through to what BEEP does... */
      
	case CMD_EXEC:
	case CMD_EXIT:
	case CMD_BUTTON:
	case CMD_FIELDS:
	case CMD_SET:
	case CMD_SUMMARY:
	case CMD_BEEP:
	  /* add more simple commands here... */
	  *inv = str;
	  return(linerope);
	case CMD_ENDSHOW:
	case CMD_ENDDOES:
	case CMD_ENDMATCH:
	  {
	    *inv = str;
	    ropfree(linerope);	/* another MPROF special */
	    return(NULL);	/* let it know they are done */
	  }
	
	case CMD_DOES:
	case CMD_MATCH:
	  while(*str)		/* fall off the end... */
	    {
	      rope tmp_ro;

	      tmp_ro.typ = ROPE;
	      tmp_ro.ind.rope = parse_me(&str);
	  
	      if(!tmp_ro.ind.rope) break;
	  
	      ropappend(linerope, tmp_ro, ROPE_SIZE);
	    }
	  *inv = str;
	  return(linerope);
	default:
	  printf("zwgc:parse not a command at line start, and fell through first half\n");
	  abort();
	}

    }
  *inv = str;			/* falling off the end. *inv=NULL? */
  ropfree(linerope);		/* another MPROF special */
  return(NULL);
}
	  
	  
rope *parse_string(inv)
     char **inv;
{
  rope *linerope, rops;

  linerope=ropinit(ROPE_SIZE);
  
  while(**inv)
    {
      rops.typ = ROPE;
      rops.ind.next = parse_me(inv);
      if(rops.ind.next) {
	ropappend(linerope, rops, ROPE_SIZE);
      } else {
	break;
      }
    }
  return(linerope);
}
