Skip to content
Mateo Rodriguez edited this page Feb 7, 2024 · 4 revisions

Built In Types

MScript has built in primitives for general computing, namely:

int

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

bigint

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.

byte

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"]

float

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

str

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\", \"\"]"

[T, K, A, *]

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!"

[T...] & [T, T, T, *]

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!"]

bool

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"
}

function

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()

Optionals

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.

Unwrapping

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
}

Fallbacks

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.

or operator

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

?= operator

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!
}

Direct Comparison

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

Aliasing

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

Class and Module Types

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.

Clone this wiki locally