Intel sponsors gamedev.net search:   
The Bag of HoldingBy ApochPiQ      
Apoch's Avatar

Apoch
XP: 64,738
Inventory
Special Items: Shpongle | XBox Live
My brain is built of paths and slides and ladders and lasers and I have invited all of you to enter its pavilion. My brain, as you enter, will smell of tangerines and brand-new running shoes.

Monday, July 28, 2008
I decided to go ahead and release the current Fugue source code as it stands, for two reasons. First, the sooner I get feedback on the system, the better. Second, I'm looking at a couple of very busy upcoming months for work, so my available time for working on Epoch related stuff is going to shrink considerably.

Here's the download: Epoch Release 1 - Prototype.rar (47KB)

Please be as anal-retentive with this as possible. If I missed a comment on a code block, point it out. If I slipped up with some inconsistent formatting, get on my case (my personal formatting conventions are different from the ones I use at work and so I occasionally use the wrong one). Most importantly, if you notice any design or architecture issues, dumb C++ mistakes, or whatever, shoot me a note.

Two minor caveats:
  • There are some hardcoded paths in the source; change these to suit your local install setup. Just do a global search for "d:\\epoch" and you should find all of them.

  • The project is only in VS2005 format at the moment. It may not be portable to other compilers. Actually, getting portability is something I'm very much interested in, so if someone wouldn't mind pumping this through GCC on some flavour of Unix, I'd be much obliged.


I'm very interested in getting reactions to the language itself and starting to generate a bit of exposure and interest. So lay it on thick - this is your chance to help shape the language of the future!


(OK, so that was cheesy and melodramatic. Shoot me.)


[edit/afterthought] You may find it handy to have an example program to run through the VM. This is my current test code:

entrypoint : () -> ()
{
	string(inputstring, "")
	integer(inputnumber, 0)
	integer(randomnumber, 0)
	boolean(quit, false)


	assign(randomnumber, random(99))
	assign(randomnumber, add(randomnumber, 1))

	testfunction()

	debugwritestring("Guess my secret number... it's between 1 and 100.")

	do
	{
		assign(inputstring, debugreadstring())
		assign(inputnumber, lexicalcast(integer, inputstring))

		if(equal(inputnumber, 0))
		{
			assign(quit, true)
		}
		else
		{
			if(less(inputnumber, randomnumber))
			{
				debugwritestring("Too low! Try again.")
			}
			if(greater(inputnumber, randomnumber))
			{
				debugwritestring("Too high! Try again.")
			}
			if(equal(inputnumber, randomnumber))
			{
				debugwritestring("You're right! You win!")
				assign(quit, true)
			}
		}
	} while(notequal(quit, true))
}

testfunction : () -> ()
{
	debugwritestring("Test of external function calls")
}


Comments: 0 - Leave a Comment

Link



Saturday, July 26, 2008
True to form, I just can't resist the urge to fit in just a little bit more polishing and feature work on Epoch before the first release. Right now I'm putting together the syntax error handling.

As anyone with experience in language implementation knows, handling syntax errors intelligently requires a string of minor miracles. Usually, this phase leaves the programmer indebted to several evil entities, all of which want a piece of his soul. There may also be virgin sacrifices involved, depending on what level of user-friendliness the errors have.

In Epoch, there are currently two types of errors: syntactical errors, and identifier resolution errors. (Obviously this will increase to include type-system violations and other such goodies as features are added to the language.) Using boost::spirit's assertive parsers, capturing syntactical errors is a breeze; very precise information on the location and cause of the error can be delivered to the user.

Identifier resolution errors aren't so convenient. For example, consider the case where we call a non-existent function Foo. Because of the way the Epoch VM is designed, we don't ever actually look for the code of Foo until the call is executed at runtime. This laziness has a small cost: by the time we are executing the program, the original text source has been discarded, and all we have is the binary form of the code. This means we can't show the user where the error actually occurred.


