Author: Claude Code

0

HELLO, WORLD: YOUR FIRST SATELLITE PROGRAM

Every programming language starts the same way: a program that says hello. In satellite that program is six short lines, and three of them are the frame that every satellite program has. Learn the frame once and you can write anything inside it.

The whole program

Here it is. Type it into any plain text editor, exactly as it is shown.

hello.satlSATELLITE 004 Download
The first program: it says hello
satellite.include(satellite)

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

What each line does

  • satellite.include(satellite) says “this file is a program”. It is always the first line. A file without it is a spaceship: a file that other files include, which satl will not start by itself.
  • satellite.capsule satellite.main() is where your program begins. A capsule is satellite’s word for a function, and satellite.main is the one every program starts in. Everything your program does happens between its { and its }.
  • satellite.console.display("Hello, World!") writes a line of text on the screen. The text goes between double quotes.
  • satellite.return(satellite) is where your program ends. Its place is the last line of satellite.main, and when satl reaches it the program is finished.

You may already have spotted the pattern in the names. A dotted path that starts with satellite names something the language owns. satellite.console.display belongs to satellite, and so do satellite.main and satellite.return. A bare name, with no satellite in front, is something you own: a variable or a capsule you made yourself. You will see this rule everywhere, and it means you can always tell at a glance which words are yours.

Saving and running it

Save the file as hello.satl. The name is up to you, but it must end in .satl, and a plain text file is all it is. Then open a terminal in the folder where you saved it and type:

BASH Download
Run the program
satl hello.satl

satl is the satellite interpreter: it reads your program and runs it. It first writes a short banner with the language’s name and its version, and then your program’s output, Hello, World!. If satl is not found, see the INSTALL page.

You may notice a new file beside your program called hello.sate. That is satl writing your program out in the language’s own numbered codes as it reads it. Nothing asks you to open it, and it is safe to delete.

A little more

Inside satellite.main you can write as many lines as you like, and they run from top to bottom. Two slashes, //, start a comment: a note for people, which satl skips.

greeting.satlSATELLITE 004 Download
Comments, a sum and a variable
satellite.include(satellite)

// A comment runs from two slashes to the end of the line.
satellite.capsule satellite.main()
{
    satellite.console.display("Hello, World!") // the classic
    satellite.console.display(6 * 7)
    satellite.variable.string name = "Ada"
    satellite.console.display("Hello, " + name + "!")
    satellite.return(satellite)
}
OUTPUT
Hello, World!
42
Hello, Ada!

satellite.variable.string name = "Ada" makes a variable: a named box that holds a value. The type comes first (satellite.variable.string, some text), then your name for it, then its value. The + joins pieces of text together. Notice the spaces around * and +: satellite asks for a space on both sides of every math sign.

When something is missing

satl checks your whole file before it runs a single line. If one of the three frame lines is missing, it says which one, and it tells you what to type. Here is the hello program with its last line forgotten:

forgot.satlSATELLITE 004 Download
A program with no satellite.return is refused before it starts
satellite.include(satellite)

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

directory: forgot.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.

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

Nothing was printed, not even the hello: satl stopped before the program began. Every error report looks like this one. It gives a code (S103), what went wrong in plain words, where it happened, and what to do about it. The post READING AN ERROR REPORT walks through one line by line.

A program always has all three: satellite.include(satellite) at the top, satellite.capsule satellite.main() where it begins, and satellite.return(satellite) where it ends.

Where next

Everything the language has is in the FULL REFERENCE. To write capsules of your own, read CAPSULES: SATELLITE FUNCTIONS. For more about printing, read DISPLAYING TEXT, and for everything satl can do from the command line, read THE SATL COMMAND.

1

CAPSULES: SATELLITE FUNCTIONS

A capsule is satellite’s word for a function: a piece of work with a name, which you write once and call as often as you like. You have already met one. satellite.main is a capsule, and every program starts inside it. This post shows how to write your own.

Writing a capsule

A capsule is declared at the top level of a file, beside satellite.main. It has a name, its parameters in brackets, and its body between { and }. Each parameter is written the way every declaration in satellite is written: the type first, then the name. Commas go between them, and a capsule that takes nothing has empty brackets.

When a capsule gives an answer back, satellite.return(value) inside its body hands the answer back and ends the capsule on the spot. Nothing after the brackets says what kind of answer it is: a capsule answers whatever it hands back.

rectangle.satlSATELLITE 004 Download
Two capsules: one that answers, one that just does something
satellite.include(satellite)

satellite.capsule area(satellite.variable.number width, satellite.variable.number height)
{
    satellite.return(width * height)
}

satellite.capsule satellite.main()
{
    satellite.variable.number floor = area(4, 5)
    satellite.console.display(floor)
    satellite.console.display(area(2, 3) + area(1, 1))
    describe("kitchen", area(3, 3))
    satellite.return(satellite)
}

satellite.capsule describe(satellite.variable.string room, satellite.variable.number size)
{
    satellite.console.display("the " + room + " is " + size.string + " square metres")
}
OUTPUT
20
7
the kitchen is 9 square metres

