Literals
Literals are fixed values written directly in source code – such as numbers, strings, or booleans.
Bool
True, False are Bool,
uppercase as they are constants (as in Python).
Integer
123 is an integer literal of arbitrary precision.
Typical integer literals like 123456 are interpreted as Int, in case of type inferring, parameter overloading and template matching.
Big integer literals are interpreted as Int64, Int128, Int256, BigInt, if required due to the size.
Integer literals can automatically be converted to other sizes than Int64, but only if the converted-to-type can contain the value of the literal (i.e. as long as there is no loss of information).
So positive integer literals up to a certain size can implicitly be used as Int8/16/32/64/128/256 (i.e. signed):
- Up to
127->Int8 - Up to
32'767->Int16 - Up to
2'147'483'647->Int32 - Up to
9'223'372'036'854'775'807->Int64/Int - Up to
170'141'183'460'469'231'731'687'303'715'884'105'727->Int128 - Up to
57'896'044'618'658'097'711'785'492'504'343'953'926'634'992'332'820'282'019'728'792'003'956'564'819'967->Int256
Negative integer literals down to a certain size can implicitly be used as Int8/16/32/64/128/256:
- Down to
-128->Int8 - Down to
-32'768->Int16 - Down to
-2'147'483'648->Int32 - Down to
-9'223'372'036'854'775'808->Int64/Int - Down to
-170'141'183'460'469'231'731'687'303'715'884'105'728->Int128 - Down to
-57'896'044'618'658'097'711'785'492'504'343'953'926'634'992'332'820'282'019'728'792'003'956'564'819'968->Int256
Positive integer literals up to a certain size can implicitly be used as UInt8/16/32/64/128/256:
- Up to
255->UInt8 - Up to
65'535->UInt16 - Up to
4'294'967'295->UInt32 - Up to
18'446'744'073'709'551'615->UInt64/UInt - Up to
340'282'366'920'938'463'463'374'607'431'768'211'455->UInt128 - Up to
115'792'089'237'316'195'423'570'985'008'687'907'853'269'984'665'640'564'039'457'584'007'913'129'639'935->UInt256
Examples:
Int8 a = 1//1fits intoInt8Int8 b = 127//127fits intoInt8// Error because 128 does not fit intoInt8 c = 128Int8Int8 d = -128//-128fits intoInt8// Error becauseInt8 e = -129-129does not fit intoInt8UInt8 f = 255//255fits intoUInt8// Error becauseUInt8 g = 256256does not fit intoUInt8// Error becauseUInt8 h = -1-1does not fit intoUInt8Int16 i = 32767Int32 j = 2'147'483'647Int64 k = 9'223'372'036'854'775'807Int l = a//Int8fits intoInt32// Error becauseUInt m = lIntdoes not always fit intoUIntUInt m = UInt(l)
// Error becauseInt n = mUIntdoes not always fit intoIntInt n = Int(m)
Storage
Constexpr constructor that accepts an arbitrary precision integer literal and can store that in ROM. Should be stored as array ofInt/UInt.
Suffixes/postfixes to write integer literals of a certain size:
123uisUInt-123uis an error.
123i8,123i16,123i32,123i64,123u8,123u16,123u32,123u64(as in Rust)
Hex, Octal, Binary
Hexadecimal, octal, and binary literals are unsigned integers (e.g. UInt), as usually you want to describe flags, bit masks, hardware registers, hardware addresses, or color values, where signed integer doesn’t fit.
0xffffffffisUIntin hexadecimal0b1011isUIntin binary0o123isUIntin octal- Using
0oas in Python, - not
0123, as that IMHO is confusing/unexpected, even though it is C++ standard.
- Using
As unsigned integer literals up to a certain size can implicitly be converted to Int (i.e. signed), usually it is also possible to give a hex literal as an Int argument:
- Up to
0x7f->Int8 - Up to
0x7fff->Int16 - Up to
0x7fffffff->Int32 - Up to
0x7fffffffffffffff->Int64/Int
Otherwise you have to explicitly cast it like
Int mostNegativeInt = Int(0x8000000000000000).
Int vs. Bool
is an error,Int a = True
- because
Boolis not anInt, and - because a
Boolshould not be accidentally interpreted as anInt. Cast if necessary:Int a = Int(True)
is an error,Bool a = 1
- because
Intis not aBool, and - because an
Intshould not be accidentally interpreted as aBool. Cast if necessary:Bool a = Bool(1)
Floating-Point
1.0 is a floating-point literal.
A plain float literal like 1.0 is a Float (AKA Float64). This way the precision is the same as in C++, but there 1.0 is called a double and 1.0f is called a single float.
A floating-point literal can be implicitly converted to a smaller floating-point type if and only if the conversion is exact/lossless.
This is surely true for integers up to a certain size (but not limited to those):
- Up to
256.0->BFloat16 - Up to
2'048.0->Float16 - Up to
16'777'216.0->Float32 - Up to
9'007'199'254'740'992.0->Float64/Float
Note
0.1asFloat64has the significand1001100110011001100110011001100110011001100110011010, so this can not implicitly be converted toFloat32orFloat16.
Floating-point literals are interpreted according to the size/precision requirements. Counting the significant digits plus the trailing zeros after them (including those after the decimal point), the rules are:
- up to 15 decimal places ->
Float64(AKAFloat) - up to 34 decimal places ->
Float128 - up to 71 decimal places ->
Float256 - more decimal places ->
BigFloat
So to explicitly write float literals with a certain precision (e.g. Float128/Float256/BigFloat), you may add trailing zeros: 0.1000000000000000…
Or use postfixes:
0.1f16, 0.1f32, 0.1f64, 0.1f128, 0.1f256 (as in Rust)
To reduce the precision, you need to downcast explicitly:
Float pi64 = 3.1415926535897
Float16 pi16 = Float16(pi)
Storage
Constexpr constructor that accepts an arbitrary precision float literal and can store that in ROM. Store the mantissa as arbitrary precision integer (i.e. array ofInt), plus the exponent as arbitrary precision integer (i.e. array ofInt, most always only a singleInt)
Infinity/-Infinity is a Float literal for infinity values, that can be converted to any float type.
NaN is a Float literal for NaN (“not a number”) values, that can be converted to any float type.
String
"Text" is a StringView with UTF-8 encoding.
Without null termination.
If necessary use "Text"sz, "Text\0", or convert using StringZ("Text").
Data is typically stored in read-only data segments (“.rodata”) or ROM.
A Cilia-to-C++-transpiler would translate every string literal to a C++ string_view-literal:
"Text" -> u8"Text"sv
("..."sv as to avoid null termination, and u8"..." as to have UTF-8 encoding.)
Multiline String Literal
Use triple double-quotes """ to start and end the literal.
Similar to Swift, Julia, Java 15, C# 11, …
"""
First line
Second line
"""
Also as single line string literal with very few restrictions, good for RegEx:
"""(.* )whatever(.*)"""
If the opening """ is followed by a newline, that newline is not part of the string content. This allows the content to start cleanly on the next line.
The position of the closing """ defines the indentation guide.
If the closing """ is on its own line:
- The newline preceding it is removed from the content.
- The exact sequence of whitespace (spaces/tabs) before the closing
"""is treated as a “prefix” and is stripped from every line of the string. - It is a compile-time error if any non-empty line begins with less indentation than the closing delimiter.
- But lines containing only whitespace that is shorter than the indentation guide are treated as empty lines (\n).
Trailing whitespace at the end of lines is preserved.
To include """ within the string content, the literal can be opened and closed with more than three double-quotes (e.g., """"). The closing delimiter must match the number of quotes used for the opening delimiter. This eliminates the need for escape backslashes within the literal, ensuring truly “raw” content.
Interpolated Strings
Like f-strings in Python:
f"M[{i},{j}] = {M[i, j]}"
Curly braces ({}) are used in std::format already.
f as in format.
TODO
Any reason to use/prefer any other syntax?
Maybe$"M[{i},{j}] = {M[i, j]}"like in C#?
Alternative String Literals
Prefixes as in C++:
u"..."andu'...'for UTF-16U"..."andU'...'for UTF-32
No and no u8"..." for UTF-8, as that is the default in Cilia.u8'...'
Maybe a"..." for ASCII and l"..." for Latin-1.
User defined string suffixes as in C++:
"..."sforstd::string.
No for "..."svstd::string_view, as that is the default in Cilia.
"..."sz for null terminated strings.
Type of "..."sz is Char*, while "...\0" is a StringView of a zero terminated string.
All these available for multiline string literals and interpolated strings, too.
TODO
Any reason, not to?
Char
' ' is a character literal.
'A'is an ASCII character literal, aChar8.
(Can implicitly be converted toChar16andChar32.)'Ä'is a non-ASCII Latin-1 character literal, aChar8.
(Can implicitly be converted toChar16andChar32.)'Ω'is aChar16character literal.
(Can implicitly be converted toChar32.)'𝄞'is aChar32character literal.is an invalid character literal, as it is a grapheme cluster consisting of multiple code points. Use the string literal'👮🏻'"👮🏻"instead.
Array
[1, 2, 3] is an array (here an Int[3]),
all elements have the same type.
Initialization List
{1, "Text", 3.0} is an initialization list, e.g. for Tuple or Pair.
Associative Array
[ 1: "one", 2: "two", 3: "three", 4: "four" ] is a String[Int] (AKA Map<Int, String>).
String[Int] keywords = [
1: "one"
2: "two"
3: "three"
4: "four"
]
ContactInfo[String] contactInfoForID = [
"John Doe": {"John", "Doe", "03465 452634"}
"Jane Doe": {"Jane", "Doe", "03245 687534"}
]
Misc
NullPtr is the null pointer,
it is of the type NullPtrType,
explicit cast necessary to convert any pointer to Int.
User Defined Literals with the same rules as in C++.