/* parser.c - recursive-descent parser for nawm configuration language */

/* Copyright (C) 1999 by the Massachusetts Institute of Technology.
 *
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in
 * advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 */

static int skip(int token)
{
  int got = next_token();

  if (got != token)
    die("Got %s while expecting %s.", tokenname(got), tokenname(token));
  return got;
}

static void parse_nawmrc(void)
{
  int tok = next_token();

  switch (tok)
    {
    case INCLUDE:
      skip(STR);
      load_module(yytext);
      skip(';');
      break;

    case OPTION:
      skip(STR);
      do_option(yytext);
      skip(';');
      break;

    case MODE:
      parse_mode();
      break;

    case DTYPE:
      parse_vardecl(tok);
      break;

    case COMMAND:
    case FUNCTION:
      parse_fundecl();
      break;

    default:
      b = parse_binding(tok);
      if (b)
	add_to_anymode(b);
      else
	die("Unexpected token '%s' at top level of nawmrc.", tokenname(tok));
      break;
    }
}

binding *parse_binding(int tok)
{
  switch (tok)
    {
    case KEYPRESS:
    case KEYRELEASE:
      return parse_key_binding(tok);

    case BUTTONPRESS:
    case BUTTONRELEASE:
      return parse_button_binding(tok);

    case MOTION:
      return parse_motion_binding();

    case ENTER:
    case LEAVE:
      return parse_crossing_binding(tok);

    case BEGIN:
    case END:
      return parse_beginend_binding(tok);

    default:
      return NULL;
    }
}

static void parse_mode(void)
{
  char *name;
  bindings *b = NULL, *btmp;

  tok = skip(STR);
  name = strdup(yytext);
  pushlexscope(0);
  skip('{');
  while (1)
    {
      tok = next_token();
      if (tok == '}')
	break;

      switch (tok)
	{
	case DTYPE:
	  parse_vardecl(tok);
	  break;

	default:
	  btmp = parse_binding(tok);
	  if (!btmp)
	    die("Unexpected token '%s' in mode body.", tokenname(tok));
	  btmp->next = b;
	  b = btmp;
	  break;
	}
    }
  defmode(name, b);
}

static node *parse_command(void)
{
  int tok = peek_token();

  switch (tok)
    {
    case IF:
      return parse_if();

    case FOR:
      return parse_for();

    case WHILE:
      return parse_while();

    case DO:
      return parse_do();

    case BREAK:
    case CONTINUE:
      return parse_loop_control(tok);

    case RETURN:
      return parse_return();

    case DEL:
      return parse_del();

    case '{':
      return parse_commandlist();

    default:
      return parse_expr();
    }
}

static node *parse_if(void)
{
  node *expr, *then;

  skip(IF);
  skip('(');
  expr = typecheck(T_BOOL, parse_expr(), "if condition");
  skip(')');
  th = parse_command();
  if (peek_token() == ELSE)
    return mknode(IF, 0, 3, expr, then, parse_command());
  else
    return mknode(IF, 0, 2, expr, then);
}

static node *parse_for(void)
{
  node *e1, *e2, *e3, *ans;

  skip(FOR);
  skip('(');
  e1 = parse_expr();
  if (peek_token() == ')' && e1->type == IN && is_lvalue(e1->vals[0]))
    {
      skip(')');
      loop_nesting++;
      ans = mknode(FOR, 0, 2, e1, parse_command());
    }
  else
    {
      skip(';');
      e2 = typecheck(T_BOOL, parse_expr(), "for condition");
      skip(';');
      e3 = parse_expr();
      skip(')');
      loop_nesting++;
      ans = mknode(FOR, 0, 4, e1, e2, e3, parse_command());
    }
  loop_nesting--;
  return ans;
}

static node *parse_while(void)
{
  node *expr, *ans;

  skip(WHILE);
  skip('(');
  expr = typecheck(T_BOOL, parse_expr(), "while condition");
  skip(')');
  loop_nesting++;
  ans = mknode(WHILE, 0, 2, expr, parse_command());
  loop_nesting--;
  return ans;
}

static node *parse_do(void)
{
  node *body, *expr;

  skip(DO);
  loop_nesting++;
  body = parse_command();
  loop_nesting--;
  skip(WHILE);
  skip('(');
  expr = typecheck(T_BOOL, parse_expr(), "do condition");
  skip(')');
  skip(';');
  return mknode(DO, 0, 2, expr, body);
}

/* break/continue */
static node *parse_loop_control(int token)
{
  node *ans;

  if (!loop_nesting)
    die("Found %s outside of loop.", tokenname(token));

  ans = mknode(tok, 0, 0);
  skip(';');
  return ans;
}

static node *parse_return(void)
{
  node *expr;

  if (!in_function)
    die("Found 'return' outside of function body.");

  if (return_type == T_VOID)
    {
      skip(';');
      return mknode(RETURN, 0, 0);
    }
  else
    {
      expr = typecheck(return_type, parse_expr(), "return value");
      skip(';');
      return mknode(RETURN, 0, 1, expr);
    }
}

static node *parse_expr(void)
{
  node *left = parse_bool_expr(), *right;
  int tok;

  if (IS_ASSIGNOP(peek_token()) && is_lvalue(left))
    {
      tok = next_token();
      right = parse_expr();
      return mknode(tok, left->etype, 2, left,
		    typecheck(left->etype, right, "assignment"));
    }
  else
    return left;
}

#define GENPARSE(parsetype, lefttype, optype) \
static node *parse_##parsetype(void)\
{\
  node *left = parse_##lefttype(), *right;\
  int tok;\
\
  if (IS_##optype(peek_token()))\
    {\
      tok = next_token();\
      right = parse_##parsetype();\
      return op_typecheck(tok, left, right);\
    }\
  else\
    return left;\
}

GENPARSE(bool_expr, comp_expr, BOOLOP)
GENPARSE(comp_expr, memb_expr, COMPOP)
GENPARSE(memb_expr, add_expr, INOP)
GENPARSE(add_expr, mult_expr, ADDOP)
GENPARSE(mult_expr, unary_expr, MULTOP)

static node *parse_unary_expr(void)
{
  node *expr;
  int tok = peek_token();

  if (IS_ADDOP(tok) || IS_UNOP(tok))
    {
      next_token();
      return op_typecheck(tok, parse_unary_expr(), NULL);
    }
  else
    return parse_subscript_expr();
}

static node *parse_subscript_expr(void)
{
  node *expr = parse_primary_expr(), *subexpr;
  int tok = peek_token();

  switch (tok)
    {
    case '[':
      skip('[');
      subexpr = parse_expr();
      skip(']');
      return mknode(SUBSCRIPT, XXX, 2, expr, subexpr);

    case '.':
      skip('.');
      tok = skip(SYM);
      return mknode(ELEMENT, XXX, 2, expr, XXX_ytext);

    case '(':
      skip('(');
      subexpr = parse_args();
      skip(')');
      return mknode(APPLY, XXX, 2, expr, subexpr);

    default:
      return expr;
    }
}

static node *parse_primary_expr(void)
{
  int tok = peek_token();
  node *ans;

  switch (tok)
    {
    case NUM:
      return mknode(NUM, T_INT, 1, XXX_yytext);

    case STR:
      return mknode(STR, T_STR, 1, XXX_yytext);

    case SYM:
      return lookup(XXX);

    case '(':
      skip('(');
      ans = parse_expr();
      skip(')');
      return ans;

    default:
      error(XXX);
    }
}
