/* * 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)); }