lima-1.0b5/
lima-1.0b5/driver/
lima-1.0b5/driver/ChangeLog.old/
lima-1.0b5/driver/Win32/
lima-1.0b5/driver/compat/
lima-1.0b5/driver/include/
lima-1.0b5/driver/testsuite/
lima-1.0b5/driver/testsuite/clone/
lima-1.0b5/driver/testsuite/command/
lima-1.0b5/driver/testsuite/data/
lima-1.0b5/driver/testsuite/etc/
lima-1.0b5/driver/testsuite/include/
lima-1.0b5/driver/testsuite/inherit/
lima-1.0b5/driver/testsuite/inherit/master/
lima-1.0b5/driver/testsuite/log/
lima-1.0b5/driver/testsuite/single/
lima-1.0b5/driver/testsuite/single/tests/compiler/
lima-1.0b5/driver/testsuite/single/tests/efuns/
lima-1.0b5/driver/testsuite/single/tests/operators/
lima-1.0b5/driver/testsuite/u/
lima-1.0b5/driver/tmp/
lima-1.0b5/etc/
lima-1.0b5/lib/WWW/help/
lima-1.0b5/lib/cmds/
lima-1.0b5/lib/cmds/create/
lima-1.0b5/lib/cmds/player/attic/
lima-1.0b5/lib/contrib/bboard/
lima-1.0b5/lib/contrib/boards/
lima-1.0b5/lib/contrib/marriage/
lima-1.0b5/lib/contrib/roommaker/
lima-1.0b5/lib/contrib/transient_effect/
lima-1.0b5/lib/daemons/channel/
lima-1.0b5/lib/daemons/imud/
lima-1.0b5/lib/data/
lima-1.0b5/lib/data/config/
lima-1.0b5/lib/data/links/
lima-1.0b5/lib/data/news/
lima-1.0b5/lib/data/players/
lima-1.0b5/lib/data/secure/
lima-1.0b5/lib/domains/
lima-1.0b5/lib/domains/std/2.4.5/maze1/
lima-1.0b5/lib/domains/std/2.4.5/npc/
lima-1.0b5/lib/domains/std/2.4.5/post_dir/
lima-1.0b5/lib/domains/std/2.4.5/sub/
lima-1.0b5/lib/domains/std/camera/
lima-1.0b5/lib/domains/std/config/
lima-1.0b5/lib/domains/std/cult/
lima-1.0b5/lib/domains/std/effects/
lima-1.0b5/lib/domains/std/misc/
lima-1.0b5/lib/domains/std/monsters/
lima-1.0b5/lib/domains/std/recorder/
lima-1.0b5/lib/domains/std/rooms/
lima-1.0b5/lib/domains/std/rooms/beach/
lima-1.0b5/lib/domains/std/rooms/labyrinth/
lima-1.0b5/lib/domains/std/school/
lima-1.0b5/lib/domains/std/school/O/
lima-1.0b5/lib/domains/std/spells/
lima-1.0b5/lib/domains/std/spells/stock-mage/
lima-1.0b5/lib/domains/std/spells/stock-priest/
lima-1.0b5/lib/help/
lima-1.0b5/lib/help/admin/
lima-1.0b5/lib/help/hints/General_Questions/
lima-1.0b5/lib/help/hints/Pirate_Quest/
lima-1.0b5/lib/help/player/
lima-1.0b5/lib/help/player/bin/
lima-1.0b5/lib/help/player/quests/
lima-1.0b5/lib/help/wizard/
lima-1.0b5/lib/help/wizard/coding/guilds/
lima-1.0b5/lib/help/wizard/coding/rooms/
lima-1.0b5/lib/help/wizard/lib/daemons/
lima-1.0b5/lib/help/wizard/lib/lfun/
lima-1.0b5/lib/help/wizard/lib/std/
lima-1.0b5/lib/help/wizard/mudos_doc/
lima-1.0b5/lib/help/wizard/mudos_doc/applies/
lima-1.0b5/lib/help/wizard/mudos_doc/applies/interactive/
lima-1.0b5/lib/help/wizard/mudos_doc/applies/parsing/
lima-1.0b5/lib/help/wizard/mudos_doc/concepts/
lima-1.0b5/lib/help/wizard/mudos_doc/driver/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/arrays/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/buffers/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/compile/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/filesystem/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/floats/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/functions/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/general/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/mappings/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/mixed/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/numbers/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/parsing/
lima-1.0b5/lib/help/wizard/mudos_doc/lpc/constructs/
lima-1.0b5/lib/help/wizard/mudos_doc/lpc/types/
lima-1.0b5/lib/include/driver/
lima-1.0b5/lib/log/
lima-1.0b5/lib/obj/admtool/
lima-1.0b5/lib/obj/admtool/internal/
lima-1.0b5/lib/obj/admtool/mudinfo/
lima-1.0b5/lib/obj/admtool/secure/
lima-1.0b5/lib/obj/secure/
lima-1.0b5/lib/obj/secure/cmd/
lima-1.0b5/lib/obj/secure/mailers/
lima-1.0b5/lib/obj/secure/shell/
lima-1.0b5/lib/obj/secure/shell/classes/
lima-1.0b5/lib/obj/tasktool/
lima-1.0b5/lib/obj/tasktool/internal/
lima-1.0b5/lib/open/
lima-1.0b5/lib/secure/
lima-1.0b5/lib/secure/cgi/
lima-1.0b5/lib/secure/modules/
lima-1.0b5/lib/secure/simul_efun/
lima-1.0b5/lib/std/adversary/
lima-1.0b5/lib/std/adversary/advancement/
lima-1.0b5/lib/std/adversary/armor/
lima-1.0b5/lib/std/adversary/blows/
lima-1.0b5/lib/std/adversary/death/
lima-1.0b5/lib/std/adversary/formula/
lima-1.0b5/lib/std/adversary/health/
lima-1.0b5/lib/std/adversary/pulse/
lima-1.0b5/lib/std/adversary/wield/
lima-1.0b5/lib/std/classes/event_info/
lima-1.0b5/lib/std/container/
lima-1.0b5/lib/std/living/
lima-1.0b5/lib/std/modules/contrib/
lima-1.0b5/lib/std/patterns/
lima-1.0b5/lib/std/race/
lima-1.0b5/lib/std/race/restricted/
lima-1.0b5/lib/std/room/
lima-1.0b5/lib/tmp/
lima-1.0b5/lib/trans/
lima-1.0b5/lib/trans/admincmds/
lima-1.0b5/lib/trans/obj/
lima-1.0b5/lib/wiz/
/* Do not remove the headers from this file! see /USAGE for more info. */

