FILES
Programs forget everything when they end, unless they write it down. Satellite’s files are built on one simple idea: a text file is a list of lines that lives on the disk. Line 1 is the first line, each line is a string, and most of what you can do to a list you can do to a file, spelled the same way.
A file is held in a satellite.variable.file. You get one from satellite.file.new or satellite.file.open, and after that you work through its name.
Making a file and reading it back
satellite.file.new("name.txt")makes a new, empty text file and opens it. It never replaces a file that is already there.f.append("text")adds a line at the end.f[n]reads linen, counting from 1.f.firstandf.lastread the first and last lines, andf.sizesays how many lines there are.f.close()saves and closes the file, and answerstruewhen the changes reached the disk.
(satellite)
satellite.capsule satellite.main()
{
satellite.variable.file shopping = satellite.file.new("shopping.txt")
shopping.append("bread")
shopping.append("milk")
shopping.append("eggs")
satellite.console.display(shopping.size)
satellite.console.display(shopping[1])
satellite.console.display(shopping.last)
satellite.console.display(shopping.close())
satellite.return(satellite)
}3
bread
eggs
trueAfter this runs, shopping.txt sits beside the program with bread, milk and eggs on three lines. A path without a / at the front is found in the folder of the .satl file that uses it, wherever you ran satl from.
Opening a file and changing it
satellite.file.open("name.txt") opens a text file that is already there. These change its lines:
f.insert(n, "text")puts a new line in as linen, and everything from there down moves down one.f.replace(n, "text")makes linensay something else.f.replace("old", "new")changes the first placeoldappears in the file intonew. A number first means a line, and text first means text.f.remove_at(n),f.remove("a line"),f.remove_first(),f.remove_last(),f.truncate(n)andf.clear()take lines out.
And these find things without changing anything:
f.index_of("a line")answers the number of the first line that is exactly that text, andf.search("text")the first line that contains it anywhere. When nothing matches, both answer0, which is never a line.f.contains("a line")answerstrueorfalse.f.read_allanswers the whole file as one string, line endings included.
To walk every line, count from 1 up to f.size:
(satellite)
satellite.capsule satellite.main()
{
satellite.variable.file shopping = satellite.file.new("shopping.txt")
shopping.append("bread")
shopping.append("milk")
shopping.append("eggs")
shopping.close()
satellite.variable.file again = satellite.file.open("shopping.txt")
again.insert(2, "butter")
again.replace(1, "brown bread")
again.replace("milk", "oat milk")
satellite.console.display(again.search("milk"))
satellite.console.display(again.index_of("cheese"))
satellite.console.display(again.contains("butter"))
satellite.statement.for(satellite.variable.number i = 1; i <= again.size; i + 1)
{
satellite.console.display(i.to_string() + ". " + again[i])
}
satellite.return(satellite)
}3
0
true
1. brown bread
2. butter
3. oat milk
4. eggsNotice there is no again.close() at the end. Satellite saves and closes every file that is still open when the program ends, so forgetting to close loses nothing. You can also save part way through with f.save().
A save is whole or not at all. Satellite writes the new version beside the old one and swaps it in, so if the program is stopped in the middle of a save, the file holds either the old lines or the new ones, never a mixture.
When a file will not open
Opening or making a file never stops your program. Instead, the file you get back is not open, and you can ask it:
f.okanswers whether it is open.f.errorsays why not, in words.satellite.file.exists("name.txt")answers whether a file is there at all.
(satellite)
satellite.capsule satellite.main()
{
satellite.variable.file missing = satellite.file.open("no_such_file.txt")
satellite.console.display(missing.ok)
satellite.console.display(satellite.file.exists("no_such_file.txt"))
satellite.variable.file diary = satellite.file.new("diary.txt")
diary.append("day one")
diary.close()
satellite.variable.file second = satellite.file.new("diary.txt")
satellite.console.display(second.ok)
satellite.variable.file reopened = satellite.file.open("diary.txt")
satellite.console.display(reopened[1])
satellite.return(satellite)
}false
false
false
day oneopen never makes a file, so a misspelled name cannot quietly become an empty one. new never replaces one, so your diary survives: the second new came back not open, and the first day is still there.
What is refused
A few things stop the program, because any answer to them would be made up:
- Reading a line that is not there, like line 2 of a one-line file, is S501 LINE_PAST_THE_END.
- Asking about the lines of a file that is not open, like
missing.size, is S503 FILE_NOT_OPEN. Checkf.okfirst. - A method a file does not have, such as
f.write("text"), or the wrong number of arguments, such asf.replace(1), is refused before the program starts. To add a line, useappendorinsert.
(satellite)
satellite.capsule satellite.main()
{
satellite.variable.file notes = satellite.file.new("notes.txt")
notes.append("the only line")
satellite.console.display(notes[1])
satellite.console.display(notes[2])
satellite.return(satellite)
}--------------------------------------------------------------------------------
SATELLITE CRITICAL ERROR REPORT
--------------------------------------------------------------------------------
S501: LINE_PAST_THE_END
satl(run): notes[2]: there is no such line -- the file has 1 line, counting from
1
directory: past_the_end.satl:8
syntax: satellite.console.display(notes[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.
--------------------------------------------------------------------------------The first line was shown before the report. The file itself is safe: notes.txt was still saved with its one line.
Only text files are built today. Asking for a binary file, satellite.file.new("data.bin", "binary"), gives a file that is not open.
Where next
Every file word is in the FULL REFERENCE. Files behave like LISTS, and two threads can share one safely with a lock: see LOCKS: SHARING AN OBJECT BETWEEN THREADS.
