Skip to content
Sword

The outside world

A program that cannot be told anything is not much use. std/os has the arguments and the environment:

import "std/io"
import "std/os"
import "std/strings"
func main() !int {
mut room := [8]string{}
args := os.Args(room[..])
mut port u64 = 8080
if args.len > 1 {
port = try strings.ParseU64(args[1])
}
host := os.EnvOr("HOST", "127.0.0.1")
try io.Printf("listening on {}:{}\n", host, port)
return 0
}

Args fills an array you hand it rather than allocating one — the same rule as everywhere else in the standard library, and the reason os.Args() takes an argument at all. args[0] is the program’s own name, as in C. Env gives back an optional, because a variable that is set to nothing is not the same as one that is not set:

if level := os.Env("LOG_LEVEL") {
try io.Printf("logging at {}\n", level)
}

std/fs reads and writes files:

import "std/fs"
import "std/io"
import "std/mem"
func main() !int {
mut backing := [65536]u8{}
mut arena := mem.NewArena(backing[..])
try fs.WriteAll("/tmp/greeting", []u8("hello\n"))
back := try fs.ReadAll("/tmp/greeting", &arena)
try io.Print(string(back))
try fs.Remove("/tmp/greeting")
return 0
}

fs.Size gives an optional, because a path that is not there has no size and a number would be a lie. fs.ReadStdin takes whatever was piped in.

std/os also has signals, which is how a server is asked to stop:

try os.Catch(os.Signal.Terminate)
sig := try os.WaitSignal()

WaitSignal is an ordinary wait — it costs a stack and not a thread — so it sits in a task beside the work it is going to interrupt.

std/time has the clocks. Two of them: Now reads a clock that only counts forward, for measuring, and Unix reads the wall clock, for stamping.

import "std/time"
start := time.Now()
work()
took := time.Since(start)
try io.Printf("took {}ms\n", took.AsMillis())

A Duration is a length of time rather than a bare number, so a function that takes one cannot be handed seconds where it wanted milliseconds:

time.Sleep(time.Millis(250))
try conn.SetTimeout(time.Seconds(5))

A method can be called on a value that has no name yet — time.Since(start).AsMillis() — unless it writes through its receiver: then the change would have nowhere to go, and the compiler asks you to bind the value first.

Sword calls C directly. extern declares a C function by its own name, and the call is an ordinary call:

extern func abs(x i32) i32
func main() int {
return int(abs(-42))
}

Only scalars cross — numbers, bool, raw pointers — because a string or a slice is two words to Sword and a pointer to C, so you pass .ptr and .len. When the function lives in a library, name the library after extern, the way -l would, and it is linked without anyone mentioning it on the command line:

extern "z" func zlibVersion() [*]u8

It is linked dynamically unless you build with shield --static, which takes it from libz.a instead.

std/c gives C’s types their C names, so a declaration reads like the header it came from, and os.Errno reads what a failing call left behind:

import "std/c"
import "std/io"
import "std/os"
extern func close(fd c.Int) c.Int
func main() !int {
if close(-1) < 0 {
try io.Printf("errno {}\n", os.Errno())
}
return 0
}

Read errno straight after the call. It belongs to the thread, and a task that waits for anything may carry on from another one.

A C function can be a value too. Its type is extern func(...), kept apart from func(...), which only ever points at Sword code. A Sword function can be handed to C wherever C expects one — this is how qsort gets its comparison:

extern func qsort(base [*]u8, n u64, size u64,
less extern func([*]u8, [*]u8) i32)

A function that only some machines have is declared extern weak. The program starts either way, and the name is an optional to check before calling:

extern weak func getentropy(buf [*]u8, n u64) i32
if fill := getentropy {
// here, fill is the function
}

And a library the program finds while it runs — a plugin — is opened with std/dl, which hands back addresses for you to say the type of:

mut ld := dl.NewLoader()
lib := ld.Open("libplugin.so") catch {
try io.Printf("{}\n", ld.Why())
return 1
}
start := extern func() i32(try ld.Symbol(lib, "plugin_start"))