inherit M_ACCESS;

private mapping inheritable = ([
  "accountant" : ACCOUNTANT,
  "aggressive" : AGGRESSIVE_MONSTER,
  "following monster" : FOLLOWING_MONSTER,
  "monster" : ADVERSARY,
  "living" : LIVING,
  "wandering monster" : WANDERING_MONSTER,

  "armor" : ARMOR,
  "book" : BOOK,
  "container" : CONTAINER,
  "key" : KEY,
  "moving room" : "/std/moving_room",
  "object" : OBJ,
  "outdoor" : OUTDOOR_ROOM,
  "portal" : PORTAL,
  "room" : ROOM,
  "door" : DOOR,
  "hidden door" : HIDDEN_DOOR,
  "secret door" : SECRET_DOOR,
  "torch" : TORCH,
  "weapon" : WEAPON,

  "actor": M_ACTIONS,
  "block" : M_BLOCKEXITS,
  "drinkable" : M_DRINKABLE,
  "gettable" : M_GETTABLE,
  "lockable" : M_LOCKABLE,
  "openable" : M_OPENABLE,
  "readable" : M_READABLE,
  "regex" : M_REGEX,
  "valuable" : M_VALUABLE,
  "vendor" : M_VENDOR,
]);

int cur, intrigger;
int unique;

string handle_oneof(string arg, array block);
string handle_expression(string arg);
mixed handle_subexpression(string arg);
string handle_block(array lines);
string handle_check(string arg, array block);
string handle_delay(string arg);
string handle_action(string arg);

void add_error(int, string);

private mapping keywords = ([
  "oneof" : (: handle_oneof :),
  "if" : (: "if (" + handle_expression($1) +
    ") {\n" + handle_block($2) + "}\n" :),
  "nexttrigger" : (: intrigger ? "continue;" : (add_error(cur, "nexttrigger illegal outside of trigger"), "") :),
  "call" : (: handle_expression("call " + $1) +";\n" :),
  "lcall" : (: handle_expression("lcall " + $1) +";\n" :),
  "check" : (: handle_check :),
  "ok" : "return 1;",
  "write" : (: "write(" + handle_expression($1) + ");\n" :),
  "lpc" : (: implode($2, "\n") + "\n" :),
  "delay" : (: handle_delay :),
  "action" : (: handle_action :),
  "return" : (: "return " + handle_expression($1) + ";\n" :),
]);

mixed handle_periodic(string arg, array lines);
mixed handle_trigger(string arg, array lines);
mixed handle_setup(string arg, array lines);

