From 81240ac91ab39d09b7bcda6b28d1fd7026ac9b12 Mon Sep 17 00:00:00 2001 From: fubark Date: Sun, 28 Jul 2024 20:07:05 +0000 Subject: [PATCH] deploy: e5f458c38bbb7ff297cf49d1ea584e1e282edbff --- index.html | 810 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 553 insertions(+), 257 deletions(-) diff --git a/index.html b/index.html index 518d94ef1..42079ce4f 100644 --- a/index.html +++ b/index.html @@ -949,6 +949,11 @@ padding: 0px; } +p { + margin-block-start: 1.3em; + margin-block-end: 1.3em; +} + blockquote em { color: #fb6767; } @@ -985,7 +990,7 @@

Cyber Docs

-
v0.4-dev 300-0a37853
+
v0.4-dev 313-e5f458c
@@ -1022,7 +1027,7 @@

Type System. #

Hello World. #

use math
 
-var worlds = ['World', '世界', 'दुनिया', 'mundo']
+var worlds = {'World', '世界', 'दुनिया', 'mundo'}
 worlds.append(math.random())
 for worlds -> w:
     print "Hello, $(w)!"
@@ -1082,12 +1087,12 @@ 

Statements. #

month > 0 and month <= 11: print 'Valid'
-

Any token inside a delimited syntax (such as parentheses or brackets) can be wrapped to the next line:

+

Any token inside a delimited syntax (such as parentheses or braces) can be wrapped to the next line:

var sum = add(1, 2, 3, 4,
     100, 200, 300, 400)
 
-var colors = ['red', 'blue', 'green',
-    'purple', 'orange', 'yellow']
+var colors = {'red', 'blue', 'green',
+    'purple', 'orange', 'yellow'}
 
^topic

Blocks. #

@@ -1100,7 +1105,7 @@

Blocks. #

Subsequent statements in the block must follow the same indentation. The block ends when a statement recedes from this indentation:

-
var items = [10, 20, 30]
+
var items = {10, 20, 30}
 for items -> it:
     if it == 20:
         print it
@@ -1314,10 +1319,10 @@ 

Comparison Operators. 1 != 1 -- Evaluates to `false` @@ -1378,7 +1383,7 @@

Operator overloading. Zero values. #

Uninitialized type fields currently default to their zero values. However, this implicit behavior will be removed in the future in favor of a default value clause. Zero values must then be expressed using the reserved zero literal.

The following shows the zero values of builtin or created types:

-
TypeZero value
booleanfalse
int0
float0.0
String''
ArrayArray('')
List[]
MapMap{}
type SS{}
@host type SS.$zero()
dynint(0)
anyint(0)
?SOption[S].none
^topic +
TypeZero value
booleanfalse
int0
float0.0
String''
List[T]List[T]{}
MapMap{}
type SS{}
@host type SS.$zero()
dynint(0)
anyint(0)
?SOption[S].none
^topic

Type casting. #

The as keyword can be used to cast a value to a specific type. Casting lets the compiler know what the expected type is and does not perform any conversions.

If the compiler knows the cast will always fail at runtime, a compile error is returned instead.

@@ -1408,11 +1413,11 @@

CYON. #

'age' = 25, -- This is a comment - cities = [ + cities = { 'New York', 'San Francisco', 'Tokyo', - ], + }, }
^topic @@ -1439,13 +1444,23 @@

