Jump to content

Recommended Posts

Posted (edited)

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

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.1k
    Total Posts
  • Recently Browsing

    • No registered users viewing this page.
  • Upcoming Events

    No upcoming events found
  • Recent Event Reviews

×
×
  • Create New...