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. */

#include <daemons.h>
#include <commands.h>

private object		script;

class parse_info {
    int cur;
    string array lines;
    string array vars;
    int trigger_num;
    int term;
}

private array
parse_file(class parse_info pi, string array term) {
    array program = ({});
    string keyword;
    int tmp;
    array tmparr;
    string arg1, arg2;
    
    while (pi->cur < sizeof(pi->lines)) {
	string line = trim_spaces(pi->lines[pi->cur++]);
	if (line[0] == '#')
	    continue;

	if (term && (tmp = member_array(line, term)) != -1) {
	    pi->term = tmp;
	    return program;
	}
	if (line[0] == '@') {
	    keyword = line[1..];
	    line = 0;
	    sscanf(keyword, "%s %s", keyword, line);
	    switch (keyword) {
	    case "function":
		if (!line) error("Missing function name.\n");
		program += ({ ({ "function", line }) + 
				  parse_file(pi, ({ "@endfunction" })) });
		break;
	    case "move":
	    case "callscript":
		if (sscanf(line, "%s %s", arg1, arg2) < 2)
		    error("Too few arguments to " + keyword);
		program += ({ ({ keyword, arg1, arg2 }) });
		break;
	    case "trigger":
		program += ({ ({ "trigger", line, pi->trigger_num }) });
		program += ({ ({ "function", "trigger_" + pi->trigger_num++ }) + 
				  parse_file(pi, ({ "@endtrigger" })) });
		break;
	    case "doevery":
	    {
		int first, second;
		if (sscanf(line, "%d to %d", first, second) != 2) {
		    first = to_int(line);
		    second = first;
		}

		program += ({ ({ "doevery", first, second }) + 
				  parse_file(pi, ({ "@enddoevery" })) });
		break;
	    }
	    case "oneof":
		program += ({ ({ "oneof" }) + parse_file(pi, ({ "@endoneof" })) });
		break;
	    case "set":
	    {
		string lhs, rhs;
		if (sscanf(line, "%s=%s", lhs, rhs) != 2)
		    error("Missing = in @set\n");
		lhs = trim_spaces(lhs);
		rhs = trim_spaces(rhs);
		
		if (lhs[0] != '$') error("Variable must begin with $\n");
		if (member_array(lhs[1..], pi->vars) == -1)
		    pi->vars += ({ lhs[1..] });
		program += ({ ({ "set", lhs, rhs }) });
		break;
	    }
	    case "action":
		program += ({ ({ keyword, line }) });
		break;
	    case "if":
		tmparr = ({ parse_file(pi, ({ "@else", "@endif" }) ) });
		if (pi->term == 0)
		    tmparr += ({ parse_file(pi, ({ "@endif" })) });
		program += ({ ({ "if", line }) + tmparr });
		break;
	    case "delay":
		tmp = to_int(line || "2");
		if (tmp <= 0) error("Bad interval " + line + ".\n");
		program += ({ ({ "delay", tmp }) +
				  parse_file(pi, term) });
		return program;
		break;
	    default:
		error("Unknown keyword " + keyword + ".\n");
	    }
	    continue;
	}
	if (line != "")
	    program += ({ line });
    }
    if (term) error("missing " + term[<1]);
    return program;
}

string handle_prefix1(string token, string expr) {
    switch (token) {
    case "new":
	return "new(" + expr + ")";
    case "not":
	return "!(" + expr + ")";
    }
}

string handle_prefix2(string token, string expr1, string expr2) {
    switch (token) {
    case "find":
	return "present(\"" + expr1 + "\", " + expr2 + ")";
    }
}

string handle_infix2(string token, string expr1, string expr2) {
    switch (token) {
    case "find":
	return "present(\"" + expr1 + "\", " + expr2 + ")";
    }
}

string parse_literal(string expr) {
    int tmp;
    string tmpstr;
    
    if (expr[0] == '$')
	return "var_" + expr[1..];
    if (expr == "here")
	return "environment(actor)";
    if (sscanf(expr, "%d%s", tmp, tmpstr) == 2 && tmpstr == "")
	return "" + tmp;
    return expr;
}

string parse_expr1(array parts) {
    string expr1, expr2;
    string token = parts[parts[0]++];
    
    switch (token) {
    case "new":
    case "not":
	expr1 = parse_expr1(parts);
	return handle_prefix1(token, expr1);
    case "find":
	expr1 = parse_expr1(parts);
	expr2 = parse_expr1(parts);
	return handle_prefix2(token, expr1, expr2);
    default:
	return parse_literal(token);
    }
}

/*
 * Simple RDP parser.  The grammar is:
 *
 * expr1: literal |
 *        prefix1 expr1 |
 *        prefix2 expr1 expr1
 *
 * expr: expr1 |
 *       expr1 infix expr
 */
