FULL REFERENCE

This is the complete reference for satellite 004, revision 08: every word the language has today, what it does, how it is written, and what satl refuses. Every example on this page was run with satl, and the box under each one is exactly what it printed. If you are new to programming, BEGINNERS and the TUTORIAL are gentler places to start. Come back here when you want the whole story of one word.

One rule runs through all of it. A dotted path that starts with satellite names something the language owns: satellite.console.display, satellite.variable.number, satellite.statement.if. A bare name is something you own: total, greet, sky_blue. You never have to guess whether a word is yours or the language’s. The spelling tells you.

satl writes its own title (the language, its version and its build) at the top of every run. The boxes on this page leave that out and show only what the program printed. When satl refuses a program, the box shows its error report instead. Reading an error report explains every part of one.

PROGRAM SHAPE

Every satellite program has the same frame. Its first line is satellite.include(satellite). Its work happens inside satellite.capsule satellite.main(). And main ends with satellite.return(satellite). satl looks for all three before it runs a single line. satellite.console.display puts a line of text on the screen; it has a post of its own, displaying text.

hello_world.satlSATELLITE 004 Download
The smallest whole program
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display("Hello, World!")
    satellite.return(satellite)
}
OUTPUT
Hello, World!

Lines, braces and names

A satellite file is read one line at a time, and a few simple rules decide how lines are written.

  • One statement is one line. There is no semicolon at the end, and two statements on one line are refused.
  • A bracket closes on the same line it opened on.
  • { and } each go on a line of their own. A { at the end of an if line is refused.
  • Indentation and blank lines are free. Four spaces a level is the custom.
  • A bare name is made of English letters, digits and _, and does not start with a digit. Capitals count: Apples and apples are two different names.
  • A name that reads as a value is taken as that value. b101 is a binary and xbeef is a hex, so neither can be a name. beef, b2 and x_count are fine.
names.satlSATELLITE 004 Download
Two names that differ only in a capital
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number Apples = 3
    satellite.variable.number apples = 4
    satellite.variable.number x_count = Apples + apples

    satellite.console.display(x_count)
    satellite.return(satellite)
}
OUTPUT
7
one_line_each.satlSATELLITE 004 Download
Two statements on one line are refused
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display("one") satellite.console.display("two")
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S110: LINE_NOT_UNDERSTOOD
satl(check): in satellite.main, satellite.console.display(...) is followed by
something that is not a method call -- a call's answer cannot be given a value,
a bracket must close on its line, and one statement is one line

directory: one_line_each.satl:5
syntax: satellite.console.display("one") satellite.console.display("two")
        /\

satellite has no scenario for this line -- it is spelled in a shape the language
does not have a meaning for.
machine code 13 satl_line_not_understood -- satl exits with this.

--------------------------------------------------------------------------------

//

// starts a comment, and the comment runs to the end of the line. satl skips it. A comment can have a line to itself, sit above everything else in the file, or follow a statement. Inside a string, // is just two characters of text. It is the only kind of comment: /* */ and # are not comments in satellite, and a line that starts with either is refused.

comments.satlSATELLITE 004 Download
Comments on their own lines and after a statement
// comments.satl -- a comment can even come before the first line
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number apples = 5 // five apples
    // satellite.console.display("this line is switched off")
    satellite.console.display(apples)
    satellite.console.display("a // inside a string is text")
    satellite.return(satellite)
}
OUTPUT
5
a // inside a string is text

satellite.include(satellite)

satellite.include(satellite) is the first line of every program (comments aside). It says: this file is a program, and it uses the language. A file without it is a spaceship, a file that other files include, and satl will not start it. The refusal comes before anything runs.

spaceship.satlSATELLITE 004 Download
A file with no satellite.include(satellite) does not start
satellite.capsule satellite.main()
{
    satellite.console.display("never shown")
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S101: FILE_HAS_NO_INCLUDE
satl.file(check): this file has no satellite.include(satellite), so it is a
spaceship and not a program

directory: spaceship.satl

a program's first line is satellite.include(satellite). Without it the file is a
SPACESHIP -- something another file includes -- and satl will not start it. Add
this as the first line:

    satellite.include(satellite)
machine code 10 satl_file_missing_satellite_include_satellite -- satl exits with
this.

--------------------------------------------------------------------------------

satellite.include

satellite.include also brings in another file of yours. Write the file’s name bare, or its path in double quotes:

  • satellite.include(shapes) and satellite.include("shapes.satl") both find shapes.satl in the same folder as your program. The .satl on the end may be left off.
  • satellite.include("parts/greetings.satl") reaches into a folder, and .. and ./ work too. A path is read from the folder of the file that holds the include, wherever you ran satl from.
  • A full path that starts with / is not found today: satl reads it without its first slash and reports the file missing. Keep the files you include beside your program or in folders near it, and reach them with a relative path.
  • An include is written outside every capsule. The custom is to put them all at the top, under satellite.include(satellite). Inside a capsule, satl refuses one.

Everything in an included file is reached through that file’s own name: shapes.area(4, 5), not area(4, 5). A bare area(4, 5) is refused, and the report tells you the spelling that works. An included file may be a spaceship, or a whole program with a main of its own so that it can also run by itself. When it is included, its main never runs.

Here is a file that works both ways. On its own it is a complete program:

shapes.satlSATELLITE 004 Download
A file that runs by itself and can be included
satellite.include(satellite)

satellite.capsule area(satellite.variable.number wide, satellite.variable.number tall)
{
    satellite.return(wide * tall)
}

satellite.capsule satellite.main()
{
    satellite.console.display("shapes.satl, run on its own")
    satellite.console.display(area(2, 3))
    satellite.return(satellite)
}
OUTPUT
shapes.satl, run on its own
6

And here is a program that includes it. Put the two files in one folder and run satl garden.satl. The box shows what it prints with shapes.satl beside it; notice that shapes.satl’s own main does not run:

garden.satlSATELLITE 004 Download
Includes shapes.satl and calls its capsule by the file's name
satellite.include(satellite)
satellite.include(shapes)

satellite.capsule satellite.main()
{
    satellite.console.display(shapes.area(4, 5))
    satellite.return(satellite)
}
OUTPUT
20

A file that is not there is refused before anything runs:

fence_plan.satlSATELLITE 004 Download
Including a file that does not exist
satellite.include(satellite)
satellite.include(fences)

satellite.capsule satellite.main()
{
    satellite.return(satellite)
}
OUTPUT
[satellite] cannot locate file: fences.satl, included by fence_plan.satl (machine_code: 8 missing_satl_file)

Namespaces, and how names reach across files, are in including files and namespaces.

satellite.main

satellite.main is where a program begins: satellite’s version of C’s int main(). It is a capsule, declared satellite.capsule satellite.main(), and your program starts on its first line and ends when it reaches satellite.return(satellite).

There are no globals in satellite. A line written outside every capsule has nowhere to keep its result and no moment to run in, so everything a program does starts from a line in main, or from a capsule that main calls. (Values written once at the top of a file are a different thing, satellite.library, with a post of its own: satellite.library.)

  • main is written once, at the top level of its file. A second one is refused as declared twice (S202), and one inside a satellite.namespace is refused (S110).
  • Only the file you run has its main run. Including a file gives you its capsules, never its main.
  • A file with no main is refused before anything runs (S102). Leave off satellite.capsule and satellite.main() is not a capsule at all: satl stops with “no satellite.main to begin in”.
no_main.satlSATELLITE 004 Download
A program with no satellite.main
satellite.include(satellite)

satellite.capsule start()
{
    satellite.console.display("never shown")
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S102: FILE_HAS_NO_MAIN
satl.file(check): this file has no satellite.main -- there are no globals, so
there is nowhere else to begin

directory: no_main.satl

there are no globals in satellite, so a statement outside a capsule has nowhere
to put its result and no moment to run in -- which means there is nowhere to
begin. Add:

    satellite.capsule satellite.main()
    {
        ...
    }
machine code 11 satl_file_missing_satellite_main -- satl exits with this.

--------------------------------------------------------------------------------

satellite.variable.arguments

main takes nothing, satellite.main(), or one parameter: the arguments variable. Declare it as satellite.variable.arguments and give it any name you like. It holds everything satl knows as it starts: the words your program was started with, satl’s own settings, and facts about the machine, each read by name after a dot. The older spelling, satellite.container.list<satellite.variable.string> arguments, is the same variable.

  • arguments.program is the file you ran, and arguments.length counts the words on the command line, the program included.
  • satl main_arguments.satl one two gives arguments.argument_1 = one, arguments.argument_2 = two and arguments.length = 3.
  • A name that is not one of its rows is refused where it is read.
main_arguments.satlSATELLITE 004 Download
Reading two rows of the arguments variable
satellite.include(satellite)

satellite.capsule satellite.main(satellite.variable.arguments arguments)
{
    satellite.console.display(arguments.program)
    satellite.console.display(arguments.length)
    satellite.return(satellite)
}
OUTPUT
main_arguments.satl
1

Every row it has, and how to add your own, is in the arguments variable.

CAPSULES

A capsule is satellite’s word for a function: a named piece of program that you can run from anywhere by calling its name. Capsules is the gentle introduction.

satellite.capsule

A capsule is declared at the top level of a file, or inside a satellite.namespace, with its name, its parameters in brackets, and its body between { and }:

  • A parameter is a type and then a name, the way every declaration in satellite is written, with commas between them: (satellite.variable.string who, satellite.variable.number times).
  • A capsule that takes nothing has empty brackets: hello().
  • The order capsules are written in does not matter. main can call a capsule written below it.
  • A capsule declared inside another capsule is refused (S110), and so is a name declared twice in one file (S202).
capsules.satlSATELLITE 004 Download
Two capsules, written after the main that calls them
satellite.include(satellite)

satellite.capsule satellite.main()
{
    greet("world", 2)
    satellite.console.display(twice(21))
    satellite.return(satellite)
}

satellite.capsule greet(satellite.variable.string who, satellite.variable.number times)
{
    satellite.variable.number i = 0
    satellite.statement.while (i < times)
    {
        satellite.console.display("hello, " + who)
        i = i + 1
    }
}

satellite.capsule twice(satellite.variable.number n)
{
    satellite.return(n * 2)
}
OUTPUT
hello, world
hello, world
42

A capsule runs in a frame of its own. It cannot see the caller’s variables, and a parameter is the capsule’s own copy: changing it inside does not change the caller’s name. What you hand a capsule is all it has.

own_copy.satlSATELLITE 004 Download
A parameter is the capsule's own copy
satellite.include(satellite)

satellite.capsule bump(satellite.variable.number n)
{
    n = n + 1
    satellite.console.display(n)
}

satellite.capsule satellite.main()
{
    satellite.variable.number n = 1
    bump(n)
    satellite.console.display(n)
    satellite.return(satellite)
}
OUTPUT
2
1

Each call is checked twice. The number of arguments is checked before anything runs; the type of each one is checked when the call is reached.

wrong_count.satlSATELLITE 004 Download
A call with one argument too many
satellite.include(satellite)

satellite.capsule twice(satellite.variable.number n)
{
    satellite.return(n * 2)
}

satellite.capsule satellite.main()
{
    satellite.console.display(twice(1, 2))
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S110: LINE_NOT_UNDERSTOOD
satl(check): in satellite.main, twice takes 1 argument, and was given 2

directory: wrong_count.satl:10
syntax: satellite.console.display(twice(1, 2))
        /\

satellite has no scenario for this line -- it is spelled in a shape the language
does not have a meaning for.
machine code 13 satl_line_not_understood -- satl exits with this.

--------------------------------------------------------------------------------
wrong_type.satlSATELLITE 004 Download
A string handed to a number parameter
satellite.include(satellite)

satellite.capsule twice(satellite.variable.number n)
{
    satellite.return(n * 2)
}

satellite.capsule satellite.main()
{
    satellite.console.display(twice("ten"))
    satellite.return(satellite)
}
OUTPUT
[satellite] satl(run): twice's n was declared satellite.variable.number, and it holds a string (machine_code: 27 types_do_not_meet)

What a capsule answers

A capsule answers whatever its satellite.return(...) hands back. Nothing after its brackets says what kind of value that is: only line ends and comments stand between a capsule’s ) and its {.

  • One whose answer is used, and that can never hand one back, is refused before anything runs (S240).
  • One whose answer is used, and that ended without handing one back, is refused when it happens (S240).
  • satellite.returns(type) after the brackets is refused by name. It was taken out of the language on 2026-09-24: delete it, and the capsule answers exactly as it did.
sign_of.satlSATELLITE 004 Download
A capsule that answers a string
satellite.include(satellite)

satellite.capsule sign_of(satellite.variable.number n)
{
    satellite.statement.if (n < 0)
    {
        satellite.return("below zero")
    }
    satellite.statement.if (n == 0)
    {
        satellite.return("zero")
    }
    satellite.return("above zero")
}

satellite.capsule satellite.main()
{
    satellite.console.display(sign_of(-5))
    satellite.console.display(sign_of(0))
    satellite.console.display(sign_of(7))
    satellite.return(satellite)
}
OUTPUT
below zero
zero
above zero
returns_taken_out.satlSATELLITE 004 Download
satellite.returns is refused by name
satellite.include(satellite)

satellite.capsule name_of() satellite.returns(satellite.variable.string)
{
    satellite.return("satellite")
}

satellite.capsule satellite.main()
{
    satellite.console.display(name_of())
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S110: LINE_NOT_UNDERSTOOD
satl(check): satellite.capsule name_of -- satellite.returns was taken out of
satellite: a capsule answers whatever its satellite.return(...) hands back, so
delete satellite.returns(...) from this line

directory: returns_taken_out.satl:3
syntax: satellite.capsule name_of() satellite.returns(satellite.variable.string)
        /\

satellite has no scenario for this line -- it is spelled in a shape the language
does not have a meaning for.
machine code 13 satl_line_not_understood -- satl exits with this.

--------------------------------------------------------------------------------

Calling a capsule

A capsule is called by its name and its arguments in brackets. The arguments are worked out where the call is, before the capsule starts.

  • On a line of its own, for what it does: greet("world", 2).
  • Inside a line, for what it answers: satellite.variable.number area = square(4), square(3) + 1, square(square(2)).
  • A capsule in an included file is called through the file’s name, shapes.area(4, 5), and one inside a satellite.namespace tools is called tools.twice(21).
calls.satlSATELLITE 004 Download
A capsule called for its answer, three ways
satellite.include(satellite)

satellite.capsule square(satellite.variable.number n)
{
    satellite.return(n * n)
}

satellite.capsule satellite.main()
{
    satellite.variable.number area = square(4)
    satellite.console.display(area)
    satellite.console.display(square(3) + 1)
    satellite.console.display(square(square(2)))
    satellite.return(satellite)
}
OUTPUT
16
10
16

Using the answer of a capsule that can never give one is refused before anything runs:

no_answer.satlSATELLITE 004 Download
A capsule with no answer, used for its answer
satellite.include(satellite)

satellite.capsule say_hi()
{
    satellite.console.display("hi")
}

satellite.capsule satellite.main()
{
    satellite.variable.number n = say_hi()
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S240: CAPSULE_GAVE_NO_ANSWER
satl(check): in satellite.main, say_hi() is used where its answer would be, and
it never hands one back -- no satellite.return(...) in it has a value

directory: no_answer.satl:10
syntax: satellite.variable.number n = say_hi()
        /\

this capsule's answer was used, and the way it went reached no
satellite.return(...) -- so there was nothing to hand back.
machine code 53 capsule_gave_no_answer -- satl exits with this.

--------------------------------------------------------------------------------

A capsule that calls itself

A capsule may call itself. How far it can go depends on where the call stands.

  • A call that is the last thing the capsule does (the last line of an if or an else, or followed only by satellite.return) is run as a loop. It can go as deep as you like: a hundred thousand turns is nothing.
  • A call with work still to do after it, such as n * factorial(n - 1), is an ordinary call. So is a call written inside satellite.return(...), such as satellite.return(gcd(b, a % b)), because handing the answer back is work left after it. Each level takes a few kilobytes while it waits. There is no depth limit but memory: when a program fills the room it started with, satl moves on to fresh room and carries on, and a million levels deep is a few gigabytes. A call written last takes none of that.
recursion.satlSATELLITE 004 Download
A last call 100,000 deep, and a factorial
satellite.include(satellite)

satellite.capsule count_down(satellite.variable.number n)
{
    satellite.statement.if (n == 0)
    {
        satellite.console.display("lift off")
    }
    satellite.statement.else
    {
        count_down(n - 1)
    }
}

satellite.capsule factorial(satellite.variable.number n)
{
    satellite.statement.if (n < 2)
    {
        satellite.return(1)
    }
    satellite.return(n * factorial(n - 1))
}

satellite.capsule satellite.main()
{
    count_down(100000)
    satellite.console.display(factorial(25))
    satellite.return(satellite)
}
OUTPUT
lift off
15511210043330985984000000

satellite.return

satellite.return(...) ends the capsule it is written in, from wherever it stands, even inside an if, a while or a for, and hands back what is between its brackets.

  • In main, satellite.return(satellite) ends the program, and satl exits with 0 (success).
  • satellite.return() and satellite.return(satellite) hand back nothing.
  • A capsule other than main may leave it out. Reaching its closing } ends it the same way, with nothing handed back.
  • A program needs at least one. A file with none is refused before anything runs (S103). Its place is the last line of main.
return_early.satlSATELLITE 004 Download
Leaving a loop, a capsule and main early
satellite.include(satellite)

satellite.capsule first_square_over(satellite.variable.number limit)
{
    satellite.statement.for (satellite.variable.number i = 1; i < limit; i++)
    {
        satellite.statement.if (i * i > limit)
        {
            satellite.return(i * i)
        }
    }
    satellite.return(0)
}

satellite.capsule count_until(satellite.variable.number stop)
{
    satellite.variable.number n = 0
    satellite.statement.while (n < 10)
    {
        satellite.statement.if (n == stop)
        {
            satellite.return()
        }
        satellite.console.display(n)
        n = n + 1
    }
}

satellite.capsule satellite.main()
{
    satellite.console.display(first_square_over(50))
    count_until(2)
    satellite.statement.if (1 == 1)
    {
        satellite.console.display("ending early")
        satellite.return(satellite)
    }
    satellite.console.display("never shown")
    satellite.return(satellite)
}
OUTPUT
64
0
1
ending early
no_return.satlSATELLITE 004 Download
A program with no satellite.return
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display("never shown")
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S103: FILE_HAS_NO_RETURN
satl.file(check): this file has no satellite.return -- execution ends inside
main

directory: no_return.satl

execution ends inside main, and main has to say so. Add this as main's last
line, just before its closing }:

    satellite.return(satellite)
machine code 12 satl_file_missing_satellite_return_satellite -- satl exits with
this.

--------------------------------------------------------------------------------

STATEMENTS

The statements are satellite.statement.if, satellite.statement.else, satellite.statement.while and satellite.statement.for. Each is followed by its body between { and }, each brace on a line of its own. If, while and for walks through them slowly.

  • A condition must answer true or false. A number where a condition goes is refused when the line is reached.
  • A body shares the variables of the capsule around it: a name changed inside is changed outside.
  • To use a name after an if or a loop, declare it before.
  • A statement with no body is refused before anything runs (S110).

satellite.statement.if

satellite.statement.if (condition) runs its body when the condition is true, and skips it when it is false.

if.satlSATELLITE 004 Download
One if that runs and one that does not
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number temperature = 30
    satellite.statement.if (temperature > 25)
    {
        satellite.console.display("warm")
    }
    satellite.statement.if (temperature > 35)
    {
        satellite.console.display("hot")
    }
    satellite.return(satellite)
}
OUTPUT
warm
not_a_condition.satlSATELLITE 004 Download
A number is not a condition
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.statement.if (5)
    {
        satellite.console.display("never shown")
    }
    satellite.return(satellite)
}
OUTPUT
[satellite] satl(run): satellite.statement.if was given a number and needs a true or false (machine_code: 27 types_do_not_meet)

satellite.statement.else

satellite.statement.else comes straight after an if‘s body and runs when that if did not. Write satellite.statement.else satellite.statement.if (condition) to test another condition, as many times as you need; the first one that is true runs, and the rest are skipped. An else with no if before it is refused (S110).

else.satlSATELLITE 004 Download
An if, an else if and an else
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number score = 72
    satellite.statement.if (score >= 90)
    {
        satellite.console.display("top marks")
    }
    satellite.statement.else satellite.statement.if (score >= 50)
    {
        satellite.console.display("a pass")
    }
    satellite.statement.else
    {
        satellite.console.display("try again")
    }
    satellite.return(satellite)
}
OUTPUT
a pass