private mapping funcs = ([
  "periodic" : (: handle_periodic :),
  "trigger" : (: handle_trigger :),
  "setup" : (: handle_setup :),
]);

string handle_string(array args);
mixed handle_is(array args);
mixed handle_variables(array args);
mixed handle_gender(array args);
mixed handle_list(string s1, string s2, array args);
mixed handle_objects(array args);
mixed handle_exits(array args);

mapping attributes;
int dest, indent, linesync;
array lines;
array errors;
string fname, tail;
string setup_args;
array cur_vars;
array globals;

array inherits;
array triggers;

void create() {
    set_privilege(1);
}

void add_error(int line, string err) {
    if (sizeof(errors) < 3)
	errors += ({ fname + ":" + line + "-> " + err });
}

void do_errors() {
    if (sizeof(errors))
	error("Script compilation failed:\n" + implode(errors, "\n") + "\n");
}

array line_info() {
    if (linesync) {
	linesync = 0;
	return ({ cur });
    }
    return ({});
}

array parse_long_string() {
    int first = cur;
    string ind;
    int indent;
    string firstline;

    if (cur == sizeof(lines)) return ({});
    firstline = lines[cur++];
    while (firstline[indent] == ' ')
	indent++;
    if (indent == 0)
	add_error(cur, "Bad indentation.");
    ind = repeat_string(" ", indent);

    while (cur < sizeof(lines)) {
	string line = lines[cur++];

	if (line[0..indent-1] != ind) {
	    if (line != "end")
		add_error(cur, "Missing end.");
	    break;
	}
    }
    return map(lines[first..cur-2], (: $1[$(indent)..] :));
}

array parse_block(int oldind, int indent) {
    int needend = -1;
    array ret = ({});

    while (cur < sizeof(lines)) {
	int newind;
	string line = lines[cur++];

	if (line == "" || line[0] == '#') {
	    linesync = 1;
	    continue;
	}

	if (line[0] == ' ') {
	    newind = 1;
	    while (line[newind] == ' ')
		newind++;
	    line = line[newind..];
	} else newind = 0;

	if (indent == -1) {
	    if (newind == 0)
		add_error(cur, "Bad indentation.");
	    indent = newind;
	}

	if (newind < indent) {
	    if (line == "end") {
		if (newind == oldind) {
		    needend = 0;
		    break;
		} else if (needend)
		    add_error(cur, "Bad indentation for 'end'.");
	    }
	    cur--;
	    break;
	} else
	if (newind > indent) {
	    if (arrayp(ret[<1]))
		add_error(cur, "Bad indentation.");
	    cur--;
	    ret[<1] = ({ ret[<1] }) + line_info() + parse_block(indent, newind);
	} else {
	    ret += line_info() + ({ line });
	    needend++;
	}
    }

    if (needend > 0)
	add_error(cur-1, "Missing 'end'.");
    return ret;
}

void assert_alphanum(int line, string what) {
    if (!regexp(what, "^[A-Za-z][A-Za-z0-9_]*$"))
	add_error(line, "Illegal name '" + what + "'.");
}

void handle_grouping() {
    int newind;

    attributes = ([ "is" : ({ LPSCRIPT_SPECIAL, (: handle_is :) }),
      "variables" : ({ LPSCRIPT_SPECIAL, (: handle_variables :) }) ]);
    unique = linesync = cur = dest = indent = 0;
    tail = setup_args = "";
    cur_vars = globals = ({});

    while (cur < sizeof(lines)) {
	mixed part1;
	string part2, part3;
	string line = lines[cur++];

	if (line == "" || line[0] == '#') {
	    linesync = 1;
	    continue;
	}

	if (line[0] == ' ')
	    add_error(cur, "Bad indentation.");
	if (sscanf(line, "%s[%s]=%s", part1, part2, part3) == 3) {
	    assert_alphanum(cur, part1);
	    part1 = ({ "=[", part1, part2 });
	    part2 = part3;
	} else if (sscanf(line, "%s[%s]:%s", part1, part2, part3) == 3) {
	    assert_alphanum(cur, part1);
	    part1 = ({ ":[", part1, part2 });
	    part2 = part3;
	} else if (sscanf(line, "%s=%s", part1, part2) == 2) {
	    assert_alphanum(cur, part1);
	    part1 = ({ "=", part1 });
	} else if (sscanf(line, "%s:%s", part1, part2) == 2) {
	    assert_alphanum(cur, part1);
	    part1 = ({ ":", part1 });
	} else if (line == "---") {
	    tail = implode(lines[cur..], "\n");
	    lines = lines[0..dest-1];
	    return;
	} else part1 = 0;

	if (part1) {
	    if (part2 == "") {
		if (part1[0][0] == "=")		  
		    lines[dest++] = line_info() + part1 + parse_long_string();
		else
		{
		    lines[dest++] = line_info() + part1 + parse_block(0, -1);
		}
	    } else {
		if (part1[0][0] == ":")
		    add_error(cur, "Illegal function declaration");
		lines[dest++] = line_info() + part1 + ({ trim_spaces(part2) });
	    }
	} else
	    add_error(cur, "Syntax error");
    }
    lines = lines[0..dest-1];
}

