/* $XConsortium: generator.cxx,v 1.5 94/11/08 20:57:08 matt Exp $ */
/*
 * Copyright (c) 1992-1993 Silicon Graphics, Inc.
 * Copyright (c) 1993 Fujitsu, Ltd.
 *
 * Permission to use, copy, modify, distribute, and sell this software and 
 * its documentation for any purpose is hereby granted without fee, provided
 * that (i) the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation, and (ii) the names of
 * Silicon Graphics and Fujitsu may not be used in any advertising or
 * publicity relating to the software without the specific, prior written
 * permission of Silicon Graphics and Fujitsu.
 *
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
 *
 * IN NO EVENT SHALL SILICON GRAPHICS OR FUJITSU BE LIABLE FOR
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
 * OF THIS SOFTWARE.
 */

/*
 * Generate code
 */

#include "generator.h"
#include "tokendefs.h"
#include <ctype.h>
#include <string.h>

implementPtrList(StringList,String)

static char tab[] = "    ";
static const long right_margin = 65;

Generator::Generator(ErrorHandler* h, const ConfigInfo& i) {
    handler_ = h;
    out_ = stdout;
    generate_include_ = true;
    symbols_ = i.symbols;
    inclpath_ = i.inclpath;
    inclpath_length_ = length(inclpath_);
    inclext_ = i.inclext;
    inclext_length_ = length(inclext_);
    includes_ = i.includes;
    stub_includes_ = i.stub_includes;
    server_includes_ = i.stub_includes;
    filename_ = i.filename;
    stubfile_ = open_output(i.stubfile);
    serverfile_ = open_output(i.serverfile);
    superclass_ = i.superclass;
    superclass_length_ = length(superclass_);
    metaclass_ = i.metaclass;
    metaclass_length_ = length(metaclass_);
    envclass_ = i.envclass;
    envclass_length_ = length(envclass_);
    envfirst_ = i.envfirst || i.cdecls;
    stubclass_ = i.stubclass;
    stubclass_length_ = length(stubclass_);
    request_ = i.request;
    request_length_ = length(request_);
    buffer_ = i.buffer;
    buffer_length_= length(buffer_);
    exchange_ = i.exchange;
    exchange_length_= length(exchange_);
    except_ = i.except;
    except_length_= length(except_);
    user_except_ = i.user_except;
    user_except_length_= length(user_except_);
    prefix_ = i.prefix;
    prefix_length_ = length(prefix_);
    direct_prefix_ = i.direct;
    direct_length_ = length(direct_prefix_);
    transcriptions_ = i.transcriptions;
    refobjs_ = i.refobjs;
    cdecls_ = i.cdecls;
    cstubs_ = i.cstubs;
    ptr_ = nil;
    source_ = true;
    need_ifndef_ = true;
    qualify_ = true;
    concat_ = false;
    varying_ = do_varying();
    ref_ = true;
    need_sep_ = false;
    body_ = true;
    subscripts_ = true;
    formals_ = true;
    counter_ = 0;
    indent_ = 0;
    column_ = 0;
    include_newline_ = false;
    scope_ = nil;
    impl_is_from_ = nil;
    prefixes_ = new StringList;
    files_ = new StringList;
    if (stubfile_ != nil || serverfile_ != nil) {
	if (superclass_ == nil) {
	    superclass_ = "BaseObject";
	    superclass_length_ = length(superclass_);
	}
	if (metaclass_ == nil) {
	    metaclass_ = "TypeObj";
	    metaclass_length_ = length(metaclass_);
	}
	if (stubclass_ == nil) {
	    stubclass_ = "Stub";
	    stubclass_length_ = length(stubclass_);
	}
    }
}

Generator::~Generator() {
    delete prefixes_;
    delete files_;
}

long Generator::length(const char* s) {
    return s == nil ? 0 : strlen(s);
}

FILE* Generator::open_output(const char* name) {
    FILE* f = nil;
    if (name != nil) {
	f = fopen(name, "w");
	if (f == nil) {
	    handler_->begin_unrecoverable();
	    handler_->put_chars("Can't write output file '");
	    handler_->put_chars(name);
	    handler_->put_chars("'");
	    handler_->end();
	}
    }
    return f;
}

long Generator::file_mask() {
    if (out_ == stubfile_) {
	return 1;
    } else if (out_ == serverfile_) {
	return 2;
    }
    return 0;
}