satellite.statement.while

satellite.statement.while (condition) runs its body again and again for as long as the condition is true. The condition is read before every turn, so a condition that is false at the start runs the body no times at all.

while.satlSATELLITE 004 Download
Multiply by three until past 100
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number n = 1
    satellite.statement.while (n < 100)
    {
        satellite.console.display(n)
        n = n * 3
    }
    satellite.return(satellite)
}
OUTPUT
1
3
9
27
81

satellite.statement.for

satellite.statement.for counts. It has three parts between its brackets, divided by two semicolons:

  1. A declaration of its counter, always a satellite.variable.number: satellite.variable.number i = 0.
  2. A condition, read before every turn: i < 3. It cannot be left empty.
  3. A step, run after every turn. It is written without an =, because the loop puts the counter’s own name in front of it: i + 1, i - 4, i * 2, i / 3, i % 5, i ^ 2 or i ** 2, with a space on both sides of the sign, or i++ and i--. The step may be left empty, and then the body moves the counter itself.

The counter belongs to the loop: after the loop it is gone, and the same name can be declared again. There is no for over the items of a list today; satl refuses (satellite.variable.string name : names).

for.satlSATELLITE 004 Download
Counting up, counting down in fours, and squaring
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.statement.for (satellite.variable.number i = 1; i <= 3; i++)
    {
        satellite.console.display(i)
    }
    satellite.statement.for (satellite.variable.number i = 10; i > 0; i - 4)
    {
        satellite.console.display(i)
    }
    satellite.statement.for (satellite.variable.number i = 2; i < 300; i ^ 2)
    {
        satellite.console.display(i)
    }
    satellite.return(satellite)
}
OUTPUT
1
2
3
10
6
2
2
4
16
256
for_step.satlSATELLITE 004 Download
A step written with = is refused
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.statement.for (satellite.variable.number i = 1; i < 20; i = i + 1)
    {
        satellite.console.display(i)
    }
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S110: LINE_NOT_UNDERSTOOD
satl(check): in satellite.main, satellite.statement.for's step does not move i
-- a for's step is i and one of + - * / % ^ ** with a space on both sides, or
i++ or i--

directory: for_step.satl:5
syntax: satellite.statement.for (satellite.variable.number i = 1; i < 20; i = i + 1)
        /\

satellite has no scenario for this line -- it is spelled in a shape the language
does not have a meaning for.
machine code 13 satl_line_not_understood -- satl exits with this.

--------------------------------------------------------------------------------

OPERATORS

Operators introduces them one at a time. This is the complete list, with every rule.

The space rule

Every operator is written with a space on both sides of its sign: 4 + 3, never 4+3. That frees the touching spellings to mean something else:

  • A touching - in front of a number or a name makes it negative: -7, -n.
  • A touching / between two numbers makes a fraction: 1/3.
  • A touching % straight after digits makes a percentage: 50%.
  • A touching b or x in front of digits makes a binary or a hex: b1010, xFF.
spaces.satlSATELLITE 004 Download
A + with no spaces is refused
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display(4+3)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S110: LINE_NOT_UNDERSTOOD
satl(run): every math operation is written with a space on both sides of the
sign, and this + has none

directory: spaces.satl:5
syntax: satellite.console.display(4+3)
        /\

satellite has no scenario for this line -- it is spelled in a shape the language
does not have a meaning for.
machine code 13 satl_line_not_understood -- satl exits with this.

--------------------------------------------------------------------------------

+ – * / %

Add, take away, multiply, divide and remainder.

  • / between two whole numbers is whole-number division: 20 / 3 is 6. It cuts toward zero, so -7 / 2 is -3. Write a float, 10.0 / 4, to keep the part after the point.
  • % is what is left over, and it takes the sign of the number being divided: -7 % 2 is -1.
  • Dividing by zero, with / or %, is refused (S401).
  • A whole number has no size limit and never wraps around.
arithmetic.satlSATELLITE 004 Download
The five operations, with whole numbers and a float
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display(2 + 3)
    satellite.console.display(10 - 4)
    satellite.console.display(6 * 7)
    satellite.console.display(20 / 3)
    satellite.console.display(20 % 3)
    satellite.console.display(-7 / 2)
    satellite.console.display(-7 % 2)
    satellite.console.display(10.0 / 4)
    satellite.console.display(99999999999999999999999 + 1)
    satellite.return(satellite)
}
OUTPUT
5
6
42
6
2
-3
-1
2.5
100000000000000000000000
divide_by_zero.satlSATELLITE 004 Download
Dividing by zero is refused
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display(1 / 0)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S401: DIVISION_BY_ZERO
satl(run): the / of 1 and 0 is a division by zero

directory: divide_by_zero.satl:5
syntax: satellite.console.display(1 / 0)
                                    /\

a divisor worked out to 0. There is no number this could answer, so it answers
nothing rather than something.
machine code 22 division_by_zero -- satl exits with this.

--------------------------------------------------------------------------------

^ and **

^ is power, and ** is its second spelling. Both group right to left, so 2 ^ 3 ^ 2 is 2 ^ 9. A negative power answers a float, because a whole number cannot hold the answer: 2 ^ -1 is 0.5. The power must be a whole number; 2 ^ 0.5 is refused (S210). ^ is never “exclusive or” in satellite.

power.satlSATELLITE 004 Download
Powers, both spellings, right to left
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display(2 ^ 10)
    satellite.console.display(2 ** 10)
    satellite.console.display(2 ^ 3 ^ 2)
    satellite.console.display(2 ^ -1)
    satellite.console.display(2 ^ 100)
    satellite.return(satellite)
}
OUTPUT
1024
1024
512
0.5
1267650600228229401496703205376

== != < > <= >=

Equal, not equal, less than, greater than, less than or equal, greater than or equal. Each answers true or false.

  • Numbers compare with numbers, and a whole number, a float, a binary and a hex all compare by what they are worth: b1010 == 10 and 2.0 == 2 are both true.
  • Strings compare with strings, in satellite’s own character order: a to z, then A to Z, then the digits, then the signs, then the space. So "a" < "B" is true.
  • Two different kinds, such as a number and a string, are refused (S301).
  • Comparisons do not chain: 1 < 2 < 3 is refused.
compare.satlSATELLITE 004 Download
Comparisons answer true or false
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number n = 87
    satellite.console.display(n > 50)
    satellite.console.display(n == 87)
    satellite.console.display(n != 87)
    satellite.console.display(3 <= 3)
    satellite.console.display("apple" < "banana")
    satellite.console.display("a" < "B")
    satellite.console.display(b1010 == 10)
    satellite.console.display(2.0 == 2)
    satellite.return(satellite)
}
OUTPUT
true
true
false
true
true
true
true
true
compare_kinds.satlSATELLITE 004 Download
A number and a string do not compare
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display(5 == "5")
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S301: TYPES_DO_NOT_MEET
satl(run): a comparison was given a number and a string, and there is no
scenario for that pair

directory: compare_kinds.satl:5
syntax: satellite.console.display(5 == "5")
                                    /\

this operator has no scenario for the two kinds it was given. Nothing was
guessed at, because a guess here is an answer that is wrong and does not say so.
machine code 27 types_do_not_meet -- satl exits with this.

--------------------------------------------------------------------------------

!

! in front of a true-or-false turns it over: !raining is true when raining is false. It goes only in front of a satellite.bool or a comparison in brackets, !(3 > 2); in front of a number it is refused.

not.satlSATELLITE 004 Download
! turns true into false
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.bool raining = satellite.bool.false
    satellite.statement.if (!raining)
    {
        satellite.console.display("no umbrella needed")
    }
    satellite.console.display(!(3 > 2))
    satellite.return(satellite)
}
OUTPUT
no umbrella needed
false

&& || & | ~ << >> .and() .or() .xor()

None of these is part of satellite 004 today, and satl refuses every one: && and || in a condition, the bit operators &, |, ~, << and >>, and the methods .and(), .or(), .xor() and .not(). To ask for two things at once, put one if inside another. To ask for either of two, use else if.

both.satlSATELLITE 004 Download
Two conditions, one inside the other
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number age = 30
    satellite.statement.if (age >= 18)
    {
        satellite.statement.if (age < 65)
        {
            satellite.console.display("working age")
        }
    }
    satellite.return(satellite)
}
OUTPUT
working age
and_and.satlSATELLITE 004 Download
&& is refused
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number age = 30
    satellite.statement.if (age >= 18 && age < 65)
    {
        satellite.console.display("working age")
    }
    satellite.return(satellite)
}
OUTPUT
[satellite] satl(run): satellite.statement.if's condition could not be read to the end -- it stops at something with no meaning there yet (& | << >> !! are undecided), so the part before it is not the whole value (machine_code: 13 satl_line_not_understood)

+ between text and numbers

+ joins two strings, and a string with a number added to it joins the number as the text its own .string gives: "n = " + 4 is n = 4, and "f " + 1.5, "p " + 50%, "b " + b1010, "h " + x1F and "q " + 1/3 join the same way. With the number first, text that spells a number — whole, like "2", or with a decimal point, like "2.5" — IS that number, and + adds: 4 + "2" is the number 6 and 1.5 + "2" is 3.5. Any other text is joined after the number: 4 + "abc" is 4abc. A bool joins as true or false with one space between — "flag:" + satellite.bool.true is flag: true — and a colour or any other kind added to a string is refused (S301): write its .string.

join_text.satlSATELLITE 004 Download
Joining text to a number, said out loud
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number n = 4
    satellite.console.display("n = " + n.string)
    satellite.console.display("n = " + n.to_string())
    satellite.return(satellite)
}
OUTPUT
n = 4
n = 4
number_plus_text.satlSATELLITE 004 Download
A number + a string: text that is a number adds, other text joins
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number n = 4
    satellite.console.display(n + "2")
    satellite.console.display(1.5 + "2")
    satellite.console.display(n + " apples")
    satellite.return(satellite)
}
OUTPUT
6
3.5
4 apples

The order of operations

When a line has more than one operator, satellite works them out in this order:

  1. Brackets, from the inside out.
  2. A touching - and a !, which belong to the value they touch. So -2 ^ 2 is 4; write 0 - 2 ^ 2 for -4.
  3. ^ and **, right to left.
  4. *, / and %, left to right.
  5. + and -, left to right.
  6. The comparisons, last of all, so 1 + 2 < 2 + 2 compares 3 with 4.
order.satlSATELLITE 004 Download
Which operation goes first
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display(2 + 3 * 4)
    satellite.console.display((2 + 3) * 4)
    satellite.console.display(10 - 3 - 2)
    satellite.console.display(2 * (3 + 4) ^ 2)
    satellite.console.display(-2 ^ 2)
    satellite.console.display(0 - 2 ^ 2)
    satellite.console.display(1 + 2 < 2 + 2)
    satellite.return(satellite)
}
OUTPUT
14
20
5
98
4
-4
true

=

name = value gives a new value to a name already declared. The new value must be the name’s own kind; see satellite.variable, below. There is one spelling: n += 1, -=, *=, /= and %= are refused by name (S210), and so is n++ outside a for‘s step. Write n = n + 1.

assign.satlSATELLITE 004 Download
Declaring a name, then changing it
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number total
    total = 10
    total = total + 5
    satellite.console.display(total)
    satellite.return(satellite)
}
OUTPUT
15
plus_equals.satlSATELLITE 004 Download
+= is refused
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number total = 10
    total += 5
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S210: NOT_BUILT_YET
satl(check): in satellite.main, total += ... is not built yet -- write total =
total + ...

directory: plus_equals.satl:6
syntax: total += 5
        /\

this word is numbered in the language and the library behind it does not exist
yet. The word is right; there is nothing yet to run.
machine code 14 not_built_yet -- satl exits with this.

--------------------------------------------------------------------------------

VALUES

satellite.variable

A name is declared with its kind in front of it: satellite.variable.number count = 3. The value is optional; a name declared without one holds nothing until it is given one, and showing nothing is refused.

The kinds described here are satellite.variable.bool, which holds satellite.bool.true or satellite.bool.false, and satellite.variable.number, .string, .float, .infinity, .fraction, .percent, .binary, .hex and .color. Lists, indexes and the other containers, files, windows and threads come later on this page.

  • A name is declared once in a capsule. A second declaration is refused (S202).
  • A name used without a declaration is refused (S201).
  • A name holds only its own kind. Giving it another is refused (machine code 27), with a few friendly exceptions listed below, under converting between values.
  • satellite.variable on its own, with no kind, is refused.
declare.satlSATELLITE 004 Download
Three names of three kinds
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number count = 3
    satellite.variable.string name = "Ada"
    satellite.variable.bool ready = satellite.bool.true
    satellite.console.display(count)
    satellite.console.display(name)
    satellite.console.display(ready)
    satellite.return(satellite)
}
OUTPUT
3
Ada
true
wrong_kind.satlSATELLITE 004 Download
A number name refuses a string
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number count = "three"
    satellite.return(satellite)
}
OUTPUT
[satellite] satl(run): count was declared satellite.variable.number, and it holds a string (machine_code: 27 types_do_not_meet)

satellite.bool

satellite.bool.true and satellite.bool.false are the two truth values, and satellite.variable.bool is a name that holds one. Every comparison answers one, so satellite.variable.bool ready = 3 > 2 holds true.

  • == and != compare two bools, and ! turns one over. < and > are refused: there is no order between true and false.
  • A bool is not a number and not a string: satellite.bool.true + 1 and "a" + satellite.bool.true are refused, and a bool name refuses 1 and "true".
  • .string (also .to_string and .str) is its text, true or false. Write it on a name, ready.string, or on something in brackets, (3 > 2).string; straight after the word, satellite.bool.true.string is refused. .number, .binary, .hex and .reverse() are refused.
bool.satlSATELLITE 004 Download
True, false, and a comparison kept in a name
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.bool ready = 3 > 2
    satellite.console.display(ready)
    satellite.console.display(satellite.bool.false)
    satellite.console.display(ready == satellite.bool.true)
    satellite.console.display(!ready)
    satellite.return(satellite)
}
OUTPUT
true
false
true
false

satellite.variable.number

A whole number of any size. You write it as digits, with a touching - in front for below zero: 34587, -7. It has no size limit but your computer’s memory, and it never wraps around. A number with a point in it, 12.34, is a float, and a number name refuses one, even 2.0. Numbers is the gentle introduction.

Its operators are all of the ones above. Its methods:

  • .string (also .to_string and .str): its digits as text.
  • .number (also .to_number and .as_number): the number itself.
  • .binary (also .bin and .as_binary): its base-2 digits as text.
  • .hex (also .as_hex and .to_hex): its base-16 digits as text.
  • .reverse(): its digits turned round, sign kept.
  • .add(x): the same as + x.

A method that takes nothing may be written with or without (), and methods chain left to right: n.binary.find("111"). A number in brackets takes methods too: (12).binary.

These words are numbered in the language but have nothing behind them yet, and satl refuses them: .max, .min, .abs, .clamp, .floor, .ceil, .round, .truncate, .sqrt, .modulus, .digits, .shift_left, .shift_right, .power (and .power_of, .to_the_power_of), and satellite.variable.number(x) as a call.

number.satlSATELLITE 004 Download
A number and every method it has
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number n = 87
    satellite.console.display(n + 13)
    satellite.console.display(n.string)
    satellite.console.display(n.binary)
    satellite.console.display(n.hex)
    satellite.console.display(n.reverse())
    satellite.console.display(n.add(13))
    satellite.console.display(-n)
    satellite.console.display(2 ^ 70)
    satellite.return(satellite)
}
OUTPUT
100
87
1010111
57
78
100
-87
1180591620717411303424
round.satlSATELLITE 004 Download
.round is not a method of a number
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number n = 87
    satellite.console.display(n.round)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S110: LINE_NOT_UNDERSTOOD
satl(check): in satellite.main, n is satellite.variable.number, and round is not
one of its methods -- only an object of a satellite.spacesuit has capsules to
call

directory: round.satl:6
syntax: satellite.console.display(n.round)
        /\

satellite has no scenario for this line -- it is spelled in a shape the language
does not have a meaning for.
machine code 13 satl_line_not_understood -- satl exits with this.

--------------------------------------------------------------------------------

satellite.variable.string

Text, written between double quotes: "hello world". satellite keeps a string as characters, not bytes, so an accent or an arrow is one character, and .reverse() turns a string round by character. Six escapes are read inside a string: \" a double quote, \\ a backslash, \n a new line, \t a tab, \r a carriage return and \' an apostrophe. Any other backslash stays in the text as it is written, so "a\qb" is a, a backslash, q and b. Strings is the gentle introduction.

Its operators:

  • + joins two strings.
  • - takes out the first place the right-hand string occurs, and leaves the string as it was when it is not there: "banana" - "an" is "bana".
  • == and != compare two strings, and <, >, <= and >= put them in satellite’s character order: a to z, then A to Z, then the digits, then the signs, then the space.

Its methods:

  • .find(x): where x first begins, counting from 0, so "abc".find("b") is 1. A string that is not there is refused (S420) rather than answered with -1.
  • .add(x): the same as + x.
  • .reverse(): the characters back to front.
  • .string (also .str and .to_string): the text, unchanged.
  • .number (also .to_number and .as_number): reads a whole number out of the text, and refuses anything else, "3.5" and " 42" included (S120).
  • .binary (also .bin and .as_binary) and .hex (also .as_hex and .to_hex): read that same whole number and answer its base-2 or base-16 digits as text. So "1010".bin is the bits of one thousand and ten, not of ten.
  • .foreground(c) and .background(c): the string in a colour. See below.

These words are numbered but have nothing behind them yet, and satl refuses them: .size, .empty, .contains (and .contain), .replace(a, b), .append(x), .clear, .at(n), .substring(start, end), .starts_with(x), .ends_with(x), .lower, .upper, .split(separator), .trim, .resolved, reading one character with s[n], and satellite.variable.string(x) as a call.