/*
 * expr1 ::= <number>
 * expr1 ::= <literal>
 * expr1 ::= '(' subexp ')'
 * expr1 ::= '$' var
 */
mixed handle_expr1(string arg) {
    string rest = "";
    mixed tmp;

    switch (arg[0]) {
    case '(':
	tmp = handle_subexpression(arg[1..]);
	if (tmp[1][0] != ')')
	    add_error(cur, "expected ')'");
	return ({ tmp[0], trim_spaces(tmp[1][1..]) });
    case '0'..'9':
	if (sscanf(arg, "%d%s", tmp, rest) == 2)
	    return ({ tmp+"", trim_spaces(rest) });
	break;
    case '$':
	sscanf(arg, "$%([A-Za-z0-9_]*)%s", arg, rest);
	if (member_array(arg, cur_vars) == -1)
	    add_error(cur, "Undefined variable '" + arg + "'");
	rest = trim_spaces(rest);
	return ({ "_" + arg, rest });
    case '"':
	if (sscanf(arg, "\"%(([^\\\"]|\\.)*)\"%s", tmp, rest) != 2)
	    add_error(cur, "Missing '\"'");
	return ({ "\"" + tmp + "\"", trim_spaces(rest) });
    case 'A'..'Z': 
    case 'a'..'z':
	sscanf(arg, "%([A-Za-z]*)%s", arg, rest);
	rest = trim_spaces(rest);

	switch (arg) {
	case "who":
	    return ({ "this_body()", rest });
	case "me":
	    return ({ "this_object()", rest });
	case "here":
	    return ({ "environment(this_body())", rest });
	case "container":
	    return ({ "environment(this_object())", rest });
	case "chance":
	    tmp = handle_expr1(rest);
	    return ({ "random(100) < " + tmp[0], tmp[1] });
	}
    }
    add_error(cur, "'" + arg + "' unrecognized.");
    return ({ "", rest });
}

string handle_call(string s1, string s2, array rest...) {
    return "call_other(" + s1 + ", " + s2 + 
    (sizeof(rest) > 0 ? "," : "") +
    implode(rest, ", ") + ")";
}

string handle_lcall(string s1, array rest...) {
    if (s1[0] == '"' && s1[<1] == '"')
	return s1[1..<2] + "(" + implode(rest, ", ") + ")";
    else
	return handle_call("this_object()", s1, rest...);
}

mixed handle_prefix(string arg) {
    string rest = "";

    sscanf(arg, "%s %s", arg, rest);
    rest = trim_spaces(rest);

    switch (arg) {
    case "find":
	return ({ (: "present(" + $1 + ", " + $2 + ")" :), rest, 2 });
    case "call":
	return ({ (: handle_call :), rest, -3 });
    case "lcall":
	return ({ (: handle_lcall :), rest, -1 });
    case "not":
	return ({ (: "!" + $1 :), rest, 1 });
    case "new":
	return ({ (: "new(" + $1 + ")" :), rest, 1 });
    }

    return 0;
}

mixed handle_infix(string arg) {
    string rest = "";

    sscanf(arg, "%s %s", arg, rest);
    rest = trim_spaces(rest);

    switch (arg) {
    case "notequal":
	return ({ (: $1 + " != " + $2 :), rest });
    case "<":
    case ">":
    case "<=":
    case ">=":
    case "!=":
    case "==":
	return ({ (: $1 + " " + $(arg) + " " + $2 :), rest });
    }

    return 0;
}

mixed handle_assignment(string arg) {
    string var;
    string rest;
    mixed tmp;

    sscanf(arg, "%([A-Za-z0-9_]*)%s", var, rest);
    rest = trim_spaces(rest);
    if (rest[0] != '=')
	add_error(cur, "Expected '=' after '$" + var + "'");
    else
	rest = rest[1..];
    if (var == "")
	add_error(cur, "Missing variable name after '$'");

    tmp = handle_expression(rest);
    if (member_array(var, cur_vars) == -1) {
	cur_vars += ({ var });
	return ({ "_" + var + "=" + tmp, "_" + var });
    } else {
	return ({ "_" + var + "=" + tmp, 0 });
    }
}

