Squire Documentation
Basic Syntax
To print something to the console, use proclaim()
:
proclaim("Hello World!")
Numerals use the Roman way of counting: I, II, III, IV, etc.
To convert a Roman numeral to a more modern way of counting (often referred to as Arabic Numerals), use the arabic()
function:
arabic(I)
Conversely, one can convert a "modern" numeral to a Roman numeral by simply using the roman()
function:
roman(1)
Minus numbers are also supported, represented as follows: -II (-2).
In Squire, the term "Boolean" does not exist. Instead, "veracity" is part of the language.
"true" is "yea" and "false" is "nay".
Interpolation is done using curly brackets (only within strings) as follows:
proclaim("yea == nay? {yea == nay}")
There are two sets of comment types: single-line and multi-line
Multi-line commenting is done using C-style comments as follows:
/* This is the start of the comment
This is the end of the comment */
Single-line commenting has two ways of being implemented:
# This is a single-line comment
N.B. But so is this!
Null is represented with "ni", as homage to the Knights Who Say "Ni".
Squire is dynamically-typed, meaning that no type has to specified when variables are created.
This means that a variable could be initiated as a string then transformed into an integer like so:
variable = "string"
variable = I
Like JavaScript, semicolons can be used to end lines, but is not required.
"\t", "\\", "\'", and "\"" are supported in regards to strings.
@__END__
can be used to halt parsing of a file from that line forwards, and variables can be whatever you want:
foo_bar = I
proclaim(arabic(foo_bar + fooBar + foo bar)) # Prints 3
To differentiate between strings and variables within certain elements of Squire, Fraktur Unicode is used, which looks like this:
𝔰𝔮𝔲𝔦𝔯𝔢 𝔦𝔰 𝔠𝔬𝔬𝔩
The Squire compiler provides a shell script to convert from normal text to Fraktur Unicode.
Books & Codices
Books, Squire's version of arrays, use the familiar bracket syntax as follows:
languages = [𝔖𝔮𝔲𝔦𝔯𝔢, ℜ𝔲𝔰𝔱, ℭ]
Please note that Squire is an I (or 1) indexed language, unlike most languages. Fraktur Unicode is also used to represent strings in books, as discussed in the previous section.
To get the length of a book, the function .length
is used like so:
proclaim(languages.length) # Prints III
Naturally, fetching of an element also uses brackets like so:
C = languages[III] # The variable C now equals "C"
It is also possible to use negative numbers to find the last element like so:
C = languages[-I] # C still equals "C"
C = languages[-II] # C now equals "Rust"
If an element is inserted into an index above the next sequential index, then the empty fields are filled with "ni":
languages[V] = 𝔎𝔫𝔦𝔤𝔥𝔱
proclaim(languages) # Index IV is nil
insert()
and delete()
can be used to add and delete indexes from books as follows:
insert(languages, I, "Quest") # The first index of the "languages" book now equals "Quest"
delete(languages, II) # "Rust" is now no longer in our book
Codices are Squire's dictionaries, maps, etc.
They store two values in one index, whatever you want to call them. They use curly bracket syntax:
numbers = {𝔬𝔫𝔢: I, two: II, 𝔱𝔥𝔯𝔢𝔢: III}
Values can be updated like so:
numbers[𝔱𝔥𝔯𝔢𝔢] = IV
Much like books, the length of a codice can be retrieved by using the .length
function.
delete()
can also be used with codices.
Control Flow
Squire has basic if statements, however "else" is switched out for "alas":
fav_colour = "black"
if fav_colour == "black" {
proclaim("Me too :)")
} alas {
proclaim("Why do you like {fav_colour}?'")
}
During full moons, if statements have a 1% chance to only execute their body if the condition passed is false.
This can be opted out of during compilation using the -DSQ_NMOON_JOKE
flag.
Switch statements exist too, represented with fork
s.
The syntax is as follows:
fork fav_colour {
path "black":
proclaim("Me too :D")
alas:
proclaim("Why do you like {fav_colour}?")
}
alas
is used as the default case. It is optional.
path
s can be expressions to shorten if statements like so:
fork yea {
path fav_colour == "black":
proclaim("The colour of darkness...nice")
}
Squire does not have for loops, instead the have while (whilst) loops:
i = I
whilst i != V {
i = i + I
}
Please note that Squire is still in development. Though for loops do not yet exist, that does not mean that they will never be added.
Thankfully, Squire supports basic error handling in it's current state, represented by attempt
and catapult
:
attempt {
if i > V {
catapult("i is above 5")
}
} alas err {
proclaim(err)
}
Squire also supports something called whence
commonly referred to as COMEFROM
in other languages.
whence
can be used to escape from nested loops like so:
i = I
whilst i < V {
j = I
whilst j < V {
if j == IV {
out:
}
}
}
whence out # If j equals IV, then the program will be moved to this line
Journeys
In place of functions, Squire has journey
s:
journey hello(name) {
proclaim("{name} says: \"Hello World!\"")
}
It is also possible to return values from functions using reward
:
journey goodbye() {
reward "Goodbye World!"
}
proclaim(goodbye()) # Goodbye World!
Implicit returns:
journey implicit() {
II + I
}
proclaim(implicit()) # III
Squire also supports lambdas since it is a high level language:
numbers = [I, II, III]
journey func(book, lambda) {
i = I
whilst i <= book.length {
book[i] = lambda(book[i])
i = i + I
}
reward book
}
output = func(numbers, journey(x) { x + I }) # [II, III, IV]
All variables are locally scoped to the journey they're in.
However, if a global variable exists with the same name as that local variable then that global variable will be used.
Locality for variables can be forced using the nigh
keyword. Likewise, globality can be forced using renowned
:
renowned variable = I
journey function() {
nigh variable = II
reward variable + I
}
proclaim("global variable == {variable + I}") # Prints II
proclaim(local variable (going by the same name) == {function()}) # Prints III
All journey
s are defined as global, however if you want to predefine a function you must mark it as renowned
as follows:
renowned predefined
proclaim(predefine()) # Prints "This function was predefined!"
journey predefined() {
reward "This function was predefined!"
}
Forms & Imitations
Classes in Squire are reffered to as form
s and constructors for those classes are called imitation
s:
form Person {
matter name, hungry # Form-specific variables to define what fields imitations can have
# "change" is the keyword used to define a class method/function
change set_name(name) {
this.name = name # soul is Squire's version of 'this', used to reference the current object
}
imitate(name) { # Constructor
set_name(name)
}
}
sam = Person(𝔖𝔞𝔪)
NOTE: Due to my lack of understanding of the concept essence
in Squire, essence
and recall
s are not covered on this site.
However, I recommend that you read the official documentation page for this section to hopefully get a grasp of the concepts.
It would be greatly appreciated if you would add essence
s and recall
s to the documentation if you understand the two concepts.
Macros
All macro invocations start with @
and end with a semicolon.
@transcribe;
is used to import other files into the current one like so:
@transcribe test.sq;
@henceforth;
is used for text replacement at compile-time (similar to #define
in C).
To escape semicolons in @henceforth
you can use the << ... >>
construct which interprets its contents literally:
@henceforth $three =
<<
proclaim(I);
proclaim(II);
proclaim(III);
>>;
$three # I, II, III
Any variables associated with macros must be prefixed with $
.
@henceforth $increment($number) = proclaim($number + I);
$increment(I) # II
There is a special macro variable worth mentioning called $__COUNTER__
which increments every time it is used.
proclaim("$__COUNTER__ = {$__COUNTER__}"); # $__COUNTER__ = N
proclaim("$__COUNTER__ = {$__COUNTER__}"); # $__COUNTER__ = I
proclaim("$__COUNTER__ = {$__COUNTER__}"); # $__COUNTER__ = II
<< ... >>
can also be used to escape ','s:
@henceforth $execute($function, $argument) =
proclaim($journey($arguments));
journey add(x, y) { reward x + y }
$execute(add, << I, II >>) # III
@nevermore
simply undeclares a variable defined by @henceforth
:
@henceforth $lol = I;
@nevermore $lol;
proclaim($lol) # Program will spit out an error - $lol is undefined
Conditional compilation! It exists in Squire!
It's nothing amazing though, just checking if a variable exists:
@henceforth $foo = I;
@whereupon $foo
proclaim("Foo exists!")
@alas
proclaim("Foo doesn't exist!")
@nowhere;
# Outputs "Foo exists!"
Miscellaneous
This section is simply a list of built in functions found in builtins.sq that haven't already been covered.
tally()
- Convert a string to a numeral e.g.,tally("III") # III
string()
- Convert a value to a string e.g.,string(II) # "II"
boolean()
- Convert a string to a boolean e.g.,boolean("yea") # yea
substr()
- Parse a string e.g.,substr("Hello", I, IV) # "Hell"
dismount()
- Exit the program from anywhere in the code e.g.,dismount(0)
One function, genus()
is not covered here as I cannot find anywhere to explain it's function to me.
If you understand what it does please help out on the repository.
hex()
- Execute command line functions e.g.,hex("echo Hello") # Hello
inquire()
- Take input from the user via the command line e.g.,input = inquire()
gamble()
- Generate a pseudo-random number e.g.,random_num = gamble()