SymbolTable* Generator::symbol_table() { return symbols_; }

Boolean Generator::set_source(SourcePosition* p) {
    if (filename_ != nil) {
	source_ = (p == nil || *p->filename() == filename_);
    }
    return source_;
}

Boolean Generator::is_source() { return source_; }

Boolean Generator::need_ifndef() { return need_ifndef_; }

     
Boolean Generator::begin_file(FILE* f) {
    if (f != nil) {
	fflush(out_);
	out_ = f;
	save_indent_ = indent_;
	save_column_ = column_;
	indent_ = 0;
	column_ = 0;
	return true;
    }
    return false;
}

void Generator::end_file() {
    fflush(out_);
    out_ = stdout;
    indent_ = save_indent_;
    column_ = save_column_;
}

Boolean Generator::interface_is_ref(Boolean b) {
    Boolean prev = ref_;
    ref_ = b;
    return prev;
}

Boolean Generator::need_sep(Boolean b) {
    Boolean prev = need_sep_;
    need_sep_ = b;
    return prev;
}

Boolean Generator::op_body(Boolean b) {
    Boolean prev = body_;
    body_ = b;
    return prev;
}

Boolean Generator::is_op_body() { return body_; }

Boolean Generator::array_decl(Boolean b) {
    Boolean prev = subscripts_;
    subscripts_ = b;
    return prev;
}

Boolean Generator::is_array_decl() { return subscripts_; }

void Generator::push_prefix(String* s) {
    prefixes_->prepend(s);
}

void Generator::pop_prefix() {
    if (prefixes_->count() > 0) {
	prefixes_->remove(0);
    }
}

String* Generator::prefix() {
    String* s = nil;
    if (prefixes_->count() > 0) {
	s = prefixes_->item(0);
    }
    return s;
}

void Generator::enter_scope(Scope* s) {
    scope_ = s;
}

void Generator::leave_scope() {
    scope_ = scope_->outer;
}

void Generator::impl_is_from(String* s) { impl_is_from_ = s; }
String* Generator::impl_is_from() { return impl_is_from_; }

Symbol* Generator::actual_type(Expr* e) {
    Symbol* s = e->symbol();
    if (s != nil) {
	s = s->actual_type();
    }
    return s;
}

Boolean Generator::addr_type(Expr* e) {
    Symbol* s = actual_type(e);
    Boolean b = false;
    if (s != nil) {
	switch (s->tag()) {
	case Symbol::sym_string:
	case Symbol::sym_struct:
	case Symbol::sym_sequence:
	case Symbol::sym_union:
	    b = true;
	    break;
	}
    }
    return b;
}

Boolean Generator::void_type(Expr* e) {
    return actual_type(e) == symbols_->void_type();
}

Boolean Generator::need_extern(Expr* e) {
    Boolean b = true;
    Symbol* s = e->symbol();
    if (s != nil) {
	if (s->declared()) {
	    b = false;
	} else {
	    s->declared(true);
	}
    }
    return b;
}

void Generator::emit(const char* format, String* s, Expr* e) {
    const char* start = format;
    for (const char* p = start; *p != '\0'; p++) {
	if (*p == '\n') {
	    if (p > start) {
		emit_substr(start, p - start);
	    }
	    putc('\n', out_);
	    column_ = 0;
	    start = p + 1;
	} else if (*p == '%') {
	    if (p > start) {
		emit_substr(start, p - start);
	    }
	    ++p;
	    emit_format(*p, s, e);
	    start = p + 1;
	}
    }
    if (p > start) {
	emit_substr(start, p - start);
    }
}

void Generator::emit_str(const char* s, long length) {
    if (column_ == 0) {
	emit_tab();
    }
    fputs(s, out_);
    column_ += length;
}

void Generator::emit_substr(const char* s, long length) {
    if (column_ == 0) {
	emit_tab();
    }
    fprintf(out_, "%.*s", length, s);
    column_ += length;
}

void Generator::emit_tab() {
    if (include_newline_) {
	putc('\n', out_);
	include_newline_ = false;
    }
    for (long n = 0; n < indent_; n++) {
	fputs(tab, out_);
	column_ += sizeof(tab) - 1;
    }
}

void Generator::emit_flush(const char* p, const char* start) {
    long n = p - start;
    if (n != 0) {
	emit_substr(start, n);
    }
}