string.satlSATELLITE 004 Download
A string, its operators and its methods
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.string s = "hello world"
    satellite.console.display(s + "!")
    satellite.console.display(s.add("!"))
    satellite.console.display(s - "o")
    satellite.console.display(s.find("world"))
    satellite.console.display(s.reverse())
    satellite.console.display("héllo→".reverse())
    satellite.console.display("42".number + 1)
    satellite.console.display("255".hex)
    satellite.console.display(s == "hello world")
    satellite.console.display("a" < "B")
    satellite.console.display("tab\there")
    satellite.return(satellite)
}
OUTPUT
hello world!
hello world!
hell world
6
dlrow olleh
→olléh
43
FF
true
true
tab	here
find_missing.satlSATELLITE 004 Download
.find refuses text that is not there
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.string s = "hello world"
    satellite.console.display(s.find("moon"))
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S420: TEXT_NOT_FOUND
satl(run): s.find did not find it

directory: find_missing.satl:6
syntax: satellite.console.display(s.find("moon"))
        /\

the text looked for is not in this string.
machine code 15 text_not_found -- satl exits with this.

--------------------------------------------------------------------------------

.foreground(c) and .background(c)

"OK".foreground(x00FF00) is the string OK drawn in green, and .background(c) sets the colour behind it. c is six hex digits with their x, like xFF8800, or a satellite.variable.color. The colour becomes part of the string, as the terminal’s own colour codes, so .find and == see them: a coloured "OK" is not equal to a plain one. satellite.console.display shows the colours on a terminal, and leaves them out when its output goes into a file or another program, which is why the box below shows plain text. Color on the console has the rest.

string_colour.satlSATELLITE 004 Download
Coloured text (plain here, coloured at a terminal)
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.color warning = xFF8800
    satellite.console.display("status: " + "OK".foreground(x00FF00))
    satellite.console.display("careful".foreground(warning).background(x000000))
    satellite.console.display("OK".foreground(x00FF00) == "OK")
    satellite.return(satellite)
}
OUTPUT
status: OK
careful
false

satellite.variable.float

A number with a point in it: 12.34, 0.5, -2.75. satellite.variable.double is its second spelling. There is no scientific notation (1.0e5 is refused), a float needs a digit on both sides of its point (.5 and 5. are refused), and there is no limit on its size. Floats is the gentle introduction.

  • A float is decimal all the way down, so nothing is rounded that you did not ask for: 0.1 + 0.2 == 0.3 is true.
  • It keeps 128 places after the point and 4096 digits before it. Those are arguments.float.decimal and arguments.float.whole, and you can change either for your machine in ~/.satl/config.ini, with a line such as float.decimal = 4096; any count of 1 or more works. Where a result has more places than that, the last one kept is rounded half away from zero.
  • A float shows at most 32 places, however many it holds. 12.50 shows 12.5, and 2.0 shows 2.0.
  • + - * / % and ^ work between two floats, and between a float and a whole number either way round. 4 / 3 is still whole-number division; write 4.0 / 3 for the float. ^ takes a whole-number power only.
  • A whole number given to a float name becomes a float: satellite.variable.float f = 3 holds 3.0. A float given to a number name is refused.
  • .string (also .to_string and .str) is its text. .number (also .to_number and .as_number) is the whole number when there is nothing after the point; 12.5.number is refused (S402), because satellite rounds nothing on its own.
  • Not built yet, and refused: .binary, .hex, .reverse() and .add(x) on a float, and a float meeting a percentage, a binary, a hex, a fraction or an infinity.
float.satlSATELLITE 004 Download
Floats, and how many places they keep
satellite.include(satellite)

satellite.capsule satellite.main(satellite.variable.arguments arguments)
{
    satellite.variable.float price = 12.05
    satellite.variable.double half = 0.5
    satellite.console.display(price * 2)
    satellite.console.display(price + half)
    satellite.console.display(4.0 / 3)
    satellite.console.display(4 / 3)
    satellite.console.display(0.1 + 0.2 == 0.3)
    satellite.console.display(12.50)
    satellite.variable.float whole = 3
    satellite.console.display(whole)
    satellite.console.display(whole.number)
    satellite.console.display(arguments.float.decimal)
    satellite.return(satellite)
}
OUTPUT
24.1
12.55
1.33333333333333333333333333333333
1
true
12.5
3.0
3
128
float_number.satlSATELLITE 004 Download
12.5 has no whole number to give
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display(12.5.number)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S402: ANSWER_IS_NOT_WHOLE
satl(run): that float.number: 12.5 is not a whole number, so there is no number
to make of it -- satellite rounds nothing on its own

directory: float_number.satl:5
syntax: satellite.console.display(12.5.number)
        /\

the answer exists and is not a whole number -- 2 ^ -1 is one half.
satellite.variable.number holds whole numbers, so this has nowhere to go.
machine code 24 answer_is_not_whole -- satl exits with this.

--------------------------------------------------------------------------------

satellite.variable.infinity and satellite.infinity()

satellite.infinity() makes an infinity, and satellite.variable.infinity is a name that holds one. It shows as (infinity), and a minus in front turns it over: (-infinity). Infinity is the gentle introduction.

  • It is larger than every number, and (-infinity) is smaller than every number. It compares with == != < > <= >= against a whole number, a binary and another infinity.
  • An infinity name also takes a plain whole number. It refuses a string, a binary, a hex, a float and a percentage, and a number name refuses an infinity.
  • .string is how it shows. .number, .binary and .hex are refused, because an infinity is larger than every number, and .reverse() is refused because it has no digits to turn round.
  • Not built yet, and refused (S210): arithmetic with an infinity (+ - * / ^ **), .power_of(x), .resize(n), .nines(), satellite.infinity(x) with something in its brackets, and an infinity meeting a float or a hex.
infinity.satlSATELLITE 004 Download
An infinity, turned over and compared
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.infinity sky = satellite.infinity()
    satellite.console.display(sky)
    satellite.console.display(-sky)
    satellite.console.display(sky > 10 ^ 100)
    satellite.console.display(-sky < 0 - 10 ^ 300)
    satellite.console.display(b1010 < sky)
    satellite.console.display(sky == -sky)
    satellite.console.display(sky.string)
    satellite.return(satellite)
}
OUTPUT
(infinity)
(-infinity)
true
true
true
false
(infinity)
infinity_number.satlSATELLITE 004 Download
An infinity has no number
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display(satellite.infinity().number)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S301: TYPES_DO_NOT_MEET
satl(run): satellite.infinity.number was written on an infinity, and an infinity
is larger than every number -- there is no number to make of it

directory: infinity_number.satl:5
syntax: satellite.console.display(satellite.infinity().number)
        /\

this operator has no scenario for the two kinds it was given. Nothing was
guessed at, because a guess here is an answer that is wrong and does not say so.
machine code 27 types_do_not_meet -- satl exits with this.

--------------------------------------------------------------------------------

satellite.variable.fraction

Two numbers tied together, one above the line and one below it. You write it with a slash that touches both numbers: 1/3. That is the whole difference between a fraction and division: 1 / 3, with spaces, is whole-number division and answers 0. Fractions is the gentle introduction.

  • A fraction stays as it was written: 2/4 shows 2/4. But two fractions compare by what they are worth, so 2/4 == 1/2 is true and 1/3 < 1/2 is true. A fraction also compares with a whole number: 1/3 < 1 is true.
  • Each part may have a point in it: 1.5/2 shows 1.5/2.
  • The sign goes on top: -1/3. A bottom number of 0 is refused (S401).
  • A whole number given to a fraction name becomes that number over 1: 3 shows 3/1.
  • .numerator and .denominator are its two parts, and .string is how it shows.
  • Not built yet, and refused: arithmetic with a fraction (1/3 + 1/3), a fraction of two names (a/b), a fraction meeting a float or a percentage, and .number, .binary, .hex and .reverse().
fraction.satlSATELLITE 004 Download
Fractions, written, shown and compared
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.fraction third = 1/3
    satellite.variable.fraction half = 2/4
    satellite.console.display(third)
    satellite.console.display(half)
    satellite.console.display(half == 1/2)
    satellite.console.display(third < half)
    satellite.console.display(third.numerator)
    satellite.console.display(third.denominator)
    satellite.console.display(1.5/2)
    satellite.console.display(-1/3)
    satellite.console.display(1 / 3)
    satellite.variable.fraction whole = 3
    satellite.console.display(whole)
    satellite.return(satellite)
}
OUTPUT
1/3
2/4
true
true
1
3
1.5/2
-1/3
0
3/1
fraction_spaced.satlSATELLITE 004 Download
A spaced slash is division, not a fraction
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.fraction third = 1 / 3
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S301: TYPES_DO_NOT_MEET
satl(check): in satellite.main, 1 / 3 is whole-number division -- a fraction is
written with a touching slash: 1/3

directory: fraction_spaced.satl:5
syntax: satellite.variable.fraction third = 1 / 3
        /\

this operator has no scenario for the two kinds it was given. Nothing was
guessed at, because a guess here is an answer that is wrong and does not say so.
machine code 27 types_do_not_meet -- satl exits with this.

--------------------------------------------------------------------------------

satellite.variable.percent

A percentage, written with its % touching the digits: 50%, 12.5%, -50%, 1000000000000%. The word is satellite.variable.percentage, and satellite.variable.percent is its second spelling; both declare the same kind. Percentages is the gentle introduction.

  • It keeps exactly 32 places after the point, so 0.1% + 0.2% is 0.3% and 100% / 3 is 33.33333333333333333333333333333333%. There is no ceiling above.
  • The % is required. A percentage name given 50 is refused before anything runs: ERROR: expected 50%.
  • A percentage compares only with another percentage: 50% < 75% is true, and 50% == 50 is refused.

What each pair answers:

  • 50% + 25% is 75%, and 50% - 25% is 25%: two percentages stay a percentage.
  • 50% * 50% is 25%: a percentage of a percentage.
  • 50% / 4 is 12.5%: a share of it.
  • 200 * 50% and 50% * 200 are both 100: a number and a percentage answer a number.
  • 200 - 50% is 100 (reduced by it), and 200 + 50% is 300 (grown by it).
  • 200 / 50% is 400: what 200 is 50% of.
  • For + and - the number goes first. 50% + 5 is refused: write 5 + 50%.
  • % and ^ with a percentage are refused, and so is 50% / 50%.
  • An answer that is not a whole number, such as 3 * 50%, is refused (S402), and so is a percentage meeting a float.
  • A percentage name holds only a percentage, so half = half * 2 is refused when it runs, because that answers a number.
  • .string is its text. .number, .binary, .hex and .reverse() are refused.
percent.satlSATELLITE 004 Download
Percentages with numbers and with each other
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.percent half = 50%
    satellite.variable.percentage tax = 20%
    satellite.console.display(half)
    satellite.console.display(200 * half)
    satellite.console.display(200 - half)
    satellite.console.display(80 + tax)
    satellite.console.display(half + 25%)
    satellite.console.display(half / 4)
    satellite.console.display(100% / 3)
    satellite.console.display(half < 75%)
    half = half - 75%
    satellite.console.display(half)
    satellite.return(satellite)
}
OUTPUT
50%
100
100
96
75%
12.5%
33.33333333333333333333333333333333%
true
-25%
percent_first.satlSATELLITE 004 Download
For + and – the number goes first
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display(50% + 5)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S301: TYPES_DO_NOT_MEET
satl(run): 50% + 5: a percentage is taken off or added onto a number, so the
number goes first -- 5 + 50%

directory: percent_first.satl:5
syntax: satellite.console.display(50% + 5)
                                      /\

this operator has no scenario for the two kinds it was given. Nothing was
guessed at, because a guess here is an answer that is wrong and does not say so.
machine code 27 types_do_not_meet -- satl exits with this.

--------------------------------------------------------------------------------

satellite.variable.binary

A number written in bits, with a b in front: b1010. satellite.variable.bin is its second spelling. Binary and hexadecimal is the gentle introduction.

  • The width is part of the value. A binary shows exactly as you wrote it, leading zeros and all, and b0011 is not b11. There is no length limit.
  • The b is required. A binary name given 1010, or C’s 0b1010, is refused before anything runs: ERROR: expected b1010. b12 is refused: a binary digit is 0 or 1.
  • A binary keeps a sign: -b0101 is worth -5, keeps its width, and is not equal to b0101. -b0000 is b0000, because zero has no sign.
  • Arithmetic reads a binary by what it is worth, and answers a plain number: b1000 + b0101 is 13, and b1010 == 10 is true. A number name given b0011 holds 3. Keeping a sum in a binary name is refused, because the sum is a number.
  • .number (also .to_number and .as_number) is what the bits are worth. .string (also .to_string and .str) is the binary as text, b and all. .binary (also .bin and .as_binary) is the digits without the b. .hex (also .as_hex and .to_hex) is its worth in base 16, as text. .reverse() turns the bits round and keeps the width. .add(x) is + x.
  • Refused: .width, .digits and .clear, reading one bit with [n], and a binary meeting a float.
binary.satlSATELLITE 004 Download
A binary keeps its width and its sign
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.binary flags = b0010
    satellite.console.display(flags)
    satellite.console.display(flags == b10)
    satellite.console.display(flags.number)
    satellite.console.display(flags.string)
    satellite.console.display(flags.bin)
    satellite.console.display(b10101010.hex)
    satellite.console.display(b1000.reverse())
    satellite.console.display(b1000 + b0101)
    satellite.variable.bin below = -b0101
    satellite.console.display(below)
    satellite.console.display(below.number)
    satellite.console.display(below < b0001)
    satellite.return(satellite)
}
OUTPUT
b0010
false
2
b0010
0010
AA
b0001
13
-b0101
-5
true
binary_without_b.satlSATELLITE 004 Download
A binary name needs its b
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.binary flags = 1010
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S301: TYPES_DO_NOT_MEET
satl(check): in satellite.main, ERROR: expected b1010

directory: binary_without_b.satl:5
syntax: satellite.variable.binary flags = 1010
        /\

this operator has no scenario for the two kinds it was given. Nothing was
guessed at, because a guess here is an answer that is wrong and does not say so.
machine code 27 types_do_not_meet -- satl exits with this.

--------------------------------------------------------------------------------

satellite.variable.hex

A number written in base 16, with an x in front: x1F, x00FF, xDEADBEEF. satellite.variable.hexadecimal is its second spelling. The digits are 0 to 9 and A to F; a lower-case digit is read the same, and always shown in capitals.

  • The x is required. A hex name given 1F is refused before anything runs: ERROR: expected x1F. C’s 0xFF is refused too.
  • A hex keeps the width it was written with, so x00FF and xFF are two different values worth the same. It keeps a sign: -x1F is worth -31.
  • In a sum a hex counts as what its digits are worth, and the answer is a number: x1F + 1 is 32, and x1F == 31 is true. So a hex name holds only a hex: h = h + x01 is refused, and the refusal tells you what to write instead. A hex given to a number name keeps its worth.
  • .number (also .to_number and .as_number) is what it is worth. .string (also .to_string and .str) is how it shows. .hex (also .as_hex and .to_hex) is its digits, without the x. .binary (also .bin and .as_binary) is its bits, four for every digit. .width counts those bits. .reverse() turns its digits round, width and sign kept. .add(x) is + x.
  • Refused: .digits, .clear and .to_binary, and a hex meeting a float, a percentage or a colour.
hex.satlSATELLITE 004 Download
A hex, its width, and its worth
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.hex mask = x00ff
    satellite.variable.hexadecimal red = xFF
    satellite.console.display(mask)
    satellite.console.display(mask + 1)
    satellite.console.display(mask == red)
    satellite.console.display(mask.number)
    satellite.console.display(mask.hex)
    satellite.console.display(mask.binary)
    satellite.console.display(mask.width)
    satellite.console.display(mask.reverse())
    satellite.console.display(-x1F)
    satellite.return(satellite)
}
OUTPUT
x00FF
256
false
255
00FF
0000000011111111
16
xFF00
-x1F
hex_sum.satlSATELLITE 004 Download
A sum is a number, so a hex name refuses it
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.hex h = x01
    h = h + x01
    satellite.return(satellite)
}
OUTPUT
[satellite] satl(run): h was declared satellite.variable.hex, and it was given the number 2 -- a hex name holds only a hex, and arithmetic on a hex answers a number, so keep that in a satellite.variable.number or write the hex it should be: x2 (machine_code: 27 types_do_not_meet)

satellite.variable.color

A colour: exactly six hex digits, two each for red, green and blue. satellite.variable.colour is its second spelling. It shows in capitals with its x: x87CEEB. Colors is the gentle introduction.

  • On the line that declares it, the x is optional: 000000, x000000, ff00aa and 00ff00 all work. Five or seven digits are refused before anything runs.
  • On a later line, c = x00ff00 always works, and so do c = 123456 and c = abcdef. Digits that start with a number and hold a letter, like c = 00ff00, need their x, and the refusal says so.
  • A colour can be see-through. Its transparency is a whole number from 0 (solid) to 99 (almost clear), set with c.transparency(50) or with a comma after the colour: c = x000000, 50. c.transparency reads it back, and a colour with one shows as x000000, 50. 100 or more, or below 0, is refused.
  • == and != compare the digits and the transparency. A colour has no order, no arithmetic and no sign.
  • .number is what the six digits are worth, .hex is the six digits, .binary is its 24 bits, and .string is how it shows. Each has the same second spellings as a number’s.
color.satlSATELLITE 004 Download
A colour, see-through, and its conversions
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.color sky = 87ceeb
    satellite.variable.colour ink = x000000, 50
    satellite.console.display(sky)
    satellite.console.display(ink)
    sky.transparency(40)
    satellite.console.display(sky.transparency)
    satellite.console.display(sky)
    satellite.console.display(sky.number)
    satellite.console.display(sky.hex)
    satellite.console.display(sky.binary)
    satellite.console.display(sky == ink)
    satellite.return(satellite)
}
OUTPUT
x87CEEB
x000000, 50
40
x87CEEB, 40
8900331
87CEEB
100001111100111011101011
false
color_digits.satlSATELLITE 004 Download
A colour is exactly six hex digits
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.color sky = 87ceb
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S301: TYPES_DO_NOT_MEET
satl(check): in satellite.main, ERROR: 87ceb is 5 hex digits -- a color is
exactly six hex digits, like x00FF00

directory: color_digits.satl:5
syntax: satellite.variable.color sky = 87ceb
        /\

this operator has no scenario for the two kinds it was given. Nothing was
guessed at, because a guess here is an answer that is wrong and does not say so.
machine code 27 types_do_not_meet -- satl exits with this.

--------------------------------------------------------------------------------

CONVERTING BETWEEN VALUES

satellite converts nothing behind your back. When you want a value as another kind, you ask for it with a method, and the method’s name says what you get. Four methods do all the converting. Each has three spellings, every spelling works on every kind that has the conversion, and any of them may be written with or without ().

.string

.string, also spelled .to_string and .str, answers a value as text, exactly as satellite.console.display would show it. Every kind of value described here has one.

