Skip to content

Latest commit

 

History

History
249 lines (200 loc) · 10.3 KB

README.md

File metadata and controls

249 lines (200 loc) · 10.3 KB

Squire

Hear ye hear ye! Thou art commandeth to partake in the most novel of programming parlances in this era: Squire. It doth contain a truly awful cohort of subliminal mechanisms for thy writing pleasure.

Squire's a medieval-themed programming language that's meant to parody most mainstream programming languages. While it does definitely contain its fair share of fun quirks, it's first and foremost meant to be a fully-fledged language, completely oblivious to its own ridiculousness.

Examples

Here's some examples of Squire. For more examples, you can see examples, and for a more complete overview of Squire itself, see overview

Basic Examples

Squire uses fun, old-timey words as alternatives to modern-day concepts:

i = 0; # semicolons are optional, but recommended
whilst i < 10 { # no parens needed
	if i % 2 { # squire uses truthy values
		proclaim("{i} is odd"); # string interpolation
	} alas {
		proclaim("{i} is even");
	}
	i += 1;
}

Journeys (ie functions)

Instead of functions, you go on journeys (and get rewarded for returning)

journey greet(what) {
	reward "Hello, {what}";
}

proclaim(greet("world"));

Because of poor design choices early on, you have to predeclare functions used later on as global variables

journey foo(x) {
	renowned bar; # `bar` is a global variable in this scope.
	reward "Hello, " + bar(x)
}

journey bar(x) {
	reward x + "!";
}

proclaim(foo("world")); # Hello, world!

Forms (ie classes)

The medieval people were really into Plato's forms, so I decided to name Squire's "Classes" after it. (And I majored in Ancient Philosophy):

form Person {
	matter name : Text; # type annotations are optional
	matter hungry;

	imitate(name) { # constructor
		soul.name = name; # `soul` is `this`/`self`.
		soul.hungry = nay;
	}

	change walk(miles) {
		if soul.hungry {
			proclaim("I'm too hungry to walk.");
		} alas {
			proclaim("I walked {miles} miles!");
			soul.hungry = yea;
		}
	}
}

me = Person("Sam");
me.walk(5); # I walked V miles! (more on `V` in the fun quirks section)
me.walk(5); # I'm too hungry to walk.

Fun quirks of Squire!

Here's a (non-exhaustive) list of Squire's quirks. Most of these things are actually encouraged (especially the Roman Numeral literals and Fraktur bare word literals) when possible, as they add to the charm of the language.

Roman Numeral Literals

Back in the medieval ages, people actually did all their mathematical computations with Roman numerals. As such, Squire supports (and encourages) using roman numeral literals:

proclaim("twenty four is: {IV * VI}") # => twenty four is: XXIV

In fact, all number to string conversions (regardless of whether they were written in Roman or Arabic numerals) will output Roman numerals. If you want Arabic numerals, you have to use the arabic function:

proclaim("twenty four is: {arabic(IV * VI)}") # => twenty four is: 24

Note: You can enable the SQ_NUMERAL_TO_ARABIC flag to switch the default string output to Arabic numerals. (Though this may be deprecated/removed.)

yea/nay/ni

Squire supports the basic concepts of true and false. However, since Mr. Boole wasn't born until 1815, the concepts of Boolean algebra did not exist. So instead we use the phrases yea and nay.

The word ni is used for null, and is a nod to the skit "Monte Python and the Holy Grail."

Fraktur Bare word literals

Squire supports bare words—that is, strings that don't require quotes around them. Now, normally this is a terrible idea, as there's no way to determine if a word is a variable or a string just by looking at it. Squire solves this problem by requiring all bare words to use Fraktur Unicode characters exclusively. Note that the bare words can also contain spaces in them, and all leading and trailing whitespace will be trimmed.

proclaim(ℌ𝔢𝔩𝔩𝔬 𝔴𝔬𝔯𝔩𝔡) # => Hello world

# You can also escape characters that'd normally end the fraktur literal:
proclaim(ℌ𝔢𝔩𝔩𝔬\, 𝔴𝔬𝔯𝔩𝔡\!) # => Hello, world!

The Fraktur characters will be converted to their ASCII equivalents within the parser, so the actual Fraktur characters are never encountered within Squire (unless you write them in quotes).

"Coding Case"-Insensitivity

Back in the medieval ages, spelling was not at all standardized—a lot of words simply would be spelled differently from region to region, and sometimes things got really bad. Squire embodies this by allowing you to have "coding case"-insensitive identifiers. That is, you can use both snake_case and camelCase and Squire will interpret them as the same name! In fact, you can also use kebab-case and space case too :-)

These are all equivalent:

foo_bar_baz
fooBarBaz
foo-bar-baz
foo bar baz
fooBar_baz
foo-bar baz