/*
 * expr2 ::= expr1
 * expr2 ::= PREFIX expr2
 * expr2 ::= PREFIX2 expr2 expr2
 */
mixed handle_expr2(string arg) {
    int grabextra;
    string rest;
    mixed tmp;
    function f;
    int num;
    array args = ({});

    if (arg == "") {
	add_error(cur, "expression expected.");
	return ({ "0", "" });
    } else if (tmp = handle_prefix(arg)) {
	num = tmp[2];
	rest = tmp[1];
	f = tmp[0];
	if (num < 0) {
	    grabextra = 1;
	    num = -(num + 1);
	}
	while (num-- > 0) {
	    tmp = handle_expr2(rest);
	    args += ({ tmp[0] });
	    rest = tmp[1];
	}
	if (grabextra) {
	    while (rest != "" && rest[0] != ')') {
		tmp = handle_expr2(rest);
		args += ({ tmp[0] });
		rest = tmp[1];
	    }
	}
	tmp[0] = evaluate(f, args...);
	tmp[1] = rest;
	return tmp;
    } else 
	return handle_expr1(arg);
}

/*
 * subexp ::= expr2 INFIX subexp
 */
mixed handle_subexpression(string arg) {
    mixed tmp, tmp2, tmp3;

    if (arg == "") {
	add_error(cur, "expression expected.");
	return ({ "0", "" });
    } else {
	tmp = handle_expr2(arg);
	if (tmp2 = handle_infix(tmp[1])) {
	    tmp3 = handle_subexpression(tmp2[1]);
	    tmp[0] = evaluate(tmp2[0], tmp[0], tmp3[0]);
	    tmp[1] = tmp3[1];
	}
	return tmp;
    }
}

string handle_expression(string arg) {
    mixed tmp;

    tmp = handle_subexpression(trim_spaces(arg));
    if (tmp[1] != "")
	add_error(cur, "junk found after valid expression");

    return tmp[0];
}

string translate_string(string str) {
    string before, var;
    string ret = "\"";

    str = replace_string(str, "\"", "\\\"");
    while (sscanf(str, "%s$%s", before, str) == 2) {
	ret += before;
	sscanf(str, "%([A-Za-z0-9_]*)%s", var, str);
	ret += "\" + _" + var + " + \"";
    }
    ret += str + "\"";
    return ret;
}

string handle_block(array lines) {
    array ret = ({ "", "" });
    int i;
    string tmp;

    foreach (mixed line in lines) {
	if (intp(line)) {
	    cur = line;
	} else
	if (arrayp(line)) {
	    string keyword, args;
	    function f;

	    keyword = line[0];
	    sscanf(keyword, "%s %s", keyword, args);
	    if (f = keywords[keyword]) {
		ret[<2] += evaluate(f, args, line[1..]);
	    } else
		add_error(cur, "Unknown keyword '" + keyword + "'.");
	}
	else if (line[0] == '!') {
	    if (member_array(M_ACTIONS, inherits) != -1)
		ret[<2] += "respond(" + translate_string(line[1..]) + ");\n";
	    else
		ret[<2] += "this_object()->do_game_command(" + translate_string(line[1..]) + ");\n";
	} else if (line[0] == '$') {
	    mixed tmp2;

	    tmp2 = handle_assignment(line[1..]);
	    if (tmp2[1])
		ret[<1] += "mixed " + tmp2[1] + ";\n";
	    ret[<2] += tmp2[0] + ";\n";
	} else {
	    string keyword, args;
	    function f;

	    keyword = line;
	    sscanf(keyword, "%s %s", keyword, args);
	    if (f = keywords[keyword]) {
		ret[<2] += evaluate(f, args, ({}));
	    } else
		add_error(cur, "Unknown keyword '" + keyword + "'.");
	    if (keyword == "delay")
		ret += ({ "", "" });
	}
    }
    tmp = "";
    for (i = 0; i < sizeof(ret); i += 2)
	tmp += "{\n" + ret[i+1] + ret[i] + ";\n}\n";

    return tmp;
}

string handle_action(string arg) {
    if (member_array(M_ACTIONS, inherits) != -1)
	return "simple_action(\"" + arg + "\", this_object());";
    else
	return "this_body()->simple_action(\"" + arg + "\", this_object());";
}

string sub_file_name() {
    array tmp = split_path(fname);
    return tmp[0] + "tmp_" + tmp[1][0..<5] + "_" + (unique++) + ".scr";
}

