/* -*- Mode: C; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* vim: set filetype=c tabstop=2 shiftwidth=2 expandtab: */

/*
 * dpp.c - defun preprocessor
 *
 * Copyright (c) 1984 Taiichi Yuasa and Masami Hagiya
 * Copyright (c) 1990 Giuseppe Attardi
 * Copyright (c) 2001 Juan Jose Garcia Ripoll
 *
 * See file 'LICENSE' for the copyright details.
 *
 */

/*
 *      Usage:
 *              dpp [in-file [out-file]]
 *
 *      The file named in-file is preprocessed and the output will be
 *      written to the file whose name is out-file. If in-file is "-"
 *      program is read from standard input, while if out-file is "-"
 *      C-program is written to standard output.
 *
 *
 *      The function definition:
 *
 *      @(defun name ({var}*
 *                    [&optional {var | (var [initform [svar]])}*]
 *                    [&rest var]
 *                    [&key {var |
 *                           ({var | (keyword var)} [initform [svar]])}*
 *                          [&allow_other_keys]]
 *                    [&aux {var | (var [initform])}*])
 *
 *              C-declaration
 *
 *      @ {
 *
 *              C-body
 *
 *      } @)
 *
 *      name is the name of the lisp function
 *
 *      &optional may be abbreviated as &o.
 *      &rest may be abbreviated as &r.
 *      &key may be abbreviated as &k.
 *      &allow_other_keys may be abbreviated as &aok.
 *      &aux may be abbreviated as &a.
 *
 *      Each variable becomes a C variable.
 *
 *      Each supplied-p parameter becomes a boolean C variable.
 *
 *      Initforms are C expressions.
 *      If an expression contains non-alphanumeric characters,
 *      it should be surrounded by backquotes (`).
 *
 *
 *      Function return:
 *
 *              @(return {form}*);
 *
 *      Return function expands into a lexical block {}, so if it's
 *      used inside IF/ELSE, then it should be enclosed, even if we
 *      use sole @(return);, because ";" will be treated as the next
 *      instruction.
 *
 *      Symbols:
 *
 *      @'name'
 *
 *      Expands into a C statement, whole value is the given symbol
 *      from symbols_list.h
 *
 *      @[name]
 *
 *      Expands into a C statement, whole value is a fixnum
 *      corresponding to the index in the builtin symbols table of the
 *      given symbol from symbols_list.h. Used for handling type errors.
 */

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

#if defined(_MSC_VER) && (_MSC_VER >= 1800)
#include <stdbool.h> 
#endif

#define DPP
#include <ecl/config.h>
#include <ecl/config-internal.h>
#include "symbols_list2.h"

/* #define POOLSIZE        2048 */
#define POOLSIZE        4096
#define MAXREQ          16
#define MAXOPT          16
#define MAXKEY          16
#define MAXAUX          16
#define MAXRES          16

#define TRUE            1
#define FALSE           0

#ifndef __cplusplus
#if ! ( defined(__bool_true_false_are_defined) \
        &&__bool_true_false_are_defined )
typedef int bool;
#endif
#endif

FILE *in, *out;

char filename[BUFSIZ];
int lineno;
int tab;
int tab_save;

char pool[POOLSIZE];
char *poolp;

char *function;
int   function_code;
char *function_symbol;
char *function_c_name;

char *required[MAXREQ];
int nreq;

int the_env_defined = 0;
enum vararg_status_t {
	VARARG_NOT_DEFINED,
	VARARG_SIMPLE,
	VARARG_ECL};
enum vararg_status_t vararg_status = VARARG_NOT_DEFINED;

struct optional {
  char *o_var;
  char *o_init;
  char *o_svar;
} optional[MAXOPT];
int nopt;

bool rest_flag;
char *rest_var;

bool key_flag;
struct keyword {
  char *k_key;
  char *k_var;
  char *k_init;
  char *k_svar;
} keyword[MAXKEY];
int nkey;
bool allow_other_keys_flag;

struct aux {
  char *a_var;
  char *a_init;
} aux[MAXAUX];
int naux;

char *result[MAXRES];
int nres;

void
put_lineno(void)
{
  static int flag = 0;
  if (flag)
    fprintf(out, "#line %d\n", lineno);
  else {
    flag++;
    fprintf(out, "#line %d \"%s\"\n", lineno, filename);
  }
}

void
error(char *s, ...)
{
  char msg[2048];
  va_list args;
  va_start(args, s);
  vsnprintf(msg, 2048, s, args);
  printf("Error in line %d: %s.\n", lineno, msg);
  va_end(args);
  exit(1);
}