The only caveat to this is if the variable starts with a capital letter, this automatic replacement is not performed.

Indentation only with tabs

As monarchs sometimes decreed arbitrary edicts that were then strictly enforced, Squire also has an (somewhat) arbitrary edict: You may not use spaces when indenting your program. Attempting to use spaces for indentation will greet you with a lovely compile-time error of THOU SHALT NOT INDENT WITH SPACES!.

You don't have to use tabs for indentation—you can always use the tried-and-true tested mechanism of ; for spacing:

# Basic fizzbuzz, nothing fancy.

i = I
whilst i <= C {
;;;mod-three = i % III
;;;mod-five = i % V

;;;if mod-three && mod-five {
;;;;;;proclaim(i)
;;;} alas if mod-three {
;;;;;;proclaim(𝔉𝔦𝔷𝔷)
;;;} alas if mod-five {
;;;;;;proclaim(𝔅𝔲𝔷𝔷)
;;;} alas {
;;;;;;proclaim(𝔉𝔦𝔷𝔷𝔅𝔲𝔷𝔷)
;;;}

;;;i = i + I
}

Were-ifs

What programming language based on the medieval ages would be complete without some form of monsters? In Squire, instead of having werewolfs, we have were-ifs: On full moons (and only on full moons), if statements have a 1% chance of embracing their inner lycanthropy and executing their body when the condition is false. That is, this may be executed on a full moon:

if nay {
	proclaim("'Ello world!")
}

What more, this will only occur when the moon is 95% full or greater. This means that certain parts of the world may have were-ifs occurring where other parts won't. Happy bug hunting! (Note that you can disable this by passing -DSQ_NMOON_JOKE when compiling, but that's no fun.)

And yes, I did embed a moon phase calculator for a joke. No I do not feel ashamed.

whence

As you may have noticed, Squire does not actually have keywords for break or continue: This is because when I was originally throwing together the language, I wanted to have a one-pass compiler, which doesn't play nicely with break and continue. So I simply omitted them. The obvious problem is that you can't break out of nested loops easily.

Enter whence. A whence <label> statement is akin to the COMEFROM statement in other languages: Whenever <label> is encountered, control flow is actually diverted to the whence label. As such, you can write loops like such:

i = I;
whilst yea {
	if i > 100 {
	done:
		# normally you'd `break` here
	}
	i = i + I
}

whence done; # whenever `done:` is hit, we go here.

That leaves one problem: What happens when multiple whences all point to the same label:

foo:
proclaim("1");

whence foo;
proclaim("2");

whence foo;
proclaim("3");

Squire will actually execute all the whences by simply forking for every whence statement but the last one. So the output of the above program would be either 233 or 323, depending on how the OS scheduled the two processes.

Macros

Yes, we have macros. And yes, Squire is a runtime language. There's not a ton of them (currently), but more will eventually be implemented:

@transcribe "foo.sq" # copy-paste `foo.sq` in right here

@henceforth $bar = baz; # kinda like `#define bar baz` in C
@henceforth $quux($a, $b) = $a + $b; # kinda like `#define quux(a,b) a + b` in C
proclaim($quux(IV, VI)) # => X

@whereupon $bar # if the macro `$bar` is defined, do this
	proclaim(":-)"); # => :-)
@alas
	proclaim(":-(");
@nowhere;

Eventually, we'll add stuff like concatenating identifiers, iterations, etc. But that'll come later.

Actually useful things

TODO

Pattern Matching

TODO

path's body

TODO

Typing

TODO

FAQs

Why?

Why not?

No seriously, why?

Well, I originally was working on a language called Quest. I had just written a very simple programming language within Quest (the precursor to Knight), and I wanted to write something a bit more complex. I finished a rough sketch of syntax, but never ended up finishing it.

Fast forward a few years, when I wanted to write a programming language that's a bit more complex than Knight, but not as complex as Quest. The original Squire looked very much like Go or JavaScript, as it was meant to simply be another toy language. But then it hit me: I could make this a parody, and have it be medieval themed (like the naming scheme of my projects). And thus, Squire was born.

Why is <code snippet> so poorly written?

When I first was writing Squire, I never intended for it to become anything. In fact, I purposefully wrote it late at night when I was tired/watching TV, and never went back to fix anything. Only much later did I start taking the project a bit more seriously and began writing cleaner code. So the bad code you'll see is from those early stages (most notably the tokenizer, parser, and compiler).

Possible additions:

  • Static fields on forms
  • Inheritence for forms
  • macros
    • Declarative
    • Function
    • [~] conditional compilation (could be cleaned up)
    • foreach
    • evaluate
  • pattern matching
  • [~] varidict functions and keyword parameters
    • varidict is done, keyword is not
  • modules
  • dictionaries
  • interpolation
  • overload
  • pattern matching on functions