A few things to notice:

  • You call a capsule by its name with its arguments in brackets. A capsule that answers can be called inside any line, as area(4, 5) is, and one that answers nothing is called on a line of its own, as describe(...) is.
  • area and describe are bare names, because they are yours. satellite.main and satellite.return start with satellite, because they belong to the language.
  • describe is written after satellite.main, and that is fine: the order capsules are declared in does not matter.
  • describe has no satellite.return. A capsule that answers nothing may simply reach its closing }.

What a capsule can see

A capsule runs in a space of its own. It cannot see the variables of the capsule that called it, and there are no global variables in satellite either, so what you hand a capsule is all it has. That keeps every capsule honest: to know what it works with, you only have to read its brackets.

scope.satlSATELLITE 004 Download
A capsule cannot reach into its caller's variables
satellite.include(satellite)

satellite.capsule show_score()
{
    satellite.console.display(score)
}

satellite.capsule satellite.main()
{
    satellite.variable.number score = 10
    show_score()
    satellite.return(satellite)
}
OUTPUT
--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S201: NAME_NOT_DECLARED
satl(check): in show_score, score has no satellite.variable line declaring it

directory: scope.satl:5
syntax: satellite.console.display(score)
        /\

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.

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

The fix is to hand the score over: declare show_score(satellite.variable.number score) and call show_score(score).

satl measures every call, too. A call with the wrong number of arguments is refused before the program starts (S110), and an argument of the wrong type is refused when the call is reached: calling area("four", 5) stops with “area’s width was declared satellite.variable.number, and it holds a string”.

A capsule that calls itself

A capsule may call itself. This is called recursion, and satellite treats it in two ways, depending on where the call stands.

When the call to itself is the very last thing the capsule does, it is a loop. satl does not stack up a new level for each call; it simply goes round again. Such a capsule can go as deep as you like: here the second capsule goes a hundred thousand levels down.

countdown.satlSATELLITE 004 Download
A call to itself as the last line is a loop, however deep it goes
satellite.include(satellite)

satellite.capsule count_down(satellite.variable.number n)
{
    satellite.console.display(n)
    satellite.statement.if (n > 0)
    {
        count_down(n - 1)
    }
}

satellite.capsule dive(satellite.variable.number depth)
{
    satellite.statement.if (depth == 0)
    {
        satellite.console.display("reached the bottom")
    }
    satellite.statement.else
    {
        dive(depth - 1)
    }
}

satellite.capsule satellite.main()
{
    count_down(3)
    dive(100000)
    satellite.return(satellite)
}
OUTPUT
3
2
1
0
reached the bottom

“The last thing” can sit inside an if or an else, as both of these do, as long as nothing in the capsule runs after it.

When the call to itself has work after it, it is an ordinary call. satl keeps each level waiting until the one below it finishes, and then carries on with the lines after the call, in order:

climb.satlSATELLITE 004 Download
A call to itself with a line after it runs that line on the way back
satellite.include(satellite)

satellite.capsule climb(satellite.variable.number n)
{
    satellite.statement.if (n > 0)
    {
        climb(n - 1)
    }
    satellite.console.display(n)
}

satellite.capsule satellite.main()
{
    climb(3)
    satellite.return(satellite)
}
OUTPUT
0
1
2
3

Each waiting level takes memory, so this shape uses more than the last-line shape does — a few kilobytes a level — but it has no depth limit of its own: when a program goes deep enough to fill the room it started with, satl moves on to fresh room and keeps going. A million levels deep is a few gigabytes while it is that deep, and all of it is given back on the way up. The only limit is your computer’s memory. The last-line shape needs none of that, so a capsule that has to go very deep is still best written with its call to itself last.

A call written inside satellite.return(...), such as satellite.return(total(n - 1)), is an ordinary call too, because handing the answer back is work left after it.

Where next

The FULL REFERENCE lists every rule for capsules. The loops that capsules usually hold are in IF, WHILE AND FOR, and capsules shared between files and grouped into namespaces are in INCLUDING FILES AND NAMESPACES.

0

NUMBERS

satellite.variable.number holds a whole number: 7, -12, 0, or one with a hundred digits. It is the type you will use most, for counting, for totals, and for anything that comes in whole pieces.

Making a number and doing sums

You write a number as its digits, with a minus touching the front when it is below zero: 34587, -7. The operators are the ones from school, plus two more:

  • + adds, - takes away, * multiplies
  • / divides and keeps the whole part, % gives the remainder
  • ^ raises to a power, and ** is its second spelling

Every operator is written with a space on both sides. 4 + 3 works, and 4+3 is refused.

fruit.satlSATELLITE 004 Download
The operators on two numbers
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number apples = 12
    satellite.variable.number pears = 5
    satellite.console.display(apples + pears)
    satellite.console.display(apples - pears)
    satellite.console.display(apples * pears)
    satellite.console.display(apples / pears)
    satellite.console.display(apples % pears)
    satellite.console.display(apples ^ 2)
    satellite.console.display(2 + 3 * 4)
    satellite.console.display((2 + 3) * 4)
    satellite.return(satellite)
}
OUTPUT
17
7
60
2
2
144
14
20