to_string.satlSATELLITE 004 Download
Every kind of value, as text
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.color sky = 87ceeb
    satellite.console.display((87).string + "!")
    satellite.console.display(12.5.string + "!")
    satellite.console.display(b0010.string + "!")
    satellite.console.display(x00FF.string + "!")
    satellite.console.display((1/3).string + "!")
    satellite.console.display((50%).string + "!")
    satellite.console.display(satellite.infinity().string + "!")
    satellite.console.display(sky.string + "!")
    satellite.return(satellite)
}
OUTPUT
87!
12.5!
b0010!
x00FF!
1/3!
50%!
(infinity)!
x87CEEB!

.number

.number, also spelled .to_number and .as_number, answers a whole number you can do sums with.

  • On a string, it reads the text as a whole number, sign included. Anything else is refused (S120), a point or a space included.
  • On a binary, a hex or a colour, it is what the digits are worth.
  • On a float, it is the whole number when there is nothing after the point, and refused otherwise (S402).
  • On a fraction, a percentage, an infinity or a bool, it is refused.
to_number.satlSATELLITE 004 Download
Whole numbers out of text, bits, digits and colours
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.color sky = 87ceeb
    satellite.console.display("42".number + 1)
    satellite.console.display("-42".number)
    satellite.console.display(b0010.number + 1)
    satellite.console.display(x00FF.number + 1)
    satellite.console.display(sky.number)
    satellite.console.display(12.0.number)
    satellite.return(satellite)
}
OUTPUT
43
-42
3
256
8900331
12
text_not_a_number.satlSATELLITE 004 Download
"3.5" is not a whole number
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display("3.5".number)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S120: NUMBER_NOT_READ
satl(run): that string.number: that text is not a whole number this can read

directory: text_not_a_number.satl:5
syntax: satellite.console.display("3.5".number)
        /\

this is not a number satellite can read. A number is an optional '-' then
digits, with no spaces or separators, and a float has one point in it: 12.34.
machine code 3 int_error -- satl exits with this.

--------------------------------------------------------------------------------

.binary

.binary, also spelled .bin and .as_binary, answers base-2 digits as text, with no b in front. It does not make a satellite.variable.binary.

  • On a number, its bits. On a string, the bits of the whole number the text holds.
  • On a binary, its digits without the b, width kept. On a hex, four bits for every digit. On a colour, its 24 bits.
  • On a float, a fraction, a percentage, an infinity or a bool, it is refused.
to_binary.satlSATELLITE 004 Download
Base-2 text from four kinds of value
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.color sky = 87ceeb
    satellite.console.display((12).binary)
    satellite.console.display("42".bin)
    satellite.console.display(b0010.binary)
    satellite.console.display(x00FF.binary)
    satellite.console.display(sky.binary)
    satellite.console.display((87).binary.find("111"))
    satellite.return(satellite)
}
OUTPUT
1100
101010
0010
0000000011111111
100001111100111011101011
4

.hex

.hex, also spelled .as_hex and .to_hex, answers base-16 digits as text, in capitals, with no x in front. It does not make a satellite.variable.hex.

  • On a number, its base-16 digits. On a string, those of the whole number the text holds.
  • On a binary, its worth in base 16. On a hex, its digits without the x, width kept. On a colour, its six digits.
  • On a float, a fraction, a percentage, an infinity or a bool, it is refused.
to_hex.satlSATELLITE 004 Download
Base-16 text from five kinds of value
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.color sky = 87ceeb
    satellite.console.display((255).hex)
    satellite.console.display("255".to_hex)
    satellite.console.display(b10101010.hex)
    satellite.console.display(x00ff.hex)
    satellite.console.display(sky.hex)
    satellite.return(satellite)
}
OUTPUT
FF
FF
AA
00FF
87CEEB

Giving a value to a name of another kind

A name holds only its own kind, but a few values fit another kind’s name without any loss, and those are taken:

  • A number name given a binary or a hex keeps what it is worth: b0011 becomes 3.
  • A float name given a whole number holds it as a float: 3 becomes 3.0.
  • A fraction name given a whole number holds it over 1: 3 becomes 3/1.
  • An infinity name given a whole number holds that number.

Everything else is refused (machine code 27), including a float given to a number name, even 2.0.

across.satlSATELLITE 004 Download
Values that fit another kind's name
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number from_bits = b0011
    satellite.variable.number from_hex = xFF
    satellite.variable.float from_whole = 3
    satellite.variable.fraction over_one = 3
    satellite.variable.infinity plain = 5
    satellite.console.display(from_bits)
    satellite.console.display(from_hex)
    satellite.console.display(from_whole)
    satellite.console.display(over_one)
    satellite.console.display(plain)
    satellite.return(satellite)
}
OUTPUT
3
255
3.0
3/1
5

The call forms

Converting is written as a method on the value, never as a call around it. string(87), number("42"), binary(87) and hex(87) are refused, because satl looks for a capsule of yours by that name. satellite.variable.number(x), satellite.variable.string(x), satellite.variable.binary(x) and satellite.variable.hex(x) are numbered words with nothing behind them yet, and satl refuses them too (S210). Write (87).string, "42".number, (87).binary and (87).hex.

call_form.satlSATELLITE 004 Download
string(87) looks for a capsule of yours
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display(string(87))
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S110: LINE_NOT_UNDERSTOOD
satl(check): in satellite.main, no capsule named string

directory: call_form.satl:5
syntax: satellite.console.display(string(87))
        /\

satellite has no scenario for this line -- it is spelled in a shape the language
does not have a meaning for.
machine code 13 satl_line_not_understood -- satl exits with this.

--------------------------------------------------------------------------------

CONTAINERS

A container holds other values. There is one family of them, and you always say which kind you want. Remember the language’s rule while you read: a dotted path that starts with satellite names something the language owns, like satellite.container.list, and a bare name, like names, is something you own.

satellite.container

satellite.container is the family name, not a type. A line that declares a plain satellite.container is refused, so you always name the kind:

  • satellite.container.list: values in a row, counted from 1.
  • satellite.container.map: keys and the values filed under them, kept in the order they went in. satellite.container.index is a second word for the same container.
  • satellite.container.multiple: one name that may hold any one of several types.

satellite.container.arguments and satellite.container.result are numbered in the language but not built yet. A line that declares one is refused with S110 before anything runs.

These rules hold for every container:

  • Braces make a list wherever a value goes: {1, 2} is a list of two, and {} is an empty one. With key: value entries they make a map: {"zoe": 30, "al": 4}.
  • A container can hold any other container, to any depth: a list of lists, a map of lists, a list of maps of lists. satellite.access(name) shows the way into every level of one.
  • What you write between < and > says what a container holds, and it is checked every time something goes in. With no <>, a container holds anything.
  • A container is a value, not a reference. b = a copies it, so changing b leaves a alone. The copy costs nothing until one of them is written to.
  • == and != compare what two containers hold. < and > are refused, because there is no order between two containers.
container_rules.satlSATELLITE 004 Download
A copy is its own list, and == compares what two lists hold
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list a = {1, 2}
    satellite.container.list b = a
    b.append(3)
    satellite.console.display(a)
    satellite.console.display(b)
    satellite.console.display(b == {1, 2, 3})
    satellite.console.display(a != b)
    satellite.return(satellite)
}
OUTPUT
{1, 2}
{1, 2, 3}
true
true
no_order.satlSATELLITE 004 Download
Two lists have no order, so < is refused
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list a = {1, 2}
    satellite.container.list b = {3}
    satellite.console.display(a < b)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S301: TYPES_DO_NOT_MEET
satl(run): < was given two lists, and only == and != compare those -- there is
no order between two containers

directory: no_order.satl:7
syntax: satellite.console.display(a < b)
                                    /\

this operator has no scenario for the two kinds it was given. Nothing was
guessed at, because a guess here is an answer that is wrong and does not say so.
machine code 27 types_do_not_meet -- satl exits with this.

--------------------------------------------------------------------------------

More in LISTS.

satellite.container.list

A list is a row of values, kept in the order you put them in. You write one with braces: {1, 2, 3} is a list, {} is a list of nothing, {1, "two"} is a list of mixed items, and {{1, 2}, {3}} is a list of lists.

How it is written:

  • satellite.container.list name = {...} holds anything.
  • satellite.container.list<satellite.variable.string> name = {...} holds strings only, and every way in is checked: the declaration, a[n] = x, .append and .insert.

Items count from 1, the same way a file’s lines do. There is no for-each loop yet, so you walk a list with satellite.statement.for and a[i].

list_walk.satlSATELLITE 004 Download
Declare a list, add to it, and walk it with a for loop
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list<satellite.variable.string> names = {"zoe", "al"}
    names.append("bo")
    satellite.console.display(names)
    satellite.console.display(names[1])
    satellite.console.display(names.size)

    satellite.container.list row = {30, 4, 100}
    satellite.statement.for(satellite.variable.number i = 1; i <= row.size; i++)
    {
        satellite.console.display(row[i] + 1)
    }
    satellite.return(satellite)
}
OUTPUT
{"zoe", "al", "bo"}
zoe
3
31
5
101

A list that says what it holds refuses anything else:

typed_list.satlSATELLITE 004 Download
A list of numbers refuses a string
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list<satellite.variable.number> scores = {10, 20}
    scores.append("thirty")
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S301: TYPES_DO_NOT_MEET
satl(run): in scores, scores.append: it holds a string

directory: typed_list.satl:6
syntax: scores.append("thirty")
        /\

this operator has no scenario for the two kinds it was given. Nothing was
guessed at, because a guess here is an answer that is wrong and does not say so.
machine code 27 types_do_not_meet -- satl exits with this.

--------------------------------------------------------------------------------

satellite.container.list()

satellite.container.list() is {} said in words: a list of nothing. It works anywhere a value goes, in a capsule or as a spacesuit’s field, and it takes nothing between its brackets. A list that holds something is written with braces, so satellite.container.list(1, 2) is refused before anything runs.

list_constructor.satlSATELLITE 004 Download
An empty list, made in words and then filled
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list<satellite.variable.number> held = satellite.container.list()
    satellite.console.display(held)
    held.append(5)
    held.append(6)
    satellite.console.display(held)
    satellite.return(satellite)
}
OUTPUT
{}
{5, 6}

a[n] and a[n] = x

a[n] reads item n, and a[n] = x replaces it. Counting starts at 1. What goes between the brackets can be worked out, so a[k + 1] and a[p[1]] both work.

The rules:

  • a[0], or an n past the end, is refused with S501, and the report says how many items the list holds.
  • A number below zero is refused with S410.
  • Writing past the end does not make the list longer. .append does that.
list_items.satlSATELLITE 004 Download
Read an item, replace one, and use a worked-out position
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list names = {"one", "two", "three"}
    satellite.console.display(names[1])
    names[2] = "CHANGED"
    satellite.console.display(names)
    satellite.variable.number k = 2
    satellite.console.display(names[k + 1])
    satellite.return(satellite)
}
OUTPUT
one
{"one", "CHANGED", "three"}
three
past_the_end.satlSATELLITE 004 Download
Item 5 of a list of two is refused
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list names = {"one", "two"}
    satellite.console.display(names[5])
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S501: LINE_PAST_THE_END
satl(run): names[5]: there is no such item -- the list holds 2 items, counting
from 1

directory: past_the_end.satl:6
syntax: satellite.console.display(names[5])
                                       /\

a line was read by its number and the file has no line with that number. Lines
count from 1, and the last one is the file's size.
machine code 47 line_past_the_end -- satl exits with this.

--------------------------------------------------------------------------------

.append(x) and .insert(n, x)

.append(x) puts x at the end. .insert(n, x) puts x in as item n, and the items from n on move along one. A method that changes a list needs a list with a name to change, so satellite.console.display({1, 2}.append(3)) is refused.

append_insert.satlSATELLITE 004 Download
Add at the end, and add in the middle
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list fruit = {"pear", "fig"}
    fruit.append("lime")
    satellite.console.display(fruit)
    fruit.insert(2, "kiwi")
    satellite.console.display(fruit)
    satellite.return(satellite)
}
OUTPUT
{"pear", "fig", "lime"}
{"pear", "kiwi", "fig", "lime"}

.remove(x), .remove_at(n), .remove_first() and .remove_last()

  • .remove(x) takes out the first item that is x.
  • .remove_at(n) takes out item n.
  • .remove_first() and .remove_last() take out the first and the last item.

The items after a removed one move up.

removing.satlSATELLITE 004 Download
Four ways to take an item out
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list fruit = {"pear", "kiwi", "apple", "fig", "lime", "plum"}
    fruit.remove("apple")
    satellite.console.display(fruit)
    fruit.remove_at(1)
    satellite.console.display(fruit)
    fruit.remove_first()
    fruit.remove_last()
    satellite.console.display(fruit)
    satellite.return(satellite)
}
OUTPUT
{"pear", "kiwi", "fig", "lime", "plum"}
{"kiwi", "fig", "lime", "plum"}
{"fig", "lime"}

.truncate(n), .clear and .reserve(n)

  • .truncate(n) keeps the first n items. A number past the end keeps them all.
  • .clear empties the list. It works with or without ().
  • .reserve(n) makes room for n items at once and changes no item. It is for speed when you know a list will grow large.
truncate_clear.satlSATELLITE 004 Download
Keep the first items, then empty the list
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list n = {1, 2, 3, 4, 5}
    n.truncate(2)
    satellite.console.display(n)
    n.truncate(100)
    satellite.console.display(n)
    n.reserve(1000)
    satellite.console.display(n.size)
    n.clear
    satellite.console.display(n)
    satellite.return(satellite)
}
OUTPUT
{1, 2}
{1, 2}
2
{}

.size, .empty, .first and .last

These answer a value and leave the list alone. They are written with no brackets, and () after them is accepted too.

  • .size is how many items there are.
  • .empty is true when there are none.
  • .first and .last are the first and the last item.
list_questions.satlSATELLITE 004 Download
How many, whether any, and the two ends
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list n = {10, 25, 30}
    satellite.console.display(n.size)
    satellite.console.display(n.empty)
    satellite.console.display(n.first)
    satellite.console.display(n.last)
    satellite.console.display(n.first + n.last)
    satellite.return(satellite)
}
OUTPUT
3
false
10
30
40

.contains(x), .index_of(x) and .search(x)

  • .contains(x) is true when some item is x. .contain(x) is a second spelling.
  • .index_of(x) is the position of the first item that is x, or 0 when there is none.
  • .search(x) is the position of the first item whose text contains x, or 0.

Because items count from 1, an answer of 0 can never be mistaken for a position.

finding.satlSATELLITE 004 Download
Is it there, and where
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list fruit = {"fig", "lime", "pear"}
    satellite.console.display(fruit.contains("pear"))
    satellite.console.display(fruit.contain("plum"))
    satellite.console.display(fruit.index_of("lime"))
    satellite.console.display(fruit.index_of("plum"))
    satellite.console.display(fruit.search("im"))
    satellite.return(satellite)
}
OUTPUT
true
false
2
0
2

.sort().by_name(), .sort().by_value() and .reverse()

.sort() on its own orders nothing. You follow it with how to order:

  • .by_name() orders by how each item reads: a to z, then A to Z, then digits.
  • .by_value() orders by what each item is worth, smallest first.
  • .reverse() after either goes the other way. On its own it turns the list back to front.

These answer a new list and leave the old one as it was. The older spellings .sort("down"), .sort_down() and .sort_up() are refused. Items that have no order between them, such as a string and a number under .by_value(), are refused with S301.

sorting.satlSATELLITE 004 Download
By name, by value, and back to front
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list words = {"Zed", "apple", "Bob", "42", "zoo"}
    satellite.console.display(words.sort().by_name())
    satellite.console.display(words.sort().by_name().reverse())
    satellite.container.list row = {30, 4, 100}
    satellite.console.display(row.sort().by_value())
    satellite.console.display(row.sort().by_name())
    satellite.console.display(row.reverse())
    satellite.console.display(row)
    satellite.return(satellite)
}
OUTPUT
{"apple", "zoo", "Bob", "Zed", "42"}
{"42", "Zed", "Bob", "zoo", "apple"}
{4, 30, 100}
{100, 30, 4}
{100, 4, 30}
{30, 4, 100}

.sum, .max, .min and .join(separator)

  • .sum adds every item with +. A list of nothing sums to 0. A list of strings is refused, and the report points you at .join.
  • .max and .min are the largest and the smallest item by what each is worth. Strings compare as text.
  • .join(separator) makes one string of every item’s text, with separator between them.
sum_max_join.satlSATELLITE 004 Download
Add up, find the ends, and join into one string
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list numbers = {3, 1, 2}
    satellite.console.display(numbers.sum)
    satellite.console.display(numbers.max)
    satellite.console.display(numbers.min)
    satellite.console.display(numbers.join(", "))
    satellite.container.list words = {"pear", "apple", "zoo"}
    satellite.console.display(words.max)
    satellite.console.display(words.join("/"))
    satellite.container.list none = {}
    satellite.console.display(none.sum)
    satellite.return(satellite)
}
OUTPUT
6
3
1
3, 1, 2
zoo
pear/apple/zoo
0

LISTS OF LISTS

A list can hold lists. That is how you make a grid, a table or a set of rows. More in GRIDS: LISTS OF LISTS.

a[x][y]

a[x][y] reads item y of item x. a[x][y] = v changes it, and a[x].append(v) adds to the inner list. Say what the inner lists hold with a list inside the <>: satellite.container.list<satellite.container.list<satellite.variable.number>>.

grid.satlSATELLITE 004 Download
A grid made row by row, read and changed through two brackets
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list<satellite.container.list<satellite.variable.number>> grid = satellite.container.list()
    grid.append({1, 2})
    grid.append(satellite.container.list())
    grid[2].append(3)
    satellite.console.display(grid)
    satellite.console.display(grid[2][1])
    grid[1][2] = 20
    satellite.console.display(grid)
    satellite.console.display(grid[1].sum)
    satellite.return(satellite)
}
OUTPUT
{{1, 2}, {3}}
3
{{1, 20}, {3}}
21

INDEXES AND MAPS

An index files values under keys, the way a dictionary files meanings under words. More in MAPS.

satellite.container.index

satellite.container.index<key, value> name declares an empty index. With no <> it takes any key and any value.

  • age["al"] = 4 files a value under a key. If the key is already there, its value changes and the key keeps its place.
  • age["al"] reads the value. A key that is not there is refused with S501, never answered with nothing.
  • A key must be a number, a string, a bool, a binary or a percentage, so that it cannot change after it is filed. A list as a key is refused. 1 and "1" are two different keys.
  • A value can be anything, a list included, and teams["red"][2] reaches into the list filed under "red".
  • Keys come back in the order you put them in, not sorted.
  • Two indexes are == when they hold the same entries, whatever order those went in.
ages.satlSATELLITE 004 Download
File values under keys, change one, and read them back
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.index<satellite.variable.string, satellite.variable.number> age
    age["zoe"] = 30
    age["al"] = 4
    age["bo"] = 100
    age["zoe"] = 31
    satellite.console.display(age)
    satellite.console.display(age["al"])
    satellite.container.index teams
    teams["red"] = {"ann", "bo"}
    teams["red"].append("cy")
    satellite.console.display(teams)
    satellite.console.display(teams["red"][2])
    satellite.return(satellite)
}
OUTPUT
{"zoe": 31, "al": 4, "bo": 100}
4
{"red": {"ann", "bo", "cy"}}
bo
no_such_key.satlSATELLITE 004 Download
A key that was never filed is refused
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.index<satellite.variable.string, satellite.variable.number> age
    age["zoe"] = 30
    satellite.console.display(age["max"])
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S501: LINE_PAST_THE_END
satl(run): age[max]: there is no such key in it -- it holds 1 key