string handle_delay(string arg) {
    int idx = unique++;
    string args, rest;
    array locals = cur_vars - globals;

    if (intrigger) 
	add_error(cur, "Delay illegal inside trigger (move to a function).\n");

    if (sizeof(locals)) {
	args = "mixed _" + implode(locals, ", mixed _");
	rest = ", _" + implode(locals, ", _");
    }
    else args = rest = "";

    return "call_out(\"unnamed" + idx + "\", " + arg + rest + "); }\n}\n\nvoid unnamed" + idx + "(" + args + ") {\n{\n";
}

string handle_check(string arg, array lines) {
    string ret = "";

    foreach (string line in lines) {
	string left = "", right = "";
	string tmpl, tmpr;

	if (sscanf(line, "%s:%s", left, right) != 2)
	    add_error(cur, "Missing ':'");
	tmpl = handle_expression(left);
	tmpr = handle_expression(right);
	ret += "if (" + tmpl + ")\n  return " + tmpr + ";\n";
    }
    ret += "return 1;";
    return ret;
}

string handle_oneof(string arg, array block) {
    string ret;

    ret = "switch (random(" + sizeof(block) + ")) {\n";

    for (int i = 0; i < sizeof(block); i++) {
	ret += "case " + i + ": {\n";
	ret += handle_block( ({ block[i] }) );
	ret += "break; }\n";
    }
    return ret + "}\n";
}

mixed handle_periodic(string arg, array lines) {
    int min, max;
    string time;

    if (sscanf(arg, "%d to %d", min, max) == 2) {
	time = min + " + random(" + (max-min) + ")";
    } else {
	sscanf(arg, "%d", min);
	time = "" + (min || 2);
    }

    cur_vars = globals;
    return ({ "setup", "f = function(function f) { " + handle_block(lines) + " call_out(f, " + time + ", f); }; call_out(f, " + time + ", f)" });
}

mixed handle_setup(string arg, array lines) {
    setup_args = arg;
    cur_vars = globals;
    return ({ "setup", handle_block(lines) });
}

mixed handle_trigger(string arg, array lines) {
    int num;
    int oldlen;

    intrigger = 1;
    oldlen = strlen(arg);
    arg = replace_string(arg, "*", "%s");
    num = strlen(arg) - oldlen;

    cur_vars = globals;
    triggers += ({ ({ arg, num, handle_block(lines) }) });

    intrigger = 0;
    return 0;
}

varargs string regenerate(array lines, int level) {
    string ind = repeat_string("  ", level);
    string ret = "";

    foreach (mixed item in lines) {
	if (intp(item)) continue;
	if (arrayp(item)) {
	    ret += ind + item[0] + "\n" + regenerate(item[1..], level + 1) + ind + "end\n";
	} else
	    ret += ind + item + "\n";
    }
    return ret;
}

// This allows certain attributes which should represent a single object
// (eg wielded = weapon) to optionally accept a random choice :
// wielded =
//   weapon1
//   weapon2
// end
string one_object(array args)
{
  string array output = ({});
  foreach (mixed line in args)
  {
    if (arrayp(line))
    {
      string fn = sub_file_name();
      string id, rest;
      object ob;

      if (sscanf(line[0], "%s=%s", id, rest) != 2 || id == "" || rest != "")
        add_error(cur, "Syntax error in subobject.\n");
      unguarded(1, (: write_file, fn, regenerate(line[1..]), 1 :));
      if (ob = find_object(fn))
        destruct(ob); // we want it to recompile too
      line = fn;
    }
    output += ({ sprintf("  \"%s\" ", line) });
  }
  if(sizeof(output)>1)
    return "choice( ({" + implode(output, ", ") + "}) )";
  else
    return output[0];
}

mixed handle_wield(array args)
{
  return ({ "setup", "set_wielding(" + one_object(args) + ")" });
}

mixed handle_wear(array args)
{
  return ({ "setup", "set_wearing(" + one_object(args) + ")" });
}

mixed handle_objects(array args) {
    string ret = "set_objects( ([\n";
    string tmp;

    foreach (mixed line in args) {
	if (arrayp(line)) {
	    string fn = sub_file_name();
	    string id, rest;
	    object ob;

	    if (sscanf(line[0], "%s=%s", id, rest) != 2 || id == "" || rest != "")
		add_error(cur, "Syntax error in subobject.\n");
	    unguarded(1, (: write_file, fn, regenerate(line[1..]), 1 :));
	    if (ob = find_object(fn))
		destruct(ob); // we want it to recompile too
	    line = fn;
	}
	if (sscanf(line, "%s(%s", line, tmp) == 2) {
	    if (tmp[<1] == ')')
		tmp = tmp[0..<2];
	    ret += sprintf("  \"%s\" : ({ %s }),\n", line, tmp);
	} else {
	    ret += sprintf("  \"%s\" : 1,\n", line);
	}
    }
    return ({ "setup", ret + "]) )" });
}