/*
 * Interpret a format specification, potentially using
 * a given string and expression for substitution.
 *
 * Formats:
 *
 *    %^ - name prefix if defined
 *    %p - suffix for an object pointer type
 *    %r - suffix for a managed object pointer type
 *    %P - same as %p if interface types should be output as pointers
 *    %* - current pointer suffix
 *    %i - increment tab indentation level
 *    %u - decrement tab indentation level
 *    %b - discretionary line break
 *    %o - white space if in the body of an operation
 *    %v - use vformat for a string
 *    %I - given string
 *    %Q - short-hand for %:%I
 *    %P - short-hand for %I%p
 *    %T - name of type object using given string as the type name
 *    %E - generate given expression
 *    %; - scope of expression using ::
 *    %- - scope of expression using _
 *    %X - fully-qualified expression
 *    %Y - fully-qualified expression using _ instead of ::
 *    %N - generate type expression's name
 *    %F - generate type expression's fully-qualified name
 *    %A - attribute parameter name for given (type) expression
 *    %S - superclass
 *    %M - metaclass
 *    %C - client stub class
 *    %c - client stub class create function (or "0" when no stubs)
 *    %D - dynamic invocation request
 *    %B - marshal buffer type
 *    %O - object exchange type
 *    %x - exception type
 *    %U - user exception type
 *    %, - emit ", " if envclass is defined
 *    %e - environment formal parameter (if envclass is defined)
 *    %f - environment formal parameter for body (if envclass is defined)
 *    %a - environment actual parameter (if envclass is defined)
 *    %~ - name of current scope
 *    %: - current scope with :: as separator and trailer
 *    %_ - current scope with _ as separator and trailer
 *    %? - scope operator (::) or name separator (_)
 *    %. - scope specification (scope::) if prefix is defined
 *    %= - pure virtual if stubclass is not defined
 */

void Generator::emit_format(int ch, String* s, Expr* e) {
    String* str;
    Boolean b;
    switch (ch) {
    case '^':
	if (prefix_ != nil) {
	    emit_str(prefix_, prefix_length_);
	}
	break;
    case 'p':
	emit_str("Ref", 3);
	break;
    case 'P':
	if (ref_) {
	    emit("%p");
	}
	break;
    case 'r':
	emit_str("_var", 4);
	break;
    case '*':
	emit_str(ptr_, strlen(ptr_));
	break;
    case 'i':
	++indent_;
	break;
    case 'u':
	--indent_;
	break;
    case 'b':
	if (column_ > right_margin) {
	    putc('\n', out_);
	    column_ = 0;
	}
	break;
    case 'o':
	if (body_) {
	    putc(' ', out_);
	    column_ += 1;
	}
	break;
    case 'v':
	if (direct_prefix_ != nil) {
	    emit_str(direct_prefix_, direct_length_);
	}
	break;
    case 'I':
	emit_substr(s->string(), s->length());
	break;
    case 'Q':
	emit("%:%I", s);
	break;
    case 'T':
	emit("_%_%I_tid", s);
	break;
    case 'E':
	e->generate(this);
	break;
    case ';':
	emit_expr_scope(e);
	break;
    case '-':
	b = concat_;
	concat_ = true;
	emit_expr_scope(e);
	concat_ = b;
	break;
    case 'X':
	emit_expr_scope(e);
	b = qualify_;
	qualify_ = false;
	e->generate(this);
	qualify_ = b;
	break;
    case 'Y':
	b = concat_;
	concat_ = true;
	emit("%^%F", nil, e);
	concat_ = b;
	break;
    case 'N':
	e->generate_name(this);
	break;
    case 'F':
	emit_expr_scope(e);
	b = qualify_;
	qualify_ = false;
	e->generate_name(this);
	qualify_ = b;
	break;
    case 'A':
	emit_str("p_", 2);
	e->generate(this);
	break;
    case 'S':
	emit_str(superclass_, superclass_length_);
	break;
    case 'M':
	emit_str(metaclass_, metaclass_length_);
	break;
    case 'C':
	emit_str(stubclass_, stubclass_length_);
	break;
    case 'c':
	emit(stubclass_ != nil ? "&_%_%I%C_create" : "0", s);
	break;
    case 'D':
	emit_str(request_, request_length_);
	break;
    case 'B':
	emit_str(buffer_, buffer_length_);
	break;
    case 'O':
	emit_str(exchange_, exchange_length_);
	break;
    case 'x':
	emit_str(except_, except_length_);
	break;
    case 'U':
	emit_str(user_except_, user_except_length_);
	break;
    case ',':
	if (envclass() != nil) {
	    emit(", ");
	}
	break;
    case 'e':
	if (envclass() != nil) {
	    if (impl_is_from() != nil) {
		emit("%f");
	    } else {
		emit_param_decls(nil, emit_env_formals);
	    }
	}
	break;
    case 'f':
	if (envclass() != nil) {
	    emit_param_decls(nil, emit_env_formals_body);
	}
	break;
    case 'a':
	if (envclass() != nil) {
	    emit_param_decls(nil, emit_env_actuals);
	}
	break;
    case '~':
	emit_scope(scope_);
	break;
    case ':':
	if (emit_scope(scope_)) {
	    emit("%?");
	}
	break;
    case '_':
	b = concat_;
	concat_ = true;
	emit("%^%:");
	concat_ = b;
	break;
    case '?':
	if (concat_ || cdecls_) {
	    emit_str("_", 1);
	} else {
	    emit_str("::", 2);
	}
	break;
    case '.':
	str = prefix();
	if (str != nil) {
	    emit_substr(str->string(), str->length());
	    emit_str("::", 2);
	}
	break;
    case '=':
	if (stubclass_ == nil && !cdecls()) {
	    emit(" = 0");
	}
	break;
    case '%':
	emit_str("%", 1);
	break;
    default:
	emit_str("%", 1);
	putc(ch, out_);
	column_ += 1;
	break;
    }
}