string parse_expr(array parts) {
    string expr1, expr2;
    string token;
    
    expr1 = parse_expr1(parts);
    if (parts[0] == sizeof(parts)) return expr1;
    token = parts[parts[0]++];
    if (token != "infix2")
	error("Syntax error.\n");
    expr2 = parse_expr1(parts);

    return handle_infix2(token, expr1, expr2);
}

string handle_expression(string e) {
    array parts = explode(e, " ");
    
    parts = ({ 1 }) + map(parts, (: trim_spaces :)) - ({ "" });

    if (sizeof(parts) == 1) error("Missing expression.\n");

    return parse_expr(parts);
}

string handle_set(string lhs, string rhs) {
    return "  var_" + lhs[1..] + " = " + handle_expression(rhs) + ";\n";
}

void compile_func(mapping funcs, string sname, array prog) {
    int i;
    string ssname;
    mixed prog_stack = ({ "", prog });
    
    funcs[sname] = "";
    while (sizeof(prog_stack)) {
	/* pop */
	funcs[sname] += prog_stack[0];
	prog = prog_stack[1];
	prog_stack = prog_stack[2..];
	
	for (i = 0; i < sizeof(prog); i++) {
	    mixed item = prog[i];
	
	    if (stringp(item))
		funcs[sname] += "  actor->respond(\"" + item + "\");\n";
	    else {
		switch (item[0]) {
		case "function":
		    compile_func(funcs, item[1], item[2..]);
		    break;
		case "action":
		    funcs[sname] += "  call_out( (: $(actor)->simple_action(\"" + item[1] + "\") :), 0);\n";
		    break;
		case "trigger":
		    //### Do a better job of making a regexp
			      item[1] = replace_string(item[1], "*", ".*");
		    if (!funcs["triggers"]) funcs["triggers"] = "";
		    funcs["triggers"] += "  if (regexp(str, \"" + item[1] + "\"))\n    trigger_" + item[2] + "();\n";
		    break;
		case "move":
		    funcs[sname] += "  " + handle_expression(item[1]) + "->move(" + handle_expression(item[2]) + ");\n";
		    break;
		case "callscript": 
		    funcs[sname] += "  present(\"" + item[1] + "\", environment(actor))->run_script(\"" + item[2] + "\");\n";
		    break;
		case "delay":
		    ssname = sname + "_";
		    funcs[sname] += "  call_out( (: " + ssname + " :), " + item[1] + ");\n";
		    compile_func(funcs, ssname, item[2..]);
		    break;
		case "set":
		    funcs[sname] += handle_set(item[1], item[2]);
		    break;
		case "if":
		    funcs[sname] += "  if (" + handle_expression(item[1]) + ") {\n";
		    if (sizeof(item) == 4) {
			prog_stack = ({ "  } else {\n", item[3], "}\n", prog[i+1..] })
			    + prog_stack;
			prog = item[2];
			i = -1;
		    } else {
			prog_stack = ({ "  }\n", prog[i+1..] }) + prog_stack;
			prog = item[2];
			i = -1;
		    }
		    break;
		case "doevery":
                    /* Not implemented */
		default:
		    error("Unknown opcode " + item[0] + "\n");
		}
	    }
	}
    }
}

string compile_program(class parse_info pi, array prog) {
    mapping funcs = ([]);
    string ret;
    
    compile_func(funcs, "main", prog);

    ret = @END
object actor;

void create(object a) {
    actor = a;
}

END;

    foreach (string var_name in pi->vars) {
	ret += "mixed var_" + var_name + ";\n";
    }

    foreach (string name, string statements in funcs)
	if (name != "triggers")
	    ret += "void " + name + "();\n";
    ret += "\n\n";
    
    if (funcs["triggers"]) {
        ret += "void triggers(string str) {\n" + funcs["triggers"] + "}\n\n";
        map_delete(funcs, "triggers");
    }

    foreach (string name, string statements in funcs)
	ret += "void " + name + "() {\n" + statements + "}\n\n";

    return ret;
}

void receive_outside_msg(string str) {
    if (str[<1] == '\n') str = str[0..<2];
    if (script)
	script->triggers(str);
}
	
void receive_private_msg(string str) {
    if (str[<1] == '\n') str = str[0..<2];
    if (script)
	script->triggers(str);
}
	
void compile_script(string fname) {
    class parse_info pi;
    array program;
    object ob;
    
    if (script) destruct(script);
    if (fname[0] != '/') {
	string lname = file_name();
	int tmp = strsrch(lname, "/", -1);
	fname = lname[0..tmp] + fname;
    }
    
    pi = new(class parse_info,
	     lines : explode(read_file(fname), "\n"),
	     vars : ({ }) );

    program = parse_file(pi, 0);
    write_file("/tmp/m_react.c", compile_program(pi, program), 1);

    if (ob = find_object("/tmp/m_react"))
	destruct(ob);
    script = new("/tmp/m_react", this_object());
    script->main();
}

void run_script(string name) {
    call_other(script, name);
}