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