User:Katharine Berry/Scripts/SPD Viewer

From The SchomEmunity Wiki
Jump to: navigation, search

Pandorabot | Rotating Online Status | SPD Viewer | Brainfsck Interpreter | Force Reload

Overview

I made an "improved" (in my opinion) version of Decimus' SPD viewer script. It only requires one object, which may be named anything. All scripts are placed inside this object, and may also be named anything.

There is a copy running on my server, which you may use to build your own object. It pulls from the Schome Park Dictionary wiki page when run.

How to use

Having placed all the generated scripts in an object (all of which can be called anything), say "/10 phrase", where phrase is whatever you want to look up. Remember that only you will hear reply!

Code

A syntax highlighted copy is available here.

<?php
define('DATA_SOURCE','http://schome.open.ac.uk/wikiworks/index.php/Schome_Park_Dictionary');
define('ITEM_FORMAT','~<p><b>(.+?)</b> - (.+?)</p>~is'); // ~<p><b>(.+?)</b> - <i>(\(.+?\))</i> (.+?)</p>~is // (Changed from this because we don't need to split out the type)
function makescript($entries)
{
	$entrylist = "[\n\t\"".implode("\",\n\t\"",$entries)."\"\n]";
	$entries = array_keys($entries);
	$keylist = "[\n\t\"".implode("\",\n\t\"",$entries)."\"\n]";
	$script = <<<LSL
list DICT_KEYS = {$keylist};

list DICT_ENTRIES = {$entrylist};

integer REQUEST_CHANNEL = 1;
integer FOUND_CHANNEL = 2;
integer NOT_FOUND_CHANNEL = 3;

default
{
	link_message(integer sender, integer num, string str, key id)
	{
		if(num == REQUEST_CHANNEL)
		{
			integer pos = llListFindList(DICT_KEYS,[str]);
			if(~pos)
			{
				llMessageLinked(LINK_THIS,FOUND_CHANNEL,llList2String(DICT_ENTRIES,pos),NULL_KEY);
			}
			else
			{
				llMessageLinked(LINK_THIS,NOT_FOUND_CHANNEL,"",NULL_KEY);
			}
		}
	}
}
LSL;
	return $script;
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
	<head>
		<title>SPD Viewer Script - Katharine's version</title>
		<style type="text/css">
		/* <![CDATA[ */
		pre {
			border-color: #020202;
			border-width: 2px;
			border-style: dashed;
		}
		h1 {
			font-size: x-large;
			font-family: Helvetica, Verdana, Arial, sans-serif;
		}
		/* ]]> */
		</style>
	</head>
	<body>
		<h1>Controller script</h1>
<pre>
integer REQUEST_CHANNEL = 1;
integer FOUND_CHANNEL = 2;
integer NOT_FOUND_CHANNEL = 3;

integer gBusy = FALSE;
integer gResponses = 0;
integer gSuccess = FALSE;
string gSearch = "";

default
{
	state_entry()
	{
		llListen(10,"",llGetOwner(),"");
	}
	
	listen(integer channel, string name, key id, string message)
	{
		if(gBusy)
		{
			llOwnerSay("Sorry, I'm still thinking about your last question!");
			return;
		}
		gSuccess = FALSE;
		gBusy = TRUE;
		gSearch = message;
		llMessageLinked(LINK_THIS,REQUEST_CHANNEL,llToLower(message),NULL_KEY);
	}
	
	link_message(integer sender, integer num, string str, key id)
	{
		if(num == FOUND_CHANNEL)
		{
			llOwnerSay(str);
			gSuccess = TRUE;
		}
		if(++gResponses == llGetInventoryNumber(INVENTORY_SCRIPT) - 1)
		{
			gBusy = FALSE;
			if(!gSuccess)
			{
				llOwnerSay("Sorry, '"+gSearch+"' is not in the dictionary. Visit <?php print DATA_SOURCE; ?> to add it!");
			}
		}
	}
}
</pre>
<?php
// Use cURL, if it exists - file_get_contents doesn't support remote URLs by default.
if(function_exists('curl_init'))
{
	$curl = curl_init(DATA_SOURCE);
	curl_setopt($curl,CURLOPT_HEADER,false);
	curl_setopt($curl,CURLOPT_RETURNTRANSFER,true);
	$data = curl_exec($curl);
	if(curl_errno($curl) !== 0)
	{
		die('<div style="error">Failed to grab <a href="'.DATA_SOURCE.'">'.DATA_SOURCE.'</a>!</div></body></html>');
	}
}
// Try with file_get_contents and hope.
else
{
	$data = @file_get_contents(DATA_SOURCE);
	if($data === false)
	{
		die('<div style="error">Failed to grab <a href="'.DATA_SOURCE.'">'.DATA_SOURCE.'</a>!</div></body></html>');
	}
}
$data = utf8_decode($data);
preg_match_all(ITEM_FORMAT,$data,$matches,PREG_SET_ORDER);
$entries = array();
$scripts = 0;
foreach($matches as $match)
{
	if(strpos($match[1],'/') === false)
	{
		$match[1] = trim($match[1]);
		if(strpos($match[1],', ') !== false)
		{
			$match[1] = implode(' ',array_reverse(explode(', ',$match[1])));
		}
		$entries[strtolower($match[1])] = addslashes(trim(strip_tags($match[1].' - '.$match[2])));
		if(count($entries) >= 72)
		{
			print "<h1>Script #".(++$scripts)."</h1>\n";
			print "<pre>".makescript($entries)."</pre>\n";
			$entries = array();
		}
	}
	else
	{
		$opts = explode('/',$match[1]);
		foreach($opts as $opt)
		{
			$opt = trim($opt);
			if(strpos($opt,', ') !== false)
			{
				$opt = implode(' ',array_reverse(explode(', ',$opt)));
			}
			$entries[strtolower($opt)] = addslashes(trim(strip_tags($opt.' - '.$match[2])));
			if(count($entries) >= 72)
			{
				print "<h1>Script #".(++$scripts)."</h1>\n";
				print "<pre>".makescript($entries)."</pre>\n";
				$entries = array();
			}
		}
	}
}
if(count($entries) > 0)
{
	print "<h1>Script #".(++$scripts)."</h1>\n";
	print "<pre>".makescript($entries)."</pre>\n";
}
?>
	</body>
</html>

How it works

When the script is run, it reads the Schome Park Dictionary from the live wiki page. It then uses a regular expression to parse this. Having done so, it generates as many scripts as needed, which use link messages to communicate. This saves on needing multiple objects, or any listeners except for a private one. It counts the number of responses, and stops waiting when all scripts have responded, responding with either success or failure.

I suppose it helps if you want to copy it straight from the wiki page if you use something like PHP (which I don't know how to write stuff in, but meh :p) rather than Python as it already has internet access from the thing running it.
I was also going to make it (and all my other scripts, including the chessboard and connect-4 ones) use link messages when I've finished converting the grammar analyser (only a single loop left to convert, pretty much :D), but I wasn't completely sure how to use them so a reference like your version is tres useful. --Decimus

Yes, I do know I should comment my code more.

And I also have the problem of not enough comments :p --Decimus
Comments are tedious and useless! But I'm apparently supposed to have them anyway. Having said that, the internal workings of LL's protocol, which are specified in a format devised by LL, would be almost nonsensical without the comments. :p - Katharine Berry 14:22, 18 July 2007 (BST)
Comments can be very useful. If you come back to a piece of code after six months or a year it can be really hard to know what is going on. What is often harder to recall is WHY you decided to code something a certain way. In other words comments that given an insight into decision-making can be especially useful. And of course if someone else is going to maintain your code commenting is a must. Of course if code is only ever going to be for you, it can stay as light as you like :-) --Dan 11:20, 24 July 2007 (BST)
I've had no issue with two year old code (okay, the code was horrible, but at least I know what it did!). And nobody else will ever need to maintain it. So I'm fine. :p
And how did you manage to type the month's name? Why not just use ~~~~ to sign? - Katharine Berry 11:24, 24 July 2007 (BST)
Dating the comments may be useful, but I don't see how :p
Anyway, one place I've found that comments are extremely useful is above lines like this:
struct.pack(">IBiIBiII", 26, user, t, 0, user, t, len(title), len(msg))
It's difficult enough to remember what the arguments after the first should be (the first argument to struct.pack() governs the types and number of the arguments after it) for long enough to write them down, never mind come back later :p —The preceding unsigned comment was added by Decimus Schomer (talkcontribs).