directory: no_such_key.satl:7
syntax: satellite.console.display(age["max"])
                                     /\

a line was read by its number and the file has no line with that number. Lines
count from 1, and the last one is the file's size.
machine code 47 line_past_the_end -- satl exits with this.

--------------------------------------------------------------------------------

Index methods

  • .keys is every key, as a list, in the order they went in. .values is every value, in the same order.
  • .contains(k) is true when k is a key.
  • .size, .empty and .clear work as they do on a list.
  • .first and .last are the first and the last key.
  • .remove(k) takes that key out. .remove_first() and .remove_last() take out the oldest and the newest entry.

An index has keys, not positions, so .append, .insert, .remove_at, .truncate, .index_of and .search are refused. .sum, .max, .min and .join ask you to say which half you mean: scores.values.sum, or scores.keys.join(", ").

index_methods.satlSATELLITE 004 Download
Keys, values, and taking entries out
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.index<satellite.variable.string, satellite.variable.number> scores
    scores["zoe"] = 1
    scores["al"] = 41
    scores["bo"] = 7
    satellite.console.display(scores.keys)
    satellite.console.display(scores.values)
    satellite.console.display(scores.values.sum)
    satellite.console.display(scores.keys.join(", "))
    satellite.console.display(scores.contains("bo"))
    satellite.console.display(scores.first)
    satellite.console.display(scores.last)
    scores.remove("al")
    satellite.console.display(scores.size)
    scores.remove_first()
    satellite.console.display(scores)
    satellite.return(satellite)
}
OUTPUT
{"zoe", "al", "bo"}
{1, 41, 7}
49
zoe, al, bo
true
zoe
bo
2
{"bo": 7}

satellite.container.map

satellite.container.map is the same container as satellite.container.index: either word declares it, and a refusal says the word you wrote. The words numbered under map are spelled this way on it: .set(k, v) is age[k] = v, .get(k) is age[k], and .has(k) is .contains(k).

  • A map is written whole with braces and key: value entries, {"zoe": 30, "al": 4}, which is how satl prints one. A key written twice keeps its first place and its last value.
  • satellite.container.map() is an empty map, as a declaration with nothing after it is. {} on its own is an empty list.
  • A key in a literal must be one that can be filed (a number, a string, a bool, a binary or a percentage), and every key and value must fit what the declaration says, or the line is refused.
map_written_whole.satlSATELLITE 004 Download
A map written in one line, and a list of maps reached through
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.map<satellite.variable.string, satellite.variable.number> age = {"zoe": 30, "al": 4}
    age["bo"] = 100
    satellite.console.display(age)
    satellite.container.list<satellite.container.map<satellite.variable.string, satellite.container.list<satellite.variable.number>>> rows = {{"a": {1, 2}}, {"b": {3}}}
    rows[2]["b"].append(4)
    satellite.console.display(rows)
    satellite.console.display(rows[1]["a"][2])
    satellite.return(satellite)
}
OUTPUT
{"zoe": 30, "al": 4, "bo": 100}
{{"a": {1, 2}}, {"b": {3, 4}}}
2

MULTIPLE TYPES IN ONE NAME

More in MULTIPLE.

satellite.container.multiple

satellite.container.multiple lets one name hold any one of several types. It is not really a container, because it holds nothing of its own. It is a declaration that says which types a name accepts, and it is checked every time the name is given a value. What the name holds behaves exactly as an ordinary string, number or list would: a string in it reverses, and a number in it adds.

  • List two or more types between < and >. Any type a name can be declared as may be one of them, containers included.
  • With no <>, it takes every type.
  • A multiple with one type is refused, because it says nothing.
  • It can be a capsule’s parameter, or the type inside another container: satellite.container.list<satellite.container.multiple<...>>.
  • A number on the list also takes a binary, and it stays a binary: b101 stays b101.

There is no way yet to ask which type the name holds right now. satellite.variable.variant is numbered for that job and is not built, so a line that declares one is refused.

either.satlSATELLITE 004 Download
One name that holds a string, then a number
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.multiple<satellite.variable.string, satellite.variable.number> either = "abc"
    satellite.console.display(either.reverse())
    either = 7
    satellite.console.display(either + 1)
    show("one")
    show({"two", "three"})
    satellite.return(satellite)
}

satellite.capsule show(satellite.container.multiple<satellite.variable.string, satellite.container.list> what)
{
    satellite.console.display(what)
}
OUTPUT
cba
8
one
{"two", "three"}

A value of a type that is not on the list is refused, and the refusal names the types the name takes:

not_on_the_list.satlSATELLITE 004 Download
A percentage is not a string or a number
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.multiple<satellite.variable.string, satellite.variable.number> either = "a"
    either = 50%
    satellite.return(satellite)
}
OUTPUT
[satellite] satl(run): either was declared satellite.container.multiple, and it holds a percentage, and this name takes satellite.variable.string or satellite.variable.number (machine_code: 27 types_do_not_meet)

SPACESUITS

A spacesuit is satellite’s class: a type you write yourself, with its own variables and its own capsules. Your program makes objects of it. More in SPACESUITS and ONE SPACESUIT EXTENDING ANOTHER.

satellite.spacesuit

satellite.spacesuit name() declares a spacesuit at the top of a file, outside every capsule. The brackets after the name may be left off. satellite.class is a second spelling of the same word.

A spacesuit has sections, written side by side between its { and }:

  • satellite.protected { } holds its fields and the capsules only its own capsules may call.
  • satellite.public { } holds the capsules anyone with an object may call.
  • satellite.constructor(arguments) { } runs when an object is made.

Inside its own capsules, a spacesuit’s fields and capsules are named bare. There is no this. A spacesuit declared inside a capsule is not built yet, and is refused with S210.

greeter.satlSATELLITE 004 Download
A spacesuit with a field, a constructor and a public capsule
satellite.include(satellite)

satellite.spacesuit greeter()
{
    satellite.protected
    {
        satellite.variable.string name = ""
        satellite.variable.number times = 0
    }

    satellite.constructor(satellite.variable.string given)
    {
        name = given
    }

    satellite.public
    {
        satellite.capsule greet()
        {
            times = times + 1
            satellite.return("hello from " + name + ", " + times.to_string())
        }
    }
}

satellite.capsule satellite.main()
{
    greeter host("satl")
    satellite.console.display(host.greet())
    greeter same = host
    satellite.console.display(same.greet())
    satellite.return(satellite)
}
OUTPUT
hello from satl, 1
hello from satl, 2

satellite.protected

satellite.protected holds a spacesuit’s fields: a type and a name on each line, and a first value if you give one. It also holds capsules that only the spacesuit’s own capsules may call.

  • Every field is reached from inside the spacesuit only, public section or not. From outside, host.name is refused with S230. A public capsule that answers the field is the way to read it.
  • A protected capsule called from outside is refused with S230 too.
  • A field’s first value cannot name another field, because it is worked out before there is an object.
  • A field of a spacesuit’s own type, with nothing after its name, starts empty until a capsule gives it an object.
field_from_outside.satlSATELLITE 004 Download
A field is reached from inside its spacesuit only
satellite.include(satellite)

satellite.spacesuit counter()
{
    satellite.protected
    {
        satellite.variable.number count = 0
    }
}