Solving this is actually fairly simple. Parsing of an Epoch program will be divided into three phases:

  1. Syntactic De-Sugaring
    Epoch supports what I call dynamic sugar, where syntax elements can be added to the language from within an Epoch program itself. This allows for very powerful DSL creation capabilities, as well as handy shorthand for various operations. Think of it like operator overloading on crack.

    The first phase of Epoch parsing is to convert this sugary version of the program into the "pure" syntax, which is very simple and looks a bit like the bastard child of Lisp and C. This phase is implemented by an ad-hoc parser rather than boost::spirit in order to handle user-defined syntax properly. The output is a pure program with identical behaviour to the sugary input program.

  2. Syntax Checking and Lexical Scope Population
    The de-sugaring parser is "dumb" - it makes syntactic replacements without verifying their validity. The pure syntax is then verified by the second phase of parsing, the syntax checker. This phase is implemented in boost::spirit and serves two purposes.

    First and most obviously, it detects any syntax issues and reports them. Secondly, it creates a lexical scope for each code block as needed, and registers all defined variables and functions into their appropriate scopes. (Yes, Epoch supports nested functions.) When this phase ends, there are two possible outcomes. Either the program contains a syntax error and parsing fails, or the program is clean, and the VM now holds a complete list of every function and variable in the program itself.

  3. Conversion to binary operation form
    The final stage of parsing involves actually analyzing each statement and expression in the program, and constructing the binary equivalents in the VM itself. This phase is also implemented via boost::spirit, and uses a very similar grammar to the second phase, with different semantic actions. This phase can only fail in one way: if identifier resolution fails. Each function call and variable reference is checked to see if the appropriate variable/function actually exists, using the scope data constructed during phase 2.

    Once this final phase finishes, the VM holds a complete binary representation of the program, ready for execution and guaranteed to be free of errors.


Obviously as more features get implemented, the work of the phase 2 parser will become more and more sophisticated. Eventually, phase 2 will be far and away the most complex and time-consuming of the parse phases, because of things like closures and higher-order functions. Partial function application and other tricks will also have to be handled by the phase 2 parser.


I'm still working (slowly) on getting the code polished up and ready for release, but at the moment I'm sidetracked on this whole error-handling thing, because it's just too damn fun. Seeing the language come together is immensely rewarding.

Sadly, there's an upcoming deadline of rather high urgency at work, so I can't afford much time at all for Epoch tinkerings. Because of that time-sink, I expect to have the release ready in a few weeks at the latest. Don't miss it - Epoch is going places.

Comments: 0 - Leave a Comment

Link



Wednesday, July 23, 2008
It took a long evening's work, but I've finally gotten a parser functional and actually generating VM operations from source code. At the moment it's a little bit shaky because I'm not terribly familiar with boost::spirit yet, but at least it runs.

At this point I have the guessing-game program being loaded from disk and interpreted in the VM. The main challenge of parsing the "pure" Epoch syntax is now taken care of; with a few minor omissions, the language's built-in keywords are functional.

The next phase will be to add user-defined functions; currently the only function is the entrypoint which, as you can probably guess, defines the program entry point, similar to C/C++'s main.

I think at this point what I'll do is go ahead and clean up the code a bit and finish some documentation work, then package up a preview release. Cleaning things up is going to be a fairly large amount of work, since I've been doing everything pretty ad-hoc up until now. Once that's taken care of, I think I'll be ready to solicit some feedback on the overall design - particularly the parser, which is kind of the weak link of the moment.


Anyways, I'm rambling a bit... but this is my baby, and it's just so darn cool to see it coming together - especially this quickly. I seriously thought it would be a bigger mess than this, but so far it's been pretty smooth sailing.

Of course, I also haven't done anything nontrivial yet... the type system is going to be a minor nightmare, and getting user-defined types and such working will be one of the biggest hurdles in creating the language. That aside, there's also the all-important processing model work....


So... progress has been made, and the remaining amount of work is only murderously daunting rather than utterly impossible.

Whee!

Comments: 2 - Leave a Comment

Link



Monday, July 21, 2008
I have officially fallen in deep love with boost::spirit. In the space of a couple of hours, I threw together a working grammar for parsing the "pure" Epoch syntax example I posted earlier.

Currently it just parses the syntax, without doing any semantic analysis. I expect that actually getting the semantics shifted over into the VM will be the real hard part, especially since I have to come up with a good way to handle syntax errors.

The happy part of all this is that Epoch is now officially out of the realm of a VM-only hack; it's actually a viable language with a working syntax that is nearly ready for actual in-the-wild usage. I'm really looking forward to tackling the semantic analysis part of things and getting externally written code to run in the VM.


So what's next after the VM can run live code? I'm not entirely sure yet, but there's a few candidates:

  • Integration with C/C++ to allow calls into Win32 (with plans to build a marshalling layer for .Net interoperability later on)

  • More low-level services like file IO (so I can do the Sudoku program I originally planned on doing)

  • Starting to play with the type system a bit

  • Starting to work on the syntactic sugar preparser

  • Working on expanding the builtin type support for things beyond strings (arrays, hashmaps, etc.)


