string code; string stdin; string stdout; integer eof; // EOF encountered in stdin. list mem; integer ptr; integer ip; string ascii = " \n \n !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; string valid_bf = "<>+-.,[]"; // When reading notecards, discard invalid opcodes. // Notecard reading global vars string notecard; integer read_pos; key ds_getsize; key ds_getline; integer numlines; integer l; // listen handler integer public = 0; key whorun; // When !public, only listen to the person who ran the script. float lasttext; // Only update llSetText every n seconds float updateduration = 1.25; integer ops_run; integer need_input; integer accept_input; czRunLoop() { float t = llGetTime(); if (t > lasttext + updateduration) { lasttext = t; string text_status; if (accept_input) { text_status += "Accepting input.\n"; } if (need_input) { text_status += "Waiting on input...\n"; } llSetText("Executing " + notecard + "...\nIP: " +(string)ip + "\nmemptr: " + (string)ptr + "\nRuntime: " + (string)t + "\nTotal ops, ops/sec: " + (string)ops_run + ", " + (string)((float)ops_run/t) + "\nstdout:" + stdout + "\n" + (string)text_status,<1,1,1>,1); //llSetText("IP: " +(string)ip + "\nmemptr: " + (string)ptr + "\nMem: " + llList2CSV(mem) + "\nstdout:" + stdout + "\n" + (string)text_status,<1,1,1>,1); } do { integer ret = czExecOpcode(); ops_run++; if (ret < 0) { // Don't continue exewcuting opcodes if we en counter an error if (ret == -42) { if (llStringLength(stdout)) { llSay(0,stdout); } llSay(0,"*** Program finished execution! Total run time:" + (string)t); if (l >= 0) llListenRemove(l); state default; } if (ret < -50) { llSay(0,"*** Fatal error encountered, halting execution..."); if (l >= 0) llListenRemove(l); accept_input = 0; state default; } return; } } while (llGetTime() < lasttext+updateduration); } integer czGetMem(integer ptr) { while (1+ptr > llGetListLength(mem)) mem += [0]; return llList2Integer(mem,ptr); } czSetMem(integer ptr, integer value) { while (1+ptr > llGetListLength(mem)) mem += [0]; mem = llListReplaceList(mem,[value], ptr, ptr); return; } integer czExecOpcode() { string opcode = llGetSubString(code,ip,ip); if (opcode == ">") { ptr++; } else if (opcode == "<") { ptr--; if (ptr < 0) { llSay(0,"ERR: Program tried to seek past boundary of memory. IP:" + (string)ip + ". ptr:" + (string)ptr); return -100; } } else if (opcode == ">") { ptr++; } else if (opcode == "+") { czSetMem(ptr, czGetMem(ptr)+1); } else if (opcode == "-") { czSetMem(ptr, czGetMem(ptr)-1); } else if (opcode == ".") { integer p = czGetMem(ptr); if (p == 13) { // Do nothing, ignore CR. } else if (p == 10) { llSay(0,stdout); stdout = ""; } else { stdout += llGetSubString(ascii,p,p); } } else if (opcode == ",") { if (llStringLength(stdin) || eof) { integer chr; if (!llStringLength(stdin)) { chr = 0; eof = 0; } else { chr = llSubStringIndex(ascii, llGetSubString(stdin,0,0)); if (chr < 32 && chr != 10 && chr != 13) { // Special case the unprintables to read as a space. chr = 32; } } czSetMem(ptr, chr); if (llStringLength(stdin) > 1) { stdin = llGetSubString(stdin,1,-1); } else { stdin = ""; } } else { if (l < 0) { key listento; if (public) { listento = NULL_KEY; } else { listento = whorun; } l = llListen(0,"",whorun,""); accept_input = 1; } need_input = 1; return 0; } } else if (opcode == "[") { if (czGetMem(ptr) == 0) { integer nest = 1; do { ip++; if (llGetSubString(code,ip,ip) == "[") nest++; if (llGetSubString(code,ip,ip) == "]") nest--; } while (nest || ip >= llStringLength(code) || llGetSubString(code,ip,ip) != "]"); if (ip == llStringLength(code)) { llSay(0,"*** Encountered unmatched [!"); return -200; } } } else if (opcode == "]") { if (czGetMem(ptr) != 0) { integer nest = 1; do { ip--; if (llGetSubString(code,ip,ip) == "[") nest--; if (llGetSubString(code,ip,ip) == "]") nest++; } while (nest || ip < 0 || llGetSubString(code,ip,ip) != "["); if (ip < 0) { llSay(0,"*** Encountered unmatched ]!"); return -201; } } } // Ignore invalid characters. ip++; if (ip >= llStringLength(code)) { return -42; } return 1; } // No propram loaded. Listen for a command to run a program. default { state_entry() { l = llListen(0,"", NULL_KEY,""); code = ""; mem = [0]; ptr=0; llSetText("No program loaded...", <1,1,1>,1); } listen(integer c, string who, key id, string msg) { if (msg == "list") { list files; integer i = 0; integer l = llGetInventoryNumber(INVENTORY_NOTECARD); for (i = 0; i < l; i++) { files += llGetInventoryName(INVENTORY_NOTECARD,i); } llSay(0,"*** Available programs:\n " + llDumpList2String(files,"\n ")); } else if (llGetSubString(msg,0,3) == "run ") { notecard = llGetSubString(msg,4,-1); if (llGetInventoryKey(notecard) == NULL_KEY) { llSay(0,"*** Unable to load " + (string)notecard + "!"); } else { whorun = id; llSetText("Loading notecard...",<1,1,1>,1); ds_getsize = llGetNumberOfNotecardLines(notecard); } } } dataserver(key id, string dat) { if (id == ds_getsize) { numlines = (integer)dat; read_pos = 0; ds_getsize = NULL_KEY; } else if (id == ds_getline) { integer i; integer l = llStringLength(dat); for (i = 0; i,1); } else { llSetText("Notecard loaded!",<1,1,1>,1); //llSay(0,"*** Program " + (string)notecard + " loaded, executing..."); llListenRemove(l); l = -1; //llSay(0,"Will execute: " + (string)code); state interpret; } } } state interpret { state_entry() { llResetTime(); // Initialize memory. Start with a single cell, and grow them as a program accesses them. mem = [0]; // Clear flags. accept_input = 0; need_input = 0; // Listener will be enabled on the first call to getch. stdin = ""; stdout = ""; eof = 0; ip = 0; ptr = 0; ops_run = 0; lasttext = -9001; llSetTimerEvent(0.001); } timer() { czRunLoop(); } listen(integer c, string who, key id, string msg) { if (!eof) { if (msg == "EOF") { eof = 1; } else { stdin += msg + "\n"; } } if (llStringLength(stdin) || !eof) need_input = 0; llListenRemove(l); l = -1; accept_input = 0; czRunLoop(); } }