/* $Header$ */

/* Language dependant support; this one is default */

#include <stdio.h>
#include <alloc.h>

#include "position.h"
#include "class.h"
#include "langdep.h"
#include "Lpars.h"
#include "idf.h"
#include "token.h"
#include "expr.h"
#include "tree.h"
#include "operator.h"

extern FILE *db_out, *db_in;

extern int
	get_name();

extern double
	atof();

static int
	print_string(),
	get_number(),
	get_string(),
	get_token(),
	print_op(),
	op_prio();

static long
	array_elsize();

static struct langdep def = {
	0,

	"%ld",
	"0%lo",
	"0x%lX",
	"%lu",
	"0x%lX",
	"%g",
	"'\\%o'",

	"[",
	"]",
	"(",
	")",
	"{",
	"}",

	print_string,
	array_elsize,
	op_prio,
	get_string,
	get_name,
	get_number,
	get_token,
	print_op
};

struct langdep *def_dep = &def;

static int
print_string(s, len)
  char	*s;
  int	len;
{
  register char	*str = s;
  int delim = '\'';

  while (*str) {
	if (*str++ == '\'') delim = '"';
  }
  fprintf(db_out, "%c%.*s%c", delim, len, s, delim);
}

extern long	int_size;

static long
array_elsize(size)
  long	size;
{
  if (! (int_size % size)) return size;
  if (! (size % int_size)) return size;
  return ((size + int_size - 1) / int_size) * int_size;
}

/*ARGSUSED*/
static int
op_prio(op)
  int	op;
{
  return 1;
}

static int
val_in_base(c, base)
  register int c;
{
  return is_dig(c) 
	? c - '0'
	: base != 16
	  ? -1
	  : is_hex(c)
	    ? (c - 'a' + 10) & 017
	    : -1;
}

static int
get_number(c)
  register int	c;
{
  char buf[512+1];
  register int base = 10;
  register char *p = &buf[0];
  register long val = 0;
  register int val_c;

  if (c == '0') {
	/* check if next char is an 'x' or an 'X' */
	c = getc(db_in);
	if (c == 'x' || c == 'X') {
		base = 16;
		c = getc(db_in);
	}
	else	base = 8;
  }
  while (val_c = val_in_base(c, base), val_c >= 0) {
	val = val * base + val_c;
	if (p - buf < 512) *p++ = c;
	c = getc(db_in);
  }
  if (base == 16 || !((c == '.' || c == 'e' || c == 'E'))) {
	ungetc(c, db_in);
	tok.ival = val;
	return INTEGER;
  }
  if (c == '.') {
	if (p - buf < 512) *p++ = c;
	c = getc(db_in);
  }
  while (is_dig(c)) {
	if (p - buf < 512) *p++ = c;
	c = getc(db_in);
  }
  if (c == 'e' || c == 'E') {
	if (p - buf < 512) *p++ = c;
	c = getc(db_in);
	if (c == '+' || c == '-') {
		if (p - buf < 512) *p++ = c;
		c = getc(db_in);
	}
	if (! is_dig(c)) {
		error("malformed floating constant");
	}
	while (is_dig(c)) {
		if (p - buf < 512) *p++ = c;
		c = getc(db_in);
	}
  }
  ungetc(c, db_in);
  *p++ = 0;
  if (p == &buf[512+1]) {
	error("floating point constant too long");
  }
  tok.fval = atof(buf);
  return REAL;
}

static int
get_token(c)
  register int	c;
{
  switch(c) {
  case '`':
  case ':':
  case ',':
	return c;
  case '.':
	return get_number(c);
  default:
	error("illegal character 0%o", c);
	return LLlex();
  }
}

static int
quoted(ch)
  int	ch;
{
  /*	quoted() replaces an escaped character sequence by the
	character meant.
  */
  /* first char after backslash already in ch */
  if (!is_oct(ch)) {		/* a quoted char */
	switch (ch) {
	case 'n':
		ch = '\n';
		break;
	case 't':
		ch = '\t';
		break;
	case 'b':
		ch = '\b';
		break;
	case 'r':
		ch = '\r';
		break;
	case 'f':
		ch = '\f';
		break;
	}
  }
  else {				/* a quoted octal */
	register int oct = 0, cnt = 0;

	do {
		oct = oct*8 + (ch-'0');
		ch = getc(db_in);
	} while (is_oct(ch) && ++cnt < 3);
	ungetc(ch, db_in);
	ch = oct;
  }
  return ch&0377;

}

static int 
get_string(c)
  int	c;
{
  register int ch;
  char buf[512];
  register int len = 0;

  while (ch = getc(db_in), ch != c) {
	if (ch == '\n') {
		error("newline in string");
		break;
	}
	if (ch == '\\') {
		ch = getc(db_in);
		ch = quoted(ch);
	}
	buf[len++] = ch;
  }
  buf[len++] = 0;
  tok.str = Salloc(buf, (unsigned) len);
  return STRING;
}

static int
print_op(p)
  p_tree	p;
{
  switch(p->t_oper) {
  case OP_UNOP:
  	switch(p->t_whichoper) {
	case E_MIN:
		fputs("-", db_out);
		print_node(p->t_args[0], 0);
		break;
	case E_PLUS:
		fputs("+", db_out);
		print_node(p->t_args[0], 0);
		break;
	case E_NOT:
		fputs("~", db_out);
		print_node(p->t_args[0], 0);
		break;
	case E_DEREF:
		fputs("*", db_out);
		print_node(p->t_args[0], 0);
		break;
	}
	break;
  case OP_BINOP:
	fputs("(", db_out);
	print_node(p->t_args[0], 0);
	switch(p->t_whichoper) {
	case E_AND:
		fputs("&&", db_out);
		break;
	case E_OR:
		fputs("||", db_out);
		break;
	case E_ZDIV:
		fputs("/", db_out);
		break;
	case E_ZMOD:
		fputs("%", db_out);
		break;
	case E_DIV:
		fputs(" div ", db_out);
		break;
	case E_MOD:
		fputs(" mod ", db_out);
		break;
	case E_IN:
		fputs(" in ", db_out);
		break;
	case E_PLUS:
		fputs("+", db_out);
		break;
	case E_MIN:
		fputs("-", db_out);
		break;
	case E_MUL:
		fputs("*", db_out);
		break;
	case E_EQUAL:
		fputs("==", db_out);
		break;
	case E_NOTEQUAL:
		fputs("!=", db_out);
		break;
	case E_LTEQUAL:
		fputs("<=", db_out);
		break;
	case E_GTEQUAL:
		fputs(">=", db_out);
		break;
	case E_LT:
		fputs("<", db_out);
		break;
	case E_GT:
		fputs(">", db_out);
		break;
	case E_SELECT:
		fputs(".", db_out);
		break;
	}
	print_node(p->t_args[1], 0);
	fputs(")", db_out);
	break;
  }
}
