lpmoo-1.2/etc/
lpmoo-1.2/mudlib/
lpmoo-1.2/mudlib/etc/
lpmoo-1.2/mudlib/include/
lpmoo-1.2/mudlib/include/moo/
lpmoo-1.2/mudlib/lpc/
lpmoo-1.2/mudlib/std/auto/
lpmoo-1.2/mudlib/std/bfuns/
/*
 * NAME:	unparser.c
 * DESCRIPTION:	translate a MOO AST into tokenized MOO form
 */

# define DEBUG  0

inherit "/std/string";

# if DEBUG
inherit "/std/vartext";
# else
# define var2str(x)  ""
# endif

# include <objects.h>
# include <moo/tokens.h>
# include <moo/errors.h>

int	indent;		/* indentation level */
int	prev_indent;	/* previous indentation level */

mapping	precedence;	/* table of operator precedence */
mapping	keywords;	/* table of MOO keywords */
mapping	vtok_vars;	/* table of variables with special tokens */

object global;		/* the global object */

# define PROTO(x)		static string u_##x(mixed *ast)
# define UNPARSE(ast, type)	u_##type(ast)
# define LHS(ast, parent)	paren(ast, parent, 0)
# define RHS(ast, parent)	paren(ast, parent, 1)

PROTO(program);
PROTO(expr);
PROTO(if);
PROTO(for);
PROTO(while);
PROTO(fork);
PROTO(return);
PROTO(list_elems);

static
void create(void)
{
  global = load_object(GLOBAL);

  precedence = ([
    TOK_DOT      : 1, TOK_COLON    : 1, TOK_LBRACKET : 1,
    TOK_BANG     : 2, TOK_U_MINUS  : 2,
    TOK_TIMES    : 3, TOK_DIVIDE   : 3, TOK_PERCENT  : 3,
    TOK_PLUS     : 4, TOK_MINUS    : 4,
    TOK_EQUAL    : 5, TOK_NEQUAL   : 5,
	TOK_IN      : 5, TOK_ASSOC    : 5,
        TOK_LESS    : 5, TOK_LSEQUAL  : 5,
        TOK_GREATER : 5, TOK_GREQUAL  : 5,
    TOK_AND      : 6, TOK_OR       : 6,
    TOK_QUESTION : 7,
    TOK_ASSIGN   : 8,
  ]);

  keywords = MOO_KEYWORDS + MOO_ERRORS;

  vtok_vars = ([
    "buf"	: VTOK_BUF,
    "err"	: VTOK_ERR,
    "float"	: VTOK_FLOAT,
    "list"	: VTOK_LIST,
    "num"	: VTOK_NUM,
    "obj"	: VTOK_OBJ,
    "str"	: VTOK_STR,
    "table"	: VTOK_TABLE,
    "player"	: VTOK_PLAYER,
    "this"	: VTOK_THIS,
    "caller"	: VTOK_CALLER,
    "args"	: VTOK_ARGS,
    "argstr"	: VTOK_ARGSTR,
    "verb"	: VTOK_VERB,
    "dobj"	: VTOK_DOBJ,
    "dobjstr"	: VTOK_DOBJSTR,
    "prepstr"	: VTOK_PREPSTR,
    "iobj"	: VTOK_IOBJ,
    "iobjstr"	: VTOK_IOBJSTR,
  ]);
}

/*
 * NAME:	main()
 * DESCRIPTION:	unparse a MOO verb AST and return tokenized source
 */
string main(mixed *ast)
{
  prev_indent = indent = 0;

  return UNPARSE(ast, program);
}

/*
 * NAME:	identifierp()
 * DESCRIPTION:	return 1 iff name is an identifier
 */
private
int identifierp(string name)
{
  int i, c;

  if (strlen(name) == 0)
    return 0;

  name = tolower(name);

  if (keywords[name] || keywords[toupper(name)])
    return 0;

  for (i = strlen(name); i--; )
    {
      c = name[i];
      if ((c < 'a' || c > 'z') && c != '_' &&
	  (i == 0 || c < '0' || c > '9'))
	return 0;
    }

  return 1;
}

/*
 * NAME:	tok()
 * DESCRIPTION:	return a token string
 */
private
string tok(int what)
{
  string token;

  token    = "x";
  token[0] = what | 0x80;

  return token;
}