void
error_symbol(char *s)
{
  printf("Error in line %d: illegal symbol %s.\n", lineno, s);
  exit(1);
}

int
readc(void)
{
  int c;

  c = getc(in);
  if (feof(in)) {
    if (function != NULL)
      error("unexpected end of file");
    exit(0);
  }
  if (c == '\n') {
    lineno++;
    tab = 0;
  } else if (c == '\t')
    tab++;
  return(c);
}

int
nextc(void)
{
  int c;

  while (isspace(c = readc()))
    ;
  return(c);
}

void
unreadc(int c)
{
  if (c == '\n')
    --lineno;
  else if (c == '\t')
    --tab;
  ungetc(c, in);
}

void
put_tabs(int n)
{
  put_lineno();
  while (n--)
    putc('\t', out);
}

void
pushc(int c)
{
  if (poolp >= &pool[POOLSIZE])
    error("buffer pool overflow");
  *poolp++ = c;
}

void
pushstr(const char *s)
{
  while (*s)
    pushc(*(s++));
}

int
search_keyword(const char *name)
{
  int i;
  char c[256];

  for (i=0; name[i] && i<255; i++)
    if (name[i] == '_')
      c[i] = '-';
    else
      c[i] = name[i];
  if (i == 255)
    error("Too long keyword");
  c[i] = 0;
  for (i = 0; cl_symbols[i].name != NULL; i++) {
    if (cl_symbols[i].name[0] == ':')
      if (!strcasecmp(c, cl_symbols[i].name+1))
        return i;
  }
  printf("Keyword not found: %s.\n", c);
  return 0;
}

char *
search_symbol(char *name, int *symbol_code, int code)
{
  int i;
  for (i = 0; cl_symbols[i].name != NULL; i++) {
    if (!strcasecmp(name, cl_symbols[i].name)) {
      name = poolp;
      if (code) {
        pushstr("ecl_make_fixnum(/*");
        pushstr(cl_symbols[i].name);
        pushstr("*/");
        if (i >= 1000)
          pushc((i / 1000) % 10 + '0');
        if (i >= 100)
          pushc((i / 100) % 10 + '0');
        if (i >= 10)
          pushc((i / 10) % 10 + '0');
        pushc(i % 10 + '0');
        pushstr(")");
        pushc(0);
      } else if (i == 0) {
        pushstr("ECL_NIL");
        pushc(0);
      } else {
        pushstr("ECL_SYM(\"");
        pushstr(cl_symbols[i].name);
        pushstr("\",");
        if (i >= 1000)
          pushc((i / 1000) % 10 + '0');
        if (i >= 100)
          pushc((i / 100) % 10 + '0');
        if (i >= 10)
          pushc((i / 10) % 10 + '0');
        pushc(i % 10 + '0');
        pushstr(")");
        pushc(0);
      }
      if (symbol_code)
        *symbol_code = i;
      return name;
    }
  }
  return NULL;
}

char *
read_symbol(int code)
{
  char c, *name = poolp;
  char end = code? ']' : '\'';

  c = readc();
  while (c != end) {
    if (c == '_') c = '-';
    pushc(c); 
    c = readc();
  }
  pushc(0);

  name = search_symbol(poolp = name, 0, code);
  if (name == NULL) {
    name = poolp;
    printf("\nUnknown symbol: %s\n", name);
    pushstr("unknown");
  }
  return name;
}

char *
read_string()
{
  char c, *str = poolp;
  char end = '"';
  pushstr("ecl_make_constant_base_string(\"");
  do {
    c = readc();
    pushc(c);
  } while (c != end);
  pushstr(", -1)");
  pushc(0);
  return str;
}

char *
search_function(char *name)
{
  int i;
  for (i = 0; cl_symbols[i].name != NULL; i++) {
    if (cl_symbols[i].translation != NULL &&
        !strcasecmp(name, cl_symbols[i].name)) {
      name = poolp;
      pushstr(cl_symbols[i].translation);
      pushc(0);
      return name;
    }
  }
  return name;
}

char *
read_function()
{
  char c, *name = poolp;

  c = readc();
  if (c == '"') {
    c = readc();
    while (c != '"') {
      pushc(c);
      c = readc();
    }
    pushc(0);
    return name;
  }
  while (c != '(' && !isspace(c) && c != ')' && c != ',') {
    if (c == '_') c = '-';
    pushc(c); 
    c = readc();
  }
  unreadc(c);
  pushc(0);
  return name;
}