void Generator::emit_expr_scope(Expr* e) {
    Symbol* sym = e->symbol();
    if (sym != nil && emit_scope(sym->scope())) {
	emit("%?");
    }
}

void Generator::copy(const char* s) {
    fputs(s, out_);
    column_ = 0;
}

void Generator::emit_char(long c) {
    putc(int(c), out_);
}

void Generator::emit_chars_length(const char* s, long length) {
    emit_substr(s, length);
}

void Generator::emit_integer(long n) {
    char buf[100];
    sprintf(buf, "%d", n);
    emit_str(buf, strlen(buf));
}

void Generator::emit_float(double d) {
    char buf[100];
    sprintf(buf, "%g", d);
    emit_str(buf, strlen(buf));
}

void Generator::emit_declarator_ident(Identifier* i) {
    if (i != nil && body_) {
	emit("%I", i->string());
    }
}

void Generator::emit_op(Opcode op) {
    char single_char_op[2];
    const char* opname;
    switch (op) {
    case LSHIFT:
	opname = "<<";
	break;
    case RSHIFT:
	opname = ">>";
	break;
    default:
	if (op >= ' ' && op <= '~') {
	    single_char_op[0] = char(op);
	    single_char_op[1] = '\0';
	    opname = single_char_op;
	} else {
	    opname = "???";
	}
	break;
    }
    copy(opname);
}

/*
 * Convert /A/B/.../C/D.idl to C_D_h
 */

void Generator::emit_filename(String* s) {
    const char* start = s->string();
    String::Index n = s->length();
    const char* p;
    const char* end = start + n - 1;
    for (p = end; p > start; p--) {
	if (*p == '.') {
	    end = p;
	    break;
	}
    }
    long slash = 0;
    for (; p > start; p--) {
	if (*p == '/') {
	    if (slash == 1) {
		++p;
		break;
	    }
	    ++slash;
	}
    }
    emit_tr_filename(p, end);
    emit_tr_filename(inclext_, inclext_ + inclext_length_);
}

void Generator::emit_tr_filename(const char* start, const char* end) {
    for (const char* p = start; p < end; p++) {
	int ch = *p;
	if (!isalnum(ch)) {
	    ch = '_';
	}
	putc(ch, out_);
    }
}

void Generator::emit_ifndef(String* filename) {
    emit("#ifndef ");
    emit_filename(filename);
    emit("\n#define ");
    emit_filename(filename);
    emit("\n\n");
    files_->prepend(new CopyString(*filename));
    need_ifndef_ = false;
}

void Generator::emit_endif(String* filename) {
    if (files_->count() > 0) {
	String* current = files_->item(0);
	if (*current != *filename) {
	    emit("#endif\n\n");
	    delete current;
	    files_->remove(0);
	}
    }
}