*, / and % are worked out before + and -, as in ordinary arithmetic, and brackets change the order. ^ is worked out right to left, so 2 ^ 3 ^ 2 is 2 ^ 9, which is 512.

Division keeps the whole part

/ between two whole numbers answers a whole number: 12 / 5 is 2, and % tells you the 2 that was left over. When the numbers are below zero, / cuts toward zero and % takes the sign of the number being divided, so -7 / 2 is -3 and -7 % 2 is -1.

When you want the part after the point, write one of the numbers with a point: 12.0 / 5 is a float and answers 2.4. See FLOATS. Dividing by zero is refused with S401, because there is no number it could answer.

How big a number can be

There is no largest number. A satellite number is built from as many pieces as it needs, and it grows by itself when a result gets big. It never wraps around to a small or negative value the way numbers in many languages do.

big.satlSATELLITE 004 Download
Numbers grow as large as they need to
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display(18446744073709551615 + 1)
    satellite.console.display(2 ^ 100)
    satellite.variable.number product = 1
    satellite.statement.for (satellite.variable.number i = 1; i <= 25; i + 1)
    {
        product = product * i
    }
    satellite.console.display(product)
    satellite.console.display(-7 / 2)
    satellite.console.display(-7 % 2)
    satellite.return(satellite)
}
OUTPUT
18446744073709551616
1267650600228229401496703205376
15511210043330985984000000
-3
-1

The first line is the largest number a common 64-bit integer can hold, plus one. In satellite it is just the next number. Small numbers stay fast: a number only spreads into more pieces when it has to. The only limit is your computer’s memory.

Comparing numbers

== (equal), != (not equal), <, >, <= and >= compare two numbers and answer true or false, which is what an if or a while needs. See IF, WHILE AND FOR.

Turning a number into something else

satellite never converts a number behind your back. "total: " + 5 is refused, because a string and a number do not add up. Instead you say what you want, with a method written after a dot:

  • .string is the number as text (also spelled .to_string and .str)
  • .binary is its digits in base 2 (also .bin), and .hex its digits in base 16
  • .reverse() turns its digits round and keeps the sign
  • .add(x) is the same as + x
convert.satlSATELLITE 004 Download
Saying the conversion out loud
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number score = 87
    satellite.console.display("score: " + score.string)
    satellite.console.display("score: " + score.to_string())
    satellite.console.display(score.binary)
    satellite.console.display(score.hex)
    satellite.console.display(1234.reverse())
    satellite.console.display(score.add(13))
    satellite.console.display(score > 50)
    satellite.return(satellite)
}
OUTPUT
score: 87
score: 87
1010111
57
4321
100
true

A method may be written with or without its empty brackets: score.string and score.to_string() are the same.

What a number refuses

A number name holds only whole numbers. Give one 12.5 and it is refused, “it holds a float”, because that value belongs in a satellite.variable.float. And one shortcut from other languages is not in satellite yet:

plus_equals.satlSATELLITE 004 Download
n += 1 is not built: write n = n + 1
satellite.include(satellite)

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

directory: plus_equals.satl:6
syntax: n += 1
        /\

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 report says what to write instead: n = n + 1. Methods such as .max, .min, .abs and .power are not built for numbers yet either, and satl refuses them by name when you try.

Where next

The FULL REFERENCE has every number word. Numbers with a point in them are in FLOATS, and numbers written in bits or in base 16 are in BINARY AND HEXADECIMAL.

0

STRINGS

A string is a piece of text: a name, a sentence, a line from a file. In satellite it is satellite.variable.string, and you write its value between double quotes.

satellite keeps a string as characters, not as bytes. An accented letter, an arrow or a Japanese character is one character, so turning a string round works the way you would expect, in any language.

Joining, taking away and finding

+ joins two strings into one, and .add(x) does the same. - takes away the first place the right-hand string appears, and leaves the string unchanged when it is not there. .find(x) tells you where x first begins, counting from 0. .reverse() turns the string round.

text.satlSATELLITE 004 Download
Joining, taking away, finding and turning round
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.string s = "hello world"
    satellite.console.display(s + "!")
    satellite.console.display(s.add("!"))
    satellite.console.display(s - "o")
    satellite.console.display(s - "z")
    satellite.console.display(s.find("world"))
    satellite.console.display(s.reverse())
    satellite.console.display("héllo→".reverse())
    satellite.console.display("日本語".reverse())
    satellite.return(satellite)
}
OUTPUT
hello world!
hello world!
hell world
hello world
6
dlrow olleh
→olléh
語本日

s - "o" took away only the first “o”, from “hello”, and left the one in “world”. If .find cannot find what you asked for, it does not answer -1 the way some languages do: it stops with S420, “did not find it”, so a missing piece of text never passes silently as a number.

Strings and numbers

