Jump to content

Sane MDScript and a basic quiz example


I am Bored

Recommended Posts

First off, I do intend to clean up this post a bit later and make the tool a bit more usable, but that will have to wait until I'm not asleep.

 

First up, the sane code. Looks like pretty normal PHP to me.



<?
(locations 1_-1x3_1)

// This is the key we give the user.
$MDS_Key = "iab-quiz-key";
// This is the key used for the storage.
$Storage_Key = "iabTestKey";
// All answers should be stored lowercase.
$Questions = array(
	1 => array(
		"What is this?",
		"clicky"
	),
	2 => array(
		"Who made this?",
		"orvid"
	)
);

// List of users who have completed the quiz.
$usersFinished = mds_storage($Storage_Key, "toau", array());
if (uv("name") == "Orvid")
{
	if (count($usersFinished) > 0)
	{
		echo "<p>The following users have completed the challenge:<br/>";
		for ($i = 0; $i < count($usersFinished); $i++)
		{
			echo $usersFinished[$i] . "<br/>";
		}
		echo "</p><br/><br/>";
	}
	
	// Uncomment the next line to reset the storage to remove all users.
	//$usersFinished = array();
}

if (mds_has_rpcq_keys($MDS_Key))
{
	// Uncomment the ne_xt line to reset your key sta_te for testing.
	//mds_take_rpcq_keys($MDS_Key);
	echo "You have already completed the quiz!";
}
else
{
	echo "<form method='post' action=''>";
	$currentQuestion = 0;
	if (isset($input['page']) AND $input['page'] == "front")
	{
		$currentQuestion = 1;
	}
	else if (isset($input['q']))
	{
		$currentQuestion = $input['q'];
		
		if (!isset($Questions[$currentQuestion]))
			$currentQuestion = 0;
	}
	
	if (isset($input['answer']))
	{
		if ($Questions[$currentQuestion][1] == strtolower($input['answer']))
		{
			// Check if this was the last question
			if (!isset($Questions[$currentQuestion + 1]))
			{
				// This was the last question, so add the users' name to our
				// list, and give them a key to prevent them from re-taking
				// the quiz.
				echo "<p>Congratulations! You've completed the quiz!</p>";
				$currentQuestion = -1;
				
				$usersFinished[] = uv("name");
				mds_give_rpcq_keys($MDS_Key);
			}
			else
			{
				// Not the last question, continue.
				echo "<p>You answered correctly!</p><br/><br/>";
				$currentQuestion++;
			}
		}
		else
		{
			echo "<p>Incorrect! Try again!</p><br/><br/>";
		}
	}
	
	if ($currentQuestion > 0 && isset($Questions[$currentQuestion]))
	{
		echo $Questions[$currentQuestion][0];
		?>
		<br />
		<input type='text' id='answer' name='answer' value='' /><br />
		<input type='hidden' name='q' value='<?=$currentQuestion?>' />
		<input name='button' type='submit' value='Submit Answer' />
		<script type="application/javascript">
			$('#answer').focus();
		</script>
		<?
	}
	else if ($currentQuestion != -1)
	{
		?>
		Welcome!<br/>
		<input type="hidden" name="page" value="front" />
		<input name="button" type="submit" value="Continue" />
		<?
	}
	echo "</form>";
}

?>

Now, the code to transform this into a form that MDScript will accept. This code could be trivially accomplished in most other languages with a regex engine, but I chose to write it in D. It is currently hard coded to a specific file, and is only really intended as a reference for those looking to write their own form of the tool. 



module main;

import std.algorithm : any, filter, map;
import std.array : array, join, split;
import std.conv : to;
import std.datetime : nsecs, StopWatch;
import std.file : readText, write;
import std.regex : ctRegex, regex, replaceAll;
import std.stdio : writeln, writefln;
import std.string : strip, toLower;


enum InputFile = `F:\MD\ClickableQuiz.omds`;

void main(string[] args)
{
	processFile(InputFile);
}

template cRegex(string pattern, string flags)
{
	debug
		enum cRegex = regex(pattern, flags);
	else
		alias cRegex = ctRegex!(pattern, flags);
}

__gshared contentEqualRegex = cRegex!(`\?>([^]*?)<\?=([^]*?)(?=\?>)`, "g");
__gshared contentRegex = cRegex!(`\?>([^]*?)<\?(?!=)`, "g");
__gshared storageRegex = cRegex!(`\$([a-zA-Z0-9_]+)\s*=\s*mds_storage\(([^]*?),\s*("[at]o[at]u")\s*,([^]*?)\)\s*;`, "g");
__gshared variableRegex = cRegex!(`\$([a-zA-Z0-9_]+)`, "g");
__gshared stringRegex = cRegex!(`"(?:\"|.)*?"`, "g");
__gshared commentRegex = cRegex!(`//.*?$`, "gm");
__gshared documentStartRegex = cRegex!(`^\s*<\?`, "g");
__gshared documentEndRegex = cRegex!(`\?>\s*$`, "g");