char *
translate_function(char *name)
{
  char *output = search_function(name);
  if (output == NULL) {
    printf("\nUnknown function: %s\n", name);
    pushstr("unknown");
    output = poolp;
  }
  return output;
}

char *
read_token(void)
{
  int c;
  int left_paren = 0;
  char *p;

  p = poolp;
  c = readc();
  while (isspace(c))
    c = readc();
  do {
    if (c == '(') {
      left_paren++;
      pushc(c);
    } else if (c == ')') {
      if (left_paren == 0) {
        break;
      } else {
        left_paren--;
        pushc(c);
      }
    } else if (isspace(c) && left_paren == 0) {
      do
        c = readc();
      while (isspace(c));
      break;
    } else if (c == '@') {
      c = readc();
      if (c == '\'') {
        (void)read_symbol(0);
        poolp--;
      } else if (c == '[') {
        (void)read_symbol(1);
        poolp--;
      } else if (c == '@') {
        pushc(c);
      } else if (c == '"') {
        read_string();
        poolp--;
      } else {
        char *name;
        unreadc(c);
        poolp = name = read_function();
        (void)translate_function(poolp);
      }
    } else {
      pushc(c);
    }
    c = readc();
  } while (1);
  unreadc(c);
  pushc('\0');
  return(p);
}

void
reset(void)
{
  int i;

  the_env_defined = 0;
  poolp = pool;
  function = NULL;
  function_symbol = "";
  function_c_name = "";
  nreq = 0;
  for (i = 0;  i < MAXREQ;  i++)
    required[i] = NULL;
  nopt = 0;
  for (i = 0;  i < MAXOPT;  i++)
    optional[i].o_var
      = optional[i].o_init
      = optional[i].o_svar
      = NULL;
  rest_flag = FALSE;
  rest_var = "ARGS";
  key_flag = FALSE;
  nkey = 0;
  for (i = 0;  i < MAXKEY;  i++)
    keyword[i].k_key
      = keyword[i].k_var
      = keyword[i].k_init
      = keyword[i].k_svar
      = NULL;
  allow_other_keys_flag = FALSE;
  naux = 0;
  for (i = 0;  i < MAXAUX;  i++)
    aux[i].a_var
      = aux[i].a_init
      = NULL;
  vararg_status = VARARG_NOT_DEFINED;
}

void
get_function(void)
{
  function = read_function();
  function_symbol = search_symbol(function, &function_code, 0);
  if (function_symbol == NULL) {
    function_symbol = poolp;
    pushstr("ECL_NIL");
    pushc('\0');
  }
  function_c_name = translate_function(function);
}

void
check_nargs(void)
{
  int narg_declared = cl_symbols[function_code].narg;
  int nreq_declared = narg_declared >= 0 ? narg_declared : (-narg_declared -1);
  if (nreq != nreq_declared) {
    error("Function %s: wrong declaration for number of required arguments, expected %d, but got %d",
          cl_symbols[function_code].name, nreq, nreq_declared);
  }
  if (narg_declared > 0 &&
      (nopt > 0 || rest_flag || key_flag)) {
    error("Detected optional, keyword or rest arguments for function with fixed number of arguments");
  }
}