void Generator::emit_include(String* name) {
    if (inclpath_ != nil) {
	include_newline_ = false;
	emit("#include \"");
	const char* start = name->string();
	String::Index n = name->length();
	/* assume n > 0 ==> end >= start */
	const char* end = start + n - 1;
	const char* p;
	for (p = start; ; p++) {
	    if (p > end) {
		for (; p > start; p--) {
		    if (*p == '/') {
			start = p + 1;
			break;
		    }
		}
		emit_str(inclpath_, inclpath_length_);
		break;
	    }
	    const char* s1, * s2;
	    for (s1 = p, s2 = inclpath_; *s1 == *s2; s1++, s2++);
	    if (*s2 == '\0') {
		start = p;
		break;
	    }
	}
	for (p = end; ; p--) {
	    if (p == start) {
		emit_str(start, end - start);
		break;
	    }
	    if (*p == '.') {
		emit_substr(start, p - start);
		emit_str(inclext_, inclext_length_);
		break;
	    }
	}
	emit("\"\n");
	include_newline_ = true;
    }
}

void Generator::emit_includes() {
    if (generate_include_) {
	StringList* list = includes_;
	if (list != nil && list->count() > 0) {
	    emit_include_list(list);
	} else {
	    const char* name = "Ox/object";
	    emit_include_substr(true, name, strlen(name));
	}
	generate_include_ = false;
    }
}

void Generator::emit_stub_includes() {
    StringList* list = stub_includes_;
    if (list != nil && list->count() > 0) {
	emit_include_list(list);
    } else {
	emit_include_filename();
	const char* name = "Ox/stub";
	emit_include_substr(true, name, strlen(name));
	include_newline_ = false;
	emit("\n");
    }
}

void Generator::emit_server_includes() {
    StringList* list = server_includes_;
    if (list != nil && list->count() > 0) {
	emit_include_list(list);
    } else {
	emit_include_filename();
	const char* name = "Ox/server";
	emit_include_substr(true, name, strlen(name));
	include_newline_ = false;
	emit("\n");
    }
}

void Generator::emit_include_list(StringList* list) {
    include_newline_ = false;
    for (ListItr(StringList) i(*list); i.more(); i.next()) {
	emit("#include %I\n", i.cur());
    }
    include_newline_ = true;
}

void Generator::emit_include_filename() {
    if (filename_ != nil) {
	const char* p;
	for (p = filename_; *p != '\0'; p++);
	for (const char* q = p - 1; q > filename_; q--) {
	    if (*q == '.') {
		p = q;
		break;
	    }
	}
	emit_include_substr(false, filename_, p - filename_);
    }
}

void Generator::emit_include_substr(Boolean path, const char* s, long length) {
    include_newline_ = false;
    emit("#include \"");
    if (path && inclpath_ != nil) {
	emit_str(inclpath_, inclpath_length_);
    }
    emit_substr(s, length);
    emit_str(inclext_, inclext_length_);
    emit("\"\n");
    include_newline_ = true;
}

void Generator::emit_param_list(ExprList* params, ParamFlags flags) {
    emit("(");
    emit_param_decls(params, flags);
    emit(")");
}

void Generator::emit_param_decls(ExprList* params, ParamFlags flags) {
    Boolean env = envclass_ != nil && (flags & emit_env) != 0;
    Boolean formals = (flags & emit_formals) != 0;
    if (env && envfirst_) {
	emit_env_param(flags);
	if (params != nil) {
	    emit(", ");
	}
    }
    if (params != nil) {
	Boolean b = formals_;
	formals_ = formals;
	ExprImpl::generate_list(params, &Expr::generate, this, ", ");
	formals_ = b;
    }
    if (env && !envfirst_) {
	if (params != nil) {
	    emit(", ");
	}
	emit_env_param(flags);
	if (formals && (flags & emit_body) == 0 && !cdecls()) {
	    emit(" = 0");
	}
    }
}

void Generator::emit_env_param(ParamFlags flags) {
    if ((flags & emit_formals) != 0) {
	emit_chars_length(envclass_, envclass_length_);
	emit("* ");
    }
    emit("_env");
}