satellite.capsule satellite.main()
{
    counter c
    satellite.console.display(c.count)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S230: MEMBER_IS_PROTECTED
satl(check): in satellite.main, count is a field of counter and fields are
reached from inside the spacesuit only -- write a capsule in counter that
answers it and call that instead

directory: field_from_outside.satl:14
syntax: satellite.console.display(c.count)
        /\

this is inside a spacesuit, where only its own capsules can reach it -- every
field is, and so is a capsule written in satellite.protected. A capsule in
satellite.public that answers it is the way in from outside.
machine code 52 member_is_protected -- satl exits with this.

--------------------------------------------------------------------------------

satellite.public

satellite.public holds the capsules anyone with an object may call: host.greet(). The call can stand on its own line or sit inside any line, as in satellite.variable.string said = host.greet(). A public capsule of the spacesuit can call a protected one by its bare name.

tally_public.satlSATELLITE 004 Download
A public capsule calling a protected one
satellite.include(satellite)

satellite.spacesuit tally()
{
    satellite.protected
    {
        satellite.variable.number count = 0

        satellite.capsule bump_by(satellite.variable.number n)
        {
            count = count + n
        }
    }

    satellite.public
    {
        satellite.capsule call_bump()
        {
            bump_by(5)
        }

        satellite.capsule call_count()
        {
            satellite.return(count)
        }
    }
}

satellite.capsule satellite.main()
{
    tally t
    t.call_bump()
    t.call_bump()
    satellite.variable.number seen = t.call_count()
    satellite.console.display(seen)
    satellite.return(satellite)
}
OUTPUT
10

satellite.constructor

satellite.constructor(arguments) { } runs when an object is made, after every field has its first value. It is public, and it hands nothing back. There is one per spacesuit.

  • An object is given exactly what its constructor takes, counted before anything runs.
  • Arguments with no constructor to take them are refused.
  • C++’s way of writing a constructor, the spacesuit’s own name with a body, is refused by name.
constructor_count.satlSATELLITE 004 Download
The constructor takes one argument, and the object was given none
satellite.include(satellite)

satellite.spacesuit lamp()
{
    satellite.protected
    {
        satellite.variable.string colour = ""
    }

    satellite.constructor(satellite.variable.string given)
    {
        colour = given
    }
}

satellite.capsule satellite.main()
{
    lamp desk
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S110: LINE_NOT_UNDERSTOOD
satl(check): in satellite.main, lamp's satellite.constructor takes 1 argument,
and desk was given 0 -- they go in brackets after the name

directory: constructor_count.satl:18
syntax: lamp desk
        /\

satellite has no scenario for this line -- it is spelled in a shape the language
does not have a meaning for.
machine code 13 satl_line_not_understood -- satl exits with this.

--------------------------------------------------------------------------------

Making an object

You make an object by declaring it: the spacesuit’s name, the object’s name, and the constructor’s arguments right there, as in greeter host("satl"). One whose constructor takes nothing is just greeter host.

  • An object is a reference. greeter other = host names the same object, and a capsule handed an object changes the caller’s.
  • Declaring the same name again makes a new object. The old one lives on in whatever still holds it.
  • A spacesuit’s type can be a capsule’s parameter or what a list holds, satellite.container.list<greeter>, and a capsule can hand an object back.
  • An object is made on a line of its own. A spacesuit’s name with brackets inside a line is refused.
  • satellite.console.display cannot show a whole object yet (S210), and == between two objects is refused (S301). Display what its public capsules answer instead.
objects.satlSATELLITE 004 Download
Objects in a list, and a name declared again
satellite.include(satellite)

satellite.spacesuit lamp()
{
    satellite.protected
    {
        satellite.variable.string colour = ""
    }

    satellite.constructor(satellite.variable.string given)
    {
        colour = given
    }

    satellite.public
    {
        satellite.capsule call_colour()
        {
            satellite.return(colour)
        }
    }
}

satellite.capsule describe(lamp any)
{
    satellite.console.display("a " + any.call_colour() + " lamp")
}

satellite.capsule satellite.main()
{
    satellite.container.list<lamp> shop = {}
    lamp one("red")
    shop.append(one)
    lamp one("blue")
    shop.append(one)
    describe(shop[1])
    describe(shop[2])
    satellite.console.display(shop.size)
    satellite.return(satellite)
}
OUTPUT
a red lamp
a blue lamp
2

satellite.spacesuit name(other)

A spacesuit may extend one other spacesuit, named in its brackets: satellite.spacesuit dog(animal).

  • Its objects have the other’s fields first, then their own, and the other’s capsules too. Its capsules call the other’s by their bare names.
  • A name declared as the other type may hold one of these: an animal name may hold a dog.
  • Every constructor runs, the other’s first. The arguments go to the nearest one.
  • It extends one spacesuit or none. A spacesuit that extends itself, however far round, is refused.
  • A built type in the brackets, as in satellite.spacesuit name(satellite.variable.string), is not built yet.

Replacing a capsule

A capsule the extending spacesuit declares again replaces the other’s for its objects. It is used even when the other spacesuit’s own capsules call it by its bare name. The replacement must be called the same way as the one it replaces: it may not move to satellite.protected, take different arguments or answer a different type.

animals.satlSATELLITE 004 Download
A dog extends an animal and replaces its sound
satellite.include(satellite)

satellite.spacesuit animal()
{
    satellite.protected
    {
        satellite.variable.string name = ""
    }

    satellite.constructor()
    {
        satellite.console.display("animal's constructor")
    }

    satellite.public
    {
        satellite.capsule call_sound()
        {
            satellite.return("...")
        }

        satellite.capsule call_speak()
        {
            satellite.return(name + " says " + call_sound())
        }
    }
}

satellite.spacesuit dog(animal)
{
    satellite.constructor(satellite.variable.string given)
    {
        satellite.console.display("dog's constructor")
        name = given
    }

    satellite.public
    {
        satellite.capsule call_sound()
        {
            satellite.return("woof")
        }
    }
}

satellite.capsule introduce(animal any)
{
    satellite.console.display(any.call_speak())
}

satellite.capsule satellite.main()
{
    dog rex("Rex")
    introduce(rex)
    animal holder = rex
    satellite.console.display(holder.call_sound())
    satellite.return(satellite)
}
OUTPUT
animal's constructor
dog's constructor
Rex says woof
woof

A spacesuit inside a spacesuit

A spacesuit may hold spacesuits of its own, written in its satellite.public or satellite.protected. The outer one’s capsules and fields name the inner one bare. From outside, a public inner spacesuit is outer.inner, and a protected one cannot be named at all. The inner spacesuit’s capsules see its own fields, not the outer one’s.

ship.satlSATELLITE 004 Download
A ship holds an engine, and the program makes a spare one
satellite.include(satellite)

satellite.spacesuit ship()
{
    satellite.protected
    {
        satellite.variable.string name = ""
        engine main_engine
    }

    satellite.constructor(satellite.variable.string given)
    {
        name = given
        engine made(900)
        main_engine = made
    }

    satellite.public
    {
        satellite.spacesuit engine()
        {
            satellite.protected
            {
                satellite.variable.number power = 0
            }

            satellite.constructor(satellite.variable.number given)
            {
                power = given
            }

            satellite.public
            {
                satellite.capsule call_power()
                {
                    satellite.return(power)
                }
            }
        }

        satellite.capsule call_report()
        {
            satellite.return(name + " has " + main_engine.call_power().to_string() + " power")
        }
    }
}

satellite.capsule satellite.main()
{
    ship kestrel("kestrel")
    satellite.console.display(kestrel.call_report())
    ship.engine spare(40)
    satellite.console.display(spare.call_power())
    satellite.return(satellite)
}
OUTPUT
kestrel has 900 power
40

FILES AS NAMESPACES

Every file you include is reached through its own name. Inside a file you can also make a namespace of your own. More in INCLUDING FILES AND NAMESPACES.

satellite.include(“file”)

satellite.include("helpers") at the top of a program brings in helpers.satl from the program’s folder. satellite.include(helpers), satellite.include("helpers.satl") and satellite.include("./helpers.satl") all reach the same file. An included file is a spaceship: it has no satellite.include(satellite) of its own, so satl will not start it as a program.

A file reaches the files it includes itself, and no others. If main.satl includes a.satl, and a.satl includes b.satl, then main.satl cannot reach b directly.

helpers.greet()

A file’s capsules, namespaces, spacesuits and satellite.library values are reached through the file’s name:

  • helpers.greet("you") calls the capsule greet in helpers.satl.
  • helpers.shapes.square(7) calls a capsule inside a namespace of that file.
  • helpers.lamp desk("green") makes an object of a spacesuit written in that file.
  • satellite.library.helpers.greeting reads a value written at the top of that file.

A bare name is always your own file’s. Your program’s own greet() is its own, even when an included file has a greet too. A bare name never reaches into another file, and when you try, the refusal tells you the spelling that does. An included file’s satellite.main never runs as your program’s.

Save these two files in one folder, then run satl main.satl.

helpers.satlSATELLITE 004 Download
The included file: a value, a capsule, a namespace and a spacesuit
satellite.library.greeting = "hello"

satellite.capsule greet(satellite.variable.string who)
{
    satellite.console.display(satellite.library.greeting + ", " + who)
}

satellite.space shapes
{
    satellite.capsule square(satellite.variable.number side)
    {
        satellite.return(side * side)
    }
}

satellite.spacesuit lamp()
{
    satellite.protected
    {
        satellite.variable.string colour = ""
    }

    satellite.constructor(satellite.variable.string given)
    {
        colour = given
    }

    satellite.public
    {
        satellite.capsule call_colour()
        {
            satellite.return(colour)
        }
    }
}
main.satlSATELLITE 004 Download
The program: its own greet, and everything of helpers.satl through its name
satellite.include(satellite)
satellite.include("helpers")

satellite.capsule greet(satellite.variable.string who)
{
    satellite.console.display("my own greet, for " + who)
}

satellite.capsule satellite.main()
{
    greet("you")
    helpers.greet("you")
    satellite.console.display(helpers.shapes.square(7))
    helpers.lamp desk("green")
    satellite.console.display(desk.call_colour())
    satellite.console.display(satellite.library.helpers.greeting)
    satellite.return(satellite)
}
OUTPUT
my own greet, for you
hello, you
49
green
hello

Without its own greet, the bare call is refused before anything runs:

TEXT Download
S110: LINE_NOT_UNDERSTOOD
satl(check): in satellite.main, greet is a capsule of helpers.satl, and a file's
capsules are reached through its name -- write helpers.greet(...)

satellite.namespace

satellite.namespace name { } makes a namespace inside a file. satellite.space is its second spelling. A namespace holds capsules, spacesuits and other namespaces, and nothing else.

  • From outside, its capsules are name.capsule(), and a namespace inside it is name.inner.capsule().
  • Inside it, its own capsules and the file’s capsules are named bare.
  • It goes at the top of a file or inside another namespace. One declared inside a capsule is not built yet, and is refused with S210.
  • A variable inside a namespace is refused: a variable belongs to a capsule or a spacesuit.
  • A namespace is not called: tools() is refused.
  • A name declared twice in one file, namespace or capsule, is refused with S202.
spaces.satlSATELLITE 004 Download
A namespace with a namespace inside it
satellite.include(satellite)

satellite.capsule greet()
{
    satellite.console.display("hello from the file")
}

satellite.namespace tools
{
    satellite.capsule add(satellite.variable.number a, satellite.variable.number b)
    {
        satellite.variable.number sum = a + b
        satellite.console.display("tools.add says " + sum.to_string())
        twice(sum)
    }

    satellite.capsule twice(satellite.variable.number n)
    {
        satellite.console.display("tools.twice says " + (n * 2).to_string())
    }

    satellite.space inner
    {
        satellite.capsule deep()
        {
            satellite.console.display("tools.inner.deep")
            greet()
        }
    }
}

satellite.capsule satellite.main()
{
    greet()
    tools.add(2, 3)
    tools.inner.deep()
    satellite.return(satellite)
}
OUTPUT
hello from the file
tools.add says 5
tools.twice says 10
tools.inner.deep
hello from the file
variable_in_a_space.satlSATELLITE 004 Download
A namespace holds no variables
satellite.include(satellite)

satellite.space tools
{
    satellite.variable.number n = 5
}

satellite.capsule satellite.main()
{
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S110: LINE_NOT_UNDERSTOOD
satl(check): the satellite.namespace tools holds capsules, spacesuits and other
spaces, not variables -- a variable belongs to a capsule or a spacesuit

directory: variable_in_a_space.satl:5
syntax: satellite.variable.number n = 5
        /\

satellite has no scenario for this line -- it is spelled in a shape the language
does not have a meaning for.
machine code 13 satl_line_not_understood -- satl exits with this.

--------------------------------------------------------------------------------

SATELLITE.LIBRARY

Values written at the top of a file, outside every capsule, and read by every capsule in it. More in SATELLITE.LIBRARY.

satellite.library.name = value

satellite.library.rounds = 4 at the top of a file gives the file a value named rounds. Every capsule in the file reads it as satellite.library.rounds, anywhere a value goes: in a line, a condition, a loop, a spacesuit’s field or a namespace.

  • A value is written down, never worked out. It is a number, a float, text, a binary, a hex, a percentage, a fraction, or satellite.bool.true or satellite.bool.false, with a minus sign in front if it has one. A colour is written as the hex it holds, like x1E2A3A.
  • Nothing runs outside a capsule, so = 1 + 2 or = helper() at the top of a file is refused with S250.
  • Nothing changes a value. satellite.library.rounds = 5 inside a capsule is refused with S250, because a value every capsule could change would be a global, and satellite has none. Copy it into a variable of your own and change that.
  • A value is written once in its file. A second is refused with S202.
  • Its name may not be the name of a file the file includes.
  • At the satl prompt there is no file, so there are no values to read.
rounds.satlSATELLITE 004 Download
Values at the top of the file, read inside main
satellite.include(satellite)

satellite.library.rounds = 4
satellite.library.label = "rounds counted"
satellite.library.cold = -12.5
satellite.library.ready = satellite.bool.true
satellite.library.tax = 20%
satellite.library.half = 1/2
satellite.library.sky = x1E2A3A

satellite.capsule satellite.main()
{
    satellite.variable.number count = 0
    satellite.statement.while (count < satellite.library.rounds)
    {
        count = count + 1
    }
    satellite.console.display(satellite.library.label)
    satellite.console.display(count)
    satellite.console.display(satellite.library.cold)
    satellite.console.display(satellite.library.ready)
    satellite.console.display(satellite.library.tax)
    satellite.console.display(satellite.library.half)
    satellite.console.display(satellite.library.sky)
    satellite.variable.number mine = satellite.library.rounds
    mine = mine + 1
    satellite.console.display(mine)
    satellite.return(satellite)
}
OUTPUT
rounds counted
4
-12.5
true
20%
1/2
x1E2A3A
5
fixed_value.satlSATELLITE 004 Download
A library value never changes
satellite.include(satellite)

satellite.library.span = 25

satellite.capsule satellite.main()
{
    satellite.library.span = 30
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S250: LIBRARY_VALUE_IS_FIXED
satl(check): in satellite.main, satellite.library.span is written at the top of
its file and never changes -- a value every capsule could change would be a
global, and satellite has none. Copy it into a variable of your own and change
that

directory: fixed_value.satl:7
syntax: satellite.library.span = 30
        /\

a satellite.library value is written down at the top of its file -- one number,
text or other literal -- and nothing works it out or changes it afterwards. A
value every capsule could change would be a global, and satellite has none.
machine code 54 library_value_is_fixed -- satl exits with this.

--------------------------------------------------------------------------------

satellite.library.system.division_digits, .max_depth, .min_free_mb and .float_digits are numbered in the language and have nothing behind them yet: reading one is refused with S110 when its line runs.

satellite.library.file.name

A file that includes another reads that file’s values through the file’s name. With satellite.include("settings") at its top, a program reads settings.satl‘s majors as satellite.library.settings.majors. The two-file example under FILES AS NAMESPACES reads satellite.library.helpers.greeting this way.

THE ARGUMENTS VARIABLE

One variable that holds the settings satl started with, the words it was given on the command line, and what it can read about the machine. More in THE ARGUMENTS VARIABLE.

satellite.variable.arguments

satellite.main hands a program the arguments variable when main asks for it by declaring it:

  • satellite.capsule satellite.main(satellite.variable.arguments args) asks for it. The name is yours: args, arguments, anything.
  • satellite.capsule satellite.main() asks for nothing.
  • satellite.container.list<satellite.variable.string> arguments, the older spelling, is the same variable.

It is filled from four places:

  1. satl’s own built-in rows, such as version, float.decimal and infinity.
  2. ~/.satl/config.ini, your own file: one key = value a line, with # for comments.
  3. The command line: satl prog.satl one two gives program, argument_1, argument_2 and length, which is 3 because the program is counted.
  4. The machine, read fresh each time a program asks: username, memory.total, memory.used, cpu.architecture and many more.

satellite.console.display(args) shows every row there is. Try it on your own machine: the list is long, and it is yours.

Reading a row

Each row is read by its name after the variable’s, with no brackets: args.program, args.memory.used, args.float.decimal. A row satl gathered when it started stays that row. A row like memory.used is read fresh each time, so it is what the machine is using now. float.decimal is 128 unless your ~/.satl/config.ini sets it. A name that is not a row is refused with S201 where it is read.

reading_rows.satlSATELLITE 004 Download
Rows that read the same on any machine
satellite.include(satellite)

satellite.capsule satellite.main(satellite.variable.arguments args)
{
    satellite.console.display(args.program)
    satellite.console.display(args.length)
    satellite.console.display(args.float.decimal)
    satellite.console.display(args.memory.used > 0)
    satellite.console.display(args.machine.pointer_bits)
    satellite.return(satellite)
}
OUTPUT
reading_rows.satl
1
128
true
64

The command line arrives as rows too. Run with no words after the file name, length is 1, because the program itself is counted:

words.satlSATELLITE 004 Download
The program's own name, and how many words satl was given
satellite.include(satellite)

satellite.capsule satellite.main(satellite.variable.arguments arguments)
{
    satellite.console.display(arguments.program)
    satellite.console.display(arguments.length)
    satellite.return(satellite)
}
OUTPUT
words.satl
1

Each word typed after the file name becomes a row of its own, counting from 1. A word in quotes stays one word:

BASH Download
satl words.satl one "two words"

With satellite.console.display(arguments.argument_1) and satellite.console.display(arguments.argument_2) added to it, that run prints:

TEXT Download
words.satl
3
one
two words

Writing a row of your own

A row is written the same way, when its name is your program’s own. args.some_var = 5 adds it, and a second args.some_var = 6 changes it. A row can hold a list and change it. A row satl holds, such as username, memory.total or argument_1, is read and never written, and a line writing one is refused with S220 before the program runs.

own_rows.satlSATELLITE 004 Download
Rows the program adds for itself
satellite.include(satellite)

satellite.capsule satellite.main(satellite.variable.arguments args)
{
    args.some_var = 5
    args.some_var = args.some_var + 1
    satellite.console.display(args.some_var)
    args.seen = satellite.container.list()
    args.seen.append("first")
    satellite.console.display(args.seen[1])
    satellite.return(satellite)
}
OUTPUT
6
first
not_yours.satlSATELLITE 004 Download
username is satl's row, so the program cannot write it
satellite.include(satellite)

satellite.capsule satellite.main(satellite.variable.arguments args)
{
    args.username = "me"
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S220: WORD_TAKES_NO_ASSIGNMENT
satl(check): in satellite.main, args.username is a row satl holds, and a program
reads it but does not write it -- the machine, the command line or config.ini
says what it is. A name of the program's own is written: args.my_username = ...

directory: not_yours.satl:5
syntax: args.username = "me"
        /\

this is not a place a program can write: a word that is not a setting and not a
variable, or a row of the arguments that the machine, the command line or
config.ini says -- a program reads it, and gives a name of its own a value
instead.
machine code 35 word_takes_no_assignment -- satl exits with this.

--------------------------------------------------------------------------------

satellite.library.main.arguments

The same facts are words of the language, read by their full name with no brackets, from any capsule, with or without an arguments variable: satellite.library.main.arguments.machine.byte_order, satellite.library.main.arguments.memory.used, satellite.library.main.arguments.cores and the rest.

full_names.satlSATELLITE 004 Download
The same facts, by their full names
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display(satellite.library.main.arguments.machine.byte_order)
    satellite.console.display(satellite.library.main.arguments.machine.pointer_bits)
    satellite.console.display(satellite.library.main.arguments.memory.used > 0)
    satellite.return(satellite)
}
OUTPUT
little
64
true

HISTORY AND ACCESS

Two switches about remembering what a program’s names held. Only the switches exist today. More in HISTORY AND ACCESS.

satellite.access

satellite.access(name) shows what a name is, what it holds now, and how to reach every level of it, worked out from the name’s declaration: one line to reach each level, one line to fill it with a value of the right shape, and .size and .keys. For an object of a spacesuit it shows the fields and their values, and the capsules to call. A line that is nothing but satellite.access(name) prints it; anywhere else it answers the same text as a string. Anything in the brackets but one name the line can see is refused before the program runs.

access_rows.satlSATELLITE 004 Download
What a list of maps is, and the code to reach into it
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list<satellite.container.map<satellite.variable.string, satellite.container.list<satellite.variable.number>>> rows = {{"a": {1, 2}}, {"b": {3}}}
    satellite.access(rows)
    satellite.return(satellite)
}
OUTPUT
rows is a list of maps (string -> list of numbers), 2 items
  value                      {{"a": {1, 2}}, {"b": {3}}}
  rows[n]                    a map (string -> list of numbers), n counting from 1 (1 to 2)
  rows[n]["key"]             a list of numbers, "key" a string
  rows[n]["key"][m]          a number, m counting from 1
  rows.append({"key": {1}})  adds a map
  rows[n]["key"] = {1}       puts a list of numbers under "key"
  rows[n]["key"].append(1)   adds a number
  rows.size                  how many maps it holds
  rows[n].keys               the keys of a map, as a list

The access switch is meant for a store that is not built yet: the last value of every name, kept in memory so you could ask about an object even after the program has stopped. It is on by default, and until that store exists it turns nothing on or off; satellite.access(name) works whatever it says.

The switch is satellite.library.main.arguments.access, or args.access on an arguments variable.

  • You read it with no brackets.
  • You write it with =, and it lasts: writing it rewrites the row access = true or access = false in ~/.satl/config.ini, adds the row when there is none, and leaves every other line of that file as it was.
  • With no row it reads true.
  • It takes satellite.bool.true or satellite.bool.false and nothing else. = 5 stops the program with machine code 34, setting_is_not_a_flag.

What satl reads when it starts is not that row but the features = b... row in the same file, which satl --rebuild composes from your settings. When the two disagree, satl says so on every run until you run satl --rebuild.

access_switch.satlSATELLITE 004 Download
Reading the access switch, which is on unless you turned it off
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.statement.if(satellite.library.main.arguments.access)
    {
        satellite.console.display("access is on")
    }
    satellite.return(satellite)
}
OUTPUT
access is on

Writing satellite.library.main.arguments.access = satellite.bool.false changes your ~/.satl/config.ini for every run after it, so the example above only reads the switch.

satellite.history

satellite.history is planned as the record of everything: every value every name ever held, written to disk, and off by default. It is not built yet. satellite.history and satellite.history(x) are both refused with S110 before anything runs.

Its switch exists only as a number: satellite.library.main.arguments.history. It has nothing behind it yet, so reading it is refused with S110 when the line runs, and writing it is refused before anything runs. In ~/.satl/config.ini the switch is the row history = true or history = false, and a missing row means false. satl --rebuild folds it in, and says that nothing will happen yet.

One place you will notice its absence: a for loop’s counter is erased when the loop ends, so using it after the loop is refused with S201.

after_the_loop.satlSATELLITE 004 Download
The loop's counter is gone once the loop ends
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.statement.for(satellite.variable.number i = 0; i < 3; i++)
    {
        satellite.console.display(i)
    }
    satellite.console.display(i)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S201: NAME_NOT_DECLARED
satl(check): in satellite.main, i has no satellite.variable line declaring it

directory: after_the_loop.satl:9
syntax: satellite.console.display(i)
        /\

this name was used and no satellite.variable line ever declared it. A name has
to be given a type before it can hold anything.
machine code 25 name_not_declared -- satl exits with this.

--------------------------------------------------------------------------------

HELP

More in THE HELP SYSTEM.

satellite.help

satellite.help is satellite’s own help, typed at the satl prompt on a line of its own. It is something you ask satl, not a statement your program runs.

  • satellite.help() lists every topic.
  • satellite.help(include) or satellite.help(satellite.include) brings up one topic.
  • A topic can be named by its whole name, by its last part, by a second spelling (double finds satellite.variable.float, colour finds satellite.variable.color), or by the start of its last part (percentage finds satellite.variable.percent).
  • When two topics answer, as satellite.window and satellite.variable.window both do for window, satl names both and lets you write the one you meant.
  • When nothing answers, satl says there is no help on that yet.

Every topic is a plain text file you can read and change: the list is satellite.help/help.txt, and a topic is the file help_text.txt in a folder named after it. The files are read when you ask, so an edited topic is the topic you get next time. satl looks for the satellite.help folder beside itself, and says where it looked when the folder is not there.

BASH Download
satl --repl
TEXT Download
satellite.help()
satellite.help(include)
satellite.help(window)

satellite.help(a_name) for a name your program uses is not built yet. Inside a program, satellite.help is refused with S210:

help_in_a_program.satlSATELLITE 004 Download
Help is asked at the prompt, not run by a program
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.help(capsule)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S210: NOT_BUILT_YET
satl(check): in satellite.main, satellite.help has no library built for it yet

directory: help_in_a_program.satl:5
syntax: satellite.help(capsule)
        /\

this word is numbered in the language and the library behind it does not exist
yet. The word is right; there is nothing yet to run.
machine code 14 not_built_yet -- satl exits with this.

--------------------------------------------------------------------------------

THE CONSOLE

satellite.console is where your program talks to a person. More in DISPLAYING TEXT, READING INPUT and COLOR ON THE CONSOLE.

satellite.console.display

satellite.console.display(value) writes one line to satl’s standard output: text as it is, a number as its digits, a comparison as true or false, a list as {1, "two"}. It takes one value. Two values are refused, and so is none.

satl’s own title block goes to standard error, so what your program displays can be piped or written to a file on its own.

display.satlSATELLITE 004 Download
One line for each kind of value
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number n = 4
    satellite.console.display("hello")
    satellite.console.display(n + 1)
    satellite.console.display(n > 3)
    satellite.console.display({1, "two"})
    satellite.console.display(1.5)
    satellite.console.display(50%)
    satellite.console.display(3/4)
    satellite.console.display(b101)
    satellite.console.display(satellite.bool.false)
    satellite.return(satellite)
}
OUTPUT
hello
5
true
{1, "two"}
1.5
50%
3/4
b101
false

After the value, display takes any of five named options, each written name=value, once:

  • end="..." ends the line with something else instead of a new line.
  • foreground=xFF8800 is the colour of the text.
  • background=x000000 is the colour behind it.
  • bold=satellite.bool.true makes it bold.
  • italic=satellite.bool.true makes it italic.

An option not on that list is refused, and so is one given twice.

end=

end="" stays on the same line, so the next display carries on from there. end="..." puts its text where the new line would have been.

end_option.satlSATELLITE 004 Download
Staying on one line
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display("Loading", end="...")
    satellite.console.display(" done")
    satellite.console.display("a", end="")
    satellite.console.display("b", end="")
    satellite.console.display("c")
    satellite.return(satellite)
}
OUTPUT
Loading... done
abc

foreground=, background=, bold= and italic=

A colour is six hex digits, like xFF8800, or a satellite.variable.color. The style lasts for that one line. satl writes it as the terminal’s own codes, so it shows in any terminal and in satl’s own console. Into a pipe or a file the text goes plain, which is what you see below. With NO_COLOR set, the colours are dropped and bold and italic are kept.

A colour written into the line that is not six hex digits, like xFFF or "red", is refused before any line runs. One held in a name is judged when its line runs.

styled.satlSATELLITE 004 Download
Styled lines, shown here as they arrive in a file: plain
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.color amber = xFFB000
    satellite.console.display("careful", foreground=xFF8800, bold=satellite.bool.true)
    satellite.console.display("slanted", italic=satellite.bool.true, background=x000000)
    satellite.console.display("amber", foreground=amber)
    satellite.return(satellite)
}
OUTPUT
careful
slanted
amber
short_colour.satlSATELLITE 004 Download
Three hex digits are not a colour
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display("hello", foreground=xFFF)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S301: TYPES_DO_NOT_MEET
satl(check): in satellite.main, satellite.console.display's foreground= takes a
colour, which is exactly six hex digits like xFF8800, and xFFF has 3

directory: short_colour.satl:5
syntax: satellite.console.display("hello", foreground=xFFF)
        /\

this operator has no scenario for the two kinds it was given. Nothing was
guessed at, because a guess here is an answer that is wrong and does not say so.
machine code 27 types_do_not_meet -- satl exits with this.

--------------------------------------------------------------------------------

satellite.console.foreground and satellite.console.background

satellite.console.foreground(xFF8800) and satellite.console.background(x000000) colour every line display writes from then on. satellite.console.foreground() and satellite.console.background(), with nothing between the brackets, put the terminal’s own colour back. A line’s own foreground= or background= wins over them.

console_colour.satlSATELLITE 004 Download
A colour for every line, then back to normal
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.foreground(x33FF33)
    satellite.console.display("green")
    satellite.console.display("still green")
    satellite.console.foreground()
    satellite.console.background(x000000)
    satellite.console.display("on black")
    satellite.console.background()
    satellite.console.display("plain again")
    satellite.return(satellite)
}
OUTPUT
green
still green
on black
plain again

text.foreground(colour) and text.background(colour)

A string can carry a colour of its own: "OK".foreground(x00FF00), and .background(c) for the colour behind it. The two can follow each other. The colour travels with the text into any line, and it shows on a terminal. Into a pipe or a file the text goes plain. Because a coloured string carries its colour with it, it is no longer == to the plain text.

coloured_text.satlSATELLITE 004 Download
A green OK inside a line
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.string ok = "OK".foreground(x00FF00)
    satellite.console.display("status: " + ok)
    satellite.console.display("warn".foreground(xFF8800).background(x000000))
    satellite.console.display(ok == "OK")
    satellite.return(satellite)
}
OUTPUT
status: OK
warn
false

satellite.console.input

satellite.console.input() and satellite.console.input(prompt) wait for a person to type a line, and answer it as a string without its new line. The prompt goes out with no new line after it, and it takes display’s foreground=, background=, bold= and italic=.

