Expand

Accessors

Accessor is way to reach inside lists, maps and other complex structures:

$obj := { tags: ["indie", "adventure", "puzzle"] }
put $obj.tags[2]
# => puzzle

In this example .tag is a field accessor and [2] is an index accessor.

Field

Field accessors are primarily used to access fields of maps. A field name can contain letters, digits and underscore; to access field names with other symbols, use index accessor.

$pod = {
    metadata: {
        name: "nginx",
        labels: {},
    },
    spec: {
        containers: [{
            image: "nginx:stable"
        }],
    },
}
access nested fields
put $pod.spec.containers[0].image
# => nginx:stable
update nested fields, create a new field in labels
$pod.metadata.labels.UsedBy = "syshell"
value semantics is preserved: when changing a variable, only that variable changes, other variables stay intact
$pod2 := $pos
$pod2.metadata.name = "nginx-2"

put $pod.metadata.name
# => nginx

put $pod2.metadata.name
# => nginx-2

Index

Index accessors can be used on lists to access n-th element, or on maps to access fields. Negative indices in lists do the reverse order, i.e. -1 is the index of the last element.

access a list at O(1)
$fibb := [1, 1, 2, 3, 5, 8, 13, 21]
put $fibb[0]  # => 1
put $fibb[5]  # => 8
put $fibb[-1] # => 21
access a map at O(1)
$map := {
    normal: 1,
    " ": 2,
    "/": 3,
}

put $map.normal    # => 1
put $map["normal"] # => 1
put $map[" "]      # => 2
put $map["/"]      # => 3
complex keys in maps are allowed. However, this should probably be avoided for big lists and maps, as hash and comparison functions will be called on each lookup
$map := {
    (["composite", "key"]): 4,
    ({ bad: "idea?" }): 5,
}
put $map[["composite", "key"]] # => 4
put $map[{ bad: "idea?" }]     # => 5
2D array demo: given two strings, find edit distance a.k.a Levinshtein distance. Computation complexity is O(N · M), where N and M are lengths of strings
fn edit-distance (runes)$a (runes)$b {
    $lenA := len $a
    $lenB := len $b

    $dp := [[0] * ($lenB + 1)] * ($lenA + 1)

    for $i in (range $lenA) {
        $dp[$i + 1][0] = $i + 1
    }
    for $j in (range $lenB) {
        $dp[0][$j + 1] = $j + 1
    }

    for $i in (range $lenA) {
        for $j in (range $lenB) {
            $cost := ($a[$i] != $b[$j]):int
            $dp[$i + 1][$j + 1] = min
                ($dp[$i+1][$j] + 1) # delete
                ($dp[$i][$j+1] + 1) # insert
                ($dp[$i][$j] + $cost) # swap
        }
    }

    put $dp[$lenA][$lenB]
}

edit-distance Hallo Hello
# => 1

edit-distance aabbcc abc
# => 3

Formatters

Format accessors are used to convert between syshell types and various text formats like JSON, or smaller things like numbers and dates. Here is a full list of formatters.

For each format there are two accessors: : to convert from a text format to a syshell value, and @ to convert in the opposite direction.

:slashes splits an incoming string by slash
$path := "/usr/local/bin"
put $path:slashes
# => ["", "usr", "local", "bin"]

put $path:slashes[-1]
# => bin
@slashes join a list of strings by slash
put ["", "proc", 1, "cmdline"]@slashes
# => /proc/1/cmdline
formatters can be used on in the assignments too. In this case, it first uses :slashes to get access to a list, then updates the list in-place and uses @slashes to convert it back to a string
$dir := "/home/alice/bin"
$dir:slashes[2] = "bob"
put $dir
# => /home/bob/bin

Path

Accessors can be stored as a value, which is useful for functions arguments and configuration.

create a path with $ following accessors
$path := $.metadata.name
apply a path on an expression using .
$pod is defined above in the Field Accessor section
put $pod.$path
# => nginx
sort a list of maps by the birthday field using sort
$accounts := [
    { name: "Alice", birthday: "1978-02-09":utc },
    { name: "Bob",   birthday: "1978-02-04":utc },
    { name: "Eve",   birthday: "1988-04-01":utc }
  ]

put $accounts... | sort $.birthday
# => { name: "Bob",   birthday: "1978-02-04":utc }
# => { name: "Alice", birthday: "1978-02-09":utc }
# => { name: "Eve",   birthday: "1988-04-01":utc }

Slice

For lists it's possible to take a slice of them using [begin..end], the specified range is half open, i.e. begin is included, while end is not. Both begin and end can be omitted, in this case 0 and list length will be assumed. Negative indices work the same as in the index accessor. Slicing is O(1).

get second to fourth elements
$list := ["a", "b", "c", "d", "e", "f"]
put $list[1..4]
# => ["b", "c", "d"]
slicing from 0 to len is equivalent to the original list.
$list := [1, 2, 3, 4]
put $list[0..(len $list)]
# => [1, 2, 3, 4]
put $list[..]
pop the first element
$list := [1, 2, 3, 4]
$list = $list[1..]
# $list == [2, 3, 4]
pop the last element
$list := [1, 2, 3, 4]
$list = $list[..-1]
# $list == [1, 2, 3]

Append

Assigning to a list without specifying index (e.g. $list[] = 5) will append provided value to this list. Appends are amortized O(1).

append an element to a list
$list := []
$list[] = 1
$list[] = 2
put $list # => [1, 2]
appends can be used together with formatters
$env.PATH:colons[] = "/home/alice/bin"