void Generator::emit_type_info(
    String* name, char* ptr, ExprList* parents,
    Boolean dii, Boolean excepts, Boolean narrow
) {
    ptr_ = ptr;
    Boolean has_offsets = false;
    if (parents != nil) {
	has_offsets = parents->count() > 1;
	emit_parent_type_info(name, parents);
    }
    emit("extern %MId %T;\n", name);
    const char* stubs = stubclass_;
    if (stubclass_ != nil && *ptr == '*') {
	stubclass_ = nil;
    }
    if (excepts) {
	emit("extern %M_UnmarshalException _%_%I_excepts[];\n", name);
    }
    if (dii) {
	emit("extern void _%_%I_receive(%S%p, ULong, %B&);\n", name);
    }
    emit("%M_Descriptor _%_%I_type = {\n%i", name);
    emit("/* type */ 0,\n/* id */ &%T,\n\"%I\",\n", name);
    emit_opt_info(parents != nil, nil, "parents", name, ", ");
    emit_opt_info(has_offsets, nil, "offsets", name, ", ");
    emit_opt_info(excepts, nil, "excepts", name, ",\n");
    emit_opt_info(dii, nil, "methods", name, ", ");
    emit_opt_info(dii, nil, "params", name, ",\n");
    emit_opt_info(dii, "&", "receive", name, "\n");
    emit("%u};\n");
    if (narrow) {
	emit("\n%Q%* %Q::_narrow(%S%p o) {\n%i", name);
	emit("return (%Q%*)_%S_tnarrow(\n%io, %T, %b%c\n%u);\n%u}\n", name);
    }
    stubclass_ = stubs;
}

void Generator::emit_parent_type_info(String* name, ExprList* parents) {
    Boolean b = interface_is_ref(false);
    emit("extern %M_Descriptor ");
    long p = 0;
    long nparents = parents->count();
    for (;;) {
	emit("_%Y_type", nil, parents->item(p));
	++p;
	if (p == nparents) {
	    break;
	}
	emit(", ");
    }
    emit(";\n");
    emit("%M_Descriptor* _%_%I_parents[] = { ", name);
    for (p = 0; p < nparents; p++) {
	emit("&_%Y_type, ", nil, parents->item(p));
    }
    emit("nil };\n");
    if (nparents > 1) {
	emit("Long _%_%I_offsets[] = {\n%i", name);
	p = 1;
	for (;;) {
	    emit(
		"Long((%F*)(%:%I*)8) - Long((%:%I*)8)", name, parents->item(p)
	    );
	    ++p;
	    if (p == nparents) {
		break;
	    }
	    emit(", ");
	}
	emit("\n%u};\n");
    }
    interface_is_ref(b);
}

void Generator::emit_opt_info(
    Boolean b, const char* start, const char* tag, String* name,
    const char* trail
) {
    if (b) {
	if (start != nil) {
	    emit(start);
	}
	emit("_%_%I_", name);
	emit(tag);
    } else {
	emit("/* ");
	emit(tag);
	emit(" */ nil");
    }
    emit(trail);
}

Boolean Generator::emit_scope(Scope* s) {
    Boolean b = false;
    if (s != nil) {
	if (s->outer != nil && s->outer->name != nil) {
	    emit_scope(s->outer);
	    emit("%?");
	    b = true;
	}
	if (s->name != nil) {
	    emit("%I", s->name);
	    b = true;
	}
    }
    return b;
}

Boolean Generator::emit_extern_stubs(Expr* type) {
    Boolean b = false;
    long f = file_mask();
    Symbol* s = type->symbol();
    switch (s->tag()) {
    case Symbol::sym_array:
	if (!s->declared_stub(f)) {
	    b = s->array()->generate_extern_stubs(this);
	    s->declare_stub(f);
	}
	break;
    case Symbol::sym_typedef:
	if (!s->declared_stub(f)) {
	    b = s->typename()->generate_extern_stubs(this);
	    s->declare_stub(f);
	}
	break;
    case Symbol::sym_enum:
    case Symbol::sym_string:
    case Symbol::sym_interface:
	break;
    default:
	if (!s->declared_stub(f)) {
	    type->generate_extern_stubs(this);
	    emit("extern void _%Y_put(\n%i%B&, %b", nil, type);
	    emit("const %F", nil, type);
	    if (addr_type(type)) {
		emit("&");
	    }
	    emit("\n%u);\n");
	    emit("extern void _%Y_get(\n%i%B&, %b", nil, type);
	    emit("%F", nil, type);
	    if (s->array() == nil) {
		emit("&");
	    }
	    emit("\n%u);\n");
	    s->declare_stub(f);
	    b = true;
	}
	break;
    }
    return b;
}

