-
Notifications
You must be signed in to change notification settings - Fork 1
Types
MScript has built in primitives for general computing, namely:
A 32-bit signed integer. Endianness is not part of the spec.
Properties
Name | Type | Description |
---|---|---|
pow | fn(self, power: int) -> bigint | Raise this integer to a given power. |
powf | fn(self, power: float) -> float | Raise this integer to a given fractional power. |
sqrt | fn(self) -> float | Return the square root of a number. |
to_int | fn(self) -> int | Cast this number to an int type. |
to_bigint | fn(self) -> bigint | Cast this number to an bigint type. |
to_byte | fn(self) -> byte | Cast this number to a byte type. |
to_float | fn(self) -> float | Cast this number to an float type. |
abs | fn(self) -> int | Returns this number's absolute value. |
x = 9
y: int = 10 # specifying a type is optional when it can be inferred!
z = x + y * 2
assert z == 29
assert (-5).abs() == 5
assert y.pow(2) == 100
assert 25.sqrt() == 5
A 128-bit signed integer. Endianness is not part of the spec. To specify a bigint
literal, prefix the number with a capital B
.
Properties
Name | Type | Description |
---|---|---|
pow | fn(self, power: int) -> bigint | Raise this integer to a given power. |
powf | fn(self, power: float) -> float | Raise this integer to a given fractional power. |
sqrt | fn(self) -> float | Return the square root of a number. |
to_int | fn(self) -> int | Cast this number to an int type. |
to_bigint | fn(self) -> bigint | Cast this number to an bigint type. |
to_byte | fn(self) -> byte | Cast this number to a byte type. |
to_float | fn(self) -> float | Cast this number to an float type. |
abs | fn(self) -> bigint | Returns this number's absolute value. |
x: bigint = B999999999999999999999999
y = 0xfffffffffffff
assert typeof (x + y) == "bigint"
assert B5.pow(32) == B23283064365386962890625
assert B1_000.pow(2) == 1_000_000 # you can use commas in any integer literal
B1 = B10 # you may use B1, B2, ... as an identifier, but you may never read their value.
print B1 # this will output `1`, since the literal takes priority over any variable name.
A 8-bit unsigned integer. Endianness is not part of the spec. To specify a byte literal, prefix a base-2 number with 0b
Properties
Name | Type | Description |
---|---|---|
pow | fn(self, power: int) -> bigint | Raise this integer to a given power. |
powf | fn(self, power: float) -> float | Raise this integer to a given fractional power. |
sqrt | fn(self) -> float | Return the square root of a number. |
to_int | fn(self) -> int | Cast this number to an int type. |
to_bigint | fn(self) -> bigint | Cast this number to an bigint type. |
to_byte | fn(self) -> byte | Cast this number to a byte type. |
to_float | fn(self) -> float | Cast this number to an float type. |
abs | fn(self) -> byte | Returns this number's absolute value. Note: byte is unsigned, so this method will always return self. |
to_ascii | fn(self) -> str | Returns a string of length 1 containing this byte's ASCII representation. |
five: byte = 0b101
ten = 10.to_byte()
assert five | ten == 0b1111
assert 1 << 7 == 128
const decoded = [0b1101000, 0b1100101, 0b1101100, 0b1101100, 0b1101111].map(fn(ord: byte) -> str {
return ord.to_ascii()
})
assert decoded == ["h", "e", "l", "l", "o"]
A 64-bit floating point number. Conforms with "binary64" IEEE 754-2008
.
Properties
Name | Type | Description |
---|---|---|
pow | fn(self, power: int) -> float | Raise this integer to a given power. |
powf | fn(self, power: float) -> float | Raise this integer to a given fractional power. |
sqrt | fn(self) -> float | Return the square root of a number. |
to_int | fn(self) -> int | Cast this number to an int type. |
to_bigint | fn(self) -> bigint | Cast this number to an bigint type. |
to_byte | fn(self) -> byte | Cast this number to a byte type. |
to_float | fn(self) -> float | Cast this number to an float type. |
abs | fn(self) -> float | Returns this number's absolute value. |
fpart | fn(self) -> float | Returns this number's decimal part (ie. 3.1415 -> 0.1415) |
ipart | fn(self) -> float | Returns this number's integer part (ie. 3.1415 -> 3.0) |
round | fn(self) -> float | Return this number rounded up at the half, as one would have learned in primary school. |
floor | fn(self) -> float | Returns this number floored to the nearest whole. |
ceil | fn(self) -> float | Returns this number rounded up to the nearest whole. |
assert 5.pow(-0.001.to_int()) == 1
assert 3.1415.ipart() == 3
assert 3.1415.fpart() == 0.1415 # this is platform dependent: you may also get 0.14150000000000018
assert (-2.718281828).abs() == 2.718281828.abs()
assert 3.5.round() == 4
assert 3.49999.round() == 3
assert 3.5.ceil() == 4
assert 3.5.floor() == 3
A UTF-8–encoded, growable string.
Properties
Name | Type | Description |
---|---|---|
len | fn(self) -> int | Returns the length of the string. |
substring | fn(self, start: int, end: int) -> str | Returns a copy of a string from start to end - 1. |
delete | fn(self, start: int, end: int) -> str | Returns a copy of a string, with characters from start to end - 1 removed. |
contains | fn(self, substring: str) -> bool | Returns whether a string contains a substring. |
index_of | fn(self, substring: str) -> int? | Returns the starting index of a substring, or nil if it is not present. |
inner_capacity | fn(self) -> int | Returns the size in bytes of the memory allocated to this string |
reverse | fn(self) -> str | Returns a copy of this string, reversed. |
insert | fn(self, new_content: str, index: int) -> str | Returns a new string, with the contents of new_content inserted at a given index. |
replace | fn(self, pattern: str, replacement: str) -> str | Returns a copy of this string with every occurrence of a given pattern replaced with another string. |
parse_int | fn(self) -> int? | Attempts to parse a base-10 integer from this string and store it in a 32 bit signed integer. Returns the result if successful, else nil. |
parse_int_radix | fn(self, radix: int) -> int? | Attempts to parse an integer from this string given a radix and store it in a 32 bit signed integer. Returns the result if successful, else nil. |
parse_bigint | fn(self) -> bigint? | Attempts to parse a base-10 integer from this string and store it in a 128 bit signed integer. Returns the result if successful, else nil. |
parse_bigint_radix | fn(self, radix: int) -> bigint? | Attempts to parse an integer from this string given a radix and store it in a 128 bit signed integer. Returns the result if successful, else nil. |
parse_bool | fn(self) -> bool? | Attempts to parse a bool from this string. Returns the result if successful, else nil. |
parse_float | fn(self) -> float? | Attempts to parse a float from this string. Returns the result if successful, else nil. |
parse_byte | fn(self) -> byte? | Attempts to parse a byte from this string. Returns the result if successful, else nil. |
split | fn(self, index: int) -> [str, str] | Split a string into two copies at a given index. If the index overflows, the second string will be empty. |
const MESSAGE = "hello world"
assert MESSAGE[6] == "w"
assert MESSAGE.len() == 11
assert MESSAGE.substring(6, MESSAGE.len()) == "world"
assert MESSAGE.contains("lo w")
assert !MESSAGE.contains("a")
assert MESSAGE.index_of("wo") == 6
assert MESSAGE.index_of("ow") == nil
assert typeof MESSAGE.index_of("ow") == "int?"
assert MESSAGE.inner_capacity() >= 11
assert MESSAGE.reverse() == "dlrow olleh"
assert MESSAGE.insert(", you are my", 5) == "hello, you are my world"
assert MESSAGE.replace(" world", "!") == "hello!"
assert MESSAGE.delete(5, 7) == "helloorld"
assert "25".parse_int() == 25
assert "-25".parse_int() == -25
assert "25.0".parse_int() == nil
assert "twenty five".parse_int() == nil
assert typeof "".parse_int() == "int?"
assert "25".parse_int_radix(16) == 0x25
assert "25".parse_int_radix(16) != 25
assert "25".parse_int_radix(10) == 25
assert "25".parse_bigint() == B25
assert "-25".parse_bigint() == -B25
assert "25.0".parse_bigint() == nil
assert typeof "".parse_bigint() == "bigint?"
assert "twenty five".parse_bigint() == nil
assert "25".parse_bigint_radix(16) == 0x25
assert "25".parse_bigint_radix(10) == B25
assert get "true".parse_bool()
assert !(get "false".parse_bool())
assert "yes".parse_bool() == nil
assert "3.14159".parse_float() == 3.14159
assert "xyz".parse_float() == nil
assert typeof "".parse_float() == "float?"
assert "3".parse_byte() == 0b11
assert "0b101".parse_byte() == 5
assert "goodwill".split(4).to_str() == "[\"good\", \"will\"]"
assert "goodwill".split(100).to_str() == "[\"goodwill\", \"\"]"
assert "goodwill".split(-1).to_str() == "[\"goodwill\", \"\"]"
A list tuple: the base of list, with added restrictions. Elements do not have to be of the same type. Once initialized, this list may never change size.
Properties
Name | Type | Description |
---|---|---|
len | fn(self) -> int | Return the amount of elements in the list |
inner_capacity | fn(self) -> int | Return the number of elements already allocated for this list that can be used before the list has to resize. |
ensure_inner_capacity | fn(self, min_capacity: int) | Internally allocate enough memory to store at least length + min_capacity elements. There is no guarantee that the allocator only creates min_capacity, as it may optimize for your runtime usage pattern and allocate more than requested to speed up performance. |
a = fn() -> int { return 5 }
assert [1, "hello!", false, a()].len() == 4
const x = [5]
take_ints = fn(in: [int]) -> [int, str] {
assert in.len() == 1
val = in[0]
assert val == 5
return [val, (val * 2).to_str() + "!"]
}
[num, doubled] = take_ints(x) # array unpacking
assert num == 5
assert doubled == "10!"
A contiguous, growable array type. Extends [T]
and inherits all of its methods.
Properties
Name | Type | Description |
---|---|---|
remove | fn(self, index: int) -> T | Removes the element at a given index from the list, and returns it. |
reverse | fn(self) | Reverses the list in-place. |
push | fn(self, new_element: T) | Pushes new_element to the end of the list. |
map | fn(self, mapper: fn(T) -> K) -> [K...] | Creates a new array with a copied length, maps each element (in order) of self to a new type K via many calls to mapper, and returns the result. |
filter | fn(self, predicate: fn(T) -> bool) -> [T...] | Creates a new array and conditionally copies elements from self based on the truthyness of the predicate. |
join | fn(self, join: [T...]) -> [T...] | Appends a list to the end of this list. Returns a reference to self. |
index_of | fn(self, search: T) -> int? | Searches for an element in this list. If found, returns the index; else, nil. |
square_it = fn(in: int) -> int {
return in * in
}
only_even = fn(in: int) -> bool {
return in % 2 == 0
}
pipeline = [3, 4, 5].map(square_it).filter(only_even)
assert pipeline == [16]
assert pipeline.len() == 1
pipeline.push(100)
pipeline.push(9999999)
assert pipeline == [16, 100, 9999999]
assert pipeline.remove(1) == 100
assert pipeline == [16, 9999999]
pipeline[1] = 8
assert pipeline.len() == 2
pipeline_str = pipeline.map(fn(in: int) -> str { return in + "!" })
pipeline_str.reverse()
assert pipeline_str == ["8!", "16!"]
Either true
or false
.
Properties
Name | Type | Description |
---|---|---|
to_str | fn(self) -> str | Returns a string representation including the function's bytecode name and location of state in memory (if present). |
name = "Jimmy Neutron"
is_cool: bool = name.len() <= 5
if is_cool {
print "sheesh!!"
} else if name == "Jimmy Neutron" {
print "🤓"
} else {
print "You aren't invited"
}
An abstraction to how the MScript interpreter calls procedures and subroutines. Every function has a type and properties.
Properties
Name | Type | Description |
---|---|---|
is_closure | fn(self) -> bool | Returns whether this function's body captures any outside variables and contributes to any external reference counting. |
to_str | fn(self) -> str | Returns a string representation including the function's bytecode name and location of state in memory (if present). |
outside = 5
assert !fn() {
print 5
}.is_closure()
assert fn() {
print outside
}.is_closure()
a = fn() {
outside
}
b = a
assert a.is_closure() == b.is_closure() == true
assert a.to_str() == b.to_str()
Any type can be made into an optional by adding a ?
after its name.
maybe_an_int: int? = 5
An empty optional is denoted by the nil
value.
empty_int: int? = nil
An optional variant of a type cannot be used everywhere that the type can. You must unwrap it to pass it as a function parameter or access its fields/methods.
One way to use an optional is via explicit unwrapping.
import random from std
const roll_die = fn() -> int? {
value = random.from(1, 10)
if value == 7 {
return nil
} else {
return value
}
}
while true {
result = get roll_die() # this will eventually return nil, and the program will panic
print result
}
You may not wish to crash your program when it encounters a nil
value. In that case, you may choose either of these fallback methods.
Provides a simple fallback value for any T?
, must be of type T
print get nil or 5 # 5
number = "5".parse_int() or 0
bad_fmt = "cats".parse_int() or -1
print number # 5
print bad_fmt # -1
Attempts to assigns a non-nil value to the name on the left hand side, and returns whether it was able to do so.
x: int? = nil
if x ?= "foo".parse_int() {
# `x` is non-nil!
}
Of course, you may also directly compare any value to nil
assert "hi" != nil
assert 5 != nil
abc: byte? = nil
assert abc == nil
assert nil == nil
Specify your own names for types using the type
keyword.
type Miles int
type Kilometers float
const marathon: Miles = 27
const in_km = (marathon * 1.609).ipart()
const expected: Kilometers = 43.0
in_km == expected
Custom class objects each have their own unique type. A class's type is valid in the scope in which the class was defined. See the intricacies of class and module types in their respective sections.