satellite.console.input(prompt, target) and satellite.console.typed() are numbered and not built yet.

ask_name.satlSATELLITE 004 Download
Asks for a name and waits for the keyboard
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.string name = satellite.console.input("What is your name? ")
    satellite.console.display("Hello, " + name + "!")
    satellite.return(satellite)
}
TEXT Download
What is your name? Ada
Hello, Ada!

When the input has ended, because a file piped in ran out or Ctrl-D was pressed, satl stops with S830 rather than answering an empty line forever. Run with nothing at all to read, the same program stops at its first line:

no_keyboard.satlSATELLITE 004 Download
With no input at all, input() stops with S830
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.string name = satellite.console.input("What is your name? ")
    satellite.console.display("Hello, " + name + "!")
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S830: INPUT_ENDED
satl(run): in name = ..., satellite.console.input(prompt) waited for a line, and
the input has ended

directory: no_keyboard.satl:5
syntax: satellite.variable.string name = satellite.console.input("What is your name? ")
                                                                                       /\

satellite.console.input() waited for a line and the input has ended -- a file
piped in ran out, or Ctrl-D was pressed. There will never be another line, so
this stops rather than answering an empty one forever.
machine code 55 input_ended -- satl exits with this.

--------------------------------------------------------------------------------

satellite.console.width and satellite.console.height

satellite.console.width and satellite.console.height, with no brackets, are how many characters fit across the terminal and how many lines fit down it. They are asked fresh every time, so a window someone resizes gives new answers. With no terminal, as when the output goes into a file, they are 80 and 24.

console_size.satlSATELLITE 004 Download
The size of the terminal, or 80 by 24 without one
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display(satellite.console.width)
    satellite.console.display(satellite.console.height)
    satellite.return(satellite)
}
OUTPUT
80
24

satellite.console.clear() and satellite.console.home()

satellite.console.clear() empties the screen and puts the cursor in the top-left corner. satellite.console.home() only moves the cursor there, so the next lines are drawn over the last ones. That is how you draw a frame over the one before it.

frames.satlSATELLITE 004 Download
Clears the screen, then draws a second frame over the first
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.clear()
    satellite.console.display("frame one")
    satellite.console.home()
    satellite.console.display("frame two")
    satellite.return(satellite)
}

satellite.console.new

satellite.console.new(title, width, height) makes a console of your own: a window whose whole inside is a terminal. It answers a satellite.variable.window, has every method a window has, and these of its own:

  • c.display("words") puts one line into that console. satellite.console.display still writes to standard output, never into a console you made.
  • c.typed(a_capsule) names the capsule that runs when a person finishes a line, and c.typed, read bare, is that line.
  • c.clear() and c.home() empty its screen and move its cursor home.
  • c.columns and c.rows are how many characters fit across and lines fit down.
  • c.colour("#000000"), c.background("#90D5FF") and c.font("IBM Plex Mono", 12) set its look.

A console holds no pieces, so .append is refused on one. With no screen, the first window word stops with S730.

own_console.satlSATELLITE 004 Download
A console window that answers each line typed into it
satellite.include(satellite)

satellite.capsule when_typed(satellite.variable.window the_console)
{
    the_console.display("you typed " + the_console.typed)
}

satellite.capsule satellite.main()
{
    satellite.variable.window c = satellite.console.new("a console", 800, 600)
    c.display("type a line and press Enter")
    c.typed(when_typed)
    satellite.return(satellite)
}

THE TERMINAL

More in THE TERMINAL.

satellite.terminal.foreground and satellite.terminal.background

satellite.terminal is the terminal satl is running in: a terminal program of yours, or satl’s own console. satellite.terminal.foreground(xFF8800) and satellite.terminal.background(x101010) change the terminal’s own colours: everything drawn in its default colour, and the empty rows too, not only the lines written after them. A colour is six hex digits or a satellite.variable.color.

  • satellite.terminal.foreground() and satellite.terminal.background() put the terminal’s own colours back.
  • satl puts back what it changed by itself when the program ends, however it ends, so a program never leaves your shell orange. Back means the colours of the terminal’s own profile.
  • Nothing is changed when satl’s output is a pipe or a file, or when NO_COLOR is set. That is why only the text shows below.
  • To colour the lines your program writes rather than the whole terminal, use satellite.console.foreground.
  • satellite.terminal on its own, and a shell prompt in a window, are not built yet.
whole_terminal.satlSATELLITE 004 Download
Green on near-black for the whole terminal, then back
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.terminal.background(x101010)
    satellite.terminal.foreground(x33FF33)
    satellite.console.display("green on near-black, the whole terminal")
    satellite.terminal.foreground()
    satellite.terminal.background()
    satellite.console.display("and back again")
    satellite.return(satellite)
}
OUTPUT
green on near-black, the whole terminal
and back again

FILES

A text file is a list of lines that lives on the disk. Every list word that means something for lines works on a file, spelled the same, so learning the list teaches you the file. Lines count from 1. More in FILES.

satellite.variable.file

satellite.variable.file is the type that holds an open file. You get one from satellite.file.new or satellite.file.open. A file is a reference: two names for one open file are two names for one list of lines, and closing it through one closes it for both.

Nothing a file does stops the program, with a few exceptions. Every word answers a value, and when something fails, the file keeps the reason in .error. The exceptions are reading a line that is not there, and asking a question of a file that is not open. Those stop the program, because any answer would be made up.

Changes are on the disk when the program ends, or at .save or .close, and never half. A save writes a new copy beside the file and renames it over the old one. satl saves and closes every file still open when a capsule ends, main included, so a file you forget to close loses nothing.

A path without a leading / is relative to the folder of the .satl file that uses it.

satellite.file.new

satellite.file.new(path) makes a text file that is not there yet, open and empty. satellite.file.new(path, "text") says the kind out loud. It never replaces a file: if anything is already at the path, the file answers ok false, and nothing is touched. "binary" is not built yet, and gives a file that is not ok. Any other kind is refused with S301.

satellite.file.open

satellite.file.open(path) opens a text file that is already there, and satellite.file.open(path, "text") says the kind. It never makes one: a missing file gives a file that is not ok, so a misspelt name cannot quietly become an empty file.

new_and_open.satlSATELLITE 004 Download
new makes a file once, open opens it, and neither replaces anything
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.file story = satellite.file.new("story.txt", "text")
    satellite.console.display(story.ok)
    story.append("once upon a time")
    story.close()
    satellite.variable.file again = satellite.file.new("story.txt")
    satellite.console.display(again.ok)
    satellite.variable.file missing = satellite.file.open("missing.txt")
    satellite.console.display(missing.ok)
    satellite.variable.file reread = satellite.file.open("story.txt")
    satellite.console.display(reread[1])
    satellite.return(satellite)
}
OUTPUT
true
false
false
once upon a time

satellite.file.exists and satellite.file.clear

satellite.file.exists(path) is true when a file is there. A folder answers false. satellite.file.clear(path) empties a file by its name and leaves it there, and answers whether it did.

exists_clear.satlSATELLITE 004 Download
Whether a file is there, and emptying one by name
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.file log = satellite.file.new("log.txt")
    log.append("one")
    log.append("two")
    log.close()
    satellite.console.display(satellite.file.exists("log.txt"))
    satellite.console.display(satellite.file.exists("nope.txt"))
    satellite.console.display(satellite.file.clear("log.txt"))
    satellite.variable.file emptied = satellite.file.open("log.txt")
    satellite.console.display(emptied.size)
    satellite.return(satellite)
}
OUTPUT
true
false
true
0

f[n]

f[n] is line n of the file, as a string, counting from 1. A line that is not there stops the program with S501, the way an item past the end of a list does. Walking a file is walking a list.

walk_a_file.satlSATELLITE 004 Download
Write three lines, then read them back one by one
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.file poem = satellite.file.new("poem.txt")
    poem.append("roses are red")
    poem.append("violets are blue")
    poem.append("satellite counts lines from one")
    satellite.statement.for(satellite.variable.number i = 1; i <= poem.size; i++)
    {
        satellite.console.display(i.to_string() + ": " + poem[i])
    }
    satellite.return(satellite)
}
OUTPUT
1: roses are red
2: violets are blue
3: satellite counts lines from one
no_line_two.satlSATELLITE 004 Download
A file of one line has no line 2
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.file log = satellite.file.new("log.txt")
    log.append("only line")
    satellite.console.display(log[2])
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S501: LINE_PAST_THE_END
satl(run): log[2]: there is no such line -- the file has 1 line, counting from 1

directory: no_line_two.satl:7
syntax: satellite.console.display(log[2])
        /\

a line was read by its number and the file has no line with that number. Lines
count from 1, and the last one is the file's size.
machine code 47 line_past_the_end -- satl exits with this.

--------------------------------------------------------------------------------

f.append(x) and f.insert(n, x)

f.append(x) adds x as a new last line. It is the fast way to write: nothing before the end moves. f.insert(n, x) puts x in as line n, and that line and everything below it move down one. Both answer whether they worked.

f.replace

f.replace has two shapes, chosen by what its first argument is:

  • f.replace(n, x), with a number first, makes line n into x.
  • f.replace(a, b), with text first, changes the first a in the file into b, looking from the top down and inside one line.

f.read_all below is the whole file as one string, so each display of it ends with an empty line.

edit_lines.satlSATELLITE 004 Download
Append, insert and both kinds of replace
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.file demo = satellite.file.new("demo.txt")
    demo.append("line_1")
    demo.append("line_2")
    demo.insert(2, "line_2")
    satellite.console.display(demo.read_all)
    demo.replace(2, "line_3")
    demo.replace("line_2", "line_4")
    satellite.console.display(demo.read_all)
    satellite.return(satellite)
}
OUTPUT
line_1
line_2
line_2

line_1
line_3
line_4

f.remove(x), f.remove_at(n), f.remove_first() and f.remove_last()

They take lines out as a list’s words take items out: the first line that is exactly x, line n, the first line, or the last. The lines below move up.

f.truncate(n) and f.clear

f.truncate(n) keeps the first n lines. f.clear leaves no lines, and the file stays. A negative number is refused with S410.

removing_lines.satlSATELLITE 004 Download
Taking lines out of a file
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.file letters = satellite.file.new("letters.txt")
    letters.append("a")
    letters.append("b")
    letters.append("c")
    letters.append("d")
    letters.append("e")
    letters.remove("c")
    letters.remove_at(1)
    letters.remove_first()
    satellite.console.display(letters.read_all)
    letters.remove_last()
    satellite.console.display(letters.size)
    letters.append("f")
    letters.truncate(1)
    satellite.console.display(letters.read_all)
    letters.clear
    satellite.console.display(letters.empty)
    satellite.return(satellite)
}
OUTPUT
d
e

1
d

true

f.size, f.empty, f.first and f.last

How many lines, whether there are none, and the first and the last line. f.first and f.last on a file with no lines stop the program, as f[n] does.

f.contains(x), f.index_of(x) and f.search(x)

  • f.contains(x) is true when some line is exactly x.
  • f.index_of(x) is the number of the first line that is exactly x, or 0.
  • f.search(x) is the number of the first line that contains x anywhere, or 0.

A search with no match answers 0 and never stops the program, and 0 is never a line.

asking_a_file.satlSATELLITE 004 Download
Questions about a file's lines
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.file notes = satellite.file.new("notes.txt")
    notes.append("buy milk")
    notes.append("call mum")
    notes.append("water the plants")
    satellite.console.display(notes.size)
    satellite.console.display(notes.empty)
    satellite.console.display(notes.first)
    satellite.console.display(notes.last)
    satellite.console.display(notes.contains("call mum"))
    satellite.console.display(notes.index_of("call mum"))
    satellite.console.display(notes.search("plant"))
    satellite.console.display(notes.search("bread"))
    satellite.return(satellite)
}
OUTPUT
3
false
buy milk
water the plants
true
2
3
0

f.read_all

f.read_all is the whole file as one string, new lines included, with every change not yet saved. Because the last line ends with a new line, displaying it leaves one empty line after it.

f.save, f.close and f.open

  • f.save puts every change on the disk now, whole or not at all, and answers whether it did.
  • f.close saves, then closes. False means the changes did not land, and f.error says why.
  • f.open opens a closed file again.

A save that fails when a capsule ends is said, in one [satellite] line that names the file and the reason, and the run exits 44. The file on the disk is left as it was.

f.ok, f.error, f.path and f.exists

These never stop a program.

  • f.ok is whether the file is open.
  • f.error is why the last thing failed, in words, with the file’s full path in it.
  • f.path is the full path the file was opened on.
  • f.exists is whether that path is still a file.
save_close_open.satlSATELLITE 004 Download
Saving, closing and opening again
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.file notes = satellite.file.new("notes.txt")
    notes.append("first")
    satellite.console.display(notes.save)
    satellite.console.display(notes.exists)
    satellite.console.display(notes.close)
    satellite.console.display(notes.ok)
    satellite.console.display(notes.open)
    satellite.console.display(notes.ok)
    satellite.console.display(notes[1])
    satellite.return(satellite)
}
OUTPUT
true
true
true
false
true
true
first

A question asked of a file that is not open stops with S503. Ask f.ok first when a file might not be open:

closed_file.satlSATELLITE 004 Download
A closed file cannot say how many lines it has
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.file log = satellite.file.new("log.txt")
    log.append("one line")
    log.close()
    satellite.console.display(log.ok)
    satellite.console.display(log.size)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S503: FILE_NOT_OPEN
satl(run): log.size: log is not open

directory: closed_file.satl:9
syntax: satellite.console.display(log.size)
        /\

a question was asked of a file that is not open -- closed, or never opened
because the open or new failed. Its answer would have been made up, so nothing
was answered. Ask the file's ok first; its error says why it is not open.
machine code 45 file_not_open -- satl exits with this.

--------------------------------------------------------------------------------

f.lock() and f.unlock()

A file has a lock, and it is off. f.lock() turns it on, and from then every method called on the file holds the lock for that one call, so two threads appending to one file never mix their lines. f.unlock() turns it off again. Without threads, nothing changes. The LOCKS section below says more.

locked_file.satlSATELLITE 004 Download
A file's lock turned on and off
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.file log = satellite.file.new("log.txt")
    log.lock()
    log.append("written while the lock was on")
    log.unlock()
    log.append("and after it was off")
    satellite.console.display(log.size)
    satellite.return(satellite)
}
OUTPUT
2

The older cursor words, read_line, write_line(s) and write(x), are numbered and not built. A file has no hidden cursor: every word names its line.

WINDOWS

Windows are built into satl: a window, and the pieces you put in one. A window needs a screen, so the examples in this section are shown without their output. More in WINDOWS.

satellite.variable.window

satellite.variable.window is the type that holds a window, or any piece you put in one. It is one type for all of them: a window, a button, a label, a canvas and a console are each a satellite.variable.window, and the piece knows which one it is.

  • A window is a thing on a screen, not a value, so it is never copied. Two names given one window are the same window.
  • satellite.console.display shows which one you hold: (window "its title") for a window, (button "text") for a piece, (switch) for a piece with no words, and (closed window "its title") once it has gone.
  • my_window.ok is whether it is still on the screen.
  • A capsule can take one as an argument, and that is how a press reaches your program.
  • Anything that is not a window is refused: satellite.variable.window w = 5 stops with machine code 27.
  • satl does not end while a window is open. The program’s last line runs, then satl waits until the last window is closed.
  • On a machine with no screen, the first window word stops with S730 NO_DISPLAY, and satl exits 50.
one_button.satlSATELLITE 004 Download
A window with one button that closes it
satellite.include(satellite)

satellite.capsule when_pressed(satellite.variable.window the_piece, satellite.variable.window its_window)
{
    satellite.console.display("the button was pressed")
    its_window.close()
}

satellite.capsule satellite.main()
{
    satellite.variable.window my_window = satellite.window.new("window_title", 800, 600)
    satellite.variable.window my_button = satellite.window.button("text")
    my_button.pressed(when_pressed)
    my_window.append(my_button, 400, 300)
    satellite.console.display(my_window)
    satellite.return(satellite)
}

satellite.window.new

satellite.window.new(title, width, height) makes a window. Write 800, 600, not 800x600: x600 reads as a hex number.

A window has .close(), .focus(), .resize(1024, 768), .fullscreen(1) and .fullscreen(0), .message("saved"), .title to read its title and .title("text") to set it, and .menu(a_menu) to put a menu across its top.

The pieces

Each of these makes a piece. A piece is on no screen until you .append it into a window, or into something that is in one.

  • satellite.window.button(text), satellite.window.label(text)
  • satellite.window.text_box(text) for one line, satellite.window.text_area(text) for many
  • satellite.window.checkbox(text), satellite.window.switch()
  • satellite.window.slider(least, most), satellite.window.number_box(least, most), satellite.window.progress()
  • satellite.window.choice(items) to pick one from a list, satellite.window.one_of(items) for a set of choices where one is on
  • satellite.window.picture(path)
  • satellite.window.row(), satellite.window.column(), satellite.window.grid() to arrange pieces
  • satellite.window.scroll(), satellite.window.frame(title), satellite.window.split(), satellite.window.tabs() to hold them
  • satellite.window.menu(title)
  • satellite.window.canvas(width, height) to draw on

Putting pieces in place

  • my_window.append(piece, across, down) puts the piece’s centre there: 400, 300 is the middle of an 800 by 600 window.
  • A row and a column take .append(piece).
  • A grid takes .append(piece, across, down) as a cell, counting from 1.
  • A scroll and a frame hold one piece, a split holds two, and a set of tabs names each tab by its piece’s .title("a name").
  • A menu has .item(a_capsule, "Open") and .separator().

Reading and setting a piece

.text, .on, .value, .chosen, .path and .title read with no brackets and set with them: .text("hi"), .on(1), .value(50), .chosen("red"). A progress bar’s .value is a percentage, as in .value(50%). .on takes 1 or 0.

Every piece but a menu has .width and .height, and .colour("red") (or .color), .background("#222228") and .font("IBM Plex Mono", 12) for its look.

A piece talks back

A piece talks back by running a capsule of yours, named bare and first:

  • .pressed(a_capsule) on a button, and .press() for your program to press it itself.
  • .changed(a_capsule) on anything a person can change: typed in, moved, ticked or picked.
  • .clicked(a_capsule) on anything but a button or a menu. .across and .down say where the click landed.
  • On a window: .closed(a_capsule), .key(a_capsule) (and .key, read bare, is which key it was), .every(a_capsule, 1000) in milliseconds, .ask(a_capsule, "sure?") and .choose_a_file(a_capsule), whose answer is the window’s .answer.

The capsule may declare nothing, the piece, or the piece and its window. satl refuses any other shape before the program runs. There are no globals, so the window a capsule is handed is the only window it can close.

a_form.satlSATELLITE 004 Download
A text box that reports each change, and a button that closes the window
satellite.include(satellite)

satellite.capsule when_typed(satellite.variable.window the_box)
{
    satellite.console.display(the_box.text)
}

satellite.capsule when_done(satellite.variable.window the_button, satellite.variable.window its_window)
{
    its_window.close()
}