void Generator::emit_put(Expr* type, char* format, Expr* value) {
    Boolean b;
    Symbol* s = actual_type(value);
    switch (s->tag()) {
    case Symbol::sym_array:
	b = array_decl(false);
	emit("_%Y_put(_b, ", nil, s->array());
	emit(format, nil, value);
	emit(");\n");
	array_decl(b);
	return;
    case Symbol::sym_enum:
	emit("_b.put_long(");
	break;
    case Symbol::sym_interface:
	emit("_b.put_object(");
	break;
    case Symbol::sym_sequence:
	emit("_%Y_put(_b, ", nil, s->sequence_type());
	break;
    case Symbol::sym_string:
	emit("_b.put_string(");
	break;
    case Symbol::sym_typedef:
	/* builtin type */
	emit("_b.put_%I(", s->typename()->str());
	break;
    default:
	emit("_%Y_put(_b, ", nil, type);
	break;
    }
    emit(format, nil, value);
    emit(");\n");
}

void Generator::emit_array_setup(Declarator* d, Expr* t, Boolean is_put) {
    ExprList* subs = d->subscripts();
    emit_array_loop_start(subs);
    if (is_put) {
	emit("const ");
    }
    emit("%F& _tmp = _array", nil, t);
    emit_array_loop_indices(subs->count());
    emit(";\n");
}

void Generator::emit_array_loop_start(ExprList* subscripts) {
    long index = 0;
    for (ListItr(ExprList) e(*subscripts); e.more(); e.next()) {
	emit("for (int _i");
	emit_integer(index);
	emit(" = 0; _i");
	emit_integer(index);
	emit(" < %E; _i", nil, e.cur());
	emit_integer(index);
	emit("++) {\n%i");
	++index;
    }
}

void Generator::emit_array_loop_indices(long nsubscripts) {
    for (long index = 0; index < nsubscripts; index++) {
	emit("[_i");
	emit_integer(index);
	emit("]");
    }
}

void Generator::emit_array_loop_finish(long nsubscripts) {
    for (long index = 0; index < nsubscripts; index++) {
	emit("%u}\n");
    }
}

void Generator::emit_get(Expr* type, char* format, Expr* value) {
    Boolean b;
    Symbol* s = actual_type(value);
    switch (s->tag()) {
    case Symbol::sym_array:
	b = array_decl(false);
	emit("_%Y_get(_b, ", nil, s->array());
	emit(format, nil, value);
	emit(");\n");
	array_decl(b);
	return;
    case Symbol::sym_enum:
	emit(format, nil, value);
	emit(" = %F(_b.get_long());\n", nil, type);
	break;
    case Symbol::sym_interface:
	emit(format, nil, value);
	b = interface_is_ref(false);
	emit(" = (%F%p)_b.get_object(", nil, type);
	emit(stubclass_ == nil ? "0" : "&_%Y%C_create", nil, type);
	emit(");\n");
	interface_is_ref(b);
	break;
    case Symbol::sym_sequence:
	emit("_%Y_get(_b, ", nil, s->sequence_type());
	emit(format, nil, value);
	emit(");\n");
	break;
    case Symbol::sym_string:
	emit(format, nil, value);
	emit(" = _b.get_string();\n");
	break;
    case Symbol::sym_typedef:
	/* builtin type */
	emit(format, nil, value);
	emit(" = _b.get_%I();\n", s->typename()->str());
	break;
    default:
	emit("_%Y_get(_b, ", nil, type);
	emit(format, nil, value);
	emit(");\n");
	break;
    }
}

void Generator::emit_transcriptions(Boolean abstract) {
    for (ListItr(StringList) i(*transcriptions_); i.more(); i.next()) {
	if (abstract) {
	    emit("virtual ");
	}
	emit("void _put(%I&)", i.cur());
	if (abstract) {
	    emit("%=");
	}
	emit(";\n", i.cur());
    }
}

void Generator::emit_edit_warning(String* s) {
    emit("/* DO NOT EDIT -- Automatically generated");
    if (s != nil) {
	emit(" from %I", s);
    }
    emit(" */\n\n");
}

void Generator::flush() {
    long n = files_->count();
    for (long i = 0; i < n; i++) {
	fprintf(out_, "\n#endif\n");
    }
    fflush(out_);
    if (stubfile_ != nil) {
	fclose(stubfile_);
    }
    if (serverfile_ != nil) {
	fclose(serverfile_);
    }
}

/*
 * Create generator.
 */

Generator* ExprKit::generator(const ConfigInfo& info) {
    return new Generator(impl_->handler_, info);
}