void
get_lambda_list(void)
{
  int c;
  char *p;

  if ((c = nextc()) != '(')
    error("( expected");
  for (;;) {
    if ((c = nextc()) == ')')
      return;
    if (c == '&') {
      p = read_token();
      goto _OPT;
    }
    unreadc(c);
    p = read_token();
    if (nreq >= MAXREQ)
      error("too many required variables");
    required[nreq++] = p;
  }

 _OPT:
  if (strcmp(p, "optional") != 0 && strcmp(p, "o") != 0)
    goto _REST;
  for (;;  nopt++) {
    if ((c = nextc()) == ')')
      return;
    if (c == '&') {
      p = read_token();
      goto _REST;
    }
    if (nopt >= MAXOPT)
      error("too many optional argument");
    if (c == '(') {
      optional[nopt].o_var = read_token();
      if ((c = nextc()) == ')')
        continue;
      unreadc(c);
      optional[nopt].o_init = read_token();
      if ((c = nextc()) == ')')
        continue;
      unreadc(c);
      optional[nopt].o_svar = read_token();
      if (nextc() != ')')
        error(") expected");
    } else {
      unreadc(c);
      optional[nopt].o_var = read_token();
    }
  }

 _REST:
  if (strcmp(p, "rest") != 0 && strcmp(p, "r") != 0)
    goto _KEY;
  rest_flag = TRUE;
  if ((c = nextc()) == ')' || c == '&')
    error("&rest var missing");
  unreadc(c);
  rest_var = read_token();
  if ((c = nextc()) == ')')
    return;
  if (c != '&')
    error("& expected");
  p = read_token();

 _KEY:
  if (strcmp(p, "key") != 0 && strcmp(p, "k") != 0)
    goto _AUX;
  key_flag = TRUE;
  for (;;  nkey++) {
    if ((c = nextc()) == ')')
      return;
    if (c == '&') {
      p = read_token();
      if (strcmp(p, "allow_other_keys") == 0 ||
          strcmp(p, "aok") == 0) {
        allow_other_keys_flag = TRUE;
        if ((c = nextc()) == ')')
          return;
        if (c != '&')
          error("& expected");
        p = read_token();
      }
      goto _AUX;
    }
    if (nkey >= MAXKEY)
      error("too many optional argument");
    if (c == '(') {
      if ((c = nextc()) == '(') {
        p = read_token();
        if (p[0] != ':' || p[1] == '\0')
          error("keyword expected");
        keyword[nkey].k_key = p + 1;
        keyword[nkey].k_var = read_token();
        if (nextc() != ')')
          error(") expected");
      } else {
        unreadc(c);
        keyword[nkey].k_key
          = keyword[nkey].k_var
          = read_token();
      }
      if ((c = nextc()) == ')')
        continue;
      unreadc(c);
      keyword[nkey].k_init = read_token();
      if ((c = nextc()) == ')')
        continue;
      unreadc(c);
      keyword[nkey].k_svar = read_token();
      if (nextc() != ')')
        error(") expected");
    } else {
      unreadc(c);
      keyword[nkey].k_key
        = keyword[nkey].k_var
        = read_token();
    }
  }

 _AUX:
  if (strcmp(p, "aux") != 0 && strcmp(p, "a") != 0)
    error("illegal lambda-list keyword");
  for (;;) {
    if ((c = nextc()) == ')')
      return;
    if (c == '&')
      error("illegal lambda-list keyword");
    if (naux >= MAXAUX)
      error("too many auxiliary variable");
    if (c == '(') {
      aux[naux].a_var = read_token();
      if ((c = nextc()) == ')')
        continue;
      unreadc(c);
      aux[naux].a_init = read_token();
      if (nextc() != ')')
        error(") expected");
    } else {
      unreadc(c);
      aux[naux].a_var = read_token();
    }
    naux++;
  }
}

void
get_return(void)
{
  int c;

  nres = 0;
  for (;;) {
    if ((c = nextc()) == ')')
      return;
    unreadc(c);
    result[nres++] = read_token();
  }
}

void
put_fhead(void)
{
  int i;

  put_lineno();
  fprintf(out, "cl_object %s(cl_narg narg", function_c_name);
  for (i = 0; i < nreq; i++)
    fprintf(out, ", cl_object %s", required[i]);
  fprintf(out, ", ...)\n{\n");
}