A number added to a string joins it as its digits: "n = " + 4 is n = 4, and so is "n = " + 4.string. With the number first, text that spells a number is added — 4 + "2" is the number 6 — and any other text is joined: 4 + " apples" is 4 apples. Every other conversion you say out loud:

  • a number becomes text with .string (also .to_string and .str)
  • text becomes a whole number with .number (also .as_number and .to_number)
  • .binary (also .bin) and .hex read the number in the text and answer its digits in base 2 or base 16, as a string

Methods chain from left to right, so "87".bin.find("111") reads 87, writes it in binary, and looks for “111” in that.

convert.satlSATELLITE 004 Download
Converting between text and numbers
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.number n = 4
    satellite.console.display("n = " + n.to_string())
    satellite.console.display("42".number + 1)
    satellite.console.display("42".bin)
    satellite.console.display("255".hex)
    satellite.console.display("87".bin)
    satellite.console.display("87".bin.find("111"))
    satellite.return(satellite)
}
OUTPUT
n = 4
43
101010
FF
1010111
4

.number reads only a whole number. "3.5".number is refused (S120), because satellite does not round anything unless you ask it to.

Here is the join with nothing written out, both ways round:

joined.satlSATELLITE 004 Download
A string and a number, either way round
satellite.include(satellite)

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

Comparing strings

== and != ask whether two strings are the same. < and > put them in order using satellite’s own alphabet: the small letters a to z first, then the capitals A to Z, then the digits, then other signs, then the space. So "a" < "B" is true, and "Z" < "a" is false.

order.satlSATELLITE 004 Download
Equal, not equal, and in order
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.string fruit = "apple"
    satellite.console.display(fruit == "apple")
    satellite.console.display(fruit != "pear")
    satellite.console.display("apple" < "banana")
    satellite.console.display("a" < "B")
    satellite.console.display("Z" < "a")
    satellite.return(satellite)
}
OUTPUT
true
true
true
true
false

Good to know

  • A string can carry colour: "OK".foreground(x00FF00) is the word OK in green when it is shown in a terminal. See COLOR ON THE CONSOLE.
  • A backslash starts an escape: \" is a double quote, \\ a backslash, \n a new line, \t a tab, \r a carriage return and \' an apostrophe, so "tab\there" shows tab, a tab, and here. Any other backslash stays in the text as you wrote it.
  • Several string methods are named in the language and not built yet, and satl refuses each one today: among them .size, .upper, .lower, .split, .replace, .trim and .substring, as well as reading one character with s[n].

Where next

The FULL REFERENCE lists every string word. For putting text on the screen, read DISPLAYING TEXT, and for turning numbers into text and back, read NUMBERS.

FLOATS 0

FLOATS

A float is a number with a point in it: a price, a measurement, an average. In satellite it is satellite.variable.float, and satellite.variable.double is its second spelling. You write one the way you read it: 12.34, 0.5, -2.75.

Floats you can trust

In most languages a float is stored in binary, and that makes simple sums come out slightly wrong: 0.1 + 0.2 gives something like 0.30000000000000004. A satellite float is decimal all the way down. It is two whole numbers put together, the digits before the point and the digits after it, so 0.1 + 0.2 is exactly 0.3, and comparing them says so.

prices.satlSATELLITE 004 Download
Sums on floats come out exact
satellite.include(satellite)

satellite.capsule satellite.main()
{
    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(10.0 - 0.01)
    satellite.console.display(0.1 + 0.2)
    satellite.console.display(0.1 + 0.2 == 0.3)
    satellite.console.display(price < 13)
    satellite.return(satellite)
}
OUTPUT
24.1
12.55
9.99
0.3
true
true

+, -, *, /, % and ^ all work between two floats, and between a float and a whole number, either way round. The comparisons ==, !=, <, >, <= and >= work too.

When a float appears

A float appears in three ways:

  1. You write a number with a point in it.
  2. You give a whole number to a float name: satellite.variable.float f = 3 holds 3.0.
  3. An answer cannot be a whole number: 2 ^ -1 is one half, so it answers the float 0.5.

Dividing two whole numbers is still whole-number division: 4 / 3 is 1. To get the float, write one side with a point, 4.0 / 3.

dividing.satlSATELLITE 004 Download
Whole-number division, and the float beside it
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display(4 / 3)
    satellite.console.display(4.0 / 3)
    satellite.console.display(2.0 / 3)
    satellite.console.display(2 ^ -1)
    satellite.variable.float f = 3
    satellite.console.display(f)
    satellite.console.display(12.50)
    satellite.return(satellite)
}
OUTPUT
1
1.33333333333333333333333333333333
0.66666666666666666666666666666667
0.5
3.0
12.5

How many digits

A float has no size limit, but a division like 4.0 / 3 never ends, so it has to stop somewhere. By default a float keeps 128 places after the point and 4096 digits before it. Where a result has more digits than that, the last digit kept is rounded half away from zero. That is the only time a float’s value is rounded.