mixed handle_mapping(string rfunc, string func, array args) {
    string ret = func + "( ([\n";

    foreach (string line in args) {
	string exit, value;

	if (sscanf(line, "%s:%s", exit, value) != 2) {
	    add_error(cur, "Illegal exit value");
	    continue;
	}
	exit = trim_spaces(exit);
	value = trim_spaces(value);
	ret += sprintf("  \"%s\" : \"%s\",\n", exit, value);
    }
    return ({ rfunc, ret + "]) )" });
}

mixed handle_int_mapping(string rfunc, string func, array args) {
    string ret = func + "( ([\n";

    foreach (string line in args) {
	string key, value;

	if (sscanf(line, "%s:%s", key, value) != 2) {
	    add_error(cur, "Illegal int map value");
	    continue;
	}
	key = trim_spaces(key);
	value = trim_spaces(value);
	ret += sprintf("  \"%s\" : %s,\n", key, value);
    }
    return ({ rfunc, ret + "]) )" });
}

string handle_string(array args) {
    string ret = implode(map(args, (: stringp($1) ? $1 : "%-%" :)), "");
    int ret_size;

    ret = replace_string(ret, " %-% ", "\n\n");
    ret = replace_string(ret, "%-%", "");
    ret = replace_string(ret, "\"", "\\\"");
    ret_size = sizeof(ret);

    if (ret_size > 65) {
	string tmp = "";

	for (int i = 0; i <= ret_size; i += 65) {
	    int j;

	    if (i+64 >= ret_size)
		j = ret_size - 1;
	    else j = i+64;

	    tmp += "\"" + ret[i..j] + "\"\n";
	}

	return tmp;
    }

    return "\"" + ret + "\"";
}

mixed handle_flags(array args) {
    string ret = "0";
    args = map(explode(args[0], ","), (: trim_spaces :));
    foreach (string arg in args) {
	switch (arg) {
	case "attached" : ret += " | ATTACHED"; break;
	}
    }
    return ({ "setup", "set_flag(" + ret + ")" });
}

mixed handle_list(string rfunc, string func, array args) {
    args = map(explode(args[0], ","), (: trim_spaces :));

    return ({ rfunc, func + "(\"" + implode(args, "\", \"") + "\")" });
}

void get_attributes(string fname) {
    object ob = load_object(fname);
    mapping tmp;

    if (!ob) return;
    foreach (string prog in deep_inherit_list(ob)) {
	tmp = prog->lpscript_attributes();
	if (tmp) attributes += tmp;
    }
    tmp = ob->lpscript_attributes();
    if (tmp) attributes += tmp;
}

mixed handle_variables(array args) {
    args = map(explode(args[0], ","), (: trim_spaces :));

    globals += args;
    return ({ -1, "mixed _" + implode(args, ";\nmixed _") });
}

mixed handle_is(array args) {
    args = map(explode(args[0], ","), (: trim_spaces :));

    foreach (string file in args) {
	if (file[0] == '"' && file[<1] == '"') {
	    get_attributes(file);
	    inherits += ({ file });
	} else if (inheritable[file]) {
	    get_attributes(inheritable[file]);
	    inherits += ({ inheritable[file] });
	} else
	    add_error(cur, "'" + file + "' unknown.");
    }
    return 0;
}

mixed handle_gender(array args) {
    int gen;

    switch (args[0]) {
    case "male":
	gen = 1;
	break;
    case "female":
	gen = 2;
	break;
    case "neuter":
	gen = 0;
	break;
    }

    return ({ "setup", "set_gender(" + gen + ")" });
}

int handle_boolean(string arg) {
    switch (arg) {
    case "true":
	return 1;
	break;
    case "false":
	return 0;
	break;
    }
}

array handle_attribute(mixed entry, array args) {
    switch (entry[0]) {
    case LPSCRIPT_LIST:
	return handle_list(entry[1], entry[2], args);
    case LPSCRIPT_STRING:
	return ({ entry[1], entry[2] + "(" + handle_string(args) + ")" });
    case LPSCRIPT_INT:
	return ({ entry[1], entry[2] + "(" + args[0] + ")" });
    case LPSCRIPT_OBJECTS:
	return handle_objects(args);
    case LPSCRIPT_WIELD:
  return handle_wield(args);
    case LPSCRIPT_WEAR:
  return handle_wear(args);
    case LPSCRIPT_GENDER:
	return handle_gender(args);
    case LPSCRIPT_BOOLEAN:
	return ({ entry[1], entry[2] + "(" + handle_boolean (args[0]) + ")" });
    case LPSCRIPT_MAPPING:
	return handle_mapping(entry[1], entry[2], args);
    case LPSCRIPT_INT_MAPPING:
	return handle_int_mapping(entry[1], entry[2], args);
    case LPSCRIPT_SPECIAL:
	return evaluate(entry[1], args);
    case LPSCRIPT_FLAGS:
	return handle_flags(args);
    case LPSCRIPT_TWO:
	return evaluate(entry[1], args[0], args[1..]);
    }
    return 0;
}