void
put_declaration(void)
{
  int i;
  int simple_varargs;

  put_lineno();
  the_env_defined = 1;
  fprintf(out,
          "#ifdef __clang__\n"
          "#pragma clang diagnostic push\n"
          "#pragma clang diagnostic ignored \"-Wunused-variable\"\n"
          "#endif\n"
          "\tconst cl_env_ptr the_env = ecl_process_env();\n"
          "#ifdef __clang__\n"
          "#pragma clang diagnostic pop\n"
          "#endif\n" );
  for (i = 0;  i < nopt;  i++) {
    put_lineno();
    fprintf(out, "\tcl_object %s;\n", optional[i].o_var);
  }
  for (i = 0;  i < nopt;  i++)
    if (optional[i].o_svar != NULL) {
      put_lineno();
      fprintf(out, "\tbool %s;\n", optional[i].o_svar);
    }
  if (key_flag) {
    put_lineno();
    if (nkey) {
      fprintf(out, "\tstatic cl_object KEYS[%d] = {", nkey);
      for (i = 0; i < nkey; i++) {
        if (i > 0)
          fprintf(out, ", ");
        fprintf(out, "(cl_object)(cl_symbols+%d)", search_keyword(keyword[i].k_key));
      }
      fprintf(out, "};\n");
    } else {
      fprintf(out, "\tcl_object *KEYS = NULL;\n");
    }
  }
  for (i = 0;  i < nkey;  i++) {
    fprintf(out, "\tcl_object %s;\n", keyword[i].k_var);
    if (keyword[i].k_svar != NULL)
      fprintf(out, "\tbool %s;\n", keyword[i].k_svar);
  }
  for (i = 0;  i < naux;  i++) {
    put_lineno();
    fprintf(out, "\tcl_object %s;\n", aux[i].a_var);
  }
  if (nopt == 0 && !rest_flag && !key_flag) {
    put_lineno();
    fprintf(out, "\tif (ecl_unlikely(narg!=%d))", nreq);
    fprintf(out, "\t   FEwrong_num_arguments(ecl_make_fixnum(%d));\n",
            function_code);
  } else {
    simple_varargs = !rest_flag && !key_flag && ((nreq + nopt) < 32);
    if (key_flag) {
      put_lineno();
      /* We do this because Microsoft VC++ does not support arrays of zero size */
      if (nkey) {
        fprintf(out, "\tcl_object KEY_VARS[%d];\n", 2*nkey);
      } else {
        fprintf(out, "\tcl_object *KEY_VARS = NULL;\n");
      }
    }
    put_lineno();
    if (simple_varargs) {
	   vararg_status = VARARG_SIMPLE;
      fprintf(out,"\tva_list %s;\n\tva_start(%s, %s);\n",
              rest_var, rest_var, ((nreq > 0) ? required[nreq-1] : "narg"));
    } else {
	   vararg_status = VARARG_ECL;
      fprintf(out,"\tecl_va_list %s;\n\tecl_va_start(%s, %s, narg, %d);\n",
              rest_var, rest_var, ((nreq > 0) ? required[nreq-1] : "narg"),
              nreq);
    }
    put_lineno();
    fprintf(out, "\tif (ecl_unlikely(narg < %d", nreq);
    if (nopt > 0 && !rest_flag && !key_flag) {
      fprintf(out, "|| narg > %d", nreq + nopt);
    }
    fprintf(out, ")) FEwrong_num_arguments(ecl_make_fixnum(%d));\n", function_code);
    for (i = 0;  i < nopt;  i++) {
      put_lineno();
      fprintf(out, "\tif (narg > %d) {\n", nreq+i);
      put_lineno();
      fprintf(out, simple_varargs?
              "\t\t%s = va_arg(%s,cl_object);\n":
              "\t\t%s = ecl_va_arg(%s);\n",
              optional[i].o_var, rest_var);
      if (optional[i].o_svar) {
        put_lineno();
        fprintf(out, "\t\t%s = TRUE;\n", optional[i].o_svar);
      }
      put_lineno();
      fprintf(out, "\t} else {\n");
      put_lineno();
      fprintf(out, "\t\t%s = %s;\n",
              optional[i].o_var,
              optional[i].o_init == NULL ? "ECL_NIL" : optional[i].o_init);
      if (optional[i].o_svar) {
        put_lineno();
        fprintf(out, "\t\t%s = FALSE;\n", optional[i].o_svar);
      }
      put_lineno();
      fprintf(out, "\t}\n");
    }
    if (key_flag) {
      put_lineno();
      fprintf(out, "\tcl_parse_key(ARGS, %d, KEYS, KEY_VARS, NULL, %d);\n",
              nkey, allow_other_keys_flag);
      for (i = 0;  i < nkey;  i++) {
        put_lineno();
        fprintf(out, "\tif (KEY_VARS[%d]==ECL_NIL) {\n", nkey+i);
        if (keyword[i].k_init != NULL) {
          put_lineno();
          fprintf(out, "\t  %s = %s;\n", keyword[i].k_var, keyword[i].k_init);
        } else {
          put_lineno();
          fprintf(out, "\t  %s = ECL_NIL;\n", keyword[i].k_var);
        }
        if (keyword[i].k_svar != NULL) {
          put_lineno();
          fprintf(out, "\t  %s = FALSE;\n", keyword[i].k_svar);
        }
        fprintf(out, "\t} else {\n");
        if (keyword[i].k_svar != NULL) {
          put_lineno();
          fprintf(out, "\t  %s = TRUE;\n", keyword[i].k_svar);
        }
        put_lineno();
        fprintf(out, "\t  %s = KEY_VARS[%d];\n\t}\n", keyword[i].k_var, i);
      }
    }
  }
  for (i = 0;  i < naux;  i++) {
    put_lineno();
    fprintf(out, "\t%s = %s;\n", aux[i].a_var,
            aux[i].a_init == NULL ? "ECL_NIL" : aux[i].a_init);
  }
}