When a float is shown, satl writes up to 32 places after the point, however many it holds, and rounds the last place it writes; the float itself keeps every digit. That is why 2.0 / 3 shows a 7 at the end. A float shows no zeros it does not need, so 12.50 shows as 12.5, and one that is whole keeps a single zero, so 3.0 stays 3.0.

A program can read the two limits through the arguments variable, as arguments.float.decimal and arguments.float.whole:

digits.satlSATELLITE 004 Download
Reading how many digits a float keeps
satellite.include(satellite)

satellite.capsule satellite.main(satellite.variable.arguments arguments)
{
    satellite.console.display(arguments.float.decimal)
    satellite.console.display(arguments.float.whole)
    satellite.return(satellite)
}
OUTPUT
128
4096

A program reads these and cannot change them. You set them for your own computer in ~/.satl/config.ini, one per line, and any count of 1 or more works:

config.iniTEXT Download
Keep more digits on this computer
float.decimal = 4096
float.whole = 20000

See THE ARGUMENTS VARIABLE for everything else it holds.

Floats and whole numbers

A float given to a whole-number name is refused: the message says it “holds a float”. You can turn a float into a whole number with .number, but only when there is nothing after the point: 12.0.number is 12, and 12.5.number is refused with S402, because satellite rounds nothing on its own. .string gives a float as text, so price.string + " dollars" works.

Not built yet

  • A power that is not a whole number, such as 2.0 ^ 0.5, is refused: only whole-number powers are built.
  • There is no scientific notation (1.5e3), and a float needs a digit before its point (0.5, not .5).
  • A float meeting a percentage, a fraction, a binary, a hex, a color or an infinity is refused as not built yet.

Where next

The FULL REFERENCE lists every float word. Whole numbers are in NUMBERS, and exact parts of a whole written with a slash are in FRACTIONS.

0

INFINITY

Most languages have a number that means “bigger than anything”, usually as a special value hidden inside their floats. satellite gives infinity a type of its own: satellite.variable.infinity. It is larger than every number there is, and its negative is smaller than every number.

Making an infinity

satellite.infinity() makes one. It shows as (infinity), in brackets, because an infinity is one value and the brackets hold it together. A minus in front turns it over: (-infinity).

endless.satlSATELLITE 004 Download
Making, showing and comparing an infinity
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.infinity forever = satellite.infinity()
    satellite.console.display(forever)
    satellite.console.display(-forever)
    satellite.console.display(forever > 10 ^ 100)
    satellite.console.display(-forever < 0 - 10 ^ 300)
    satellite.console.display(b1010 < forever)
    satellite.console.display(forever == -forever)
    satellite.console.display(forever == satellite.infinity())
    satellite.return(satellite)
}
OUTPUT
(infinity)
(-infinity)
true
true
true
false
true

10 ^ 100 is a one followed by a hundred zeros, and infinity is still larger. You can compare an infinity with ==, !=, <, >, <= and >= against a whole number, a binary, or another infinity.

A starting point that anything beats

The most useful thing about infinity is that every number is smaller. That makes it a natural starting value when you are looking for the smallest of something: the first real value you meet will beat it, and so will every smaller one after that. It also sorts: infinity always goes last, and (-infinity) always goes first.

cheapest.satlSATELLITE 004 Download
Infinity as a starting value, and in a sorted list
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.container.list prices = {12, 7, 30, 9}
    satellite.variable.infinity cheapest = satellite.infinity()
    satellite.statement.for (satellite.variable.number i = 1; i <= 4; i++)
    {
        satellite.statement.if (prices[i] < cheapest)
        {
            cheapest = prices[i]
        }
    }
    satellite.console.display(cheapest)

    satellite.variable.infinity top = satellite.infinity()
    satellite.container.list values = {5, top, -top, 0, 42}
    satellite.console.display(values.sort().by_value())
    satellite.return(satellite)
}
OUTPUT
7
{(-infinity), 0, 5, 42, (infinity)}

cheapest starts as infinity and is given each price that is lower than it, so it ends as 7. This works because an infinity name also holds a plain whole number. The items of a list count from 1, so the loop runs from 1 to 4. See LISTS for more about satellite.container.list and sorting.

What an infinity takes and gives

  • An infinity name also takes a plain whole number: satellite.variable.infinity limit = 7 holds 7. It refuses a string, a binary and a percentage.
  • A satellite.variable.number name refuses an infinity, because no whole number is that large.
  • .string gives its text, (infinity).
  • .number, .binary and .hex are refused, because there is no number to make of it, and .reverse() is refused because it has no digits to turn round.
no_number.satlSATELLITE 004 Download
An infinity has no number to give
satellite.include(satellite)

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

