Skip to content

A Toy Stack based Compiler. We Going Full Circle Boys!

License

Notifications You must be signed in to change notification settings

Eshanatnight/Sparrow

Repository files navigation

Sparrow

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!

Background

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.

General Information

File Suffix: .spar Assembler: Nasm (In Repo) Linker: GoLink (In Repo)

Something to Understand Before Checking Sparrow out

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.

How to write in Sparrow

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.

Table of Contents

This is where we go get complicated!


A Simple Example

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:

  • Operator: +
  • Operator: #

Best practices in Sparrow indicate that the stack should be empty by the end of the program.

To Top


Hello, World!

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:

To Top


Conditional Branching

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.

To Top


Loops

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.

To Top


Complications

For a more complex example, see rule 110

Related:

To Top


Definitions:

Sections:

Stack Notation

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]

To Top

Operators

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.

'#' - DUMP

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

To Operators


'+' - ADD

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:

To Operators


'-' - SUBTRACT

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:

To Operators


'*' - MULTIPLY

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:

To Operators


'/' - DIVIDE

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:

To Operators


'%' - MODULO

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:

To Operators


'=' - EQUAL

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:

To Operators


'>' - GREATER-THAN

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:

To Operators


'<' - LESS-THAN

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:

To Operators


'>=' - GREATER-THAN-OR-EQUAL

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:

To Operators


'<=' - LESS-THAN-OR-EQUAL

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:

To Operators


'<<' - BITWISE-SHIFT LEFT

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:

To Operators


'>>' - BITWISE-SHIFT RIGHT

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:

To Operators


'&&' - AND

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:

To Operators


'||' - OR

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


Keywords

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.

'if' - Conditional Branch

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

To Keywords


'while' - Looping

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

To Keywords


'dup' - Stack Operation

Duplicates the item at the top of the stack.

[a] -> [a][a]

Equivalent:

  • No equivalent

Related:

Example:

69 dup

Stack Output:

[69][69]

Standard Output:

To Keywords


'twodup' - Stack Operation

Duplicates the top two items of the stack.

[a][b] -> [a][b][a][b]

Equivalent:

  • No equivalent

Related:

Example:

69 420
twodup

Stack Output:

[69][420][69][420]

Standard Output:

To Keywords


'drop' - Stack Operation

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:

To Keywords


'swap' - Stack Operation

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:

To Keywords


'over' - Stack Operation

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:

To Keywords


'dump' - Tool

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

To Keywords


'mem' - Memory Address

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.

Remember, it is up to you to not access invalid memory addresses.

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:

To Keywords


'store' - Memory Manipulation

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
Remember, it is up to you to not access invalid memory addresses.
[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:

To Keywords


'load' - Memory Manipulation

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
Remember, it is up to you to not access invalid memory addresses.
[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

To Keywords


'shl' - Bitwise Operator

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.

Remember, it is up to you to not access invalid memory addresses.

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:

Example:

// 1: 001
//      /
//     /
//    V
// 4: 100
1 2 shl

Stack Output:

[4]

Standard Output:

To Keywords


'shr' - Bitwise Operator

Shifts the bits of a to the right by b amount of bits.

[a][b] -> [a >> b]

Equivalent:

  • Operator: >>

Related:

Example:

// 32: 100000
//     \
//      \
//       V
// 8:  001000
32 2 shr

Stack Output:

[8]

Standard Output:

To Keywords


'and' - Bitwise Operator

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:

To Keywords


'or' - Bitwise Operator

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:

Example:

// 7:  0111
// 14: 1110
// 15: 1111
7 14 or

Stack Output:

[15]

Standard Output:

To Keywords


'mod' - Operator

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:

To Keywords


File Operations

The file operations are highly unstable, inperformant. Requires further testing and Fixing.

'open_file' - Operator

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:

Example:

"myFile.txt" write open_file

Stack Output:

[pointer to writeable file]

Standard Output:

To Keywords


'write_to_file' - Operator

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:

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


'close_file' - Operator

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:

Example:

"myFile.txt" write open_file

close_file

Stack Output:

[]

Standard Output:

myFile.txt Contents:

To Keywords


'length_s' - Operator

Pop a string off the stack, then return the length of that string back on to the stack.

[string] -> [length]

Equivalent:

  • No equivalent

Related:

Example:

"Hello, World!" length_s dump

Stack Output:

[]

Standard Output:

13

To Keywords


'write' - Operator

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:

To Keywords


'append' - Operator

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


How to build a Sparrow program

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

Windows

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

NASM

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)

Windows

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.

Common Errors

  • "Assembler not found at x"
    • Solution: Specify a valid path, including file name and extension, to the assembler executable using -a or --assembler-path
  • "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
  • 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.

To Top


How to build Sparrow from source

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

To Top


Easy Test Up

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.

To Top