/*
 * NAME:	indent()
 * DESCRIPTION:	return an indentation token
 */
private
string indent(void)
{
  if (indent == 0)
    return "";

  if (indent == prev_indent)
    return "\200";  /* 0x80 */
  else if (indent > prev_indent)
    {
      prev_indent = indent;
      return "\202";  /* 0x82 */
    }
  else  /* indent < prev_indent */
    {
      prev_indent = indent;
      return "\201";  /* 0x81 */
    }
}

/*
 * NAME:	paren()
 * DESCRIPTION:	return parenthesized expression
 */
private
string paren(mixed *ast, int parent, int rhs)
{
  int child;
  string expr;

  child  = precedence[TAG(ast)];
  parent = precedence[parent];
  expr   = UNPARSE(ast, expr);

  if (rhs ? parent <= child : parent < child)
    return "(" + expr + ")";
  else if (child <= 1)
    return expr;
  else
    return "\373" + expr + "\375";

  /* 0373 == '{' | 0x80; 0375 == '}' | 0x80 */
}

PROTO(program)
{
  string code;
  int i, sz;

  code = "";

  for (i = 0, sz = sizeof(ast); i < sz; ++i)
    {
      if (sizeof(ast[i]) == 0)
	continue;
      switch (TAG(ast[i]))
	{
	case TOK_IF:
	  code += UNPARSE(ast[i], if);
	  break;
	case TOK_FOR:
	  code += UNPARSE(ast[i], for);
	  break;
	case TOK_WHILE:
	  code += UNPARSE(ast[i], while);
	  break;
	case TOK_FORK:
	  code += UNPARSE(ast[i], fork);
	  break;
	case TOK_RETURN:
	  code += UNPARSE(ast[i], return);
	  break;
	default:
	  /* expression */
	  code += indent() + UNPARSE(ast[i], expr) + tok(TOK_SEMICOLON);
	}
    }

  return code;
}

PROTO(expr)
{
  int tag, vtok;

  switch (tag = TAG(ast))
    {
    case TOK_LIT_NUM:
      return ((string) ast[1]);
    case TOK_LIT_STR:
      return "\"" + ast[1] + "\"";
    case TOK_LIT_OBJ:
      return "#" + ast[1];
    case TOK_LIT_ERR:
      return global->error_name(ast[1]);
    case TOK_LIST:
    case TOK_TABLE:
    case TOK_AMBAGGR:
      return "{" + UNPARSE(ast, list_elems) + "}";
    case TOK_BUFFER:
      return "[" + UNPARSE(ast, list_elems) + "]";
    case TOK_LIT_FLT:
      return ast[1];
    case TOK_LPAREN:
      return ast[1] + "(" + UNPARSE(ast[2], list_elems) + ")";
    case TOK_LBRACKET:
      return LHS(ast[1], tag) + "[" + UNPARSE(ast[2], expr) + "]";
    case TOK_IDENTIFIER:
      return (vtok = vtok_vars[tolower(ast[1])]) ? tok(vtok) : ast[1];

    case TOK_BANG:
      return "!" + LHS(ast[1], tag);
    case TOK_U_MINUS:
      return "-" + LHS(ast[1], tag);

    case TOK_ASSIGN:
    case TOK_PLUS:
    case TOK_MINUS:
    case TOK_TIMES:
    case TOK_DIVIDE:
    case TOK_PERCENT:
    case TOK_EQUAL:
    case TOK_NEQUAL:
    case TOK_LESS:
    case TOK_LSEQUAL:
    case TOK_GREATER:
    case TOK_GREQUAL:
    case TOK_IN:
# if 0
    case TOK_ASSOC:
# endif
    case TOK_AND:
    case TOK_OR:
      return LHS(ast[1], tag) + tok(tag) + RHS(ast[2], tag);

    case TOK_RANGE:
      return UNPARSE(ast[1], expr) + tok(TOK_RANGE) +
	     UNPARSE(ast[2], expr);

    case TOK_QUESTION:
      return RHS(ast[1], tag)      + tok(TOK_QUESTION) +
	     UNPARSE(ast[2], expr) + tok(TOK_PIPE) +
	     RHS(ast[3], tag);

    case TOK_DOT:
      if (TAG(ast[2]) == TOK_LIT_STR && identifierp(ast[2][1]))
	{
	  if (TAG(ast[1]) == TOK_LIT_OBJ && ast[1][1] == 0)
	    return "$" + ast[2][1];
	  else
	    return LHS(ast[1], tag) + "." + ast[2][1];
	}
      else
	return LHS(ast[1], tag) + ".(" + UNPARSE(ast[2], expr) + ")";

    case TOK_COLON:
      return LHS(ast[1], tag) + ":" +
	(TAG(ast[2][0]) == TOK_LIT_STR && identifierp(ast[2][0][1]) ?
	 ast[2][0][1] : "(" + UNPARSE(ast[2][0], expr) + ")") +
	   "(" + UNPARSE(ast[2][1], list_elems) + ")";

    default:
# if DEBUG
      error("Unknown AST token: " + var2str(ast));
# else
      error("Unknown AST token");
# endif
    }
}

