It's like Porth, which is like Forth but written in Python, but written in C++. But I don't actually know for sure since I never programmed in Porth or Forth, I only heard that they are some sort of stack-based programming language. Sparrow is also stack-based programming language. We going full circle at this point!
This was literlly fueled by a high I was chasing at that momemt. I wanted to be better at Programming specifically low level programming.
Sparrow is:
- A stack based programming language I do recommend you to read the link above.
- Turing complete(see rule 110 example)
- Compileable to a dynamic executable linked with the C RunTime.
- Totally awesome and annoying language.
File Suffix: .spar Assembler: Nasm (In Repo) Linker: GoLink (In Repo)
Sparrow uses the system function within C++
to run external commands on your computer. These commands can be affected by user input, so running sparrow
has the potential to run any command on your system if you tell it to, including malicious ones. Be sure to
check and double check any commands you see that use the -a
or -l
compiler options, as these tell Sparrow
to run a different command than the default. Every command run by Sparrow is echoed to the standard out
with a '[CMD]' prefix. Be careful when running commands that you don't want to run, as they can be used to
run malicious code.
Sparrow, like Porth (like Forth), is a stack based language. We going on full circle boys!
This means that in order to do any operation, a value must be pushed onto the stack.
Think of this as a literal stack of objects.
Likely, the operation will take it's arguments from the top of the stack, or pop
from the stack, and sometimes it will return a value or two back onto it, which is called pushing
on to the stack.
This is where we go get complicated!
Take a look at this example in Sparrow:
34 35 + #
It is a basic Sparrow program that will add two numbers together, and then display the sum to the console.
Expected output for the above program when run:
69
Let's break down how it works piece-by-piece:
Step | Code | Description |
---|---|---|
1 | 34 |
A value is pushed on to the stack, meaning stacked on top. |
2 | 35 |
Another value is pushed on to the stack. |
3 | + |
The + symbol will pop the two most-recent values off the stack, add them, then push the sum on to the stack. |
4 | # |
The # symbol will dump from the stack, aka pop a value off and then print it to the console. |
Stack breakdown by step:
0. []
1. [34]
2. [34][35]
3. [69]
4. []
Related:
String literals are now supported in Sparrow; this makes printing a string to the console as simple as dumping with the _s
suffix indicating a string format should be used.
"Hello, World!\n" dump_s
As you can see, Sparrow supports some escaped characters within strings.
They are \n
, \r
, and \t
, to be exact.
You can also dump single characters using an ASCII code like so:
10 dump_c
Expected output:
Hello, World!
Related:
- Keyword: dump
Let's look at a slightly more complicated example program:
500 80 - 420 = if
69 #
else
420 #
endif
This program should first evaulate 500 - 80
, then compare if that sum is equal to 420
. If true, print
69. If false, print 420.
Expected output:
69
Let's break down how it works piece-by-piece:
Step | Code | Description |
---|---|---|
1 | 500 80 - |
Push two values on to the stack, subtract them, then push the result on to the stack. |
2 | 420 |
Push 420 onto the stack |
3 | = |
Use the equality comparison operator; it pops two values off the stack, compares them, then pushes back a 1 if they are equal or a 0 if they aren't. |
4 | if |
Pop the condition just pushed onto the stack, jump to else /endif if it is false, otherwise, like in this case, fall-through to next instruction. |
5 | 69 # |
Push a value onto the stack, then dump it to console output. |
6 | else |
Label to jump to if if condition is false. Label to jump over to endif if if condition is true. |
7 | 420 # |
This would push a value onto the stack, then dump it to console output, however it will be jumped over due to the if condition evaluating to true. |
8 | endif |
Label to jump to if if condition is false and no else label is present or if if condition is true and an else label is present. |
Sparrow now fully supports loops! Check out the following example:
1
while dup 30 <= do
dup dump // Print loop counter without destroying it
10 dump_c // Print a newline character
1 + // Increment loop counter
endwhile
This program will print every whole number from 1
to 30
, each being on a new line.
Let's break down how it works:
Step | Code | Description |
---|---|---|
1 | 1 |
Push a one onto the stack to initialize the loop counter. |
2 | while |
Generate an address to jump to upon reaching endwhile. |
3 | dup 30 <= |
Push a boolean condition on the stack comparing whether the last item on the stack (duplicated) is less than or equal to 30 . |
4 | do |
Pop the condition just pushed onto the stack, jump just past endwhile (step 7) if it is zero, otherwise, like in this case, fall-through to next instruction. |
5 | dup # |
Duplicate the top-most value onto the stack, then dump the duplicate to console output. This prints the current loop counter, as that is what's on the stack. |
6 | 1 + |
Add 1 to top-most value on the stack. This increments the loop counter. |
7 | endwhile |
Upon reaching, jump back to while (step 2) and continue execution from there. |
It is known that this program will trigger a stack validator warning, telling us that the stack at the end of the program is not empty.
With programs as simple as these, it's okay to do, however best practices indicate that the stack should be empty by the end of the program.
For a more complex example, see rule 110
Related:
Sections:
The stack notation of the Sparrow documentation is quite simple.
A stack item is surrounded in square brackets.
The arrow '->' indicates a before and after view of the stack.
Example of a notation with no effect on the stack:
[] -> []
Example of a notation that pushes a value, a
, on to the stack:
[] -> [a]
Example of a notation that pops two values and pushes the sum of them on to the stack:
[a][b] -> [a + b]
An operator will take value(s) from the stack and optionally push some back on. The amount of values removed/added from/to the stack by a given operator can be seen by the stack notation in the following table. For further information on the operator, click on the link to be directed to a more verbose definition of the operator, down below.
Operator | Stack Notation | Description |
---|---|---|
# | [a] -> [] |
Print value on top of stack formatted as unsigned integer. |
+ | [a][b] -> [a + b] |
Sum two elements on top of stack. |
- | [a][b] -> [a - b] |
Subtract two elements on top of stack. |
* | [a][b] -> [a * b] |
Multiply two elements on top of stack. |
/ | [a][b] -> [a / b] |
Divide two elements on top of stack, push quotient. |
% | [a][b] -> [a % b] |
Divide two elements on top of stack, push remainder. |
= | [a][b] -> [a == b] |
Compare if equal top two elements of stack. |
> | [a][b] -> [a > b] |
Compare greater than top two elements of stack. |
< | [a][b] -> [a < b] |
Compare less than top two elements of stack. |
>= | [a][b] -> [a >= b] |
Compare greater than or equal top two elements of stack. |
<= | [a][b] -> [a <= b] |
Compare less than or equal top two elements of stack. |
<< | [a][b] -> [a << b] |
Shift bits of a left by b bits. |
>> | [a][b] -> [a >> b] |
Shift bits of a right by b bits. |
&& | [a][b] -> [a && b] |
Bitwise and on top two elements of stack. |
|| | [a][b] -> [a || b] |
Bitwise or on top two elements of stack. |
Humankind's best friend; pops a single value, a
, off the stack, then prints a
to the console formatted as an unsigned integer.
For alternate formats, see related.
[a] -> []
Eqiuvalent
Related
Example:
420 #
Stack Output:
[]
Standard Output:
420
Pops two values, a
and b
, off of the stack, then pushes the sum of those values.
[a][b] -> [a + b]
Equivalent
- No equivalents
Related
- No related
Example:
34 35 +
Stack Output:
[69]
Standard Output:
Pops two values, a
and b
, off of the stack, then pushes the difference of those values.
[a][b] -> [a - b]
Equivalent
- No equivalents
Related
- No related
Example:
500 80 -
Stack Output:
[420]
Standard Output:
Pops two values, a
and b
, off of the stack, then pushes the product of those values.
[a][b] -> [a * b]
Equivalent
- No equivalents
Related
- No related
Example:
23 3 *
Stack Output:
[69]
Standard Output:
Pops two values, a
and b
, off of the stack, then pushes the product of those values.
[a][b] -> [a * b]
Equivalent
- No equivalents
Related
- No related
Example:
1260 3 /
Stack Output:
[420]
Standard Output:
Pops two values, a
and b
, off of the stack, then pushes a
modulo b
on to the stack.
A modulus operation entails dividing, yet the result is the remainder, not the quotient.
[a][b] -> [a % b]
Equivalent
Related
- No related
Example:
18 15 %
Stack Output:
[3]
Standard Output:
Pops two values off of the stack then pushes a 1
if the values are equal, or a 0
otherwise.
[a][b] -> [a == b]
Equivalent
- No equivalent
Related
Example:
5 5 * 25 =
Stack Output:
[1]
Standard Output:
Pops two values, a
and b
, off of the stack then pushes a 1
if a
is larger than b
, or a 0
otherwise.
[a][b] -> [a > b]
Equivalent
- No equivalent
Related
Example:
105 4 * 300 >
Stack Output:
[1]
Standard Output:
Pops two values, a
and b
, off of the stack then pushes a 1
if a
is smaller than b
, or a 0
otherwise.
[a][b] -> [a < b]
Equivalent
- No equivalent
Related
Example:
105 5 - 420 <
Stack Output:
[1]
Standard Output:
Pops two values, a
and b
, off of the stack then pushes a 1
if a
is larger than or equal to b
, or a 0
otherwise.
[a][b] -> [a >= b]
Equivalent
- No equivalent
Related
Example:
105 4 * 420 >=
Stack Output:
[1]
Standard Output:
Pops two values, a
and b
, off of the stack then pushes a 1
if a
is smaller than or equal to b
, or a 0
otherwise.
[a][b] -> [a <= b]
Equivalent
- No equivalent
Related
Example:
34 35 + 69 <=
Stack Output:
[1]
Standard Output:
Pops two values, a
and b
, off of the stack then pushes bits of a
shifted left by b
amount of bits.
[a][b] -> [a << b]
Equivalent
Related
- No related
Example:
1 3 <<
Stack Output:
[8]
Standard Output:
Pops two values, a
and b
, off of the stack then pushes bits of a
shifted right by b
amount of bits.
[a][b] -> [a >> b]
Equivalent
Related
- No related
Example:
32 2 >>
Stack Output:
[8]
Standard Output:
Pops two values, a
and b
, off of the stack, then pushes the bitwise-and of the two values.
[a][b] -> [a && b]
Equivalent
Related
- No related
Example:
9 3 &&
Stack Output:
[1]
Standard Output:
Pops two values, a
and b
, off of the stack, then pushes the bitwise-or of the two values.
[a][b] -> [a || b]
Equivalent
Related
- No related
Example:
9 3 ||
Stack Output:
[11]
Standard Output:
To Operators
To Definitions
To Top
Keyword | Notation | Description |
---|---|---|
if | [a] -> [] |
Jump to else /endif only if popped value is equal to zero. |
else | [] -> [] |
Inside this block is what will be ran if if condition is false. |
endif | [] -> [] |
Required block-ending-symbol for if keyword. |
do | [a] -> [] |
Jumps just past endwhile if popped value is zero. |
while | [] -> [] |
Generates a label for endwhile to jump to. |
endwhile | [] -> [] |
Generates a label for do to jump to upon false condition. |
dup | [a] -> [a][a] |
Duplicate top of stack. |
twodup | [a][b] -> [a][b][a][b] |
Duplicate two items on top of stack. |
drop | [a] -> [] |
Deletes the top-most item off the stack. |
swap | [a][b] -> [b][a] |
Pushes two popped values back in reverse order. |
over | [a][b] -> [a][b][a] |
Pushes the stack item below the top on to the top. |
dump | [a] -> [] |
Equivalent to # operator. |
dump_c | [a] -> [] |
Pops a value off the stack, then prints it formatted as a char. |
dump_s | [a] -> [] |
Pops a value off the stack, then prints it formatted as a string. |
mem | [] -> [addr] |
Pushes the address of the usable memory in Sparrow. |
store | [addr][a] -> [] |
Stores the popped value at popped memory address. |
load | [addr] -> [a] |
Pushes the value read at popped address on to the stack. |
shl | [a][b] -> [a << b] |
Equivalent to << operator. |
shr | [a][b] -> [a >> b] |
Equivalent to >> operator. |
and | [a][b] -> [a && b] |
Equivalent to && operator. |
or | [a][b] -> [a || b] |
Equivalent to || operator. |
mod | [a][b] -> [a % b] |
Equivalent to % operator. |
open_file | [path][mode] -> [ptr] |
Push a pointer to a file at path on to the |
write_to_file | [str][1][len][ptr] -> [] |
Write a string str to a file ptr . |
close_file | [ptr] -> [] |
Safely close an opened file. |
length_s | [str] -> [len] |
Push the length of a string on to the stack. |
write | [] -> [mode] |
Push the write file mode constant on to the stack. |
append | [] -> [mode] |
Push the append file mode constant on to the stack. |
The if
keyword pops a value off the stack, then jumps to endif
if the value is zero ,or else
if it is present between if
/endif
.
if
[a] -> []
else
, endif
[] -> []
Equivalent:
- No equivalent
Related:
- No related
Example:
1 1 = if
420 #
else
69 #
endif
Stack Output:
[]
Standard Output:
420
The while
keyword generates a label for endwhile
to jump to unconditionally.
The do
keyword is similar to the if
keyword; if the item on the top of the stack is zero, it will jump just past endwhile
, stopping the loop.
The endwhile
keyword is a necessary block ending symbol for the while
keyword.
do
[a] -> []
while
, endwhile
[] -> []
Equivalent:
- No equivalent
Related:
- No related
Example:
1 // initialize loop counter
while dup 5 <= do
dup dump // print loop counter
10 dump_c // print newline
1 + // increment loop counter
endwhile
drop // drop loop counter from stack
Stack Output:
[]
Standard Output:
1
2
3
4
5
Duplicates the item at the top of the stack.
[a] -> [a][a]
Equivalent:
- No equivalent
Related:
- Keyword: twodup
Example:
69 dup
Stack Output:
[69][69]
Standard Output:
Duplicates the top two items of the stack.
[a][b] -> [a][b][a][b]
Equivalent:
- No equivalent
Related:
- Keyword: dup
Example:
69 420
twodup
Stack Output:
[69][420][69][420]
Standard Output:
Deletes the item at the top of the stack, leaving no reference to it. This is useful to shut up warnings from the stack validator (aka follow best practices managing your memory).
Most often seen used after while loops to delete the loop counter from the stack.
[a] -> []
Equivalent:
- No equivalent
Related:
- No related
Example:
420 drop
Stack Output:
[]
Standard Output:
Moves the top-most item and the item below it to each other's positions.
[a][b] -> [b][a]
Equivalent:
- No equivalent
Related:
- No related
Example:
80 500 swap
Stack Output:
[500][80]
Standard Output:
Push the item below the top of the stack on to the top of the stack, duplicating it.
[a][b] -> [a][b][a]
Equivalent:
- No equivalent
Related:
- No related
Example:
1 2 over
Stack Output:
[1][2][1]
Standard Output:
Print the item at the top of the stack to the standard output.
Internally, this uses the C RunTime printf
method, so a format needs to be specified by using a specific keyword.
Keyword | Print Format |
---|---|
dump or # |
unsigned integer |
dump_c |
character |
dump_s |
string |
[a] -> []
Equivalent:
- Operator: #
Related:
- No related
Example:
// Print a number
69420 dump
// Print a newline (10 = ascii newline)
10 dump_c
// Print a string constant
"Dennis is a bastard man\n" dump_s
// Print a string from memory
// I don't recommend constructing them from hand like
// this, but it's really useful for building strings
// within loops and not having to print each character.
// i = string index
// c = character ascii code
// <i> <c>
mem 10 storeb // <newline>
mem 1 + 82 storeb // R
mem 2 + 101 storeb // e
mem 3 + 103 storeb // g
mem 4 + 103 storeb // g
mem 5 + 105 storeb // i
mem 6 + 101 storeb // e
mem 7 + 32 storeb // <space>
mem 8 + 87 storeb // W
mem 9 + 97 storeb // a
mem 10 + 116 storeb // t
mem 11 + 116 storeb // t
mem 12 + 115 storeb // s
mem 13 + 10 storeb // <newline>
mem 14 + 0 storeb // null terminator
mem dump_s
Stack Output:
[]
Standard Output:
69420
Dennis is a bastard man
Reggie Watts
Pushes the address of the memory allocated at run-time.
For now, this is hard-coded in the Sparrow executable to 720kb. Although that should be enough for everyone, there will be a CCLI option in the future to specify the exact amount of bytes you would like to allocate.
To access any address within the memory, simply add the byte offset to the address, like so mem <byte offset> +
.
Next, use it with the memory access keywords that accept memory addresses as arguments (see related).
[] -> [addr]
Equivalent:
- No equivalent
Related:
Example:
mem
Stack Output:
[addr]
Standard Output:
Store a value at an address in the memory allocated during run-time (see mem).
This allows string-building, variables (albeit un-named ones), and as much as your mind can imagine.
A size of value to store at an address must be selected with the following format:
store<x>
Where x
is any of the following:
- 'b' - byte | 8 bits
- 'w' - word | 16 bits
- 'd' - double word | 32 bits
- 'q' - quad word | 64 bits
[addr][value] -> []
Equivalent:
- No equivalent
Related:
Example:
// Store a byte with value of 69 at mem[0]
mem 69 storeb
// Store a word with value of 69420 at mem[1]
mem 1 + 69420 storew
// Store a double word with value of 6969696969 at mem[3]
// Note the byte offset taken into account due to previously
// storing a word (2 bytes) at mem[1].
mem 3 + 6969696969 stored
// Store a quad word with value of 19696942042069696969 at mem[7]
mem 7 + 19696942042069696969 storeq
// Final memory layout
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ...
// 69 69420 6969696969- 19696942042069696969------- ...
Stack Output:
Standard Output:
Push a value on to the stack from a given address in the memory allocated during runtime (see related).
A size of value to store at an address must be selected with the following format:
load<x>
Where x
is any of the following:
- 'b' - byte | 8 bits
- 'w' - word | 16 bits
- 'd' - double word | 32 bits
- 'q' - quad word | 64 bits
[addr] -> [value]
Equivalent:
- No equivalent
Related:
Example:
// This is the `store` example from above with a few
// modifications made after the HERE comment.
// Store a byte with value of 69 at mem[0]
mem 69 storeb
// Store a word with value of 69420 at mem[1]
mem 1 + 69420 storew
// Store a double word with value of 6969696969 at mem[3]
// Note the byte offset taken into account due to previously
// storing a word (2 bytes) at mem[1].
mem 3 + 6969696969 stored
// Store a quad word with value of 19696942042069696969 at mem[7]
mem 7 + 19696942042069696969 storeq
// Final memory layout
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ...
// 69 69420 6969696969- 19696942042069696969------- ...
// HERE
// Print 8 bits of memory starting at mem[0]
mem loadb dump
// Print 16 bits of memory starting at mem[1]
mem 1 + loadw dump
// Print 32 bits of memory starting at mem[3]
mem 3 + loadd dump
// Print 64 bits of memory starting at mem[7]
mem 7 + loadq dump
Stack Output:
Standard Output:
69
69420
6969696969
19696942042069696969
Pushes the address of the memory allocated at run-time.
For now, this is hard-coded in the Sparrow executable to 720kb. Although that should be enough for everyone, there will be a CCLI option in the future to specify the exact amount of bytes you would like to allocate.
To access any address within the memory, simply add the byte offset to the address, like so mem <byte offset> +
.
Next, use it with the memory access keywords that accept memory addresses as arguments (see related).
[a][b] -> [a << b]
Equivalent:
- Operator: <<
Related:
- Keyword: shr
Example:
// 1: 001
// /
// /
// V
// 4: 100
1 2 shl
Stack Output:
[4]
Standard Output:
Shifts the bits of a
to the right by b
amount of bits.
[a][b] -> [a >> b]
Equivalent:
- Operator: >>
Related:
- Keyword: shl
Example:
// 32: 100000
// \
// \
// V
// 8: 001000
32 2 shr
Stack Output:
[8]
Standard Output:
Perform a bitwise AND operation on two popped values, a
and b
.
An AND operation entails the output only containing a 1
if both inputs do.
[a][b] -> [a && b]
Equivalent:
- Operator: &&
Related:
- Keyword: or
Example:
// 7: 0111
// 14: 1110
// 6: 0110
7 14 and
Stack Output:
[6]
Standard Output:
Perform a bitwise OR operation on two popped values, a
and b
.
An OR operation entails the output containing a 1
if one or both of the inputs do.
[a][b] -> [a || b]
Equivalent:
- Operator: ||
Related:
- Keyword: and
Example:
// 7: 0111
// 14: 1110
// 15: 1111
7 14 or
Stack Output:
[15]
Standard Output:
Pop two values off the stack, a
and b
, then push the result of a
modulo b
.
A modulus operation entails dividing and then taking the remainder, aka what is left-over.
[a][b] -> [a % b]
Equivalent:
- Operator: %
Related:
- No related
Example:
20 15 mod
Stack Output:
[5]
Standard Output:
The file operations are highly unstable, inperformant. Requires further testing and Fixing.
Pop two values off the stack, path
and mode
then push a file pointer to an opened file.
Used with file operation keywords (see related).
File paths are always relative to wherever the Sparrow executable was when it compiled the program.
File paths are NOT relative to the source code, or generated executable.
[path][mode] -> [file pointer]
Equivalent:
- No equivalent
Related:
- Keyword: write
- Keyword: append
- Keyword: write_to_file
- Keyword: close_file
Example:
"myFile.txt" write open_file
Stack Output:
[pointer to writeable file]
Standard Output:
Pop four values off the stack, then use them as arguments to call fwrite
from the C RunTime.
Number of bytes per character is usually one unless you are doing some weird utf-16 stuff.
File paths are always relative to wherever the Sparrow executable was when it compiled the program.
File paths are NOT relative to the source code, or generated executable.
[content str][num bytes per character][num characters to write][file pointer] -> []
Equivalent:
- No equivalent
Related:
- Keyword: open_file
- Keyword: close_file
Example:
// Store file pointer in mem[0] thru mem[7]
mem "myFile.txt" write open_file storeq
// Content String
"I want to write this string to a text file\n"
// The length of the string is determined
dup length_s
// Next, the number of bytes per character must be set
1
// Then, it must be arranged correctly in-between the
// string and it's length
// [str][len][1] -> [str][1][len]
swap
// Load file pointer from memory
mem loadq
// Call `write_to_file`
// 4 arguments: [content str][num bytes per character][num characters][file ptr]
write_to_file
// Load file pointer and close it
mem loadq close_file
Stack Output:
[]
Standard Output:
myFile.txt
Contents:
I want to write this string to a text file
Pops a single value off the stack, file ptr
, then closes the file opened at that pointer.
Best practices indicate that every opened file must be closed before execution halts.
File paths are always relative to wherever the Sparrow executable was when it compiled the program.
File paths are NOT relative to the source code, or generated executable.
[file pointer] -> []
Equivalent:
- No equivalent
Related:
- Keyword: open_file
Example:
"myFile.txt" write open_file
close_file
Stack Output:
[]
Standard Output:
myFile.txt
Contents:
Pop a string off the stack, then return the length of that string back on to the stack.
[string] -> [length]
Equivalent:
- No equivalent
Related:
- Keyword: write_to_file
Example:
"Hello, World!" length_s dump
Stack Output:
[]
Standard Output:
13
Pushes a file mode constant on to the stack.
Used with open_file
to indicate that the file should start empty, creating the file if it doesn't already exist.
This means opening a file in this way over-writes any data previously stored there.
[] -> [file mode `write` constant]
Equivalent:
- No equivalent
Related:
Example:
"myFile.txt" write open_file
close_file
Stack Output:
[]
Standard Output:
myFile.txt
Contents:
Pushes a file mode constant on to the stack.
Used with open_file
to indicate that it should keep the contents of the file.
Anything written to the file is put after the contents that were there already.
[] -> [file mode `append` constant]
Equivalent:
- No equivalent
Related:
Example:
"myFile.txt" append open_file
close_file
Stack Output:
[]
Standard Output:
myFile.txt
Contents:
To Keywords
To Definitions
To Top
So, you've written a program, what do you do now that you want to run it?
Build it yourself using CMake after cloning the repository its really simple! (further instructions down below).
There are two assembly syntaxes Sparrow supports (for now):
GAS, or the GNU assembler
I would not really recommand this!
For Windows, there is a little funky business... MinGW, the 'normal' installation manager for GNU tools on Windows, doesn't support 64 bits.
Luckily, there is a community-fix, TDM-GCC-64, that solves this exact problem, so go donate to this person for doing the hard work that all of us can now use. If for some reason the github was taken down, or anything of the sort, here is a link to a wayback machine snapshot.
It's a very easy to use installer, and comes with a whole host of very useful 64 bit tools on Windows. Install it at the default location, otherwise Sparrow will need to be passed the path to the gcc
executable using the SCLI -a
option.
To familiarize yourself with the Sparrow Command Line Interface (CCLI), run the following command:
Sparrow.exe -h` or `Sparrow.exe --help
This will list all of the possible flags and options that may be passed to Sparrow.
Example command to compile test.spar
to an executable on Windows: \
Sparrow.exe -GAS test.spar
Example command with output renamed: \
Sparrow.exe -GAS -add-ao "-o my-output-name" -o my-output-name test.spar
Just use the one that is packed with the Sparrow Repository.
But if you want an updated version on Windows you can download the installer from the official website
You must ensure that you have some sort of linker on your machine that can link against the standard C runtime of whatever platform you're on
- GoLink is my recommendation on Windows.
GoLink is easy to use and fast to setup; simply extract it and it's ready.
Once all the pre-requisites are installed, now comes time to use the CCLI, or Sparrow Command Line Interface.
To avoid headache as much as possible, Sparrow sets default values based on your operating system.
If you get any errors, there are a multitude of command line options to help rectify the situation. (see Common Errors)
Open a terminal and navigate to the directory containing Sparrow.exe
.
To familiarize yourself with the options of the CCLI, run the following command:
Sparrow.exe -h
or Sparrow.exe --help
A lot of different options will come up, with clear explanations on what everything does.
Basic example:
Sparrow.exe -o my_program test.spar
Or, if Sparrow is giving errors about not finding assembler/linker:
Sparrow.exe -a /Path/To/NASM/nasm.exe -l /Path/To/GoLink/golink.exe test.spar
Alternatively, you could add the directory containing the executable to your system's PATH environment variable:
Sparrow.exe -a nasm.exe -l golink.exe test.spar
By default, the assembler and linker options are setup for Windows, using NASM and GoLink.
If your situation is different, make sure to specify the correct options using -ao
and -lo
respectively.
- "Assembler not found at x"
- Solution: Specify a valid path, including file name and extension, to the assembler executable using
-a
or--assembler-path
- Solution: Specify a valid path, including file name and extension, to the assembler executable using
- "Linker not found at x"
- Solution: Specify a valid path, including file name and extension, to the linker executable using
-l Path/To/Linker.exe
or--linker-path Path/To/Linker.exe
- Solution: Specify a valid path, including file name and extension, to the linker executable using
- The stdout and stderr of any commands run are redirected to a log file in the same directory as the Sparrow executable. The contents of these files are printed to the console when the verbose flag is passed to Sparrow through the CCLI with
-v
or--verbose
.
This project uses CMake to build Sparrow for any platform that CMake supports (which is a lot).
This means Sparrow source code can be easily built in your favorite IDE that supports C++.
First, on any platform, clone this repository to your local machine.
Run the following command in the repository directory:
cmake -S . -B build/
This will use CMake to build a build system with the default generator on your platform.
Once complete, open the build directory, and build Sparrow using the build system you just generated.
Windows example:
Open Visual Studio solution and build with F6
Easiest way to see how the Compiler Works is to use the Powershell file. This would basically run the two tests.
test.ps1
But, you can always create the build manually.