Have a target for your programming language
When learning a programming language, it’s a great idea to use that language for at least one "real" project. How real is up to you, but the important thing is to get past merely thinking you understand the concepts and enter the realm of proving it to yourself by using them.
The same holds true for making a programming language.
I’ve made two toy languages that had real specific end goal programs:
The end goal program for Meow5 is in it’s name: the ability to compile a program that could print out "Meow" five times. Silly, I know, but the whole project was silly.
Snobol4th was a nested target, a toy Forth interpreter to test my knowledge of Snobol4 and a "99 Bottles of Beer" lyric generator program to test the Forth interpreter.
One of my favorite things about these specific target program goals is that you know when you’ve completed the task. I can think of few classes of application that invite more yak shaving than programming languages. A target program keeps it on track.
With Forth, in particular, I spent quite a bit of time reading articles and web posts about "minimal" implementations. Specifically, which set of "words" (think functions) would allow a developer to write reasonable (readable) programs, but still be as small as possible.
Of course, if you’ve ever put even the smallest bit of thought into that problem before, you know that it’s a massive and fascinating topic of its own. I love Guy Steele’s "Growing a Language" talk about the merits of "big" and "small" languages:
If I hadn’t picked the 99 Bottles program, I might still be obsessively trying to come up with the perfect set of words!
With the 99 Bottles program, I was able to reverse the task by starting the words used in that program and then work backwards recursively to determine which "dependency" words would be needed to implement those words. I literally did that by hand for each word using NASMJF as my reference implementation.
A well-chosen target program will also prove that your language can, if nothing else, execute at least one specific bit of functionality. You can’t possibly think of every edge case anyway, so having something fun or useful is a great motivator to keep going when the design challenges get tough.
Also, stepping stones
In both of the above examples, I had plenty of little stepping stones along the way to executing the final target program.
Meow5 executed everything in test.sh before I could run five.meow and then five-loop.meow.
In the case of Snobol4th, I had test.forth before the goal of 99.forth.
These were little snippets of functionality that needed to work (and stay working) before the final program became approachable.
For Forth, specifically, see my page Implementing a Forth
See also: programming and forth