Whatever ends up getting picked, there's certainly plenty of work left to do before this thing is a truly viable language.


I have a feeling that I've finished the easy part, and it's all heavy lifting from here. But I guess we'll find out soon enough.

Comments: 0 - Leave a Comment

Link



Saturday, July 19, 2008
I decided to bite the bullet and go ahead and begin rudimentary syntax work. This is for a couple of reasons.

First, I'm running out of things to add to the language without getting into highly nontrivial areas like type theory, dynamic memory management, and so on. Second, it's such a pain to do anything - trivial or not - in the current setup. The VM code is designed to have a parser front-end plugged into it; and although it works fine to "write" Epoch programs within the VM itself, it's a righteous pain in the ass to do so.

Therefore, I'm digging out boost::spirit and trying to recollect all my old parser theory. The goal is to get a parser working that can correctly read a simple Epoch program and then feed it into the VM for execution.

I'm going to be doing this in two phases. First will be a very stripped-down core of the syntax, which uses function-call syntax for everything. The second phase will be introducing syntactic sugar that turns the language into something more closely resembling C.


Here's the guessing game, written in the first syntax mode I came up with:

entrypoint : () -> ()
{
	string(inputstring, "")
	int(inputnumber, 0)
	int(randomnumber, 0)
	boolean(quit, false)

	assign(randomnumber, random(99))
	assign(randomnumber, randomnumber + 1)

	debugwritestring("Guess my secret number... it's between 1 and 100.")

	do
	{
		assign(inputstring, debugreadstring())
		assign(inputnumber, lexicalcast(int, inputstring))

		if(equal(inputnumber, 0))
		{
			assign(quit, true)
		}
		else
		{
			if(less(inputnumber, randomnumber))
			{
				debugwritestring("Too low! Try again.")
			}
			if(greater(inputnumber, randomnumber))
			{
				debugwritestring("Too high! Try again.")
			}
			if(equal(inputnumber, randomnumber))
			{
				debugwritestring("You're right! You win!")
				assign(quit, true)
			}
		}
	}
	while(notequal(quit, true))
}



As you can see, it's drawing a fair bit of inspiration from C, but has lots of minor differences (like no semicolons). The uniform function-call syntax is designed for a simple, pragmatic purpose: it keeps the first-phase parser minimally simple, while mapping each operation's ID string directly to an Operation class in the VM.

One thing I'd like to point out is the lexicalcast function. Its first parameter is a type ID, not a variable ID; this is similar to template parameters in C++, but I'm taking them a step further - it is actually legal to pass a type to a function rather than simply a variable of that type. This allows for some powerful alternatives to function overloading, as well as opening up all kinds of metaprogramming options.

This is a foretaste of something I really want to get into the language eventually, which is the ability to pass functions, operators, even entire blocks of code around as objects. These "metaobjects" can be used for some truly awesome code generation that draws heavily from Lisp's macro concept.

Anyways, back to parsing.

What will happen in the second parser phase is pretty straightforward; there will actually be a preprocessor that converts sugar-laced syntax to this raw, "pure" syntax. My goal is to make the preprocessor open ended so that entirely new forms of syntax can be introduced. That means there's no requirement that one Epoch program use anything remotely like the syntax of another Epoch program. This in turn solves the high-priority goal of being easy to adapt to domain-specific uses.

So what does this sugarized syntax look like? Here's a rough version:
entrypoint : () -> ()
{
	string(inputstring, "")
	int(inputnumber, 0)
	int(randomnumber, 0)
	boolean(quit, false)

	randomnumber = random(99)
	randomnumber = randomnumber + 1

	debugwritestring("Guess my secret number... it's between 1 and 100.")

	do
	{
		inputstring = debugreadstring()
		inputnumber = lexicalcast(int, inputstring)

		if(inputnumber == 0)
			quit = true

		else if(inputnumber < randomnumber)
			debugwritestring("Too low! Try again.")
		else if(inputnumber > randomnumber)
			debugwritestring("Too high! Try again.")
		else if(inputnumber == randomnumber)
		{
			debugwritestring("You're right! You win!")
			quit = true
		}
	}
	while(!quit)
}


There are four principal additions made in this version of the syntax:
  1. Blocks of one line do not require braces

  2. Infix operators are permitted

  3. Special characters for operators/functions are permitted rather than only string IDs

  4. If/else blocks can be chained