string generateRegexTree(string[] str)
{
	static class Node
	{
		char leaf;
		Node[] children;
		
		this() { this.leaf = '\0'; }
		
		this(char c)
		{
			this.leaf = c;
		}
		
		static Node buildNode(string child, Node parent)
		{
			if (child == "")
			{
				auto n = new Node('\0');
				parent.children ~= n;
				return n;
			}
			foreach (c; parent.children)
			{
				if (c.leaf == child[0])
					return buildNode(child[1..$], c);
			}
			auto n = new Node(child[0]);
			parent.children ~= n;
			return buildNode(child[1..$], n);
		}
		
		string build()
		{
			string ret = "";
			if (leaf != '\0')
				ret ~= leaf;
			bool hasEndNode = children.any!(c => c.leaf == '\0');
			if (children.length == 1 && hasEndNode)
				hasEndNode = false;
			bool addGrouping = children.length > 1 || hasEndNode;
			
			if (addGrouping)
				ret ~= "(?:";
			ret ~= children.filter!(c => c.leaf != '\0').map!(c => c.build()).join('|');
			if (addGrouping)
				ret ~= ')';
			if (hasEndNode)
				ret ~= "?";
			
			return ret;
		}
	}
	
	Node root = new Node();
	foreach (c; str)
		root.buildNode(c, root);
	return root.build();
}

enum knownReservedWords = import("KnownReservedWords.txt").split('\n').map!(i => i.strip()).array;
__gshared knownReservedWordsRegex = cRegex!(generateRegexTree(knownReservedWords), "gi");

void processFile(string inputFile)
{
	auto sw = StopWatch();
	auto txt = readText(inputFile);

	sw.start();

	// First, extract the content sections.
	string[] contentSections;
	txt = txt.replaceAll!((match) {
		contentSections ~= match[1];
		auto tmp = `echo @content[` ~ (contentSections.length - 1).to!string() ~ "];\r\n";
		return tmp ~ `echo "" . (` ~ match[2] ~ ");\r\n";
	})(contentEqualRegex);

	txt = txt.replaceAll!((match) {
		contentSections ~= match[1];
		return `echo @content[` ~ (contentSections.length - 1).to!string() ~ "];\r\n";
	})(contentRegex);

	// And remove the opening and closing tags of the document (if present).
	txt = txt.replaceAll(documentStartRegex, "");
	txt = txt.replaceAll(documentEndRegex, "");

	// Next, remap the variables, with special handling for storage.
	string[string] variableMap = [
		"input": "@input",
	];

	txt = txt.replaceAll!((match) {
		if (match[1] in variableMap)
			throw new Exception("Already mapped variable '" ~ match[1] ~ "'!");
		auto vIdx = `@va[` ~ variableMap.length.to!string() ~ `]`;
		auto tmp = vIdx ~ ` = mds_storage(` ~ match[2] ~ `, ` ~ match[3] ~ ");\r\n";
		tmp ~= `if (!isset(@storage[` ~ vIdx ~ "]))\r\n";
		tmp ~= "\t@storage[" ~ vIdx ~ `] = ` ~ match[4] ~ ";\r\n";
		variableMap[match[1]] = `@storage[` ~ vIdx ~ `]`;
		return tmp;
	})(storageRegex);

	txt = txt.replaceAll!((match) {
		if (match[1] in variableMap)
				return variableMap[match[1]];
		return (variableMap[match[1]] = `@va[` ~ variableMap.length.to!string() ~ `]`);
	})(variableRegex);


	// Now to deal with reserved words.
	txt = txt.replaceAll!((match) {
		return match[0].replaceAll!((match) {
			auto half = match[0].length / 2;
			return match[0][0..half] ~ `" . "` ~ match[0][half..$];
		})(knownReservedWordsRegex);
	})(stringRegex);

	txt = txt.replaceAll!((match) {
		return match[0].replaceAll!((match) {
			auto half = match[0].length / 2;
			return match[0][0..half] ~ `_` ~ match[0][half..$];
		})(knownReservedWordsRegex);
	})(commentRegex);

	sw.stop();
	writefln("Took %s ms to format '%s'", cast(real)sw.peek().nsecs / 1_000_000f, inputFile);

	write(inputFile ~ ".html", contentSections.join("<!-- content separator -->"));
	write(inputFile ~ ".php", txt);
}

And, lastly, the content of KnownReservedWords.txt:



call
error
ext
fprintf
inf
ini
is_a
mod
move
open
parse
read
split
sprintf
stat
t_as
t_sr
try
use
val

Have fun, and may all your vars be @va.

Edited by I am Bored
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Forum Statistics

    17.5k
    Total Topics
    182.5k
    Total Posts
  • Recently Browsing

    • No registered users viewing this page.
  • Upcoming Events

    No upcoming events found
  • Recent Event Reviews

×
×
  • Create New...