directory: no_number.satl:6
syntax: satellite.console.display(forever.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.

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

Not built yet

Arithmetic with an infinity is not built yet. +, -, *, / and ^ on an infinity are each refused by name with S210 (“not built yet”), and so is satellite.infinity(x) with something between its brackets. Today an infinity is made, shown, compared and sorted.

Where next

The FULL REFERENCE lists every infinity word. The numbers an infinity is compared with are in NUMBERS, and the other exact number types are in FRACTIONS and PERCENTAGES.

FRACTIONS 0

FRACTIONS

A third is not 0.333. It is one part in three, and no decimal ever quite reaches it. satellite.variable.fraction keeps a fraction the way you would write it on paper: one number above the line and one below it, tied together.

Writing a fraction

You write a fraction with a slash that touches both numbers: 1/3. That touching slash is the whole difference between a fraction and a division. 1 / 3, with a space on each side, is whole-number division and answers 0.

pie.satlSATELLITE 004 Download
Making, showing and comparing fractions
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.fraction third = 1/3
    satellite.variable.fraction half = 2/4
    satellite.console.display(third)
    satellite.console.display(half)
    satellite.console.display(half == 1/2)
    satellite.console.display(third < half)
    satellite.console.display(3/4 > 2/3)
    satellite.console.display(third.numerator)
    satellite.console.display(third.denominator)
    satellite.console.display(third.string + " of a pie")
    satellite.return(satellite)
}
OUTPUT
1/3
2/4
true
true
true
1
3
1/3 of a pie

Two things to notice here:

  • A fraction stays as you wrote it. 2/4 shows as 2/4. satellite does not reduce it to 1/2 behind your back.
  • Fractions compare by what they are worth. 2/4 == 1/2 is true, and 1/3 < 1/2 is true, even though the numbers are written differently.

.numerator is the number above the line, .denominator the number below it, and .string is the fraction as text.

Signs, decimals and whole numbers

The minus sign goes on top: -1/3. Each part is held as a float, and is shown as the plain number it is. So 1/3 shows 1/3 and never 1.0/3.0, while a part with digits after its point shows them: 1.5/2. A whole number given to a fraction name becomes that number over 1.

parts.satlSATELLITE 004 Download
The sign on top, a part with a point, and a whole number
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.console.display(-1/3)
    satellite.console.display(1.5/2)
    satellite.variable.fraction whole = 3
    satellite.console.display(whole)
    satellite.console.display(10/4 >= 5/2)
    satellite.console.display(1 / 3)
    satellite.return(satellite)
}
OUTPUT
-1/3
1.5/2
3/1
true
0

The last line shows the spaced slash at work: 1 / 3 is division, and a whole-number division of 1 by 3 is 0.

A bottom number of 0 is refused (S401), because nothing can be split into zero parts.

The spaced slash, refused

If you give a fraction name a spaced slash, satl catches it before the program starts, and tells you how to write it:

spaced.satlSATELLITE 004 Download
A fraction is written with a touching slash
satellite.include(satellite)

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

directory: 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.

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

Not built yet

Today a fraction is written, shown and compared. These are refused:

  • arithmetic between fractions: 1/3 + 1/3 is “fraction arithmetic is not built yet”
  • a fraction made of two names, such as a/b
  • a fraction meeting a float, a percentage or another of the newer types, such as 1/3 == 0.5
  • .number on a fraction

Where next

The FULL REFERENCE lists every fraction word. Decimals are in FLOATS, and parts of a hundred are in PERCENTAGES.

0

PERCENTAGES

Prices go up by 20%, a sale takes 25% off, a battery is at 50%. satellite.variable.percentage holds a percentage as its own kind of value, and it knows what those everyday sums mean. satellite.variable.percent is its second spelling, so both words declare the same type.

Writing a percentage

You write a percentage with its % touching the digits: 50%, 12.5%, -50%. The % is required. A percentage name given a plain 50 is refused before the program starts, with “ERROR: expected 50%”.

What each sum means

A percentage on its own, or with another percentage, stays a percentage. A number and a percentage together answer a number:

  • 50% + 25% is 75%, and 50% - 25% is 25%
  • 50% * 50% is 25%: a percentage of a percentage
  • 50% / 4 is 12.5%: a share of it
  • 200 * 50% is 100, and so is 50% * 200: that percentage of a number
  • 200 + 50% is 300: the number grown by it
  • 200 - 50% is 100: the number reduced by it
  • 200 / 50% is 400: the number that 200 is 50% of
shop.satlSATELLITE 004 Download
Percentages at the shop
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.percentage tax = 20%
    satellite.variable.percent sale = 25%
    satellite.variable.number price = 80
    satellite.console.display(price + tax)
    satellite.console.display(price - sale)
    satellite.console.display(price * sale)
    satellite.console.display(200 / 50%)
    satellite.console.display(tax + sale)
    satellite.console.display(sale / 2)
    satellite.console.display(tax.string + " tax")
    satellite.return(satellite)
}
OUTPUT
96
60
20
400
45%
12.5%
20% tax

80 plus 20% tax is 96, and 80 with 25% off is 60. The 25% of 80 that came off is 20.

Exact to 32 places

A percentage keeps 32 digits after the point, exactly. It is not a binary float, so 0.1% + 0.2% is 0.3% with nothing left over. There is no ceiling above: 1000000000000% is a perfectly good percentage. A 33rd digit after the point is rounded half away from zero.

