Chatbot
Compile and run the LSL you chat on a channel, faster than you can compile and run it thru the 2007-08 SL GUI.
Created by Ppaatt Lynagh. "Allow anyone to copy", "next owner can copy & modify", and "mark item for sale price L$0", are the rights on my copies of this script in SL, as of 2007-09-04.
The LSL commands you can chat include:
/7 llSetColor(<0.3, 0.3, 0.3>, ALL_SIDES); /7 llSetColor(<1, 1, 1>, ALL_SIDES);
/7 llSetText("look at me white", <1.0, 1.0, 1.0>, 1.0); /7 llSetText("look at me green", <0.0, 1.0, 0.0>, 1.0); /7 llSetText("look at me black", <0.0, 0.0, 0.0>, 1.0);
The sample code follows.
To keep this sample code conveniently short, this example understands only a few library function names, a few code constant names, a few punctuation marks, and a few data types. I hope you find it easy to add the library function names and code constant names that interest you. Adding the list data type as a possible parameter type might require real work: LSL doesn't understand lists of lists yet. Adding arithmetic operators might require real work, but may be needed for experimenting with conversions between local and global coordinates.
I do think the SL GUI should let me toggle a switch to do this, i.e., to teach any prim of mine to listen to my LSL chat, for the sake of script folk like me who think by chatting. Meanwhile, I write code like this to let me chat commands to my prims anyhow. I wrote this sample just after walking thru the delightfully concise Getting Started with LSL tutorial, which only taught me to call llSetColor and llSetText. Likely I'll soon return here to add a few more example commands, after I learn some other fun commands.
Enjoy,
// Chatbot.lsl // // Compile and run the Lsl you type on a channel, // faster than you can thru the 2007-08 SL GUI. // integer theChannel = 7; // Evaluate any one parameter list theCodes = [ TRUE, FALSE, PI, TWO_PI, PI_BY_TWO, DEG_TO_RAD, RAD_TO_DEG, SQRT2, NULL_KEY, ALL_SIDES, EOF, ZERO_VECTOR, ZERO_ROTATION, 0 ]; list theCodenames = [ "TRUE", "FALSE", "PI", "TWO_PI", "PI_BY_TWO", "DEG_TO_RAD", "RAD_TO_DEG", "SQRT2", "NULL_KEY", "ALL_SIDES", "EOF", "ZERO_VECTOR", "ZERO_ROTATION", "0" ]; list valueOf(string word) { if (0 <= llSubStringIndex(word, ".")) { return [(float) word]; } else if (0 <= llSubStringIndex("0123456789", llGetSubString(word, 0, 0))) { return [(integer) word]; } else { integer index = llListFindList(theCodenames, [word]); if (0 <= index) { return llList2List(theCodes, index, index); } else { return [word]; // unevaluated } } } // Strip the leading and trailing quotes from a quoted evaluated string string cbList2String(list parameters, integer index) { return llGetSubString(llList2String(parameters, index), 1, -2); } // Pass the parameters to the named routine. // Return no result, one result, else the callable and parameters. list resultOf(string callable, list parameters) { list ps = parameters; // abbreviate "parameters" llOwnerSay(callable + "(" + llList2CSV(ps) + ")"); if ("llSetColor" == callable) // color, face { llSetColor(llList2Vector(ps, 0), llList2Integer(ps, 1)); } else if ("llSetText" == callable) // text, color, alpha { llSetText(cbList2String(ps, 0), llList2Vector(ps, 1), llList2Float(ps, 2)); } else { return [callable, "not found"]; } return []; } // Obey one command. list callEach(list words) { // Begin with almost nothing list values = [""]; list depths = [0]; // Take each action in order integer lenWords = llGetListLength(words); integer index; for (index = 0; index < lenWords; ++index) { string word = llList2String(words, index); integer beyond = llGetListLength(values); // Count values of "( ... )" or of "< ... >" if ((word == "(") || (word == "<")) { depths += [beyond]; // push the depth } else if ((word == ")") || (word == ">")) { // Pop the depth integer first = llList2Integer(depths, -1); depths = llList2List(depths, 0, -2); // pop the tail // Pop the parameters integer last = beyond - 1; list parameters = []; if (first <= last) { parameters = llList2List(values, first, last); values = llList2List(values, 0, first - 1); } // Join together the components of a vector if (word == ">") { integer depth = llGetListLength(parameters); if (depth == 3) { vector vec; vec.x = llList2Float(parameters, 0); vec.y = llList2Float(parameters, 1); vec.z = llList2Float(parameters, 2); values += [vec]; } // Join together the components of a rotation if (depth == 4) { rotation rot; rot.x = llList2Float(parameters, 0); rot.y = llList2Float(parameters, 1); rot.z = llList2Float(parameters, 2); rot.s = llList2Float(parameters, 2); values += [rot]; } } // Call with a list of parameters (of non-list types) else if (word == ")") { string callable = llList2String(values, -1); values = llList2List(values, 0, -2); // pop the tail list results = resultOf(callable, parameters); if (llGetListLength(results) == 1) { values += results; } } } // Push any other word as a parameter else { values += word; } } // Succeed return llList2List(values, 1, -1); // pop the head } // Divide a string into words, // by returning each of the spacers as a word // and then all the chars between as words. list split(string source, list spacers) { list outputs = [source]; // Pass never more than 8 spacers to llParseString2List integer lenSpacers = llGetListLength(spacers); integer ss; for (ss = 0; ss < lenSpacers; ss += 8) { list someSpacers = llList2List(spacers, ss, ss + 7); // Call llParseString2List again for each result so far list inputs = outputs; outputs = []; integer lenInputs = llGetListLength(inputs); integer ii; for (ii = 0; ii < lenInputs; ++ii) { string input = llList2String(inputs, ii); outputs += llParseString2List(input, [], someSpacers); } } // Succeed return outputs; } // Divide the command into words. // Take care to join together the words of each quotation. // Return a list of the words of the command. list valueEach(string chars) { // Begin with nothing list values = []; // Describe the language of an LSL expression without operators string escape = "\\"; string quote = "\""; list separators = ["\t", " ", ",", ";"]; list spacers = [quote, escape, "(", ")", "/", "<", ">", "[", "]"]; // Divide the command into words, but also shatter quotations list words = split(chars, separators + spacers); integer lenWords = llGetListLength(words); // Consider each word or word of a quotation integer index = 0; while (index < lenWords) { string word = llList2String(words, index++); // Join together the words of a quotation if (word == quote) { string value = ""; do { word = llList2String(words, index++); if (word != quote) { value += word; } } while ((word != quote) && (index < lenWords)); // Quote the quotation simply, so the quotation is never found in spacers values += quote + value + quote; } // Keep spacers else if (0 <= llListFindList(spacers, [word])) { values += word; } // Discard separators else if ((0 <= llListFindList(separators, [word])) || (word == "")) { ; } // Evaluate other words else { values += valueOf(word); } } // Succeed return values; } // Hear and obey the chat of the owner at the channel. default { state_entry() { llListen(theChannel, "", llGetOwner(), ""); } listen(integer channel, string name, key id, string message) { llOwnerSay(message); list values = valueEach(message); // llOwnerSay(llList2CSV(values)); list results = callEach(values); if (results != []) { llOwnerSay(llList2CSV(results)); } } }