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.
(satellite)
satellite.capsule satellite.main()
{
satellite.console.display("Hello, World!")
satellite.return(satellite)
}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 anifline 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:Applesandapplesare two different names. - A name that reads as a value is taken as that value.
b101is a binary andxbeefis a hex, so neither can be a name.beef,b2andx_countare fine.
(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)
}7(satellite)
satellite.capsule satellite.main()
{
satellite.console.display("one") satellite.console.display("two")
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.satl -- a comment can even come before the first line
(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)
}5
a // inside a string is textsatellite.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.
satellite.capsule satellite.main()
{
satellite.console.display("never shown")
satellite.return(satellite)
}--------------------------------------------------------------------------------
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)andsatellite.include("shapes.satl")both findshapes.satlin the same folder as your program. The.satlon 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:
(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)
}shapes.satl, run on its own
6And 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:
(satellite)
(shapes)
satellite.capsule satellite.main()
{
satellite.console.display(shapes.area(4, 5))
satellite.return(satellite)
}20A file that is not there is refused before anything runs:
(satellite)
(fences)
satellite.capsule satellite.main()
{
satellite.return(satellite)
}[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.namespaceis 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.capsuleandsatellite.main()is not a capsule at all: satl stops with “no satellite.main to begin in”.
(satellite)
satellite.capsule start()
{
satellite.console.display("never shown")
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.programis the file you ran, andarguments.lengthcounts the words on the command line, the program included.satl main_arguments.satl one twogivesarguments.argument_1=one,arguments.argument_2=twoandarguments.length= 3.- A name that is not one of its rows is refused where it is read.
(satellite)
satellite.capsule satellite.main(satellite.variable.arguments arguments)
{
satellite.console.display(arguments.program)
satellite.console.display(arguments.length)
satellite.return(satellite)
}main_arguments.satl
1Every 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).
(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)
}hello, world
hello, world
42A 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.
(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)
}2
1Each 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.
(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)
}--------------------------------------------------------------------------------
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.
--------------------------------------------------------------------------------(satellite)
satellite.capsule twice(satellite.variable.number n)
{
satellite.return(n * 2)
}
satellite.capsule satellite.main()
{
satellite.console.display(twice("ten"))
satellite.return(satellite)
}[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.
(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)
}below zero
zero
above zero(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)
}--------------------------------------------------------------------------------
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 asatellite.namespace toolsis calledtools.twice(21).
(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)
}16
10
16Using the answer of a capsule that can never give one is refused before anything runs:
(satellite)
satellite.capsule say_hi()
{
satellite.console.display("hi")
}
satellite.capsule satellite.main()
{
satellite.variable.number n = say_hi()
satellite.return(satellite)
}--------------------------------------------------------------------------------
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
ifor anelse, or followed only bysatellite.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 insidesatellite.return(...), such assatellite.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.
(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)
}lift off
15511210043330985984000000satellite.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()andsatellite.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.
(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)
}64
0
1
ending early(satellite)
satellite.capsule satellite.main()
{
satellite.console.display("never shown")
}--------------------------------------------------------------------------------
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
ifor 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.
(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)
}warm(satellite)
satellite.capsule satellite.main()
{
satellite.statement.if (5)
{
satellite.console.display("never shown")
}
satellite.return(satellite)
}[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).
(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)
}a passsatellite.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.
(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)
}1
3
9
27
81satellite.statement.for
satellite.statement.for counts. It has three parts between its brackets, divided by two semicolons:
- A declaration of its counter, always a
satellite.variable.number:satellite.variable.number i = 0. - A condition, read before every turn:
i < 3. It cannot be left empty. - 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 ^ 2ori ** 2, with a space on both sides of the sign, ori++andi--. 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).
(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)
}1
2
3
10
6
2
2
4
16
256(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)
}--------------------------------------------------------------------------------
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
borxin front of digits makes a binary or a hex:b1010,xFF.
(satellite)
satellite.capsule satellite.main()
{
satellite.console.display(4+3)
satellite.return(satellite)
}--------------------------------------------------------------------------------
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 / 3is 6. It cuts toward zero, so-7 / 2is -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 % 2is -1.- Dividing by zero, with
/or%, is refused (S401). - A whole number has no size limit and never wraps around.
(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)
}5
6
42
6
2
-3
-1
2.5
100000000000000000000000(satellite)
satellite.capsule satellite.main()
{
satellite.console.display(1 / 0)
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.
(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)
}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 == 10and2.0 == 2are 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 < 3is refused.
(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)
}true
true
false
true
true
true
true
true(satellite)
satellite.capsule satellite.main()
{
satellite.console.display(5 == "5")
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.
(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)
}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.
(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)
}working age(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)
}[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.
(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)
}n = 4
n = 4(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)
}6
3.5
4 applesThe order of operations
When a line has more than one operator, satellite works them out in this order:
- Brackets, from the inside out.
- A touching
-and a!, which belong to the value they touch. So-2 ^ 2is 4; write0 - 2 ^ 2for -4. ^and**, right to left.*,/and%, left to right.+and-, left to right.- The comparisons, last of all, so
1 + 2 < 2 + 2compares 3 with 4.
(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)
}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.
(satellite)
satellite.capsule satellite.main()
{
satellite.variable.number total
total = 10
total = total + 5
satellite.console.display(total)
satellite.return(satellite)
}15(satellite)
satellite.capsule satellite.main()
{
satellite.variable.number total = 10
total += 5
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.variableon its own, with no kind, is refused.
(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)
}3
Ada
true(satellite)
satellite.capsule satellite.main()
{
satellite.variable.number count = "three"
satellite.return(satellite)
}[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 + 1and"a" + satellite.bool.trueare refused, and a bool name refuses 1 and"true". .string(also.to_stringand.str) is its text,trueorfalse. Write it on a name,ready.string, or on something in brackets,(3 > 2).string; straight after the word,satellite.bool.true.stringis refused..number,.binary,.hexand.reverse()are refused.
(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)
}true
false
true
falsesatellite.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_stringand.str): its digits as text..number(also.to_numberand.as_number): the number itself..binary(also.binand.as_binary): its base-2 digits as text..hex(also.as_hexand.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.
(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)
}100
87
1010111
57
78
100
-87
1180591620717411303424(satellite)
satellite.capsule satellite.main()
{
satellite.variable.number n = 87
satellite.console.display(n.round)
satellite.return(satellite)
}--------------------------------------------------------------------------------
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): wherexfirst 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.strand.to_string): the text, unchanged..number(also.to_numberand.as_number): reads a whole number out of the text, and refuses anything else,"3.5"and" 42"included (S120)..binary(also.binand.as_binary) and.hex(also.as_hexand.to_hex): read that same whole number and answer its base-2 or base-16 digits as text. So"1010".binis 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.
(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)
}hello world!
hello world!
hell world
6
dlrow olleh
→olléh
43
FF
true
true
tab here(satellite)
satellite.capsule satellite.main()
{
satellite.variable.string s = "hello world"
satellite.console.display(s.find("moon"))
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.
(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)
}status: OK
careful
falsesatellite.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.3is true. - It keeps 128 places after the point and 4096 digits before it. Those are
arguments.float.decimalandarguments.float.whole, and you can change either for your machine in~/.satl/config.ini, with a line such asfloat.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.50shows12.5, and2.0shows2.0. + - * / %and^work between two floats, and between a float and a whole number either way round.4 / 3is still whole-number division; write4.0 / 3for the float.^takes a whole-number power only.- A whole number given to a float name becomes a float:
satellite.variable.float f = 3holds3.0. A float given to a number name is refused. .string(also.to_stringand.str) is its text..number(also.to_numberand.as_number) is the whole number when there is nothing after the point;12.5.numberis 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.
(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)
}24.1
12.55
1.33333333333333333333333333333333
1
true
12.5
3.0
3
128(satellite)
satellite.capsule satellite.main()
{
satellite.console.display(12.5.number)
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.
.stringis how it shows..number,.binaryand.hexare 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.
(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)
}(infinity)
(-infinity)
true
true
true
false
(infinity)(satellite)
satellite.capsule satellite.main()
{
satellite.console.display(satellite.infinity().number)
satellite.return(satellite)
}--------------------------------------------------------------------------------
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/4shows2/4. But two fractions compare by what they are worth, so2/4 == 1/2is true and1/3 < 1/2is true. A fraction also compares with a whole number:1/3 < 1is true. - Each part may have a point in it:
1.5/2shows1.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:
3shows3/1. .numeratorand.denominatorare its two parts, and.stringis 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,.hexand.reverse().
(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)
}1/3
2/4
true
true
1
3
1.5/2
-1/3
0
3/1(satellite)
satellite.capsule satellite.main()
{
satellite.variable.fraction third = 1 / 3
satellite.return(satellite)
}--------------------------------------------------------------------------------
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%is0.3%and100% / 3is33.33333333333333333333333333333333%. There is no ceiling above. - The
%is required. A percentage name given50is refused before anything runs:ERROR: expected 50%. - A percentage compares only with another percentage:
50% < 75%is true, and50% == 50is refused.
What each pair answers:
50% + 25%is75%, and50% - 25%is25%: two percentages stay a percentage.50% * 50%is25%: a percentage of a percentage.50% / 4is12.5%: a share of it.200 * 50%and50% * 200are both 100: a number and a percentage answer a number.200 - 50%is 100 (reduced by it), and200 + 50%is 300 (grown by it).200 / 50%is 400: what 200 is 50% of.- For
+and-the number goes first.50% + 5is refused: write5 + 50%. %and^with a percentage are refused, and so is50% / 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 * 2is refused when it runs, because that answers a number. .stringis its text..number,.binary,.hexand.reverse()are refused.
(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)
}50%
100
100
96
75%
12.5%
33.33333333333333333333333333333333%
true
-25%(satellite)
satellite.capsule satellite.main()
{
satellite.console.display(50% + 5)
satellite.return(satellite)
}--------------------------------------------------------------------------------
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
b0011is notb11. There is no length limit. - The
bis required. A binary name given1010, or C’s0b1010, is refused before anything runs:ERROR: expected b1010.b12is refused: a binary digit is 0 or 1. - A binary keeps a sign:
-b0101is worth -5, keeps its width, and is not equal tob0101.-b0000isb0000, because zero has no sign. - Arithmetic reads a binary by what it is worth, and answers a plain number:
b1000 + b0101is 13, andb1010 == 10is true. A number name givenb0011holds 3. Keeping a sum in a binary name is refused, because the sum is a number. .number(also.to_numberand.as_number) is what the bits are worth..string(also.to_stringand.str) is the binary as text,band all..binary(also.binand.as_binary) is the digits without theb..hex(also.as_hexand.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,.digitsand.clear, reading one bit with[n], and a binary meeting a float.
(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)
}b0010
false
2
b0010
0010
AA
b0001
13
-b0101
-5
true(satellite)
satellite.capsule satellite.main()
{
satellite.variable.binary flags = 1010
satellite.return(satellite)
}--------------------------------------------------------------------------------
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
xis required. A hex name given1Fis refused before anything runs:ERROR: expected x1F. C’s0xFFis refused too. - A hex keeps the width it was written with, so
x00FFandxFFare two different values worth the same. It keeps a sign:-x1Fis worth -31. - In a sum a hex counts as what its digits are worth, and the answer is a number:
x1F + 1is 32, andx1F == 31is true. So a hex name holds only a hex:h = h + x01is refused, and the refusal tells you what to write instead. A hex given to a number name keeps its worth. .number(also.to_numberand.as_number) is what it is worth..string(also.to_stringand.str) is how it shows..hex(also.as_hexand.to_hex) is its digits, without thex..binary(also.binand.as_binary) is its bits, four for every digit..widthcounts those bits..reverse()turns its digits round, width and sign kept..add(x)is+ x.- Refused:
.digits,.clearand.to_binary, and a hex meeting a float, a percentage or a colour.
(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)
}x00FF
256
false
255
00FF
0000000011111111
16
xFF00
-x1F(satellite)
satellite.capsule satellite.main()
{
satellite.variable.hex h = x01
h = h + x01
satellite.return(satellite)
}[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
xis optional:000000,x000000,ff00aaand00ff00all work. Five or seven digits are refused before anything runs. - On a later line,
c = x00ff00always works, and so doc = 123456andc = abcdef. Digits that start with a number and hold a letter, likec = 00ff00, need theirx, 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.transparencyreads it back, and a colour with one shows asx000000, 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..numberis what the six digits are worth,.hexis the six digits,.binaryis its 24 bits, and.stringis how it shows. Each has the same second spellings as a number’s.
(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)
}x87CEEB
x000000, 50
40
x87CEEB, 40
8900331
87CEEB
100001111100111011101011
false(satellite)
satellite.capsule satellite.main()
{
satellite.variable.color sky = 87ceb
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.
(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)
}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.
(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)
}43
-42
3
256
8900331
12(satellite)
satellite.capsule satellite.main()
{
satellite.console.display("3.5".number)
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.
(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)
}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.
(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)
}FF
FF
AA
00FF
87CEEBGiving 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:
b0011becomes 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.
(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)
}3
255
3.0
3/1
5The 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.
(satellite)
satellite.capsule satellite.main()
{
satellite.console.display(string(87))
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.indexis 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. Withkey: valueentries 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 = acopies it, so changingbleavesaalone. 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.
(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)
}{1, 2}
{1, 2, 3}
true
true(satellite)
satellite.capsule satellite.main()
{
satellite.container.list a = {1, 2}
satellite.container.list b = {3}
satellite.console.display(a < b)
satellite.return(satellite)
}--------------------------------------------------------------------------------
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,.appendand.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].
(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)
}{"zoe", "al", "bo"}
zoe
3
31
5
101A list that says what it holds refuses anything else:
(satellite)
satellite.capsule satellite.main()
{
satellite.container.list<satellite.variable.number> scores = {10, 20}
scores.append("thirty")
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.
(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)
}{}
{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 annpast 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.
.appenddoes that.
(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)
}one
{"one", "CHANGED", "three"}
three(satellite)
satellite.capsule satellite.main()
{
satellite.container.list names = {"one", "two"}
satellite.console.display(names[5])
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.
(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)
}{"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 isx..remove_at(n)takes out itemn..remove_first()and.remove_last()take out the first and the last item.
The items after a removed one move up.
(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)
}{"pear", "kiwi", "fig", "lime", "plum"}
{"kiwi", "fig", "lime", "plum"}
{"fig", "lime"}.truncate(n), .clear and .reserve(n)
.truncate(n)keeps the firstnitems. A number past the end keeps them all..clearempties the list. It works with or without()..reserve(n)makes room fornitems at once and changes no item. It is for speed when you know a list will grow large.
(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)
}{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.
.sizeis how many items there are..emptyis true when there are none..firstand.lastare the first and the last item.
(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)
}3
false
10
30
40.contains(x), .index_of(x) and .search(x)
.contains(x)is true when some item isx..contain(x)is a second spelling..index_of(x)is the position of the first item that isx, or 0 when there is none..search(x)is the position of the first item whose text containsx, or 0.
Because items count from 1, an answer of 0 can never be mistaken for a position.
(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)
}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.
(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)
}{"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)
.sumadds every item with+. A list of nothing sums to 0. A list of strings is refused, and the report points you at.join..maxand.minare 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, withseparatorbetween them.
(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)
}6
3
1
3, 1, 2
zoo
pear/apple/zoo
0LISTS 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>>.
(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)
}{{1, 2}, {3}}
3
{{1, 20}, {3}}
21INDEXES 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"] = 4files 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.
1and"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.
(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)
}{"zoe": 31, "al": 4, "bo": 100}
4
{"red": {"ann", "bo", "cy"}}
bo(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)
}--------------------------------------------------------------------------------
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
.keysis every key, as a list, in the order they went in..valuesis every value, in the same order..contains(k)is true whenkis a key..size,.emptyand.clearwork as they do on a list..firstand.lastare 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(", ").
(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)
}{"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: valueentries,{"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.
(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)
}{"zoe": 30, "al": 4, "bo": 100}
{{"a": {1, 2}}, {"b": {3, 4}}}
2MULTIPLE 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:
b101staysb101.
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.
(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)
}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:
(satellite)
satellite.capsule satellite.main()
{
satellite.container.multiple<satellite.variable.string, satellite.variable.number> either = "a"
either = 50%
satellite.return(satellite)
}[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.
(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)
}hello from satl, 1
hello from satl, 2satellite.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.nameis 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.
(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)
}--------------------------------------------------------------------------------
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.
(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)
}10satellite.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.
(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)
}--------------------------------------------------------------------------------
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 = hostnames 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.displaycannot show a whole object yet (S210), and==between two objects is refused (S301). Display what its public capsules answer instead.
(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)
}a red lamp
a blue lamp
2satellite.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
animalname may hold adog. - 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.
(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)
}animal's constructor
dog's constructor
Rex says woof
woofA 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.
(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)
}kestrel has 900 power
40FILES 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 capsulegreetinhelpers.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.greetingreads 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.
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)
}
}
}(satellite)
("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)
}my own greet, for you
hello, you
49
green
helloWithout its own greet, the bare call is refused before anything runs:
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 isname.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.
(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)
}hello from the file
tools.add says 5
tools.twice says 10
tools.inner.deep
hello from the file(satellite)
satellite.space tools
{
satellite.variable.number n = 5
}
satellite.capsule satellite.main()
{
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.trueorsatellite.bool.false, with a minus sign in front if it has one. A colour is written as the hex it holds, likex1E2A3A. - Nothing runs outside a capsule, so
= 1 + 2or= helper()at the top of a file is refused with S250. - Nothing changes a value.
satellite.library.rounds = 5inside 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.
(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)
}rounds counted
4
-12.5
true
20%
1/2
x1E2A3A
5(satellite)
satellite.library.span = 25
satellite.capsule satellite.main()
{
satellite.library.span = 30
satellite.return(satellite)
}--------------------------------------------------------------------------------
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:
- satl’s own built-in rows, such as
version,float.decimalandinfinity. ~/.satl/config.ini, your own file: onekey = valuea line, with#for comments.- The command line:
satl prog.satl one twogivesprogram,argument_1,argument_2andlength, which is 3 because the program is counted. - The machine, read fresh each time a program asks:
username,memory.total,memory.used,cpu.architectureand 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.
(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)
}reading_rows.satl
1
128
true
64The command line arrives as rows too. Run with no words after the file name, length is 1, because the program itself is counted:
(satellite)
satellite.capsule satellite.main(satellite.variable.arguments arguments)
{
satellite.console.display(arguments.program)
satellite.console.display(arguments.length)
satellite.return(satellite)
}words.satl
1Each word typed after the file name becomes a row of its own, counting from 1. A word in quotes stays one word:
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:
words.satl
3
one
two wordsWriting 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.
(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)
}6
first(satellite)
satellite.capsule satellite.main(satellite.variable.arguments args)
{
args.username = "me"
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.
(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)
}little
64
trueHISTORY 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.
(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)
}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 listThe 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 rowaccess = trueoraccess = falsein~/.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.trueorsatellite.bool.falseand nothing else.= 5stops 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.
(satellite)
satellite.capsule satellite.main()
{
satellite.statement.if(satellite.library.main.arguments.access)
{
satellite.console.display("access is on")
}
satellite.return(satellite)
}access is onWriting
satellite.library.main.arguments.access = satellite.bool.falsechanges your~/.satl/config.inifor 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.
(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)
}--------------------------------------------------------------------------------
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)orsatellite.help(satellite.include)brings up one topic.- A topic can be named by its whole name, by its last part, by a second spelling (
doublefindssatellite.variable.float,colourfindssatellite.variable.color), or by the start of its last part (percentagefindssatellite.variable.percent). - When two topics answer, as
satellite.windowandsatellite.variable.windowboth do forwindow, 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.
satl --replsatellite.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:
(satellite)
satellite.capsule satellite.main()
{
satellite.help(capsule)
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.
(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)
}hello
5
true
{1, "two"}
1.5
50%
3/4
b101
falseAfter 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=xFF8800is the colour of the text.background=x000000is the colour behind it.bold=satellite.bool.truemakes it bold.italic=satellite.bool.truemakes 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.
(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)
}Loading... done
abcforeground=, 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.
(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)
}careful
slanted
amber(satellite)
satellite.capsule satellite.main()
{
satellite.console.display("hello", foreground=xFFF)
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.
(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)
}green
still green
on black
plain againtext.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.
(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)
}status: OK
warn
falsesatellite.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.
(satellite)
satellite.capsule satellite.main()
{
satellite.variable.string name = satellite.console.input("What is your name? ")
satellite.console.display("Hello, " + name + "!")
satellite.return(satellite)
}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:
(satellite)
satellite.capsule satellite.main()
{
satellite.variable.string name = satellite.console.input("What is your name? ")
satellite.console.display("Hello, " + name + "!")
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.
(satellite)
satellite.capsule satellite.main()
{
satellite.console.display(satellite.console.width)
satellite.console.display(satellite.console.height)
satellite.return(satellite)
}80
24satellite.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.
(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.displaystill 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, andc.typed, read bare, is that line.c.clear()andc.home()empty its screen and move its cursor home.c.columnsandc.rowsare how many characters fit across and lines fit down.c.colour("#000000"),c.background("#90D5FF")andc.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.
(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()andsatellite.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_COLORis 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.terminalon its own, and a shell prompt in a window, are not built yet.
(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)
}green on near-black, the whole terminal
and back againFILES
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.
(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)
}true
false
false
once upon a timesatellite.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.
(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)
}true
false
true
0f[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.
(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)
}1: roses are red
2: violets are blue
3: satellite counts lines from one(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)
}--------------------------------------------------------------------------------
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 linenintox.f.replace(a, b), with text first, changes the firstain the file intob, 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.
(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)
}line_1
line_2
line_2
line_1
line_3
line_4f.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.
(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)
}d
e
1
d
truef.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 exactlyx.f.index_of(x)is the number of the first line that is exactlyx, or 0.f.search(x)is the number of the first line that containsxanywhere, or 0.
A search with no match answers 0 and never stops the program, and 0 is never a line.
(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)
}3
false
buy milk
water the plants
true
2
3
0f.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.saveputs every change on the disk now, whole or not at all, and answers whether it did.f.closesaves, then closes. False means the changes did not land, andf.errorsays why.f.openopens 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.okis whether the file is open.f.erroris why the last thing failed, in words, with the file’s full path in it.f.pathis the full path the file was opened on.f.existsis whether that path is still a file.
(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)
}true
true
true
false
true
true
firstA question asked of a file that is not open stops with S503. Ask f.ok first when a file might not be open:
(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)
}--------------------------------------------------------------------------------
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.
(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)
}2The 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.displayshows 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.okis 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 = 5stops 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.
(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 manysatellite.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 onsatellite.window.picture(path)satellite.window.row(),satellite.window.column(),satellite.window.grid()to arrange piecessatellite.window.scroll(),satellite.window.frame(title),satellite.window.split(),satellite.window.tabs()to hold themsatellite.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, 300is 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..acrossand.downsay 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.
(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.
(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)
}(thread add_up, not started)
500500
(thread add_up, finished)
false(satellite)
satellite.capsule work()
{
satellite.console.display("working")
}
satellite.capsule satellite.main()
{
satellite.variable.thread t = satellite.thread.new(work)
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.
(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)
}(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.
(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)
}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.
(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)
}--------------------------------------------------------------------------------
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,whileandforhold 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.
(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)
}4000S728: 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
(satellite)
satellite.capsule satellite.main()
{
satellite.variable.number n = 10
satellite.variable.number zero = 0
satellite.console.display(n / zero)
satellite.return(satellite)
}--------------------------------------------------------------------------------
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:
S401: DIVISION_BY_ZEROis 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.- 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. directory:is the file and the line number.syntax:is the line as you wrote it, with a caret/\under the place.- The paragraph says what this kind of refusal means.
- 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.libraryvalue 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.newwas 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:
(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)
}--------------------------------------------------------------------------------
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:
satellite.capsule satellite.main()
{
satellite.console.display("hi")
satellite.return(satellite)
}--------------------------------------------------------------------------------
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.
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.
satl --run -draft.satlsatl –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:
[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.
satl --replsatl –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.
satl --versionIts first two lines, from the build this page was checked against:
THE SATELLITE PROGRAMMING LANGUAGE
VERSION 004 REVISION 08 BUILD 0043Exit 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.