void handle_parsing() {
    cur = 0;
    inherits = ({});
    triggers = ({});

    for (int i = 0; i < sizeof(lines); i++) {
	int j;
	mixed arr = lines[i];
	mixed entry;

	if (intp(arr[0])) {
	    cur = arr[0];
	    j = 1;
	} else 
	    j = 0;

	switch (arr[j]) {
	case "=":
	    if (entry = attributes[arr[j+1]]) {
		cur++;
		lines[i] = handle_attribute(entry, arr[j+2..]);
	    } else
		add_error(cur++, "Unknown attribute '" + arr[j+1] + "'.");
	    break;
	case "=[":
	    if (entry = attributes[arr[j+1]]) {
		cur++;
		lines[i] = handle_attribute(entry, arr[j+2..]);
	    } else
		add_error(cur++, "Unknown attribute '" + arr[j+1] + "'.");
	    break;
	case ":":
	    cur_vars = globals;
	    lines[i] = ({ arr[j+1], handle_block(arr[j+2..]) });
	    break;
	case ":[":
	    if (entry = funcs[arr[j+1]]) {
		cur++;
		lines[i] = evaluate(entry, arr[j+2], arr[j+3..]);
	    } else
		add_error(cur++, "Unknown function '" + arr[j+1] + "'.");
	    break;
	default:
	    error("Internal error.\n");
	}
    }
}

private nomask void handle_generation(string outname) {
    string header = implode(inherits, (: $1 + "inherit \"" + $2 + "\";\n" :), "") + "\n";
    string ret = "";
    string globals = "";
    string protos = "";
    array tmp;
    string actions = "";

    if (sizeof(triggers)) {
	for (int i = 0; i < sizeof(triggers); i++)
	    actions += "case " + i + ":\n" + triggers[i][2] + "return;\n";

	ret += "array patterns = ({ ";
	for (int i = 0; i < sizeof(triggers); i++) {
	    if (i) ret += ", ";
	    ret += "\"" + triggers[i][0] + "\"";

	}
	ret += "});\n";
	ret += "array num = ({ ";
	for (int i = 0; i < sizeof(triggers); i++) {
	    if (i) ret += ", ";
	    ret += triggers[i][1];
	}
	ret += "});\n";	    
	/*******************************/
	ret += @END
	
void receive_outside_msg(string str) {
string _1, _2, _3, _4, _5, _6;
if (str[<1] == '\n') str = str[0..<2];
for (int i = 0; i < sizeof(patterns); i++) {
if (sscanf(str, patterns[i], _1, _2, _3, _4, _5, _6) == num[i]) {
switch (i) {
END + actions + "}}}}";
	/*******************************/
    }

    tmp = unique_array(lines - ({ 0 }), (: $1[0] :));
    foreach (array item in tmp) {
	if (item[0][0] == -1) {
	    foreach (array block in item)
	    globals += block[1] + ";\n";
	    continue;
	} else if (item[0][0] == "setup") {
	    protos += "void setup(" + setup_args + ");\n";
	    ret += "\nvoid setup(" + setup_args + ") {\nfunction f;\n";
	} else {
	    protos += "mixed " + item[0][0] + "();\n";
	    ret += "\nmixed " + item[0][0] + "() {\n";
	}

	foreach (array block in item)
	ret += block[1] + ";\n";
	ret += "}\n\n";
    }

    unguarded(1, (: write_file, outname,"#pragma no_warnings\n" + header + globals + protos + tail + ret, 1 :));
}

object compile(string scrname) {
    mixed tmp = split_path(scrname);
    mixed path = tmp[0];
    mixed name = tmp[1][0..<5]; /* no .scr */
    mixed outname = path + "tmp_" + name + ".c";
    mixed text;

    fname = scrname;
    text = read_file(scrname);
    if (!text) return 0;
    lines = explode(text, "\n");
    errors = ({});
    handle_grouping();
    do_errors();
    handle_parsing();
    do_errors();
    handle_generation(outname);

    return load_object(outname);
}