Misc
- C++ Compatibility / Interoperability
- Compatible to C++ and maybe other languages of this “language family” / “ecosystem”,
- as with
- Java and Kotlin, Scala, Groovy, Clojure, Fantom, Ceylon, Jython, JRuby …
- C# and C++/CLI, Visual Basic .NET, F#, A# (Ada), IronPython, IronRuby …
- Objective-C and Swift
- Bi-directional interoperability, so (with a hypothetical C++/Cilia compiler) it is possible to include
- C++ headers and modules from Cilia,
- Cilia headers and modules from C++.
- Can call C functions, access C structs (as C++ can do).
- The compiler recognizes the language (C, C++, or Cilia) by:
- Marked blocks
lang "C++" { ... }lang "Cilia" { ... }lang "C" { ... }- TODO Limited to top level?
- Limited to languages where the scope is marked with
{ }.
- File extension
- Cilia:
*.cil,*.hil - C++:
*.cpp,*.hppor*.cxx,*.hxx- Even
*.h, but that is a problem, as the header could be C or C++ code.
So use of*.hppis recommended for C++ code.
This can probably best be solved using path based rules.
- Even
- C:
*.c*.h
- Cilia:
- Path based rules,
- to handle C or C++ standard headers in certain directories.
- File specific configuration,
- can be set in the IDE or on the command line,
for each file individually.
- can be set in the IDE or on the command line,
- Marked blocks
- as with
- Compatible to C++ and maybe other languages of this “language family” / “ecosystem”,
- Two-Pass Compiler
- so no forward declarations necessary,
- as with C# and Java (but unlike C/C++, due to its single-pass compiler).
- Mixed arithmetic
- Mixing signed with unsigned integer
Signed + - * / Unsignedis an error,- you have to cast explicitly,
- i.e. no implicit cast (neither
norUInt->Int).Int->UInt Int(i.e. signed) is almost always used anyways.
- Error with
if aUInt < anInt,- you have to cast:
if Int(aUInt) < anInt.
- you have to cast:
- Error with
if aUInt < 0,- if the literal on the right is
<= 0 - TODO Checking for
if aUInt <= -1would be simple, as-1can not implicitly be converted to an UInt. But0can, so how to check for that?
- if the literal on the right is
- Mixing integer with float
1 * aFloatis possible- Warning, if the integer literal cannot be reproduced exactly as
Float32/64
- Warning, if the integer literal cannot be reproduced exactly as
anInt * aFloatis possible- Warning that the integer variable may not be reproduced exactly as
Float32/64,
i.e. withaFloat32 * anInt8// OKaFloat32 * anInt16// OKaFloat32 * anInt32// ErroraFloat32 * Float32(anInt32)// OK
aFloat32 * anInt64// ErroraFloat32 * Float32(anInt64)// OK
aFloat64 * anInt8// OKaFloat64 * anInt16// OKaFloat64 * anInt32// OKaFloat64 * anInt64// ErroraFloat64 * Float64(anInt64)// OK
- Warning that the integer variable may not be reproduced exactly as
- Mixing signed with unsigned integer
- Endianness
- Big Endian (Motorola)
cilia::be::Int,Int8,Int16,Int32,Int64- MSB first (most significant byte first), starting with the “big end”
- MSb first (most significant bit first)
- classical “network byte order”
- Little Endian (Intel)
cilia::le::Int,Int8,Int16,Int32,Int64- LSB first (least significant byte first), starting with the “little end”
- LSb first (least significant bit first)
- “USB byte order”
- Big Endian (Motorola)
- Extended & Arbitrary Precision Integer & Float
Int128,Int256UInt128,UInt256e.g. for SHA256BigInt– Arbitrary Precision Integer- for cryptography, maybe computer algebra, numerics
- see Boost.Multiprecision, GMP
BFloat16(Brain Floating Point) with 1 bit sign, 8 bit exponent, 7 bit significand,- i.e. the 16 low bits of the Float32 significand are cut off.
Float128with 1 bit sign, 15 bit exponent, 112 bit significandFloat256with 1 bit sign, 19 bit exponent, 237 bit significand- Non-standardized floating point types
BigFloatfor arbitrary precision float,HighPrecisionFloat<Int SignificandBits, Int ExponentBits>as template for custom float types with statically fixed precision, likeFloat1024,Float2048, …- NumberOfBits must be multiples of
sizeof(Int)(i.e. multiples of 64).
- NumberOfBits must be multiples of
DDFloat,TDFloat,QDFloat- double-double/triple-double/quad-double arithemtic
- wiki.org/Double-Double Arithmetic
- https://stackoverflow.com/a/6770329
- General problem: All these types, when saved in binary form, are incompatible to the IEEE 754 format for 128/256/… bit float. So better to save them as string.
- Integer operations with carry (flag or UInt)
(to implementInt128,Int256etc.)- Add with carry (flag, i.e. one bit only)
UInt c = add(UInt a, UInt b, inout Bool carryFlag)c = bits63..0(a + b + carryFlag)
carryFlag = bit64(a + b + carryFlag)
a.add(UInt b, inout Bool carryFlag)a = bits63..0(a + b + carryFlag)
carryFlag = bit64(a + b + carryFlag)
- Mutiply with carry (high data, i.e. one UInt)
UInt c = multiply(UInt a, UInt b, out UInt cHigh)c = bits63..0(a * b)
cHigh = bit127..64(a * b)
a.multiply(UInt b, out UInt aHigh)a = bits63..0(a * b)
aHigh = bit127..64(a * b)
- Mutiply-Add with carry (high data, i.e. one UInt)
UInt d = multiplyAdd(UInt a, UInt b, UInt c, out UInt dHigh)d = bits63..0(a * b + c)
dHigh = bit127..64(a * b + c)
a.multiplyAdd(UInt b, UInt c, out UInt aHigh)a = bits63..0(a * b + c)
aHigh = bit127..64(a * b + c)
- Shift
b = shiftLeftAdd(UInt a, Int steps, inout UInt addAndHigh)a.shiftLeftAdd(Int steps, inout UInt addAndHigh)b = shiftOneLeft(UInt a, inout Bool carryFlag)a.shiftOneLeft(inout carryFlag)
- Add with carry (flag, i.e. one bit only)
cilia::saturating::Int- Like
cilia::Int, but with saturation for all operations.- Limit to maximum, no wrap around.
- Typically using SIMD (as those „media/DSP instructions“ do support saturation natively).
- see https://en.wikipedia.org/wiki/Saturation_arithmetic
saturating::Int8/Int16/Int32/Int64saturating::UIntsaturating::UInt8/UInt16/UInt32/UInt64
- Like
- Reserved keywords for future use (maybe, maybe not).
parallelforparallel for ...interfacefor pure abstract base classes or similar constructs?structfor some variant of C++ strcuts/classes?Nullinstead ofNullPtr? (But actually it always is a pointer in our context.)template, as it still is unclear, if the “new”, shorter template expressions are really better.letforconstvariable declarations?constfuncinstead ofconstexprorconsteval?
- Versioning of the Cilia source code
- Via file “.cilVersion” in a (project) directory,
- similar to “.clang_format”,
- also possible file by file: “Matrix.cilVersion” (for “Matrix.cil”).
- Via file extension:
- “*.cil” – always the latest language version (if not overridden via “.cilVersion”)
- “*.25.cil” – version from the year 2025
- “*.25b.cil” – second version from the year 2025
- Via file “.cilVersion” in a (project) directory,
- No function-like macros, just object-like:
#define#if#else#endif
- Optional Chaining
String? name = ...
translates to
Optional<String> name = ...Int? len = name?.length()
translates to
Optional<Int> len = (name ? Optional((*name).length()) : NullOptInt len = name?.length() ?? 0
translates to
Int len = (name ? Optional((*name).length()) : NullOpt).valueOr(0)is not allowed, i.e. no implicitInt len = name?.length().value(), that could throw an exception.
Bool? isJpeg = name?.endsWith(".jpeg")
translates to
Optional<Bool> isJpeg = name ? Optional((*name).endsWith(".jpeg")) : NullOptBool isJpeg = name?.endsWith(".jpeg") ?? false
translates to
Bool isJpeg = (name ? Optional((*name).endsWith(".jpeg")) : NullOpt).valueOr(false)Int? len = pointerToWindow?.title()?.length()
translates toOptional<String> __tmpTitle = pointerToWindow ? Optional((*pointerToWindow).title()) : NullOpt Optional<Int> len = __tmpTitle ? Optional((*__tmpTitle).length()) : NullOpt
- Should work for
Optional<T>andT*,T^,T+,T-- With
ContactInfo* contactInfo = ...String? name = contactInfo?.name
translates to
Optional<String> name = (contactInfo ? Optional((*contactInfo).name) : NullOpt);String name = contactInfo?.name ?? ""
translates to
Optional<String> name = (contactInfo ? Optional((*contactInfo).name) : NullOpt).valueOr("");
- With
- Technically an
Optional<T>is an objectTplus aBool hasValue.Optional<>for pointers:- As a pointer can be null in itself:
Optional<T*>internally is just aT*,Optional<T^>internally is just aT^,Optional<T+>internally is just aT+,Optional<T->internally is just aT-.
- So in Cilia for an
Optional<T*>that has a value, that value is neverNullPtr. - This is different than in C++, so for interop with C++ you may need to use
std::optional<T*>.
- As a pointer can be null in itself:
- Anonymous Members
- You write
class OkDialog : Window { Label("Message to user") Button("Ok") }instead of
class OkDialog : Window { Label __anonymousLabel1("Message to user") Button __anonymousButton1("Ok") } - Accessable only through static reflection (C++26).
- Internally the compiler generates proxy names, e.g.
Label __anonymousLabel1andButton __anonymousButton1. - These widgets are member variables of the container, so they don’t need to be deleted explicitly.
- They are added with
container.addChild(Widget* child), signaling non-ownership. - Other children are added with
container.addChild(Widget+ child), usingWidget+/UniquePtr<Widget>to signal ownership, i.e. the child is deleted when the container is deleted. - The container has two arrays:
Array<Widget*> childrento manage all children (e.g. their order),Array<Widget+> ownedChildrento manage those children that need to be deleted (in the destructor of the container).
- They are added with
- Maybe even
class OkDialog : Window { Label("Message to user").horizontalAlignment(Alignment::Center) Button("Ok").onClick(&OkDialog::onOk) }instead of
class OkDialog : Window { Label __anonymousLabel1("Message to user") Button __anonymousButton1("Ok") OkDialog() { __anonymousLabel1.horizontalAlignment(Alignment::Center) __anonymousButton1.onClick(&OkDialog::onOk) } }
- You write
- Anonymous Class Declarations
- You write
VStack { Label("Message to user") Button("Ok") }instead of
class __AnonymousVStack1 : VStack { Label("Message to user") Button("Ok") } __anonymousVStack1 - You write
VStack { Label label("Message to user") Button okButton("Ok") } verticalinstead of
class __AnonymousVStack1 : VStack { Label label("Message to user") Button okButton("Ok") } vertical
- You write
- Late / Deferred Compiled Member Functions
- for Compile Time Polymorphism,
- instead of CRTP (Curiously Recurring Template Pattern).
- You write
class OkDialog : Window { Label("Message to user") Button("Ok") }based on
class Window { Window() { registerWidgetMembers() } late virtual func registerWidgetMembers() { // Via static reflection: // Register all members of the derived class, that are derived from type Widget. } }- instead of
class OkDialog : Window<OkDialog> { Label("Message to user") Button("Ok") }based on
class Window<type T> { Window() { registerWidgetMembers<T>() } func registerWidgetMembers<type T>() { // Via static reflection: // Register all members of T, that are derived from type Widget. } }
- instead of