exact.satlSATELLITE 004 Download
Percentages are exact, and compare with each other
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.percent battery = 50%
    satellite.console.display(0.1% + 0.2%)
    satellite.console.display(100% / 3)
    satellite.console.display(battery < 75%)
    battery = battery - 75%
    satellite.console.display(battery)
    satellite.return(satellite)
}
OUTPUT
0.3%
33.33333333333333333333333333333333%
true
-25%

A percentage compares with ==, !=, <, >, <= and >=, but only with another percentage: 50% == 50 is refused, because a percentage and a plain number are different things.

The number goes first

When you add a percentage to a number or take one away, the number goes first: you add 20% onto 80, so you write 80 + 20%. The other way round is refused, and the message tells you which way to write it:

backwards.satlSATELLITE 004 Download
For + and -, the number goes first
satellite.include(satellite)

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

directory: backwards.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.

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

What else is refused today

  • % and ^ with a percentage, and one percentage divided by another (50% / 50%).
  • An answer that is not a whole number: 3 * 50% is 1.5, and a percentage answering a float is not built yet, so it is refused with S402.
  • A percentage meeting a float, such as 10.0 * 50%, is not built yet.
  • A percentage name holds only a percentage, so battery = battery * 2 is refused when it runs, because that sum answers a number.
  • .string is the only conversion: .number, .binary, .hex and .reverse() are refused.

Where next

The FULL REFERENCE lists every percentage word. Whole numbers are in NUMBERS, and exact parts of a whole are in FRACTIONS.

1

BINARY AND HEXADECIMAL

Computers count in bits, and people who work close to the machine often write numbers the same way: in binary (base 2, the digits 0 and 1) or in hexadecimal (base 16, the digits 0 to 9 and A to F). satellite has a type for each, and both keep a number exactly as you wrote it, leading zeros and all.

Binary: a b in front

satellite.variable.binary is a number written in bits with a b in front: b1010. satellite.variable.bin is its second spelling. The b is required, so a binary name given 1010 is refused with “ERROR: expected b1010”, and so is 0b1010, the spelling some other languages use.

The width is part of the value. b0010 shows as b0010, and it is not the same binary as b10, although both are worth 2. A binary can also carry a sign: -b0101 is worth -5. There is no length limit.

flags.satlSATELLITE 004 Download
A binary number and its methods
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.binary flags = b0010
    satellite.console.display(flags)
    satellite.console.display(flags == b10)
    satellite.console.display(flags.number)
    satellite.console.display(flags.string)
    satellite.console.display(flags.bin)
    satellite.console.display(b10101010.hex)
    satellite.console.display(b1000.reverse())
    satellite.variable.bin below = -b0101
    satellite.console.display(below)
    satellite.console.display(below.number)
    satellite.return(satellite)
}
OUTPUT
b0010
false
2
b0010
0010
AA
b0001
-b0101
-5
  • .number is what the bits are worth.
  • .string is the binary as text, b and all, and .binary (or .bin) is the digits without the b.
  • .hex is its worth written in base 16.
  • .reverse() turns the bits round and keeps the width.

Hexadecimal: an x in front

satellite.variable.hex is a number in base 16 with an x in front: x1F, x00FF, xDEADBEEF. satellite.variable.hexadecimal is its second spelling. A lower-case digit is read the same as a capital one and always shown in capitals. As with a binary, the x is required, and the width and sign are kept: x00FF and xFF are worth the same and are different values.

mask.satlSATELLITE 004 Download
A hex number and its methods
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.hex mask = x00ff
    satellite.variable.hexadecimal red = xFF
    satellite.console.display(mask)
    satellite.console.display(mask == red)
    satellite.console.display(mask.number == red.number)
    satellite.console.display(mask.binary)
    satellite.console.display(mask.width)
    satellite.console.display(mask.reverse())
    satellite.console.display(xDEADBEEF.number)
    satellite.console.display(-x1F)
    satellite.return(satellite)
}
OUTPUT
x00FF
false
true
0000000011111111
16
xFF00
3735928559
-x1F

A hex has .number, .string and .hex like a binary, plus .binary, which gives four bits for every hex digit, and .width, which counts those bits.

Sums answer a plain number

In a sum, a binary or a hex counts as the number it is worth, and the answer is a plain whole number. That holds when you mix them too: b1100 + xFF is 267. They compare by worth the same way, so b1010 == 10 and b1111 == xF are both true.

To see a value in the other base, ask for its text: .hex on a binary, .binary on a hex, or either one on an ordinary number.

together.satlSATELLITE 004 Download
Binary and hex together, and converting between them
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.binary low = b1100
    satellite.console.display(low + xFF)
    satellite.console.display(b1000 + b0101)
    satellite.console.display(b1111 == xF)
    satellite.console.display(low.hex)
    satellite.console.display(xFF.binary)
    satellite.console.display((12).binary)
    satellite.console.display(255.hex)
    satellite.console.display("255".hex)
    satellite.variable.number n = b0011
    satellite.console.display(n)
    satellite.return(satellite)
}
OUTPUT
267
13
true
C
11111111
1100
FF
FF
3

