Data Types. #
In Cyber, there are primitive types and object types. Primitives are copied around by value and don’t need additional heap memory or reference counts. Primitives include Booleans, Floats, Integers, Enums, Symbols, Errors, Static Strings, and the none
value. Object types include Lists, Tuples, Maps, Strings, Custom Objects, Lambdas, Fibers, Errors with payloads, Pointers, and several internal object types.
The none
value represents an empty value. This is similar to null in other languages.
Booleans. #
Booleans can be true
or false
.
var a = true
if a:
print 'a is true'
When other value types are coerced to the boolean type, the truthy value is determined as follows.
- The
none
value isfalse
. - Other objects and values are always
true
.
Numbers. #
Integers. #
int
is the default integer type. It has 48-bits and can represent integers in the range -(247) to 247-1.
When a numeric literal is used and the type can not be inferred, it will default to the int
type:
var a = 123
Integer notations always produce a int
value:
var a = 0xFF -- hex.
a = 0o17 -- octal.
a = 0b1010 -- binary.
a = 0u'🐶' -- UTF-8 rune.
Arbitrary values can be converted to a int
using the type as a function.
var a = '123'
var b = int(a)
In addition to arithmetic operations, integers can also perform bitwise operations.
Floats. #
float
is the default floating point type. It has a (IEEE 754) 64-bit floating point format.
Although a float
represents a decimal number, it can also represent integers between -(253-1) and (253-1). Any integers beyond the safe integer range is not guaranteed to have a unique representation.
A numeric literal can be used to create a float
if the inferred type is a float
:
a float = 123
Decimal and scientific notations always produce a float
value:
var a = 2.34567
var b = 123.0e4
Arbitrary values can be converted to a float
using the type as a function.
var a = '12.3'
var b = float(a)
Big Numbers. #
Planned Feature
Strings. #
The string
type represents a sequence of UTF-8 codepoints, also known as runes
. Each rune is stored internally as 1-4 bytes and can be represented as an int
. Under the hood, Cyber implements 6 different internal string types to optimize string operations, but the user just sees them as one type and doesn’t need to care about this detail under normal usage.
Strings are immutable, so operations that do string manipulation return a new string. By default, small strings are interned to reduce memory footprint.
To mutate an existing string, use the StringBuffer.
Planned Feature
A string is always UTF-8 validated. rawstrings outperform strings but you’ll have to validate them and take care of indexing yourself.
A single line string literal is surrounded in single quotes.
var apple = 'a fruit'
You can escape the single quote inside the literal or use double quotes.
var apple = 'Bob\'s fruit'
apple = "Bob's fruit"
Strings are UTF-8 encoded.
var str = 'abc🦊xyz🐶'
Use double quotes to surround a multi-line string.
var str = "line a
line b
line c"
You can escape double quotes inside the literal or use triple quotes.
var str = "line a
line \"b\"
line c"
-- Using triple quotes.
str = '''line a
line "b"
line c
'''
The following escape sequences are supported:
Escape Sequence | Code | Description |
---|---|---|
\a | 0x07 | Terminal bell. |
\b | 0x08 | Backspace. |
\e | 0x1b | Escape character. |
\n | 0x0a | Line feed character. |
\r | 0x0d | Carriage return character. |
\t | 0x09 | Horizontal tab character. |
The boundary of each line can be set with a vertical line character. This makes it easier to see the whitespace.
var poem = "line a
| two spaces from the left
| indented further"
Using the index operator will return the UTF-8 rune at the given index as a slice. This is equivalent to calling the method sliceAt()
.
var str = 'abcd'
print str[1] -- "b"
print str[-1] -- "d"
Using the slice index operator will return a view of the string at the given start and end (exclusive) indexes. The start index defaults to 0 and the end index defaults to the string’s length.
var str = 'abcxyz'
var sub = str[0..3]
print sub -- "abc"
print str[..5] -- "abcxy"
print str[1..] -- "bcxyz"
-- One way to use slices is to continue a string operation.
str = 'abcabcabc'
var i = str.findRune(0u'c')
print(i) -- "2"
i += 1
print(i + str[i..].findRune(0u'c')) -- "5"
type string
#
func concat(self, str string) string
-- Returns a new string that concats this string and `str`.
func endsWith(self, suffix string) bool
-- Returns whether the string ends with `suffix`.
func find(self, needle string) int?
-- Returns the first index of substring `needle` in the string or `none` if not found.
func findAnyRune(self, set string) int?
-- Returns the first index of any UTF-8 rune in `set` or `none` if not found.
func findRune(self, needle int) int?
-- Returns the first index of UTF-8 rune `needle` in the string or `none` if not found.
func insert(self, idx int, str string) string
-- Returns a new string with `str` inserted at index `idx`.
func isAscii(self) bool
-- Returns whether the string contains all ASCII runes.
func len(self) int
-- Returns the number of UTF-8 runes in the string.
func less(self, str string) bool
-- Returns whether this string is lexicographically before `str`.
func lower(self) string
-- Returns this string in lowercase.
func replace(self, needle string, replacement string) string
-- Returns a new string with all occurrences of `needle` replaced with `replacement`. |
func repeat(self, n int) string
-- Returns a new string with this string repeated `n` times.
func runeAt(self, idx int) int
-- Returns the UTF-8 rune at index `idx`.
func slice(self, start int, end int) string
-- Returns a slice into this string from `start` to `end` (exclusive) indexes. This is equivalent to using the slice index operator `[start..end]`.
func sliceAt(self, idx int) string
-- Returns the UTF-8 rune at index `idx` as a single rune string.
func split(self, delim string) List
-- Returns a list of UTF-8 strings split at occurrences of `delim`.
func startsWith(self, prefix string) bool
-- Returns whether the string starts with `prefix`.
func trim(self, mode symbol, trimRunes any) string
-- Returns the string with ends trimmed from runes in `trimRunes`. `mode` can be #left, #right, or #ends.
func upper(self) string
-- Returns this string in uppercase.
String Interpolation. #
You can embed expressions into string templates using braces.
var name = 'Bob'
var points = 123
var str = 'Scoreboard: {name} {points}'
Escape braces with a backslash.
var points = 123
var str = 'Scoreboard: \{ Bob \} {points}'
String templates can not contain nested string templates.
rawstring. #
A rawstring
does not automatically validate the string and is indexed by bytes and not UTF-8 runes.
Using the index operator will return the UTF-8 rune starting at the given byte index as a slice. If the index does not begin a valid UTF-8 rune, error.InvalidRune
is returned. This is equivalent to calling the method sliceAt()
.
var str = rawstring('abcd').insertByte(1, 255)
print str[0] -- "a"
print str[1] -- error.InvalidRune
print str[-1] -- "d"
type rawstring
#
func byteAt(self, idx int) int
-- Returns the byte value (0-255) at the given index `idx`.
func concat(self, str string) string
-- Returns a new string that concats this string and `str`.
func endsWith(self, suffix string) bool
-- Returns whether the string ends with `suffix`.
func find(self, needle string) int?
-- Returns the first index of substring `needle` in the string or `none` if not found.
func findAnyRune(self, set string) int?
-- Returns the first index of any UTF-8 rune in `set` or `none` if not found.
func findRune(self, needle int) int?
-- Returns the first index of UTF-8 rune `needle` in the string or `none` if not found.
func insert(self, idx int, str string) string
-- Returns a new string with `str` inserted at index `idx`.
func insertByte(self, idx int, byte int) string
-- Returns a new string with `byte` inserted at index `idx`.
func isAscii(self) bool
-- Returns whether the string contains all ASCII runes.
func len(self) int
-- Returns the number of bytes in the string.
func less(self, str rawstring) bool
-- Returns whether this rawstring is lexicographically before `str`.
func lower(self) string
-- Returns this string in lowercase.
func repeat(self, n int) rawstring
-- Returns a new rawstring with this rawstring repeated `n` times.
func replace(self, needle string, replacement string) string
-- Returns a new string with all occurrences of `needle` replaced with `replacement`.
func runeAt(self, idx int) int
-- Returns the UTF-8 rune at index `idx`. If the index does not begin a UTF-8 rune, `error.InvalidRune` is returned.
func slice(self, start int, end int) rawstring
-- Returns a slice into this string from `start` to `end` (exclusive) indexes. This is equivalent to using the slice index operator `[start..end]`.
func sliceAt(self, idx int) string
-- Returns the UTF-8 rune at index `idx` as a single rune string. If the index does not begin a UTF-8 rune, `error.InvalidRune` is returned.
func split(self, delim string) List
-- Returns a list of rawstrings split at occurrences of `delim`.
func startsWith(self, prefix string) bool
-- Returns whether the string starts with `prefix`.
func upper(self) string
-- Returns this string in uppercase.
func trim(self, mode symbol, trimRunes any) rawstring
-- Returns the string with ends trimmed from runes in `trimRunes`. `mode` can be #left, #right, or #ends.
func utf8(self) string
-- Returns a valid UTF-8 string or returns `error.InvalidRune`.
Lists. #
Lists are a builtin type that holds an ordered collection of elements. Lists grow or shrink as you insert or remove elements.
-- Construct a new list.
var list = [1, 2, 3]
-- The first element of the list starts at index 0.
print list[0] -- Prints '1'
-- Using a negative index starts at the back of the list.
print list[-1] -- Prints '3'
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. Negative start or end values count from the end of the list.
var list = [ 1, 2, 3, 4, 5 ]
list[0..0] -- [] Empty list.
list[0..3] -- [ 1, 2, 3 ] From start to end index.
list[3..] -- [ 4, 5 ] From start index to end of list.
list[..3] -- [ 1, 2, 3 ] From start of list to end index.
list[2..+2] -- [ 3, 4 ] From start index to start index + amount.
List operations.
var list = [234]
-- Append a value.
list.append 123
print list[-1] -- Prints '123'
-- Inserting a value at an index.
list.insert(1, 345)
-- Get the length.
print list.len() -- Prints '2'
-- Sort the list in place.
list.sort((a, b) => a < b)
-- Iterating a list.
for list each it:
print it
-- Remove an element at a specific index.
list.remove(1)
type List
#
Method | Summary |
---|---|
append(val any) none | Appends a value to the end of the list. |
concat(val any) none | Concats the elements of another list to the end of this list. |
insert(idx int, val any) none | Inserts a value at index idx . |
iterator() Iterator<any> | Returns a new iterator over the list elements. |
joinString(separator any) string | Returns a new string that joins the elements with separator . |
len() int | Returns the number of elements in the list. |
seqIterator() SequenceIterator<int, any> | Returns a new sequence iterator over the list elements. |
remove(idx int) none | Removes an element at index idx . |
resize(len int) none | Resizes the list to len elements. If the new size is bigger, none values are appended to the list. If the new size is smaller, elements at the end of the list are removed. |
sort(less func (a, b) bool) none | Sorts the list with the given less function. If element a should be ordered before b , the function should return true otherwise false . |
Tuples. #
Incomplete: Tuples can only be created from @host funcs at the moment.
Maps. #
Maps are a builtin type that store key value pairs in dictionaries.
var map = { a: 123, b: () => 5 }
print map['a']
-- You can also access the map using an access expression.
print map.a
-- Map entries can be separated by the new line.
map = {
foo: 1
bar: 2
}
Entries can also follow a {}:
block.
This gives structure to the entries and has
the added benefit of allowing multi-line lambdas.
Planned Feature
var colors = {}:
red: 0xFF0000
green: 0x00FF00
blue: 0x0000FF
dump func (c):
print c.red
print c.green
print c.blue
-- Nested map.
darker {}:
red: 0xAA0000
green: 0x00AA00
blue: 0x0000AA
Map operations.
var map = {}
-- Set a key value pair.
map[123] = 234
-- Get the size of the map.
print map.size()
-- Remove an entry by key.
map.remove 123
-- Iterating a list.
for map each [val, key]:
print '{key} -> {value}'
type Map
#
Method | Summary |
---|---|
iterator() Iterator<any> | Returns a new iterator over the map elements. |
seqIterator() SequenceIterator<any, any> | Returns a new sequence iterator over the map elements. |
remove(key any) none | Removes the element with the given key key . |
size() int | Returns the number of key-value pairs in the map. |
Objects. #
Any value that isn’t a primitive is an object. You can declare your own object types using the type object
declaration. Object types are similar to structs and classes in other languages. You can declare members and methods. Unlike classes, there is no concept of inheritance at the language level.
type Node object:
value
next
var node = Node{ value: 123, next: none }
print node.value -- '123'
New instances of an object template are created using the type name and braces that surround the initial member values.
Methods. #
The first parameter of a method must be self
. Otherwise, it declares a static function that can only be invoked from the type’s namespace.
type Node object:
value
next
-- A static function.
func create():
return Node{ value: 123, next: none }
-- A method.
func dump(self):
print self.value
var n = Node.create()
n.dump()
Although self
is required in a method’s signature, it’s optional when referencing the type’s members.
type Node object:
value
func double(self):
return value * 2
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.
By default, the symbols generate unique ids starting from 0.
type Fruit enum:
apple
orange
banana
kiwi
var fruit = Fruit.kiwi
print fruit -- 'Fruit.kiwi'
print int(fruit) -- '3'
When the type of the value is known to be an enum, it can be assigned using a symbol literal.
var fruit = Fruit.kiwi
fruit = .orange
print(fruit == Fruit.orange) -- 'true'
Symbols. #
Symbol literals begin with .
, followed by an identifier. They have their own global unique id.
var currency = .usd
print(currency == .usd) -- 'true'
print int(currency) -- '123' or some arbitrary id.