Basic Types. #

  • Mutable strings.
  • +
  • Symbols.
  • ^top

    -

    In Cyber, there are primitive types and object types. By default, primitives are copied around by value and don't need additional heap memory or reference counts.

    -

    Primitives include Booleans, Floats, Integers, Enums, Symbols, and Error Values.

    -

    Object types include Lists, Tuples, Maps, Strings, Arrays, Objects, Lambdas, Fibers, Choices, Optionals, Pointers, and several internal object types.

    +

    In Cyber, there are value types and reference types. +This section will describe common builtin types.

    Booleans. #

    Booleans can be true or false. See type bool.

    var a = true
    @@ -1617,48 +1630,124 @@ 

    Line-join literal. #
  • Allows each line to be indented along with the surrounding syntax.
  • The starting whitespace for each line is made explicit.
  • -
    var paragraph = [
    +
    var paragraph = {
         \'the line-join literal
         \'hello\nworld
         \"hello $(name)
         \'last line
         \'
    -]
    +}
     
    ^topic

    Mutable strings. #

    To mutate an existing string, use type MutString. Planned Feature

    ^topic +

    Optionals. #

    +

    An Optional is a value type that provides null safety by forcing the inner value to be unwrapped before it can be used.

    +

    The Option template type is a choice type that either holds a none value or contains some value. The option template is defined as:

    +
    type Option[T type] enum:
    +    case none
    +    case some T
    +
    +

    A type prefixed with ? is the idiomatic way to create an option type. The following String optional types are equivalent:

    +
    Option[String]
    +?String
    +
    +^topic +

    Wrap value. #

    +

    A value is automatically wrapped into the inferred optional's some case:

    +
    var a ?String = 'abc'
    +print a     --> some(abc)'
    +
    +^topic +

    Wrap none. #

    +

    none is automatically initialized to the inferred optional's none case:

    +
    var a ?String = none
    +print a     --> none
    +
    +^topic +

    Unwrap or panic. #

    +

    The .? access operator is used to unwrap an optional. If the expression evaluates to the none case, the runtime panics:

    +
    var opt ?int = 123
    +var v = opt.?
    +print v     --> 123
    +
    +^topic +

    Unwrap or default. #

    +

    The ?else control flow operator either returns the unwrapped value or a default value when the optional is none:

    +
    var opt ?int = none
    +var v = opt ?else 123
    +print v     --> 123
    +
    +

    ?else can be used in an assignment block: Planned Feature

    +
    var v = opt ?else:
    +    break 'empty'
    +
    +var v = opt ?else:
    +    throw error.Missing
    +
    +^topic +

    Optional chaining. #

    +

    Given the last member's type T in a chain of ?. access operators, the chain's execution will either return Option[T].none on the first encounter of none or returns the last member as an Option[T].some: Planned Feature

    +
    print root?.a?.b?.c?.last
    +
    +^topic +

    if unwrap. #

    +

    The if statement can be amended to unwrap an optional value using the capture -> operator:

    +
    var opt ?String = 'abc'
    +if opt -> v:
    +    print v     -- Prints 'abc'
    +
    +^topic +

    while unwrap. #

    +

    The while statement can be amended to unwrap an optional value using the capture -> operator. +The loop exits when none is encountered:

    +
    var iter = dir.walk()
    +while iter.next() -> entry:
    +    print entry.name
    +
    +^topic

    Arrays. #

    -

    An Array is an immutable sequence of bytes. +

    An array type is a value type and is denoted as [N]T where N is the size of the array and T is the element type. See type Array.

    -
    var a = Array('abcd')
    -a = a.insertByte(1, 255)
    -print a[0]     -- "97"
    -print a[1]     -- "255"
    +

    Arrays are initialized with its type followed by the initializer literal:

    +
    var a = [3]int{1, 2, 3}
    +
    +

    The number of elements can be inferred using pseudo type [.]T: Planned Feature

    +
    var a = [.]int{1, 2, 3}
    +
    +

    The array type can be inferred by the dot initializer literal:

    +
    var a [3]int = .{1, 2, 3}
    +
    +

    Arrays can be indexed:

    +
    a[2] = 300
    +print a[2]    --> 300
     
    ^topic

    Lists. #

    Lists are a builtin type that holds an ordered collection of elements. Lists grow or shrink as you insert or remove elements. See type List.

    -
    -- Construct a new list.
    -var list = [1, 2, 3]
    -
    --- The first element of the list starts at index 0.
    -print list[0]    -- Prints '1'
    +

    The first element of the list starts at index 0. +Lists are initialized with elements in braces:

    +
    var list = {1, 2, 3}
    +print list[0]    --> 1
    +
    +

    The empty list is initialized with an underscore as the only element:

    +
    var list = {_}
    +print list.len() --> 0
     

    Lists can be sliced with the range .. clause. The sliced list becomes a new list that you can modify without affecting the original list. The end index is non-inclusive.

    -
    var list = [ 1, 2, 3, 4, 5 ]
    -list[0..0]    -- []          
    -list[0..3]    -- [ 1, 2, 3 ] 
    -list[3..]     -- [ 4, 5 ]    
    -list[..3]     -- [ 1, 2, 3 ] 
    +
    var list = {1, 2, 3, 4, 5}
    +print list[0..0]    --> {_}          
    +print list[0..3]    --> {1, 2, 3}
    +print list[3..]     --> {4, 5}
    +print list[..3]     --> {1, 2, 3}
     

    The +.. invokes the slice operator with an end position that is an increment from the start: Planned Feature

    -
    var list = [ 1, 2, 3, 4, 5 ]
    -list[2+..2]   -- [ 3, 4 ]
    +
    var list = {1, 2, 3, 4, 5}
    +print list[2+..2]   --> {3, 4}
     

    List operations.

    -
    var list = [234]
    +
    var list = {234}
     
     -- Append a value.
     list.append(123)
    @@ -1680,15 +1769,12 @@ 

    Lists. #

    list.remove(1)

    Since List is a generic type, an explicit List type can be attached to the initializer:

    -
    var a = List[int].[1, 2, 3]
    +
    var a = List[int]{1, 2, 3}
     

    When the intializer is only prefixed with a dot, it will infer the List type constraint:

    -
    var a List[int] = .[1, 2, 3]
    +
    var a List[int] = .{1, 2, 3}
     
    ^topic -

    Tuples. #

    -

    Incomplete: Tuples can only be created from @host funcs at the moment.

    -
    ^topic

    Tables. #

    A Table is a versatile object that can have an arbitrary set of fields.

    By default, the record literal initializes a Table:

    @@ -1752,7 +1838,7 @@

    Map operations. #

    map.remove(123) -- Iterating a map. -for map -> [val, key]: +for map -> {val, key}: print "$(key) -> $(val)"
    ^topic @@ -1792,7 +1878,7 @@

    any. #

    return i * i var a any = 123 -a = ['a', 'list'] --> Valid assignment to a value with a different type. +a = {'a', 'list'} --> Valid assignment to a value with a different type. a = 10 print square(a) --> CompileError. Expected `int`, got `any`. @@ -1826,6 +1912,10 @@

    Custom Types. #

  • Copy structs.
  • +
  • Tuples.
  • + + + - -
    ^topic +

    Tuples. #

    +

    Incomplete: Tuples can only be created from @host funcs at the moment.

    +
    ^topic

    Enums. #

    A new enum type can be declared with the type enum declaration. An enum value can only be one of the unique symbols declared in the enum type. @@ -2126,71 +2206,6 @@

    Access choice. #

    print s.!line --> 20
    ^topic -

    Optionals. #

    -

    Optionals provide Null Safety by forcing values to be unwrapped before they can be used.

    -

    The Option template type is a choice type that either holds a none value or contains some value. The option template is defined as:

    -
    type Option[T type] enum:
    -    case none
    -    case some T
    -
    -

    A type prefixed with ? is the idiomatic way to create an option type. The following String optional types are equivalent:

    -
    Option[String]
    -?String
    -
    -^topic -

    Wrap value. #

    -

    A value is automatically wrapped into the inferred optional's some case:

    -
    var a ?String = 'abc'
    -print a     --> some(abc)'
    -
    -^topic -

    Wrap none. #

    -

    none is automatically initialized to the inferred optional's none case:

    -
    var a ?String = none
    -print a     --> none
    -
    -^topic -

    Unwrap or panic. #

    -

    The .? access operator is used to unwrap an optional. If the expression evaluates to the none case, the runtime panics:

    -
    var opt ?int = 123
    -var v = opt.?
    -print v     --> 123
    -
    -^topic -

    Unwrap or default. #

    -

    The ?else control flow operator either returns the unwrapped value or a default value when the optional is none:

    -
    var opt ?int = none
    -var v = opt ?else 123
    -print v     --> 123
    -
    -

    ?else can be used in an assignment block: Planned Feature

    -
    var v = opt ?else:
    -    break 'empty'
    -
    -var v = opt ?else:
    -    throw error.Missing
    -
    -^topic -

    Optional chaining. #

    -

    Given the last member's type T in a chain of ?. access operators, the chain's execution will either return Option[T].none on the first encounter of none or returns the last member as an Option[T].some: Planned Feature

    -
    print root?.a?.b?.c?.last
    -
    -^topic -

    if unwrap. #

    -

    The if statement can be amended to unwrap an optional value using the capture -> operator:

    -
    var opt ?String = 'abc'
    -if opt -> v:
    -    print v     -- Prints 'abc'
    -
    -^topic -

    while unwrap. #

    -

    The while statement can be amended to unwrap an optional value using the capture -> operator. -The loop exits when none is encountered:

    -
    var iter = dir.walk()
    -while iter.next() -> entry:
    -    print entry.name
    -
    -^topic

    Type aliases. #

    A type alias refers to a different target type. Once declared, the alias and the target type can be used interchangeably.

    @@ -2302,7 +2317,7 @@

    C Types. #

  • Pointer conversions.
  • -
  • Slices.
  • +
  • Pointer slices.
  • Union types.
  • ^top

    @@ -2320,7 +2335,7 @@

    C structs. #

    x float y float ptr *int - str []byte + str [*]byte

    A C struct may contain:

      @@ -2361,13 +2376,13 @@

      C struct methods. #

    ^topic

    C struct reference. #

    -

    The & operator is used to obtain the reference to a C struct value:

    +

    The * operator is used to obtain the pointer to a C struct value:

    type Vec2 cstruct:
         x float
         y float
     
     var v = Vec2{x=1, y=2}
    -var ref = &v
    +var ref = *v
     ref.x = 4
     
     print v                 --> Vec2{x=4, y=2}
    @@ -2377,30 +2392,30 @@ 

    C struct reference. #< a.x *= n a.y *= n -add(&v, 10) +add(*v, 10) print v --> Vec2{x=40, y=20}

    ^topic

    Pointers. #

    A pointer is a reference to a memory location. Its type is denoted as *T where T is the type that the pointer references in memory:

    -
    func setName(p *Person, name []byte):
    +
    func setName(p *Person, name [*]byte):
         p.name = name
     
     var p = Person{}
    -setName(&p, 'Spock')
    +setName(*p, 'Spock')
     

    Depending on the target architecture, the alignment of the pointer will either be at least 8 bytes on a 64-bit machine or 4 bytes on a 32-bit machine. Aligned pointers will be supported in a future version.

    Pointers are unsafe since they can reference corrupted or invalidated memory. When runtime memory checks are enabled, pointers will occupy an extra word size in order to set traps and prevent unsafe uses of pointers. See Memory / Runtime memory checks.

    A pointer can be created with an explicit address using pointer.

    -
    var ptr = pointer(*void, 0xDEADBEEF)
    +
    var ptr = pointer(void, 0xDEADBEEF)
     print ptr.value()     --'3735928559'
     
    ^topic

    Dereferencing pointers. #

    Pointers are dereferenced using the accessor operator .*:

    var a = 123
    -var ptr = &a
    +var ptr = *a
     print ptr.*      --> 123
     
     ptr.* = 10
    @@ -2422,7 +2437,7 @@ 

    Pointer casting. #

    Planned Feature

    ^topic

    Pointer slicing. #

    -

    The slice operator will produce a new slice of type []T from a *T pointer type:

    +

    The slice operator will produce a new slice of type [*]T from a *T pointer type:

    var ints = mem.alloc(int, 10)
     var slice = ints[0..5]
     print typeof(slice)    --> []int
    @@ -2431,11 +2446,11 @@ 

    Pointer slicing. #

    Pointer conversions. #

    Planned Feature

    ^topic -

    Slices. #

    -

    Slices are pointers with a length field. They are denoted as []T where T is the element type.

    -

    A slice can be created by taking the address of an array: Planned feature

    -
    var arr = [_]int.[1, 2, 3]
    -var s = &arr
    +

    Pointer slices. #

    +

    Slices are pointers with a length field. They are denoted as [*]T where T is the element type.

    +

    A slice can be created by taking the pointer of an array: Planned feature

    +
    var arr = [3]int{1, 2, 3}
    +var s = *arr
     

    Read and write an element with the index operator:

    print s[0]        --> 1
    @@ -2446,7 +2461,7 @@ 

    Slices. #

    print s[100]      --> Panic. Out of bounds.
     

    Slice operations can be applied: Planned feature

    -
    print s[0..2]     --> [1, 2]
    +
    print s[0..2]     --> {1, 2}
     
    ^topic

    Union types. #

    @@ -2558,26 +2573,26 @@

    for range. #

    for each. #

    The for clause can iterate over any type that implements the Iterable trait. An Iterable contains an iterator() method which returns an Iterator object. The for loop continually invokes the Iterator's next() method until none is returned.

    Lists can be iterated since they implement the Iterable trait. The capture operator -> is used to capture the value returned from an iterator's next():

    -
    var list = [1, 2, 3, 4, 5]
    +
    var list = {1, 2, 3, 4, 5}
     
     for list -> n:
         print n
     

    Maps can be iterated. next() returns a key and value tuple:

    -
    var map = Map{ a=123, b=234 }
    +
    var map = Map{a=123, b=234}
     
     for map -> entry:
         print entry[0]
         print entry[1]
     

    Use the destructure syntax to extract the key and value into two separate variables:

    -
    for map -> [ key, val ]:
    +
    for map -> {key, val}:
         print "key $(key) -> value $(val)"
     
    ^topic

    for each with index. #

    A counting index can be declared after the each variable. The count starts at 0 for the first value:

    -
    var list = [1, 2, 3, 4, 5]
    +
    var list = {1, 2, 3, 4, 5}
     
     for list -> val, i:
         print "index $(i), value $(val)"
    @@ -2700,10 +2715,10 @@ 

    Static functions. #Functions can only return one value. However, the value can be destructured: Planned Feature

    use {cos, sin} 'math'
     
    -func compute(rad float) [float, float]:
    -    return [ cos(rad), sin(rad) ]
    +func compute(rad float) [2]float:
    +    return .{cos(rad), sin(rad)}
     
    -var [ x, y ] = compute(pi)
    +var {x, y} = compute(pi)
     
    ^topic

    Function overloading. #

    @@ -3078,6 +3093,7 @@

    mod core #

    Prints a value. The host determines how it is printed.

    func queueTask(fn any)

    Queues a callback function as an async task.

    +

    func refcast(T type, ptr *T)

    func runestr(val int) String

    Converts a rune to a string.

    func sizeof(T type) int

    @@ -3163,6 +3179,8 @@

    type float #

    ^topic

    type placeholder1 #

    ^topic +

    type placeholder2 #

    +^topic

    type taglit #

    ^topic

    type dyn #

    @@ -3230,7 +3248,11 @@

    type MapIterator ^topic

    type String #

    -

    func $infix+(self any, o any) String

    +

    func $index(self any, idx int) int

    +

    Returns the rune at byte index idx. The replacement character (0xFFFD) is returned for an invalid UTF-8 rune.

    +

    func $index(self any, range Range) String

    +

    Returns a slice into this string from a Range with start (inclusive) to end (exclusive) byte indexes.

    +

    func $infix+(self any, o any) String

    Returns a new string that concats this string and str.

    func concat(self any, o String) String

    Returns a new string that concats this string and str.

    @@ -3240,12 +3262,26 @@

    type String #

    Returns whether the string ends with suffix.

    func find(self any, needle String) ?int

    Returns the first byte index of substring needle in the string or none if not found. SIMD enabled.

    +

    func findAnyByte(self any, set List[byte]) ?int

    +

    Returns the first index of any byte in set or none if not found.

    func findAnyRune(self any, runes List[int]) ?int

    Returns the first byte index of any rune in runes or none if not found. SIMD enabled.

    +

    func findByte(self any, b byte) ?int

    +

    Returns the first index of byte in the array or none if not found.

    func findRune(self any, rune int) ?int

    Returns the first byte index of a rune needle in the string or none if not found. SIMD enabled.

    +

    func fmtBytes(self any, format NumberFormat) String

    +

    Formats each byte in the string using a NumberFormat. Each byte is zero padded.

    +

    func getByte(self any, idx int) byte

    +

    Returns the byte value (0-255) at the given index idx.

    +

    func getInt(self any, idx int, endian symbol) int

    +

    Returns the int value of the 6 bytes starting from idx with the given endianness (.little or .big).

    +

    func getInt32(self any, idx int, endian symbol) int

    +

    Returns the int value of the 4 bytes starting from idx with the given endianness (.little or .big).

    func insert(self any, idx int, str String) String

    Returns a new string with str inserted at byte index idx.

    +

    func insertByte(self any, idx int, byte int) String

    +

    Returns a new array with byte inserted at index idx.

    func isAscii(self any) bool

    Returns whether the string contains all ASCII runes.

    func len(self any) int

    @@ -3262,10 +3298,6 @@

    type String #

    Returns the starting byte index for the rune index idx.

    func sliceAt(self any, idx int) String

    Returns the UTF-8 rune starting at byte index idx as a string.

    -

    func $index(self any, idx int) int

    -

    Returns the rune at byte index idx. The replacement character (0xFFFD) is returned for an invalid UTF-8 rune.

    -

    func $index(self any, range Range) String

    -

    Returns a slice into this string from a Range with start (inclusive) to end (exclusive) byte indexes.

    func split(self any, sep String) List[String]

    Returns a list of UTF-8 strings split at occurrences of sep.

    func startsWith(self any, prefix String) bool

    @@ -3277,73 +3309,31 @@

    type String #

    func String.$call(val any) String

    Converts a value to a string.

    ^topic +

    type array_t #

    +^topic

    type Array #

    -

    func $infix+(self any, o any) Array

    -

    func concat(self any, other Array) Array

    -

    Returns a new array that concats this array and other.

    -

    func decode(self any) String

    -

    Calls decode(.utf8)

    -

    func decode(self any, encoding symbol) String

    -

    Decodes the array based on an encoding. Supported encodings: .utf8. Returns the decoded string or throws error.Decode.

    -

    func endsWith(self any, suffix Array) bool

    -

    Returns whether the array ends with suffix.

    -

    func find(self any, needle Array) ?int

    -

    Returns the first index of needle in the array or none if not found.

    -

    func findAnyByte(self any, bytes Array) ?int

    -

    Returns the first index of any bytes in arrays or none if not found.

    -

    func findByte(self any, byte int) ?int

    -

    Returns the first index of byte in the array or none if not found.

    -

    func fmt(self any, format NumberFormat) String

    -

    Formats each byte in the array using a NumberFormat. Each byte is zero padded.

    -

    func getByte(self any, idx int) int

    -

    Returns the byte value (0-255) at the given index idx.

    -

    func getInt(self any, idx int, endian symbol) int

    -

    Returns the int value of the 6 bytes starting from idx with the given endianness (.little or .big).

    -

    func getInt32(self any, idx int, endian symbol) int

    -

    Returns the int value of the 4 bytes starting from idx with the given endianness (.little or .big).

    -

    func insert(self any, idx int, arr Array) Array

    -

    Returns a new array with arr inserted at index idx.

    -

    func insertByte(self any, idx int, byte int) Array

    -

    Returns a new array with byte inserted at index idx.

    -

    func iterator(self any) ArrayIterator

    -

    Returns a new iterator over the array bytes.

    +

    func $index(self any, idx int)

    +

    func iterator(self any) RefSliceIterator[T]

    +

    Returns a new iterator over the array.

    func len(self any) int

    -

    Returns the number of bytes in the array.

    -

    func repeat(self any, n int) Array

    -

    Returns a new array with this array repeated n times.

    -

    func replace(self any, needle Array, replacement Array) Array

    -

    Returns a new array with all occurrences of needle replaced with replacement.

    -

    func $index(self any, idx int) int

    -

    func $index(self any, range Range) Array

    -

    Returns a slice into this array from a Range with start (inclusive) to end (exclusive) indexes.

    -

    func split(self any, sep Array) List[Array]

    -

    Returns a list of arrays split at occurrences of sep.

    -

    func startsWith(self any, prefix Array) bool

    -

    Returns whether the array starts with prefix.

    -

    func trim(self any, mode symbol, delims Array) Array

    -

    Returns the array with ends trimmed from runes in delims. mode can be .left, .right, or .ends.

    -

    func Array.$call(val any) Array

    -

    Converts a string to an byte Array.

    -
    ^topic -

    type ArrayIterator #

    -

    func next(self any) ?any

    +

    Returns the number of elements in the array.

    ^topic

    type pointer #

    func $index(self any, idx int) *T

    -

    func $index(self any, range Range) []T

    +

    func $index(self any, range Range) [*]T

    func $setIndex(self any, idx int, val T)

    func addr(self any) int

    When pointer runtime safety is enabled, this returns the raw pointer address as an int64. Otherwise, the pointer itself is bitcasted to an int64.

    func asObject(self any) any

    Casts the pointer to a Cyber object. The object is retained before it's returned.

    -

    func fromCstr(self any, offset int) Array

    -

    Returns an Array from a null terminated C string.

    +

    func fromCstr(self any, offset int) String

    +

    Returns a String from a null terminated C string.

    func get(self any, offset int, ctype symbol) dyn

    Dereferences the pointer at a byte offset and returns the C value converted to Cyber.

    +

    func getString(self any, offset int, len int) String

    +

    Returns a String with a copy of the byte data starting from an offset to the specified length.

    func set(self any, offset int, ctype symbol, val any)

    Converts the value to a compatible C value and writes it to a byte offset from this pointer.

    -

    func toArray(self any, offset int, len int) Array

    -

    Returns an Array with a copy of the byte data starting from an offset to the specified length.

    func pointer.$call(T type, addr int) *T

    Converts an int to a pointer value.

    ^topic @@ -3363,6 +3353,7 @@

    type Fiber #

    ^topic

    type metatype #

    func id(self any) int

    +

    func metatype.$call(T type) metatype

    ^topic

    type Range #

    ^topic @@ -3378,25 +3369,47 @@

    type FutureResolver ^topic -

    type Slice #

    +

    type Ref #

    +^topic +

    type RefSlice #

    +

    func $index(self any, idx int)

    +

    func $index(self any, range Range)

    +

    func $setIndex(self any, idx int, val T)

    +

    func iterator(self any) RefSliceIterator[T]

    +

    func len(self any) int

    +
    ^topic +

    type RefSliceIterator #

    +

    func next(self any) ?T

    +
    ^topic +

    type PtrSlice #

    func $index(self any, idx int) *T

    -

    func $index(self any, range Range) []T

    +

    func $index(self any, range Range) [*]T

    func $setIndex(self any, idx int, val T)

    +

    func endsWith(self any, suffix [*]T) bool

    +

    Returns whether the array ends with suffix.

    +

    func findScalar(self any, needle T) ?int

    +

    Returns the first index of needle in the slice or none if not found.

    +

    func iterator(self any) PtrSliceIterator[T]

    func len(self any) int

    +

    func startsWith(self any, prefix [*]T) bool

    +

    Returns whether the array starts with prefix.

    +
    ^topic +

    type PtrSliceIterator #

    +

    func next(self any) ?T

    ^topic

    type IMemory trait #

    -

    func alloc(self any, len int) []byte

    -

    func free(self any, buf []byte)

    +

    func alloc(self any, len int) [*]byte

    +

    func free(self any, buf [*]byte)

    ^topic

    type Memory #

    func new(self any, T type) *T

    -

    func alloc(self any, T type, n int) []T

    +

    func alloc(self any, T type, n int) [*]T

    func free(self any, ptr *T)

    -

    func free(self any, slice []T)

    +

    func free(self any, slice [*]T)

    ^topic

    type DefaultMemory #

    -

    func alloc(self any, len int) []byte

    -

    func free(self any, buf []byte)

    +

    func alloc(self any, len int) [*]byte

    +

    func free(self any, buf [*]byte)

    ^topic

    mod cy #

    @@ -3590,7 +3603,7 @@

    mod os #

    use os
     
     var map = os.getEnvAll()
    -for map -> [k, v]:
    +for map -> {k, v}:
         print "$(k) -> $(v)"
     

    ^topic

    @@ -3621,7 +3634,7 @@

    mod os #

    Creates the directory at path. Returns true if successful.

    func createFile(path String, truncate bool) File

    Creates and opens the file at path. If truncate is true, an existing file will be truncated.

    -

    func cstr(s any) *

    +

    func cstr(s String) *

    Returns a null terminated C string.

    func cwd() String

    Returns the current working directory.

    @@ -3633,7 +3646,7 @@

    mod os #

    Returns the current executable's path.

    func exit(status int)

    Exits the program with a status code.

    -

    func fetchUrl(url String) Array

    +

    func fetchUrl(url String) String

    Fetches the contents at url using the HTTP GET request method.

    func free(ptr *)

    Frees the memory located at ptr.

    @@ -3675,17 +3688,17 @@

    mod os #

    Pauses the current thread for given milliseconds.

    func unsetEnv(key String)

    Removes an environment variable by key.

    -

    func writeFile(path String, contents any)

    +

    func writeFile(path String, contents String)

    Writes contents as a string or bytes to a file.

    ^topic

    type File #

    func close(self any)

    Closes the file handle. File ops invoked afterwards will return error.Closed.

    -

    func iterator(self any) any

    -

    func next(self any) any

    -

    func read(self any, n int) Array

    +

    func iterator(self any) File

    +

    func next(self any) String

    +

    func read(self any, n int) String

    Reads at most n bytes as an Array. n must be at least 1. A result with length 0 indicates the end of file was reached.

    -

    func readAll(self any) Array

    +

    func readAll(self any) String

    Reads to the end of the file and returns the content as an Array.

    func seek(self any, n int)

    Seeks the read/write position to pos bytes from the start. Negative pos is invalid.

    @@ -3699,8 +3712,8 @@

    type File #

    Equivalent to streamLines(4096).

    func streamLines(self any, bufSize int) File

    Returns an iterable that streams lines ending in \n, \r, \r\n, or the EOF. The lines returned include the new line character(s). A buffer size of bufSize bytes is allocated for reading. If \r is found at the end of the read buffer, the line is returned instead of waiting to see if the next read has a connecting \n.

    -

    func write(self any, val any) int

    -

    Writes a String or Array at the current file position. The number of bytes written is returned.

    +

    func write(self any, val String) int

    +

    Writes a String at the current file position. The number of bytes written is returned.

    ^topic

    type Dir #

    func iterator(self any) DirIterator

    @@ -3758,6 +3771,8 @@

    mod test #

    Returns whether two values are equal. Panics with error.AssertError if types or values do not match up.

    func eqList(a List[T], b List[T]) bool

    Returns true if two lists have the same size and the elements are equal as if eq was called on those corresponding elements.

    +

    func eqSlice(a , b ) bool

    +

    Returns true if two slices have the same size and the elements are equal as if eq was called on those corresponding elements.

    func eqNear(a T, b T) bool

    Returns true if two numbers are near each other within epsilon 1e-5.

    func fail()

    @@ -3794,7 +3809,7 @@

    FFI context. #

    ^topic

    Declare functions. #

    Functions from a library are first declared using cfunc which accepts C types in the form of symbols. In a future update they will accept C syntax instead.

    -
    ffi.cfunc('add', [.int, .int], .int)
    +
    ffi.cfunc('add', {.int, .int}, .int)
     

    The first argument refers to the symbol's name in the dynamic library. The second argument contains the function's parameter types and finally the last argument is the function's return type.

    @@ -3838,8 +3853,8 @@

    Bind to Cyber type. #< b *void c bool -ffi.cbind(MyObject, [.float, .voidPtr, .bool]) -ffi.cfunc('foo', [MyObject], MyObject) +ffi.cbind(MyObject, {.float, .voidPtr, .bool}) +ffi.cfunc('foo', {MyObject}, MyObject) let lib = ffi.bindLib('./mylib.so') var res = lib.foo(MyObject{a=123.0, b=os.cstr('foo'), c=true}) @@ -3856,7 +3871,7 @@

    Bind to Cyber type. #< }

    cbind also generates ptrTo[Type] as a helper function to dereference an opaque ptr to a new Cyber object:

    -
    ffi.cfunc('foo', [MyObject], .voidPtr)
    +
    ffi.cfunc('foo', {MyObject}, .voidPtr)
     let lib = ffi.bindLib('./mylib.so')
     
     var ptr = lib.foo(MyObject{a=123, b=os.cstr('foo'), c=true})
    @@ -4376,7 +4391,7 @@ 

    Dynamic variables. # --> CompileError: Can not find the symbol `$infix/` in `String`

    When a is assigned a different type of value, its recent type is updated so the compiler can continue to surface errors ahead of time:

    -
    a = [1, 2, 3]
    +
    a = {1, 2, 3}
     print a / 2
     --> CompileError: Can not find the symbol `$infix/` in `List`
     
    @@ -4527,7 +4542,7 @@

    Operator overloading. ^topic @@ -4912,18 +4927,37 @@

    finalizer #

    ^topic

    Memory. #

    + + +
    + +

    ^top

    -

    Cyber provides memory safety by default.

    -

    ARC. #

    -

    Cyber uses ARC or automatic reference counting to manage memory. -ARC is deterministic and has less overhead compared to a tracing garbage collector. Reference counting distributes memory management, which reduces GC pauses and makes ARC suitable for realtime applications. One common issue in ARC implementations is reference cycles which Cyber addresses with Weak References and it's very own Cycle Detection.

    +

    Cyber provides memory safety by default with structured and automatic memory. +Manual memory is also supported but discouraged.

    +

    Structured memory. #

    +

    Cyber uses single value ownership and variable scopes to determine the lifetime of values. +When lifetimes are known at compile-time, the memory occupied by values do not need to be manually managed which prevents memory bugs such as:

    +
      +
    • Use after free.
    • +
    • Use after invalidation.
    • +
    • Free with wrong allocator.
    • +
    • Double free.
    • +
    • Memory leaks.
    • +
    • Null pointer dereferencing.
    • +
    +

    At the same time, structured memory allows performant code to be written since it provides safe semantics to directly reference values and child values.

    ^topic -

    Reference counting. #

    -

    In Cyber, there are primitive and object values. Primitives don't need any memory management, since they are copied by value and no heap allocation is required (with the exception of primitives being captured by a closure.

    +

    Value ownership. #

    +

    Every value in safe memory has a single owner. +An owner can be a variable that binds to a value. Otherwise, the owner can be a parent value or the value itself. +The owner is responsible for deinitializing or dropping the value when it has gone out of scope (no longer reachable). +For example, at the end of the block, a variable can no longer be accessed so it drops the value that it owns:

    +
    var a = 123
    +print a    --> 123
    +-- Deinit `a`.
    +
    +

    In this case, there is nothing to deinitialize since the value is an integer.

    +

    If the value was a String, the deinit logic would release (-1) on a reference counted byte buffer since strings are just immutable views over byte buffers:

    +
    var a = 'hello'
    +print a    --> hello
    +-- Deinit `a`.
    +-- `a.buf` is released.
    +-- `a.buf` is freed.
    +
    +

    Since the string buffer's reference count reaches 0, it's freed as well.

    +

    Finally, let's take a look at ListValue which manages a dynamically sized array of elements:

    +
    var a = ListValue[int]{1, 2, 3}
    +print a    --> {1, 2, 3}
    +-- Deinit `a`.
    +-- `a.buf` is freed.
    +
    +

    When a is deinitialized, the buffer that holds the 3 integer elements is freed. +You may have surmised that it's named ListValue because it's a value type (it can only be passed around by copying itself). The object type, List, wraps ListValue and can be passed around by reference.

    +

    The concept of a value having a single owner is very simple yet powerful. +A value can represent any data structure from primitives to dynamically allocated buffers. +A value always knows how to deinitialize itself, and the owner knows when to deinitialize the value. +Later, we'll see that this same concept also applies to shared ownership.

    +^topic +

    Copy semantics. #

    +

    By default, values are passed around by copying (shallow copying), but now all values can perform a copy.

    +

    A primitive, such as an integer, can always be copied:

    +
    var a = 123
    +var b = a
    +-- Deinit `b`.
    +-- Deinit `a`.
    +
    +

    After a copy, a new value is created and given the owner of b. At the end of the block, both a and b are deinitialized (which does nothing since they are just primitives).

    +

    Strings are also copyable since they are immutable views over byte buffers:

    +
    var a = 'hello'
    +var b = a
    +-- Deinit `b`.
    +-- `b.buf` is released.
    +-- Deinit `a`.
    +-- `a.buf` is released.
    +-- `a.buf` is freed.
    +
    +

    The copy b also reuses the byte buffer of a by retaining (+1) on the reference counted byte buffer. The byte buffer is finally freed once there are no references pointing to it.

    +

    Unlike the integer and string, a ListValue can not be copied since doing so requires duping a heap allocated buffer which is considered expensive:

    +
    var a = ListValue[int]{1, 2, 3}
    +var b = a      --> error: Can not copy `ListValue`. Can only be cloned or moved.
    +
    +

    Instead a can only be cloned or moved.

    +

    By default, a declared value type is copyable if all of it's members are also copyable:

    +
    type Foo struct:
    +    a int
    +    b String
    +
    +var a = Foo{a=123, b='hello'}
    +var b = a
    +
    +

    Since integers and strings are both copyable, Foo is also copyable.

    +

    Foo is non-copyable if it contains at least one non-copyable member:

    +
    type Foo struct:
    +    a int
    +    b String
    +    c ListValue[int]
    +
    +var a = Foo{a=123, b='hello'}
    +var b = a      --> error: Can not copy `Foo`. Can only be moved.
    +
    +

    Foo is also non-copyable if it contains unsafe types such as pointers or pointer slices:

    +
    type Foo struct:
    +    a int
    +    b String
    +    c *Bar
    +    d [*]float
    +
    +

    Foo can implement Copyable to override the default behavior and define it's own copy logic:

    +
    type Foo struct:
    +    with Copyable
    +    a int
    +    b String
    +    c *Bar
    +    d [*]float
    +
    +    func copy(self) Foo:
    +        return .{
    +            a = self.a,
    +            b = self.b,
    +            c = self.c,
    +            d = self.d,
    +        }
    +
    +

    Likewise, Foo can implement NonCopyable which indicates that it can never be copied:

    +
    type Foo struct:
    +    with NonCopyable
    +    a int
    +    b String
    +
    +^topic +

    Cloning. #

    +

    Some value types are not allowed to be copied by default and must be cloned instead:

    +
    var a = ListValue[int]{1, 2, 3}
    +var b = a.clone()
    +
    +

    Any Copyable type is also Cloneable. For example, performing a clone on an integer will simply perform a copy:

    +
    var a = 123
    +var b = a.clone()
    +
    +

    A value type can implement Cloneable to override the default behavior and define it's own clone logic:

    +
    type Foo struct:
    +    with Cloneable
    +    a int
    +    b String
    +
    +    func clone(self) Foo:
    +        return .{
    +            a = self.a + 1,
    +            b = self.b,
    +        }
    +
    +

    Likewise, Foo can implement NonCloneable which indicates that it can never be cloned:

    +
    type Foo struct:
    +    with NonCloneable
    +    a int
    +    b String
    +
    +^topic +

    Moving. #

    +

    Values can be moved, thereby transfering ownership from one variable to another:

    +
    var a = 123
    +var b = move a
    +print a     --> error: `a` does not own a value.
    +
    +

    Some types such as ListValue can not be passed around by default without moving (or cloning) the value:

    +
    var a = ListValue[int]{1, 2, 3}
    +print computeSum(move a)    --> 6
    +
    +

    In this case, the list value is moved into the computeSum function, so the list is deinitialized in the function before the function returns.

    +^topic +

    References. #

    +

    References are safe pointers to values. +Unlike unsafe pointers, a reference is never concerned with when to free or deinitialize a value since that responsibility always belongs to the value's owner. +They are considered safe pointers because they are guaranteed to point to their values and never outlive the lifetime of their values.

    +

    References grant stable mutability which allows a value to be modified as long as it does not invalidate other references. +Multiple references can be alive at once as long as an exclusive reference is not also alive.

    +

    The & operator is used to obtain a reference to a value:

    +
    var a = 123
    +var ref = &a
    +ref.* = 234
    +print a        --> 234
    +
    +

    A reference can not outlive the value it's referencing:

    +
    var a = 123
    +var ref = &a
    +if true:
    +    var b = 234
    +    ref = &b   --> error: `ref` can not oulive `b`.
    +
    +

    A reference type is denoted as &T where T is the type that the reference points to:

    +
    var a = 123
    +
    +func inc(a &int):
    +    a.* = a.* + 1
    +
    +

    References allow stable mutation:

    +
    var a = ListValue[int]{1, 2, 3}
    +var third = &a[2]
    +third.* = 300
    +print a        --> {1, 2, 300}
    +
    +

    The element that third points to can be mutated because it does not invalidate other references.

    +

    References however can not perform a unstable mutation. +An unstable mutation requires an exclusive reference:

    +
    var a = ListValue[int]{1, 2, 3}
    +var ref = &a
    +ref.append(4)  --> error: Expected exclusive reference.
    +
    +^topic +

    Exclusive reference. #

    +

    An exclusive reference grants full mutability which allows a value to be modified even if could potentially invalidate unsafe pointers.

    +

    A single exclusive reference can be alive as long as no other references are also alive. +Since no other references (safe pointers) are allowed to be alive at the same time, no references can become invalidated.

    +

    The &! operator is used to obtain an exclusive reference to a value. +An exclusive reference type is denoted as &!T where T is the type that the reference points to.

    +

    ListValue is an example of a type that requires an exclusive reference for operations that can resize or reallocate its dynamic buffer:

    +
    var a = ListValue[int]{1, 2, 3}
    +a.append(4)
    +print a        --> {1, 2, 3, 4}
    +
    +

    Note that invoking the method append here automatically obtains an exclusive reference for self without an explicit &! operator.

    +

    If another reference is alive before append, the compiler will not allow an exclusive reference to be obtained from a. +Doing so would allow append to potentially reallocate its dynamic buffer, thereby invalidating other references:

    +
    var a = ListValue[int]{1, 2, 3}
    +var third = &a[2]
    +a.append(4)    --> error: Can not obtain exclusive reference, `third` is still alive.
    +print third
    +
    +^topic +

    self reference. #

    +

    By default self has a type of &T when declared in a value type's method:

    +
    type Pair struct:
    +    a int
    +    b int
    +
    +    func sum(self) int:
    +        return self.a + self.b
    +
    +

    If self requires an exclusive reference, then it must be prepended with !:

    +
    type Pair struct:
    +    a int
    +    b int
    +
    +    func sum(!self) int:
    +        return self.a + self.b
    +
    +

    Invoking methods automatically obtains the correct reference as specified by the method:

    +
    var p = Pair{a=1, b=2}
    +print p.sum()     --> 3
    +
    +^topic +

    Lifted values. #

    +

    Planned Feature

    +
    ^topic +

    Deferred references. #

    +

    Planned Feature

    +
    ^topic +

    Implicit lifetimes. #

    +

    Planned Feature

    +
    ^topic +

    Reference lifetimes. #

    +

    Planned Feature

    +
    ^topic +

    Shared ownership. #

    +

    Planned Feature

    +
    ^topic +

    Deinitializer. #

    +

    Planned Feature

    +
    ^topic +

    Pointer interop. #

    +

    Planned Feature

    +
    ^topic +

    Automatic memory. #

    +

    Cyber uses an ARC/GC hybrid to automatically manage objects instantiated from object types. Value types typically do not need to be automatically managed unless they were lifted by a closure or a dynamic container.

    +^topic +

    ARC. #

    +

    ARC also known as automatic reference counting is deterministic and has less overhead compared to a tracing garbage collector. Reference counting distributes memory management, which reduces GC pauses and makes ARC suitable for realtime applications. One common issue in ARC implementations is reference cycles which Cyber addresses with a GC supplement when it is required.

    Objects are managed by ARC. Each object has its own reference counter. Upon creating a new object, it receives a reference count of 1. When the object is copied, it's retained and the reference count increments by 1. When an object value is removed from it's parent or is no longer reachable in the current stack frame, it is released and the reference count decrements by 1.

    Once the reference count reaches 0 the object begins its destruction procedure.

    ^topic @@ -4954,8 +5248,11 @@

    Object destructor. # Since objects freed by the GC either belongs to a reference cycle or branched from one, the GC will still end up invoking the destructor of all unreachable objects. This implies that the destructor order is not reliable, but destructors are guaranteed to be invoked for all unreachable objects.

    ^topic -

    Optimizations. #

    -

    The compiler can reduce the number of retain/release ops since it can infer value types even though they are dynamically typed to the user. Arguments passed to functions are only retained depending on the analysis from the callsite.

    +

    Retain optimizations. #

    +

    When the lifetime of an object's reference is known on the stack, a large amount of retain/release ops can be avoided. +For example, calling a function with an object doesn't need a retain since it is guaranteed to be alive when the function returns. +This leaves only cases where an object must retain to ensure correctness such as escaping the stack.

    +

    When using dynamic types, the compiler can omit retain/release ops when it can infer the actual type even though they are dynamically typed to the user.

    ^topic

    Closures. #

    When primitive variables are captured by a closure, they are boxed and allocated on the heap. This means they are managed by ARC and cleaned up when there are no more references to them.

    @@ -4963,20 +5260,19 @@

    Closures. #

    Fibers. #

    Fibers are freed by ARC just like any other object. Once there are no references to the fiber, it begins to release it's child references by unwinding it's call stack.

    ^topic -

    Heap. #

    -

    Many object types in Cyber are small enough to be at or under 40 bytes. To take advantage of this, Cyber can reserve object pools to quickly allocate and free these small objects with very little bookkeeping. Bigger objects are allocated and managed by mimalloc which has proven to be a fast and reliable general-purpose heap allocator.

    +

    Heap objects. #

    +

    Many object types are small enough to be at or under 40 bytes. To take advantage of this, object pools are reserved to quickly allocate and free these small objects with very little bookkeeping. Bigger objects are allocated and managed by mimalloc which has proven to be a fast and reliable general-purpose heap allocator.

    ^topic -

    Weak references. #

    -

    Planned Feature

    -
    ^topic -

    Cycle detection. #

    -

    The cycle detector is also considered a GC and frees abandoned objects managed by ARC. Although weak references can remove cycles altogether, Cyber does not force you to use them and provides a manual GC as a one-time catch all solution.

    -

    Incomplete Feature: Only the main fiber stack is cleaned up at the moment.

    -

    To invoke the GC, call the builtin function: performGC.

    +

    GC. #

    +

    The garbage collector is only used if the program may contain objects that form reference cycles. This property is statically determined by the compiler. Since ARC frees most objects, the GC's only responsibility is to free abandoned objects that form reference cycles. +This reduces the amount of work for GC marking since only cyclable objects (objects that may contain a reference cycle) are considered.

    +

    Weak references are not supported for object types because objects are intended to behave like GC objects (the user should not be concerned with reference cycles). If weak references do get supported in the future, they will be introduced as a Weak[T] type that is used with an explicit reference counted Rc[T] type.

    +

    Currently, the GC can be manually invoked. However, the plan is for this to be automatic by either running in a separate thread or per virtual thread by running the GC incrementally.

    +

    To invoke the GC, call the builtin function: performGC. Incomplete Feature: Only the main fiber stack is cleaned up at the moment.

    func foo():
         -- Create a reference cycle.
    -    var a = []
    -    var b = []
    +    var a = {_}
    +    var b = {_}
         a.append(b)
         b.append(a)
     
    @@ -5458,7 +5754,7 @@ 

    C backend. #

    keyword: [ 'func', 'mod', 'for', 'coinit', 'coresume', 'coyield', 'use', 'await', 'context', 'return', 'if', 'else', 'as', 'while', 'var', 'let', 'dynobject', 'object', 'struct', 'cstruct', 'with', 'caught', - 'break', 'continue', 'switch', 'pass', 'or', 'and', 'not', 'is', 'error', 'throws', + 'break', 'continue', 'switch', 'pass', 'or', 'and', 'not', 'is', 'error', 'throws', 'move', 'true', 'false', 'none', 'throw', 'try', 'catch', 'recover', 'enum', 'type', 'case', 'trait' ], type: [