Because a sum answers a number, it cannot be stored back in a binary or hex name. flags = flags + b0001 is refused when it runs (“it holds a number”). For a hex the message goes further and names the hex you meant: h = h + x01, with h holding xFF, suggests keeping 256 in a satellite.variable.number or writing x100. A number name, on the other hand, happily takes a binary or a hex and keeps its worth, as n does above.

A missing prefix

no_b.satlSATELLITE 004 Download
The b in front of a binary is required
satellite.include(satellite)

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

directory: no_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.

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

A binary digit is 0 or 1, so b12 is refused too, and a hex name given FF asks for xFF.

Not built yet

Sums that answer a binary or a hex, rather than a number, are not built yet, and neither are the bit operators (&, |, ~, <<, >>) or .width on a binary. OPERATORS: LOGIC AND BITS shows what satl says when you try them.

Where next

The FULL REFERENCE lists every binary and hex word. Colors, which are six hex digits, are in COLORS, and ordinary whole numbers are in NUMBERS.

0

COLORS

A color on a screen is three amounts of light: red, green and blue. Written in hexadecimal, each amount takes two digits, from 00 (none) to FF (full), so a whole color is six hex digits. 87CEEB is a sky blue, FF0000 is pure red, and 000000 is black. satellite.variable.color holds exactly that, and satellite.variable.colour is its second spelling.

Making a color

On the line that declares a color, the x in front is optional: 000000, x000000, ff00aa and 87ceeb all work. However you write it, a color is shown in capitals with its x, the way a hex number is.

A color can also be see-through. Its transparency is a whole number from 0 (solid) to 99 (almost clear). You can give it after a comma when you write the color, or set it later with .transparency(n). Written on its own, .transparency reads it back.

sky.satlSATELLITE 004 Download
Declaring colors, with and without transparency
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.color sky = 87ceeb
    satellite.variable.colour ink = x000000, 50
    satellite.console.display(sky)
    satellite.console.display(ink)
    satellite.console.display(ink.transparency)
    sky.transparency(40)
    satellite.console.display(sky.transparency)
    satellite.console.display(sky)
    satellite.return(satellite)
}
OUTPUT
x87CEEB
x000000, 50
50
40
x87CEEB, 40

A color with a transparency shows as x000000, 50, just the way you write it.

Changing a color later

On a later line, c = x00ff00 always works, and so do digits that start with a letter, or that have no letters at all, such as c = abcdef and c = 123456. The one shape that needs its x is digits that start with a number and hold a letter, such as 00ff00. The comma works on a later line too.

grass.satlSATELLITE 004 Download
Giving a color a new value
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.color grass = 00ff00
    satellite.console.display(grass)
    grass = x228b22
    satellite.console.display(grass)
    grass = abcdef
    satellite.console.display(grass)
    grass = 123456, 10
    satellite.console.display(grass)
    satellite.return(satellite)
}
OUTPUT
x00FF00
x228B22
xABCDEF
x123456, 10

Comparing and converting

== and != compare two colors, and both the digits and the transparency have to match. A color has no order, so < and > are refused, and it has no arithmetic: + on two colors is refused, and the message points you to .number if you want to do sums.

  • .number is what the six digits are worth
  • .hex is the six digits, without the x
  • .binary is its 24 bits
  • .string is the color as it is shown
compare.satlSATELLITE 004 Download
Comparing colors and reading their digits
satellite.include(satellite)

satellite.capsule satellite.main()
{
    satellite.variable.color red = ff0000
    satellite.variable.color also_red = xFF0000
    satellite.variable.color glass = ff0000, 30
    satellite.console.display(red == also_red)
    satellite.console.display(red == glass)
    satellite.console.display(red.number)
    satellite.console.display(red.hex)
    satellite.console.display(red.binary)
    satellite.console.display(glass.string)
    satellite.return(satellite)
}
OUTPUT
true
false
16711680
FF0000
111111110000000000000000
xFF0000, 30

red and glass have the same digits, but glass is 30 see-through, so they are not equal.

Six digits exactly

A color is always six digits. Five or seven are refused before the program starts:

short.satlSATELLITE 004 Download
A color is exactly six hex digits
satellite.include(satellite)

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

directory: short.satl:5
syntax: satellite.variable.color pink = ff00a
        /\

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.

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

A transparency of 100 or more is refused the same way: the most is 99.

Using a color

A color is what you hand to the words that paint text. "OK".foreground(grass) is the word OK in the color grass holds, and six hex digits work there too: "OK".foreground(x00FF00) is OK in green. The color shows when the text reaches a terminal. See COLOR ON THE CONSOLE.

Comparing a color with a bare hex number, such as grass == x228B22, is not built yet. Compare two colors instead, or compare their .number values.

Where next

The FULL REFERENCE lists every color word. Hex numbers themselves are in BINARY AND HEXADECIMAL.