void unregister_varargs()
{
	if (vararg_status == VARARG_SIMPLE) {
		fprintf(out, "va_end(%s);\n", rest_var);
	}
	else if (vararg_status == VARARG_ECL) {
		fprintf(out, "ecl_va_end(%s);\n", rest_var);
	}
}

void
put_return(void)
{
  int i, t;

  t = tab_save+1;
        
  fprintf(out, "{\n");
  if (!the_env_defined) {
    put_tabs(t);
    fprintf(out, "const cl_env_ptr the_env = ecl_process_env();\n");
  }
  if (nres == 0) {
    fprintf(out, "the_env->nvalues = 0; return ECL_NIL;\n");
  } else {
    put_tabs(t);
    for (i = 0;  i < nres;  i++) {
      put_tabs(t);
      fprintf(out, "cl_object __value%d = %s;\n", i, result[i]);
    }
    put_tabs(t);
    fprintf(out, "the_env->nvalues = %d;\n", nres);
    for (i = nres-1;  i >= 0;  i--) {
      put_tabs(t);
      fprintf(out, "the_env->values[%d] = __value%d;\n", i, i);
    }
    put_tabs(t);
    unregister_varargs();
    put_tabs(t);
    fprintf(out, "return __value0;\n");
  }
  put_tabs(tab_save);
  fprintf(out, "}\n");
}

int
jump_to_at(void)
{
  int c;
 GO_ON:
  while ((c = readc()) != '@')
    putc(c, out);
  if ((c = readc()) == '@') {
    putc(c, out);
    goto GO_ON;
  }
  return c;
}

void
main_loop(void)
{
  int c;
  int in_defun=0;
  char *p;

  lineno = 1;

  reset();
  put_lineno();
 LOOP:
  c = jump_to_at();
  if (c == ')') {
    if (!in_defun)
      error("unmatched @) found");
    in_defun = 0;
    putc('}',out);
    reset();
    goto LOOP;
  } else if (c == '\'') {
    char *p;
    char* tmp = poolp;
    p = read_symbol(0);
    pushc('\0');
    fprintf(out,"%s",p);
    poolp = tmp;
    goto LOOP;
  }  else if (c == '[') {
    char *p;
    char * tmp = poolp;
    p = read_symbol(1);
    pushc('\0');
    fprintf(out,"%s",p);
    poolp = tmp;
    goto LOOP;
  } else if (c == '"') {
    char *p;
    char * tmp = poolp;
    p = read_string();
    fprintf(out,"%s",p);
    poolp = tmp;
    goto LOOP;
  } else if (c != '(') {
    char *p;
    char * tmp = poolp;
    unreadc(c);
    //poolp = pool;
    p = read_function();
    fprintf(out,"%s",translate_function(p));
    poolp = tmp;
    goto LOOP;
  }
  p = read_token();
  if (strcmp(p, "defun") == 0) {
    if (in_defun)
      error("@) expected before new function definition");
    in_defun = 1;
    get_function();
    get_lambda_list();
    check_nargs();
    put_fhead();
    put_lineno();
    c = jump_to_at();
    put_declaration();
    put_lineno();
  } else if (strcmp(p, "return") == 0) {
    tab_save = tab;
    get_return();
    put_return();
  } else
    error_symbol(p);
  goto LOOP;
}

int
main(int argc, char **argv)
{
  char outfile[BUFSIZ];
#ifdef _MSC_VER
  char *p;
#endif
  if (argc < 2 || !strcmp(argv[1],"-")) {
    in = stdin;
    strcpy(filename, "-");
  } else {
    in = fopen(argv[1],"r");
    strncpy(filename, argv[1], BUFSIZ-1);
    filename[BUFSIZ-1] = '\0';
  }
#ifdef _MSC_VER
  /* Convert all backslashes in filename into slashes,
   * to avoid warnings when compiling with MSVC
   */
  for ( p=filename; *p; p++ )
    if ( *p == '\\' )
      *p = '/';
#endif
  if (argc < 3 || !strcmp(argv[2],"-")) {
    out = stdout;
    strcpy(outfile, "-");
  } else {
    out = fopen(argv[2],"w");
    strncpy(outfile, argv[2], BUFSIZ-1);
    outfile[BUFSIZ-1] = '\0';
  }
  if (in == NULL)
    error("can't open input file");
  if (out == NULL)
    error("can't open output file");
  printf("dpp: %s -> %s\n", filename, outfile);
  main_loop();
  return 0;
}