PROTO(list_elems)
{
  string code;
  int i, sz;

  if ((sz = sizeof(ast)) == 1)
    return TAG(ast) == TOK_TABLE ? "~" : "";

  code = "";
  for (i = 1; i < sz; ++i)
    {
      if (TAG(ast[i]) == TOK_SPLICE)
	code += "@" + UNPARSE(ast[i][1], expr);
      else if (TAG(ast[i]) == TOK_ASSOC)
	code += UNPARSE(ast[i][1], expr) + tok(TOK_ASSOC) +
	        UNPARSE(ast[i][2], expr);
      else
	code += UNPARSE(ast[i], expr);
      if (i < sz - 1)
	code += tok(TOK_COMMA);
    }

  return code;
}

PROTO(if)
{
  string code;
  int i, sz;

  code = indent() + tok(TOK_IF) + UNPARSE(ast[1], expr) + tok(TOK_RPAREN);
  ++indent, code += UNPARSE(ast[2], program), --indent;

  i = 3, sz = sizeof(ast);
  while (i < sz && ast[i] == TOK_ELSEIF)
    {
      ++i;
      code += indent() + tok(TOK_ELSEIF) +
	UNPARSE(ast[i++], expr) + tok(TOK_RPAREN);
      ++indent, code += UNPARSE(ast[i++], program), --indent;
    }
  if (i < sz && ast[i] == TOK_ELSE)
    {
      code += indent() + tok(TOK_ELSE);
      ++indent, code += UNPARSE(ast[i + 1], program), --indent;
    }

  return code + indent() + tok(TOK_ENDIF);
}

PROTO(for)
{
  string code;

  code = indent() + tok(TOK_FOR) +
    (arrayp(ast[1]) ? ast[1][0] + tok(TOK_ASSOC) + ast[1][1] : ast[1]) +
      tok(TOK_IN);

  if (TAG(ast[2]) == TOK_RANGE)
    code += "[" + UNPARSE(ast[2][1], expr) + tok(TOK_RANGE) +
                  UNPARSE(ast[2][2], expr) + tok(TOK_RBRACKET);
  else  /* TOK_LPAREN */
    code += "(" + UNPARSE(ast[2][1], expr) + tok(TOK_RPAREN);

  ++indent, code += UNPARSE(ast[3], program), --indent;

  return code + indent() + tok(TOK_ENDFOR);
}

PROTO(while)
{
  string code;

  code = indent() + tok(TOK_WHILE) + UNPARSE(ast[1], expr) + tok(TOK_RPAREN);
  ++indent, code += UNPARSE(ast[2], program), --indent;

  return code + indent() + tok(TOK_ENDWHILE);
}

PROTO(fork)
{
  string code;

  code = indent() + tok(TOK_FORK) + (ast[1] == 0 ? "" : ast[1] + " ") +
    "(" + UNPARSE(ast[2], expr) + tok(TOK_RPAREN);
  ++indent, code += UNPARSE(ast[3], program), --indent;

  return code + indent() + tok(TOK_ENDFORK);
}

PROTO(return)
{
  return indent() + tok(TOK_RETURN) +
    (sizeof(ast) == 1 ? tok(TOK_SEMICOLON) :
     " " + UNPARSE(ast[1], expr) + tok(TOK_SEMICOLON));
}