satellite.capsule satellite.main()
{
    satellite.variable.window my_window = satellite.window.new("a form", 400, 300)
    satellite.variable.window a_column = satellite.window.column()
    satellite.variable.window your_name = satellite.window.text_box("")
    your_name.changed(when_typed)
    satellite.variable.window done = satellite.window.button("done")
    done.pressed(when_done)
    a_column.append(your_name)
    a_column.append(done)
    my_window.append(a_column, 200, 150)
    satellite.return(satellite)
}

Drawing on a canvas

A canvas draws with .line(from_across, from_down, to_across, to_down), .box(across, down, wide, tall), .circle(across, down, radius), .arc(across, down, radius, from_degrees, to_degrees) and .write(across, down, "words"). .outline(1) draws shapes as outlines and .outline(0) fills them again, .thickness(3) sets the pen’s width, .clear() wipes it, and .save("a.png") saves it as a picture.

satl refuses a wrong argument count, a method no piece has, and a capsule nobody wrote, all before a line runs. A method this piece lacks, like .resize on a button, is refused when its line runs.

THREADS

A thread runs one of your capsules alongside the rest of your program. More in THREADS.

satellite.variable.thread

satellite.variable.thread is the type that holds a thread. You get one from satellite.thread.new. satellite.thread() on its own is not built, and is refused with S210. Displaying one shows the capsule it runs and where it is: not started, running, stopped, failed or finished. Two thread names are == when they name the same thread. There is no < between threads.

satellite.thread.new

satellite.thread.new(capsule_name(arguments)) makes a thread for one call of one of your capsules. The arguments are worked out right then, on this thread, and the capsule does not begin until .start(). What goes inside the brackets must be a call: satellite.thread.new(work) is refused with S721. A spacesuit’s capsule can run on a thread too, as satellite.thread.new(obj.call_x()).

.start()

t.start() runs the capsule on a fresh thread of its own and answers at once. A thread runs once: a second .start() is refused with S722.

.join() and .wait()

t.join() waits for the thread to finish, then answers what its capsule handed back. t.wait() is a second name for .join(). Joining before starting is refused with S723. A second join answers the same value again, and satl says S724 once, as a notice, without stopping.

two_halves.satlSATELLITE 004 Download
Two threads add up half each, and main joins them
satellite.include(satellite)

satellite.capsule add_up(satellite.variable.number from, satellite.variable.number to)
{
    satellite.variable.number total = 0
    satellite.statement.for(satellite.variable.number i = from; i <= to; i++)
    {
        total = total + i
    }
    satellite.return(total)
}

satellite.capsule satellite.main()
{
    satellite.variable.thread low = satellite.thread.new(add_up(1, 500))
    satellite.variable.thread high = satellite.thread.new(add_up(501, 1000))
    satellite.console.display(low)
    low.start()
    high.start()
    satellite.variable.number a = low.join()
    satellite.variable.number b = high.wait()
    satellite.console.display(a + b)
    satellite.console.display(low)
    satellite.console.display(low == high)
    satellite.return(satellite)
}
OUTPUT
(thread add_up, not started)
500500
(thread add_up, finished)
false
not_a_call.satlSATELLITE 004 Download
A thread is made from a call, with its brackets
satellite.include(satellite)

satellite.capsule work()
{
    satellite.console.display("working")
}

satellite.capsule satellite.main()
{
    satellite.variable.thread t = satellite.thread.new(work)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S721: THREAD_NEEDS_A_CAPSULE_CALL
satl(check): in satellite.main, satellite.thread.new(work) names a capsule and
does not call it -- write work(), with what it takes inside the brackets

directory: not_a_call.satl:10
syntax: satellite.variable.thread t = satellite.thread.new(work)
        /\

satellite.thread.new runs a capsule of your own on a thread, so what goes inside
it is a CALL -- my_capsule() or my_capsule(x). Its arguments are worked out at
new; the capsule does not begin until start().
machine code 56 thread_needs_a_capsule_call -- satl exits with this.

--------------------------------------------------------------------------------

.stop()

t.stop() asks the thread to stop at its next statement. A thread is never ended in the middle of a statement. After a stop, .join() answers nothing.

When main reaches satellite.return(satellite), every thread still running is asked to stop and is waited for. So a thread started on the last line may never run at all.

stopping.satlSATELLITE 004 Download
A thread that would count forever, stopped from main
satellite.include(satellite)

satellite.capsule count_forever()
{
    satellite.variable.number n = 0
    satellite.statement.while (n >= 0)
    {
        n = n + 1
    }
}

satellite.capsule satellite.main()
{
    satellite.variable.thread busy = satellite.thread.new(count_forever())
    busy.start()
    satellite.console.display(busy)
    busy.stop()
    busy.join()
    satellite.console.display(busy)
    satellite.return(satellite)
}
OUTPUT
(thread count_forever, running)
(thread count_forever, stopped)

What a thread shares

  • An object of a spacesuit handed to a thread is the same object on both sides. So is a file.
  • Numbers, strings and lists are values: each thread gets its own copy.
  • A window belongs to the main thread, which draws it. Handing one to a thread, or using a window word on one, is refused with S727.
  • Output is written one whole line at a time, so lines from two threads never mix inside a line.
sharing.satlSATELLITE 004 Download
The thread changes the shared object, and only its own copy of the list
satellite.include(satellite)

satellite.spacesuit box()
{
    satellite.protected
    {
        satellite.variable.string label = "empty"
    }

    satellite.public
    {
        satellite.capsule call_set(satellite.variable.string given)
        {
            label = given
        }

        satellite.capsule call_label()
        {
            satellite.return(label)
        }
    }
}

satellite.capsule fill(box b, satellite.container.list items)
{
    b.call_set("filled on a thread")
    items.append("added on a thread")
}

satellite.capsule satellite.main()
{
    box shared
    satellite.container.list mine = {"first"}
    satellite.variable.thread worker = satellite.thread.new(fill(shared, mine))
    worker.start()
    worker.join()
    satellite.console.display(shared.call_label())
    satellite.console.display(mine)
    satellite.return(satellite)
}
OUTPUT
filled on a thread
{"first"}

When a thread fails

A refusal on a thread is reported where it happened, with its own line and caret. .join() then stops the thread that joined with the same code, and prints nothing more. A thread that fails and is never joined still fails the run with its code.

thread_fails.satlSATELLITE 004 Download
The division by zero happens on the thread, and the report points there
satellite.include(satellite)

satellite.capsule share_out(satellite.variable.number sweets, satellite.variable.number children)
{
    satellite.return(sweets / children)
}

satellite.capsule satellite.main()
{
    satellite.variable.thread t = satellite.thread.new(share_out(10, 0))
    t.start()
    satellite.console.display(t.join())
    satellite.console.display("never printed")
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S401: DIVISION_BY_ZERO
satl(run): in satellite.return(...), the / of 10 and 0 is a division by zero

directory: thread_fails.satl:5
syntax: satellite.return(sweets / children)
                                /\

a divisor worked out to 0. There is no number this could answer, so it answers
nothing rather than something.
machine code 22 division_by_zero -- satl exits with this.

--------------------------------------------------------------------------------

LOCKS

When two threads write to one object at the same moment, one write can be lost. A lock makes those writes take turns. More in LOCKS.

obj.lock()

Every object of a spacesuit, and every file, has a lock, and it is off. Nothing is ever locked for it, and threads share it freely. obj.lock() turns the lock on. That locks nothing by itself: the lock is taken only while a statement writes to the object. .lock() is built for spacesuit objects and files. On a number, a string or a list it is refused with S210.

obj.unlock()

obj.unlock() turns the lock off again. Nothing is locked for the object until the next .lock().

What a lock holds

Once an object’s lock is on:

  • A statement that writes the object holds the lock for that one statement. Writing means assigning one of its fields, calling a method on a field or an item of one (like items.append(x)), or calling one of its capsules.
  • A statement that only reads it waits while a write is happening, or while a writer is waiting, and never for another read.
  • satellite.statement.if, while and for hold the lock for their condition only, never for their body.
  • Any method on a locked file holds the lock for that call.
  • A thread already holding an object’s lock never waits for itself.
  • .join() and .wait() let go of the locks their line holds while they wait, and take them back after.
tally.satlSATELLITE 004 Download
Four threads add 1,000 each to one locked object, and nothing is lost
satellite.include(satellite)

satellite.spacesuit tally()
{
    satellite.protected
    {
        satellite.variable.number total = 0
    }

    satellite.public
    {
        satellite.capsule call_add()
        {
            total = total + 1
        }

        satellite.capsule call_total()
        {
            satellite.return(total)
        }
    }
}

satellite.capsule add_many(tally shared)
{
    satellite.statement.for(satellite.variable.number i = 1; i <= 1000; i++)
    {
        shared.call_add()
    }
}

satellite.capsule satellite.main()
{
    tally shared
    shared.lock()
    satellite.variable.thread a = satellite.thread.new(add_many(shared))
    satellite.variable.thread b = satellite.thread.new(add_many(shared))
    satellite.variable.thread c = satellite.thread.new(add_many(shared))
    satellite.variable.thread d = satellite.thread.new(add_many(shared))
    a.start()
    b.start()
    c.start()
    d.start()
    a.join()
    b.join()
    c.join()
    d.join()
    shared.unlock()
    satellite.console.display(shared.call_total())
    satellite.return(satellite)
}
OUTPUT
4000

S728: a wait that would never end

Two threads can each hold a lock the other needs: one locks a and waits for b, while the other locks b and waits for a. Without help, that program would freeze. satellite’s own lock follows the chain of who waits for whom, and when the chain comes back to the thread about to wait, the line stops with S728 WAIT_NEVER_ENDS, exit 63, instead of freezing. The fix in your program is not to hold one object’s lock while waiting for a thread that needs it.

ERROR REPORTS

When satl refuses something, it prints a report on standard error and exits with a number. More in READING AN ERROR REPORT.

Reading a report

divide.satlSATELLITE 004 Download
Dividing by a name that holds 0
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number n = 10
    satellite.variable.number zero = 0
    satellite.console.display(n / zero)
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S401: DIVISION_BY_ZERO
satl(run): the / of 10 and 0 is a division by zero

directory: divide.satl:7
syntax: satellite.console.display(n / zero)
                                    /\

a divisor worked out to 0. There is no number this could answer, so it answers
nothing rather than something.
machine code 22 division_by_zero -- satl exits with this.

--------------------------------------------------------------------------------

Read it from the top:

  1. S401: DIVISION_BY_ZERO is the S-code and its name. The number says how bad it is: S0xx is a warning and the run carries on, and it climbs from there to S999, which is the machine refusing satl any memory at all.
  2. The next line says what happened on this line. It starts with where satl was: satl(check) means nothing ran, because the whole program is checked before main begins. satl(run) means the program got as far as this line and stopped there. satl.file(check) means something is missing from the whole file.
  3. directory: is the file and the line number.
  4. syntax: is the line as you wrote it, with a caret /\ under the place.
  5. The paragraph says what this kind of refusal means.
  6. The last line is the machine code, which is also the number satl exits with. echo $? shows it.

A few refusals still print a single line instead of a full report. It starts [satellite], says what happened, and ends with the machine code in brackets, as in (machine_code: 27 types_do_not_meet).

The S-codes you will meet most

  • S010 CONFIG_FILE_MISSING, a notice: there is no ~/.satl/config.ini, so every setting is its built-in default. The run carries on.
  • S101 FILE_HAS_NO_INCLUDE, exit 10: the file has no satellite.include(satellite), so it is something another file includes, not a program. The report shows the line to add.
  • S102 FILE_HAS_NO_MAIN, exit 11: there is no satellite.main, so there is nowhere to begin.
  • S103 FILE_HAS_NO_RETURN, exit 12: there is no satellite.return(satellite) to end on.
  • S110 LINE_NOT_UNDERSTOOD, exit 13: the line is spelled in a shape the language has no meaning for. The line above the caret says what satl expected.
  • S120 NUMBER_NOT_READ, exit 3: something that should be a number cannot be read as one, like 1.2.3.
  • S201 NAME_NOT_DECLARED, exit 25: a name was used and nothing declared it.
  • S202 NAME_DECLARED_TWICE, exit 26: a name is declared twice in one capsule, file or namespace.
  • S210 NOT_BUILT_YET, exit 14: the word is right, and there is nothing behind it yet.
  • S220 WORD_TAKES_NO_ASSIGNMENT, exit 35: this is not a place a program can write.
  • S230 MEMBER_IS_PROTECTED, exit 52: a field, or a protected capsule, reached from outside its spacesuit.
  • S240 CAPSULE_GAVE_NO_ANSWER, exit 53: a capsule’s answer was used, and the way it went reached no satellite.return(...) with a value.
  • S250 LIBRARY_VALUE_IS_FIXED, exit 54: a satellite.library value worked out or changed.
  • S301 TYPES_DO_NOT_MEET, exit 27: two kinds of value that have no meaning together, or a value given to a name of another type.
  • S401 DIVISION_BY_ZERO, exit 22: a divisor worked out to 0.
  • S410 NOT_A_POSITION, exit 19: a position or line number below zero.
  • S420 TEXT_NOT_FOUND, exit 15: the text looked for is not in the string.
  • S501 LINE_PAST_THE_END, exit 47: an item, a line or a key that is not there.
  • S503 FILE_NOT_OPEN, exit 45: a question asked of a file that is not open.
  • S504 FILE_NOT_SAVED, exit 44: changes to a file could not be put on the disk. The file is as it was before them. When this happens as a capsule ends, satl says it in one [satellite] line.
  • S721 THREAD_NEEDS_A_CAPSULE_CALL, exit 56: satellite.thread.new was given something that is not a call.
  • S722 THREAD_ALREADY_STARTED, exit 57: a thread started a second time.
  • S723 JOIN_BEFORE_START, exit 58: a thread joined before it was started.
  • S724 THREAD_ALREADY_JOINED, a notice: a second join gave back the same answer again. The run carries on.
  • S727 THREAD_CANNOT_SHARE_YET: a window handed to a thread, or a window word used on one.
  • S728 WAIT_NEVER_ENDS, exit 63: two threads each waiting for the other.
  • S730 NO_DISPLAY, exit 50: a window was asked for on a machine with no screen.
  • S830 INPUT_ENDED, exit 55: satellite.console.input() waited, and the input has ended.

Here is S240, from a capsule that answers only some of the time. half_of(10) prints 5, and then half_of(0) stops the program with this report:

half_of.satlSATELLITE 004 Download
A capsule that hands nothing back for 0
satellite.include(satellite)

satellite.capsule half_of(satellite.variable.number n)
{
    satellite.statement.if (n > 0)
    {
        satellite.return(n / 2)
    }
}

satellite.capsule satellite.main()
{
    satellite.console.display(half_of(10))
    satellite.console.display(half_of(0))
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S240: CAPSULE_GAVE_NO_ANSWER
satl(run): half_of's answer is used, and it ended without handing one back --
the way it went reached no satellite.return(...) with a value

directory: half_of.satl:3
syntax: satellite.capsule half_of(satellite.variable.number n)
        /\

this capsule's answer was used, and the way it went reached no
satellite.return(...) -- so there was nothing to hand back.
machine code 53 capsule_gave_no_answer -- satl exits with this.

--------------------------------------------------------------------------------

And S101, which is about the whole file, so it has no caret:

forgot_the_include.satlSATELLITE 004 Download
The first line is missing
satellite.capsule satellite.main()
{
    satellite.console.display("hi")
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S101: FILE_HAS_NO_INCLUDE
satl.file(check): this file has no satellite.include(satellite), so it is a
spaceship and not a program

directory: forgot_the_include.satl

a program's first line is satellite.include(satellite). Without it the file is a
SPACESHIP -- something another file includes -- and satl will not start it. Add
this as the first line:

    satellite.include(satellite)
machine code 10 satl_file_missing_satellite_include_satellite -- satl exits with
this.

--------------------------------------------------------------------------------

THE SATL COMMAND

satl is satellite’s interpreter: the one command that runs your programs. More in THE SATL COMMAND and HOW SATELLITE 004 RUNS.

satl file.satl

satl hello.satl runs a program. Every word after the file name is the program’s own, and arrives in the arguments variable as argument_1, argument_2 and so on, even words that look like satl’s options. satl writes the program’s own output to standard output and its title block and reports to standard error.

Each run also writes hello.sate beside hello.satl: the program as satellite’s numbers, one 16-bit code a line.

BASH Download
satl hello.satl
satl hello.satl one "two words"

satl –run

satl --run file [words...] runs a program exactly as satl file does. Use it when the file’s name begins with -, which satl would otherwise read as an option and refuse.

BASH Download
satl --run -draft.satl

satl –debug

satl --debug file.satl [words...] runs the program and shows every state and argument as it goes, one [satellite] line each, among them every row of the arguments variable:

TEXT Download
[satellite] arguments.program = hello.satl (machine_code: 0 success)
[satellite] arguments.argument_1 = one (machine_code: 0 success)
[satellite] arguments.argument_2 = two (machine_code: 0 success)
[satellite] arguments.length = 3 (machine_code: 0 success)

--debug and --console come before the file, --run or --repl.

satl –repl

satl --repl is the prompt: type a line, see it run. Besides satellite lines, the prompt has words of its own: interpret file.satl and run file.satl run a program as satl file.satl does, and exit ends the session. satellite.help is asked here. Lines can also be piped in, and then the session’s exit status is that of the first line that fails.

BASH Download
satl --repl

satl –console

satl --console [file.satl] [words...] runs your program, or with no file the prompt, in a console window of satl’s own. satl’s input and output are that console, in the same process, so the exit status is still your program’s. A program that finishes closes it. One that stops holds it open, with the machine code it stopped on, until you press a key. satl opens this console by itself when nothing gave it one: no terminal, output that is not a pipe or a file, and a screen. Set SATL_NO_WINDOW to stop it doing that. With no screen, satl --console says the console could not be opened, and exits 50.

satl –rebuild

satl --rebuild composes every setting in ~/.satl/config.ini into one features = b... row, which is what satl reads when it starts. Run it after you change a switch such as access or history. It lists what is turned on, and says which switches are not built yet.

satl –config

satl --config [most] measures, once, how many threads this machine can hold, and keeps the count in ~/.satl/machine.conf. Until it has run, machine.threads answers the lowest of the thread limits the system states.

satl –feedback

satl --feedback shows what satellite.feedback has kept on this machine, with when each entry was written and the file it is kept in. It holds what you typed and when, and nothing about you or your machine.

satl –license

satl --license shows every licence in the binary: satellite’s own MIT licence, and the licences of the other projects built into satl. satl --license n or satl --license name shows one, and satl --license all shows them all. --licence, --licenses and --licences work too. The licences are listed on the LICENSES page.

satl –version and satl –help

satl --version, or -V, prints the version, revision and build. satl --help, or -h, lists every way to start satl, a first program to type in, and how to read a report. Each of these is the whole command line. satl alone prints a short list of the ways to start it.

BASH Download
satl --version

Its first two lines, from the build this page was checked against:

TEXT Download
THE SATELLITE PROGRAMMING LANGUAGE
VERSION 004 REVISION 08 BUILD 0043

Exit status

satl exits with the machine code the program stopped on, and 0 when it ran to the end. A code below 0 or above 254 exits 255 and is written in full on standard error. A file satl cannot find exits 8, and a word satl does not take on its command line exits 23. Ctrl-C stops a program, and the shell sees 130.