So there you have it - the official first draft of Epoch syntax. (Actually, it's not really the first - I think I've even posted other drafts here in the past. But this one is the first that I'm actually going to bother trying to parse.)

Depending on how long it takes for me to get my brain wrapped around boost::spirit, I should probably have a rudimentary phase-one parser ready in a couple of weekends. At that point, it will be time to stitch up the package, do some documentation and cleanup work, and make the first official open-source release of the Epoch language.

My secret personal goal is to have something ready to demo for potentially interested parties by next year's GDC. That gives me several months to get the parsers finished and polish up the VM, plus implement some more interesting features.


Call me a total nerd, but I'm really excited about this.

Comments: 1 - Leave a Comment

Link



Friday, July 18, 2008
Well, it's official - Epoch has its first fully implemented and operational program!

It's just a boring number guessing game, of course, but it still counts - as it should after the amount of effort that's gone into the VM just to get this tiny bit of code running.

The source that created that game is a bit convoluted because of the VM's structure, but it should be easy enough to follow with some concentration:

std::wstring var_inputstring(L"inputstring");
std::wstring var_inputnumber(L"inputnumber");
std::wstring var_randomnumber(L"randomnumber");
std::wstring var_quitflag(L"quit");

std::wstring const_zero(L"zero");

std::wstring param_maximum(L"maximum");

std::wstring msg_guess(L"Guess my secret number... it's between 1 and 100.");
std::wstring msg_toolow(L"Too low! Try again.");
std::wstring msg_toohigh(L"Too high! Try again.");
std::wstring msg_youwin(L"You're right! You win!");

VM::IntegralRValue one(1);

VM::Scope scope;
scope.AddStringVariable(var_inputstring, EMPTYSTRING);
scope.AddIntegralVariable(var_inputnumber, 0);
scope.AddIntegralVariable(var_quitflag, 0);
scope.AddIntegralVariable(const_zero, 0);

VM::Scope parameters;
parameters.AddIntegralVariable(param_maximum, 99);
scope.AddIntegralVariable(var_randomnumber, VM::TypesManager::CastRValue<VM::IntegralRValue>(*Library::RandomIntegralValue().Invoke(parameters)).GetValue());
VM::Operations::AssignExpression(var_randomnumber, new VM::Operations::SumIntegralVariableConstant(var_randomnumber, 1)).Execute(scope);

VM::Operations::DebugWriteStaticString(msg_guess).Execute(scope);

VM::Block* loopbody = new VM::Block;
loopbody->AddOperation(new VM::Operations::AssignExpression(var_inputstring, new VM::Operations::DebugReadStaticString()));
loopbody->AddOperation(new VM::Operations::AssignExpression(var_inputnumber, new VM::LexicalCast::StringToIntegral(var_inputstring)));

VM::Operation* lessthancondition = new VM::Operations::IsLesser(var_inputnumber, var_randomnumber);
VM::Block* lessthanblock = new VM::Block;
lessthanblock->AddOperation(new VM::Operations::DebugWriteStaticString(msg_toolow));
loopbody->AddOperation(new VM::Operations::If(lessthancondition, lessthanblock, NULL));

VM::Operation* greaterthancondition = new VM::Operations::IsGreater(var_inputnumber, var_randomnumber);
VM::Block* greaterthanblock = new VM::Block;
greaterthanblock->AddOperation(new VM::Operations::DebugWriteStaticString(msg_toohigh));
loopbody->AddOperation(new VM::Operations::If(greaterthancondition, greaterthanblock, NULL));

VM::Operation* wincondition = new VM::Operations::IsEqual(var_inputnumber, var_randomnumber);
VM::Block* winblock = new VM::Block;
winblock->AddOperation(new VM::Operations::DebugWriteStaticString(msg_youwin));
winblock->AddOperation(new VM::Operations::AssignValue(var_quitflag, one));
loopbody->AddOperation(new VM::Operations::If(wincondition, winblock, NULL));

VM::Operation* quitcondition = new VM::Operations::IsEqual(var_inputnumber, const_zero);
VM::Block* quitblock = new VM::Block;
quitblock->AddOperation(new VM::Operations::AssignValue(var_quitflag, one));
loopbody->AddOperation(new VM::Operations::If(quitcondition, quitblock, NULL));

VM::Operation* loopcondition = new VM::Operations::IsEqual(var_quitflag, const_zero);
VM::Operation* loop = new VM::Operations::DoWhileLoop(loopcondition, loopbody);

loop->Execute(scope);



So thus far the objectives for Epoch are holding steady:
  • Create a solid VM for experimentation and prototyping

  • Develop applications from day one that use the language directly

  • Make games!


Here it is in all its console-tastic glory:

Epoch guessing game

Not much to look at, to be sure, but still a proud moment for me. I might actually get this crazy project off the ground after all

Comments: 2 - Leave a Comment

Link



Thursday, July 17, 2008
I've finally gotten back around to working on the Epoch virtual machine again. In addition to a handful of arithmetic operators, I've added some key elements:

  • Code block support (the value of a block is the value of the last executed statement)

  • Function support (code block + parameter list)

  • Library function framework

  • Random number generator library function


This means that with a little more work on the UI side, I can hack up a quick guess-the-number game, which will be the first official program written entirely in Epoch.


It's still pretty weird to work with since all the language primitives are being manipulated in C++ rather than in an actual separate syntax. This gives us wonderfully verbose code like this:

bool LibraryCallTest::Test()
{
	VM::Scope parameters;
	parameters.AddIntegralVariable(L"maximum", 1000);

	Library::RandomIntegralValue func;
	VM::RValuePtr result = func.Invoke(parameters);

	UNITTEST_ASSUMPTION(result->GetType() == VM::EpochVariableType_Integer);

	int resultvalue = VM::TypesManager::CastRValue<VM::IntegralRValue>(*result).GetValue();
	UNITTEST_ASSUMPTION(resultvalue >= 0 && resultvalue < 1000);

	std::wostringstream str;
	str << L"Random number generated: " << resultvalue;

	VM::Scope scope;
	VM::Operations::DebugWriteStaticString(str.str()).Execute(scope);

	return true;
}



I still haven't decided when I'm going to actually get around to writing a syntax front-end for the thing. My hesitation stems from two points: general laziness (writing a parser is dreadful work), and concern that I'll lock in a syntax too quickly if I do it before the bulk of the language is implemented.

I really don't relish the idea of building much more complication into the language using this clunky approach, but at the same time, I really need a solid rapid-prototyping toolchain before I can really play with the syntax. Since syntax will be one of the most difficult parts of the language to refine, I want to make sure I get it right as quickly as possible.


Anyways... that's about all the babbling on that subject that I've got. Stay tuned - as soon as the VM is capable of running the guessing-game, I'll be releasing the very first public preview of the source for everyone to experiment with.

Comments: 3 - Leave a Comment

Link



Monday, July 14, 2008
So for the past several weeks I've had this glorious, functional MS-DOS 6.22 machine running happily under my coffee table. It's provided several hours of blissful retro gaming awesomeness.

But, as with any good story, there's a dark side.

I have no mouse.


This rules out a lot of the games that I wanted to play on the machine in the first place - Monkey Island, Return of the Phantom, SimCity 2000, Warcraft.... I've been bound to the torment of a keyboard-only existence, and it has fractured my very soul.

Imagine my glee, then, when today's mail arrived - and, with it, came a nice, shiny new serial port mouse - one I could use with my ancient machine.

I plugged the sucker in and fired up the drivers, only to be confronted with a mysterious error: COM port cannot found. Huh.

After some fiddling in the BIOS, I managed to get COM1 and COM2 sort of working... except the mouse drivers (all three that I tried) still refused to acknowledge that there was, in fact, a mouse plugged in.

Thus it came to be that my only course of action was to drop some money, and acquire a PCI board with some COM ports on it, just to make sure.


As it turns out, $50 later, the PCI board also doesn't improve the situation.


I did notice something intriguing while opening up the machine, however - there, nestled right up under the AT keyboard jack, was a PS2 mouse jack. I hadn't seen or been able to make use of that jack previously, for one simple reason: the case doesn't have a hole there.

Without a hole, it's kind of impossible to plug anything into the PS2 mouse jack. Impossible, unless, of course, the case was dispensed with and the motherboard laid bare on the sofa for examination.

After some minor surgery of the screw-removal type (and a few cable rearrangements) I got the port somewhat accessible, and popped in my sole functional PS2 mouse.


Instant success.


So now I have not only wasted time but also money on trying to get a serial mouse to work, when all this time I could have been using the PS2 port... if only the case had a hole.


That means that now it is time to make a hole, because damnit, I wanna play my games.

I am now brandishing a pair of tin snips threateningly at the case, and should hopefully be filling my new hole in short order.


Get your mind out of the gutter.

Comments: 8 - Leave a Comment

Link



Wednesday, July 9, 2008
Alright, ladies and germs, go get your controversy hats and make sure they're snugly mounted on your head - I'm going to drop a bombshell here.


Documentation in General
Everyone should hopefully already agree that documenting one's code is a good practice. Interfaces for libraries or self-contained modules should come with complete descriptions of how to use the interface itself, potential pitfalls, and so on. Internally, code should be sprinkled with comments that describe why certain algorithmic choices were made, how various design decisions were reached, and so on.

Now, documentation styles are as varied as programmers themselves - so I'm not going to get into any specific details of how to document code. That's a largely personal decision, and in my opinion it's something that one can easily decide will a little bit of careful thought and some common sense.

So instead, I'll be approaching a topic that is much more rarely discussed - when to do documentation.


The Prevalent Opinion
The conventional wisdom - and my own view for a long time - is that the best time to do documentation is right after you write the code. Everything is still fresh in your mind, the details of each decision are still clear, and generally things are still "warm." This seems like an ideal time to document code.

Sometimes, we might even want to pre-document code - that is, write documentation before any code is in place. This is typically a good idea when defining an interface for a library or some similarly contained module, where it is important that certain interface constraints and behaviours be met.

Pre-documentation is also a convenient way to explore new algorithms. First, write up a list of basic steps that the algorithm needs to perform, then slowly replace those comments with lines of code that accomplish what the comments indicated. Eventually, you finish all the replacements, and voila - working algorithm implementation (hopefully, anyways).

This is all well and good, but I think there's a better option.


The Bombshell
Here's my suggestion, stated simply: document code a long time after you write it. Put it off as long as practical within the constraints of your project.

Now why the hell would I say something so clearly insane? Procrastination is death, surely; with all that time between code and documentation, isn't it inevitable that something will fall through the cracks or just plain be remembered incorrectly?


My answer to this is it all depends on how careful you are during documentation. I suggest two passes: first, document the code the way you think you remember it works - or, even better, how it is supposed to work. Then, perform a code review.

During the code review, carefully analyze the code and make sure it lines up with the documentation. Remember, your memory is probably fuzzy here, so you'll have to think critically about each and every line of code to make sure that it (A) is correct and (B) matches the documentation.

I'm aware that this is going to be a weird concept for most people, because it runs so counter to the general expectations of how the mind works when coding and documenting. So let's go over the specific benefits of this method:

  • You can get code done without worrying about documenting it until after the fact. This can come in handy when working on a tight deadline.

  • This method works well with agile methodologies or general sprint-oriented development practices; do one sprint to write the code, then do a shorter sprint to document the oldest code which currently needs documentation.

  • By allowing yourself to forget how the code works, you force yourself to be analytical and review the code as if you'd never seen it before. This is a tremendous benefit - the single major problem with reviewing code is that it is easy to ignore or skip things because you're too close to the system to see the flaws. Stepping back for a significant time alleviates this danger.

  • If you have the staff, this is a great way to get everyone to understand critical central systems. For instance, if Joe writes a library that Bob and Frank will be using, Joe first writes the documentation, then hands off the completed package to Bob and Frank, who then perform the code review process. Now Bob and Frank are fully educated on how the module should work, and Joe gets the bonus of having a very carefully peer-reviewed chunk of code.



So there's my bit of programming heresy for the week. Now you can properly enjoy your procrastination, secure in the knowledge that it's actually making your work better!

Comments: 3 - Leave a Comment

Link


All times are ET (US)

In locus hic, omnes res dementes sunt.
 
S
M
T
W
T
F
S
1
2
3
4
5
6
7
8
10
11
12
13
15
16
20
22
24
25
27
29
30

OPTIONS
Track this Journal

 RSS 

ARCHIVES
July, 2009
June, 2009
May, 2009
April, 2009
March, 2009
February, 2009
January, 2009
October, 2008
September, 2008
August, 2008
July, 2008
June, 2008
May, 2008
April, 2008
March, 2008
February, 2008
January, 2008
December, 2007
November, 2007
October, 2007
September, 2007
August, 2007
July, 2007
June, 2007
May, 2007
April, 2007
March, 2007
February, 2007
January, 2007
December, 2006
November, 2006
October, 2006
September, 2006
August, 2006
July, 2006
June, 2006
May, 2006
April, 2006
March, 2006
February, 2006
January, 2006
December, 2005
November, 2005
October, 2005