Original Post
Warning: Long Content Well, I got sidetracked by being unable to do something in C++, and this was the result. I didn't feel like I could put this to you guys as Epoch - that wouldn't be very fair, so instead I have named it Kaleidoscope. This is a long document! I hope you enjoy it. I hope you read it. I'm sure you would do things differently too, but I figured we needed something concrete after so many pages of blabbering. Edit: I've structured it in such a way that you can start reading at the top, and not meet any material that makes more than a brief mention of material we haven't yet covered. In other words, it builds up from what most of you would know, to the whole language.
<font color=green>//=====================================================================</font>
<font color=green>//</font>
<font color=green>// Overview</font>
<font color=green>// ----------</font>
<font color=green>// This document turned out to be a lot longer than I originally</font>
<font color=green>// intended - and there's still stuff left to write. I am very</font>
<font color=green>// proud of this work, as I feel I've captured many aspects of what</font>
<font color=green>// people are looking for - considerations on speeed, lambda functions,</font>
<font color=green>// dynamic typing, static typing, templating, garbage collection,</font>
<font color=green>// heirarchy of types, etc.</font>
<font color=green>//</font>
<font color=green>// However, I'm no language expert. What I've done here is to</font>
<font color=green>// define the rules of the language. Hopefully, one of you will read</font>
<font color=green>// through this, and tell me if what I'm proposing is sound. I suspect</font>
<font color=green>// most of it is, although I'm confident there are going to be syntax</font>
<font color=green>// ambiguities that I've missed, although I've designed it in a way</font>
<font color=green>// to reduce those possibilities.</font>
<font color=green>//</font>
<font color=green>// This is a work-in-progress, so feel free to make suggestions, etc.</font>
<font color=green>// I'm very happy with the fundamental structure of the language, but</font>
<font color=green>// things like references, the "singular use of concepts" rule - small</font>
<font color=green>// things that potentially make the language harder (or just "more" to</font>
<font color=green>// store in your head) I'm totally good for changing.</font>
<font color=green>//</font>
<font color=green>// There are some things I haven't even mentioned, such as the</font>
<font color=green>// compilation model, preprocessor directives, and whether or not this</font>
<font color=green>// is run on a VM or compiled straight (I favour both). In these cases</font>
<font color=green>// feel free to offer suggestions on what would compliment the</font>
<font color=green>// language.</font>
<font color=green>//</font>
<font color=green>// Also, if the implementation of dynamic-typing & static-typing (and</font>
<font color=green>// no, I'm not talking about the side-by-side examples which would</font>
<font color=green>// more than likely have name resolution ambiguities) is utterly</font>
<font color=green>// impossible via some rules of compilation theory, inform a moderator</font>
<font color=green>// quickly to remove the thread so I don't look so foolish. :)</font>
<font color=green>//</font>
<font color=green>// Finally, I'd like to note, and this happened quite by accident, that</font>
<font color=green>// I seem to have followed the "if you don't use it, you don't pay for</font>
<font color=green>// it" mantra. This is realised through the use of a great many more</font>
<font color=green>// keywords/paradigms than most other languages, and as such it should</font>
<font color=green>// be obvious that one can write damn near identical code as C code,</font>
<font color=green>// and as such should be very fast. That is, of course, by -not- using</font>
<font color=green>// dynamic typing, automatic memory management, expressions or</font>
<font color=green>// concepts.</font>
<font color=green>//</font>
<font color=green>// I actually had my own mantra when I set out, "make it ridiculously</font>
<font color=green>// powerful, but make the default values for everything good enough</font>
<font color=green>// for 90% of the work". That way, beginners can use and abuse it, and</font>
<font color=green>// experts can modify everything as they require. It's a little like</font>
<font color=green>// people using C++'s std::vector, and never actually thinking, "what</font>
<font color=green>// does 'allocater<_T>' mean?".</font>
<font color=green>//</font>
<font color=green>// Anyway, grab some coffee or something - it's long.</font>
<font color=green>//</font>
<font color=green>// - Jonathan, 19th June, 2006</font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=green>//=====================================================================</font>
<font color=green>//</font>
<font color=green>// Primitives</font>
<font color=green>// ------------</font>
<font color=green>// I went with a suggested form of primitives, where we have integer,</font>
<font color=green>// boolean, and real (and possibly complex??) as primitives. Both</font>
<font color=green>// integer and real have a language-defined extension, allowing us to</font>
<font color=green>// specify the size (to avoid the C++ "long long" problem). They are</font>
<font color=green>// used as such:</font>
<font color=green>// integer<32> a_long;</font>
<font color=green>// integer<16> a_short;</font>
<font color=green>// integer<8> a_char;</font>
<font color=green>// integer<128> a_long_long_long?;</font>
<font color=green>// real<32> a_float;</font>
<font color=green>// real<64> a_double;</font>
<font color=green>// real<128> a_quadruple?;</font>
<font color=green>//</font>
<font color=green>// To make things easier, there is a compiler-defined default value,</font>
<font color=green>// which for machines currently would probably be 32, and 64 very</font>
<font color=green>// soon. It's up to compiler-people if they allow you to change this</font>
<font color=green>// via compiler-settings. If you use the default, you can omit the</font>
<font color=green>// whole "<xx>" part (and just use "integer").</font>
<font color=green>//</font>
<font color=green>// Furthermore, the integer primitive has the "unsigned" flag which</font>
<font color=green>// can precede it. You know how it works - I won't go over it. The</font>
<font color=green>// formal definitions are:</font>
<font color=green>//</font>
<font color=green>// - [ unsigned ] integer [ <X> ] "variable-name" ;</font>
<font color=green>// - real [ <X> ] "variable-name" ;</font>
<font color=green>// - boolean "variable-name" ;</font>
<font color=green>//</font>
<font color=green>// All those primitives belong to the "number" concept. See below</font>
<font color=green>// for "concepts".</font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=green>//=====================================================================</font>
<font color=green>//</font>
<font color=green>// Typedefing (Aliasing)</font>
<font color=green>// ------------</font>
<font color=green>// Typedefing is exactly the same as in C++, with the change of the</font>
<font color=green>// keyword to "alias". In this case, I'm aliasing "unsigned integer"</font>
<font color=green>// with "uint" for ease of typing.</font>
<font color=green>//</font>
<font color=green>// Note: Since "uint" is only an alias for "unsigned integer", we can</font>
<font color=green>// still use it like "uint<32>", or "uint<64>" or what-have-you.</font>
<font color=green>// If we had declared it as "alias unsigned integer<32> uint",</font>
<font color=green>// we would not have that option.</font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=blue>alias</font> <font color=blue>unsigned</font> <font color=blue>integer</font> uint;
<font color=green>//=====================================================================</font>
<font color=green>//</font>
<font color=green>// Added Keywords</font>
<font color=green>// ----------------</font>
<font color=green>// I say "added", because I haven't seen these in C/C++/Java/C#/VB,</font>
<font color=green>// they more than likely exist somewhere.</font>
<font color=green>//</font>
<font color=green>// autotype</font>
<font color=green>// ----------</font>
<font color=green>// Replaces C++'s "auto" - keeping in line with naming conventions</font>
<font color=green>//</font>
<font color=green>// for_count( "unsigned-integer" ) { }</font>
<font color=green>// --------------</font>
<font color=green>// Takes an unsigned integer and loops that many times. Unsure</font>
<font color=green>// about including it (but why not?).</font>
<font color=green>//</font>
<font color=green>// alias</font>
<font color=green>// -------</font>
<font color=green>// Replaces "typedef", as written above.</font>
<font color=green>//</font>
<font color=green>// typeof( "variable" )</font>
<font color=green>// ------------</font>
<font color=green>// Returns the type of a variable, via a datatype_jar. See "Jars"</font>
<font color=green>// for more information. It is fairly self-explanatory in the</font>
<font color=green>// examples (and only has one use).</font>
<font color=green>//</font>
<font color=green>// muud( "variable" )</font>
<font color=green>// ----------</font>
<font color=green>// Returns the boolean result of the expression </font>
<font color=green>//</font>
<font color=green>// typeof(the_variable) == mu</font>
<font color=green>//</font>
<font color=green>// See "The Mu-Type" for more information.</font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=green>//=====================================================================</font>
<font color=green>//</font>
<font color=green>// Namespaces</font>
<font color=green>// ------------</font>
<font color=green>// I really like namespaces in C++ - I think they're far cleaner than</font>
<font color=green>// modules in Java, especially since we're going to be allowing</font>
<font color=green>// functions to be running around by themselves.</font>
<font color=green>//</font>
<font color=green>// However, I don't think they're powerful enough. As it stands in C++,</font>
<font color=green>// any variables declared in a namespace are global, albiet global in</font>
<font color=green>// that namespace. I think there should be the ability to have datatypes</font>
<font color=green>// and variables that can be declared private in a namespace, so only</font>
<font color=green>// classes and functions of that namespace can access them. It would</font>
<font color=green>// certainly help with modularity.</font>
<font color=green>//</font>
<font color=green>// Thus:</font>
<font color=green>//</font>
<font color=green>// namespace "namespace-name"</font>
<font color=green>// {</font>
<font color=green>// [private | protected | public] "member"; // repeated</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// Where "public" is the default permission.</font>
<font color=green>// I have yet to decide if the planned functionality for the "protected"</font>
<font color=green>// permission will go ahead. Maybe?</font>
<font color=green>//</font>
<font color=green>// "ksl" is the Kaleidoscope Standard Library. :P</font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=blue>namespace</font> ksl
{
<font color=green>// not accessable for anything declared outside the KSL.</font>
<font color=blue>private</font> <font color=blue>const</font> <font color=blue>real</font> KSLVersion = 0.1;
<font color=green>// accessable by default</font>
<font color=blue>const</font> <font color=blue>real</font> KSL_Happiness_Level = 7;
}
<font color=green>//=====================================================================</font>
<font color=green>//</font>
<font color=green>// Functions</font>
<font color=green>// -----------</font>
<font color=green>// I'll give the formal definition first:</font>
<font color=green>//</font>
<font color=green>// [inline | static | const | virtual ] function "return-type" "function-name"</font>
<font color=green>// [ < "datatype-list" > ]</font>
<font color=green>// ( [ "parameter-list" ] )</font>
<font color=green>// [ where ( "expression" ) ]</font>
<font color=green>// {</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// Modifiers</font>
<font color=green>// -----------</font>
<font color=green>// inline, static, const and virtual modifiers are placed before the "function"</font>
<font color=green>// declaration. Their usage is just like C++.</font>
<font color=green>//</font>
<font color=green>// Compile-Time Templates</font>
<font color=green>// ------------------------</font>
<font color=green>// Kaleidoscope offers compile-time templating, with the typenaming occuring</font>
<font color=green>// after the function name, like so:</font>
<font color=green>//</font>
<font color=green>// function T add<datatype T>(const T& t1, const T& t2)</font>
<font color=green>// {</font>
<font color=green>// return t1 + t2;</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// The datatype keyword is covered later. For now, it has the same usage</font>
<font color=green>// (and even meaning) of "typename", or "class" as in templates.</font>
<font color=green>//</font>
<font color=green>// Validation</font>
<font color=green>// ------------</font>
<font color=green>// Validation occurs after the parameter list, and is an expression that</font>
<font color=green>// evaluates to a boolean value, like so:</font>
<font color=green>//</font>
<font color=green>// function T add<datatype T>(const T& t1, const T& t2) where (t1 < 17)</font>
<font color=green>// {</font>
<font color=green>// return t1 + t2;</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// In the above case, the validation only makes sense if T has a less-than</font>
<font color=green>// operator, taking a number. </font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=green>//=====================================================================</font>
<font color=green>// Real World(tm) Examples</font>
<font color=green>//=====================================================================</font>
<font color=blue>namespace</font> ksl
{
<font color=blue>function</font> <font color=blue>void</font> copy_memory(<font color=blue>void</font> * destination, <font color=blue>void</font> * source, uint size) <font color=blue>where</font> (dest && source)
{
<font color=blue>for</font> (uint i = 0; i < size; ++i)
{
<font color=blue>reinterpret_cast</font><<font color=blue>integer</font><8>*>(dest) = <font color=blue>reinterpret_cast</font><<font color=blue>integer</font><8>*>(source);
}
}
<font color=blue>function</font> <font color=blue>void</font> set_memory(<font color=blue>void</font> * dest, uint size, <font color=blue>integer</font><8> value = 0) <font color=blue>where</font> (dest)
{
<font color=blue>for</font> (uint i = 0; i < size; ++i)
{
<font color=blue>reinterpret_cast</font><<font color=blue>integer</font><8>*>(dest) = value;
}
}
<font color=blue>function</font> uint strlen(<font color=blue>const</font> char * s) <font color=blue>where</font> (s)
{
uint length = 0;
<font color=blue>for</font> (uint i = 0; s != '0'; ++i) ++length;
<font color=blue>return</font> length;
}
}
<font color=green>//=====================================================================</font>
<font color=green>// </font>
<font color=green>// The Mu-Type</font>
<font color=green>// -------------</font>
<font color=green>//</font>
<font color=green>/// Pinching mu from ApochPiQ, it signifies a variable whose datatype</font>
<font color=green>// is changable. It signifies the "null-type" datatype for dynamic</font>
<font color=green>// types. Indeed it is used in two contexts. I do not find this</font>
<font color=green>// counter-inuitive at all, quite the opposite, actually.</font>
<font color=green>//</font>
<font color=green>// mu [ < "access-list" > ] "variable-name" [ < "initial-type" > ] ;</font>
<font color=green>//</font>
<font color=green>// mu is very similar to "void", except that it can be typed, and the </font>
<font color=green>// typing is persistant. The type can be changed again at a later time, </font>
<font color=green>// and that change too is persistant. Any operation on a mu-type while </font>
<font color=green>// it's still initialised to mu is illegal. Note: typeof( ) can still</font>
<font color=green>// be used on mu-types which are uninitialised, which will of course</font>
<font color=green>// return mu.</font>
<font color=green>//</font>
<font color=green>// mu-types can be set back to "mu" as a sort of null-state.</font>
<font color=green>//</font>
<font color=green>//</font>
<font color=green>// Initialisation</font>
<font color=green>// ----------------</font>
<font color=green>// The default initialisation of mu is mu. This means with</font>
<font color=green>//</font>
<font color=green>// mu my_name;</font>
<font color=green>//</font>
<font color=green>// my_name's type is initially mu, which could be found with a</font>
<font color=green>// typeof(my_name). This, hwoever, can be changed, with the default</font>
<font color=green>// initialisation been defined at compile time, like so:</font>
<font color=green>//</font>
<font color=green>// mu my_name<string>;</font>
<font color=green>//</font>
<font color=green>// Here, declaring a dynamic variable who's initial type is type string,</font>
<font color=green>// but which can change at a later point in time, directly like so:</font>
<font color=green>//</font>
<font color=green>// mu my_name;</font>
<font color=green>// my_name<string>; // set type</font>
<font color=green>//</font>
<font color=green>// They can be assigned another variable, like so:</font>
<font color=green>//</font>
<font color=green>// mu my_name;</font>
<font color=green>// string fred = "fred";</font>
<font color=green>// my_name = fred;</font>
<font color=green>// </font>
<font color=green>// There is enough information to type my_name and then assign it</font>
<font color=green>// a value. Ambiguities can be picked up at compile-time:</font>
<font color=green>//</font>
<font color=green>// mu my_age;</font>
<font color=green>// my_age = 4; // unsigned, signed, 32-bits, 16 bits??</font>
<font color=green>//</font>
<font color=green>// and can be explicitly typed:</font>
<font color=green>// </font>
<font color=green>// mu my_age;</font>
<font color=green>// my_age<unsigned integer> = 4;</font>
<font color=green>// my_age = real<32>(4.2);</font>
<font color=green>// my_age(real<128>(203958230985.3));</font>
<font color=green>//</font>
<font color=green>//</font>
<font color=green>// Permissions</font>
<font color=green>// -------------</font>
<font color=green>// There may be cases where we might want a dynamic type to be exposed</font>
<font color=green>// publicly to the user, but have them unable to change its type. This</font>
<font color=green>// can be achieved via permissions (although I should note that it's</font>
<font color=green>// probably a design flaw):</font>
<font color=green>//</font>
<font color=green>// datatype Cat</font>
<font color=green>// {</font>
<font color=green>// public mu<Cat> Name<string>;</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// Here, only members and methods in the scope of Cat can change the the</font>
<font color=green>// type of Name, but it is still publicly accessable to anyone. This can</font>
<font color=green>// be limited to a range of functions, such as:</font>
<font color=green>//</font>
<font color=green>// datatype Animal</font>
<font color=green>// {</font>
<font color=green>// public mu<speak, run> State<string>;</font>
<font color=green>//</font>
<font color=green>// public virtual function speak();</font>
<font color=green>// public virtual function run();</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// The default is global type-changing.</font>
<font color=green>//</font>
<font color=green>//</font>
<font color=green>// Parameters</font>
<font color=green>// ------------</font>
<font color=green>// A mu-type as a parameter allows any datatype to be passed in. Thus</font>
<font color=green>// the function:</font>
<font color=green>//</font>
<font color=green>// function boolean equals_3(const mu& m)</font>
<font color=green>// {</font>
<font color=green>// return m == 3;</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// will accept any type, and compare that value to three. To know the</font>
<font color=green>// type that was passed in, a simple typeof( ) can be used on the</font>
<font color=green>// variable, both in the function, and in the validation:</font>
<font color=green>//</font>
<font color=green>// function boolean equals_3(const mu& m) where (typeof(m) != ksl::string)</font>
<font color=green>// {</font>
<font color=green>// return m == 3;</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// Now the function accepts any type except ksl::string. Or perhaps</font>
<font color=green>// more usefully:</font>
<font color=green>//</font>
<font color=green>// function boolean equals_3(const mu& m)</font>
<font color=green>// {</font>
<font color=green>// if (typeof(m) == ksl::string) return m == "3";</font>
<font color=green>// return m == 3;</font>
<font color=green>// }</font>
<font color=green>// </font>
<font color=green>// Note: Clearly, scoping permissions can not be used in parameters.</font>
<font color=green>//</font>
<font color=green>//</font>
<font color=green>// Default Parameter Type</font>
<font color=green>// ------------------------</font>
<font color=green>// The default parameter type is "mu&". That means:</font>
<font color=green>//</font>
<font color=green>// function void print_stuff(x)</font>
<font color=green>// {</font>
<font color=green>// ksl::cout << x << ksl::endl;</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// is valid, as x is interpreted as "mu& x" (by reference). Likewise,</font>
<font color=green>// we have the added advantage of having</font>
<font color=green>//</font>
<font color=green>// function void print_stuff(const x)</font>
<font color=green>// {</font>
<font color=green>// ksl::cout << x << ksl::endl;</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// being valid, because "const x" means "const mu& x". Nice one. NOTE: This </font>
<font color=green>// may or may not make the final cut (parsing ambiguities are possible).</font>
<font color=green>//</font>
<font color=green>//</font>
<font color=green>// Default Parameters</font>
<font color=green>// --------------------</font>
<font color=green>// These work just like C++, although ambiguities can arise if the parameter</font>
<font color=green>// type is left out:</font>
<font color=green>//</font>
<font color=green>// function void print_stuff(x = 4) // unsigned/signed, etc...</font>
<font color=green>// {</font>
<font color=green>// //...</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>//</font>
<font color=green>// muud( )</font>
<font color=green>// ---------</font>
<font color=green>// Langauge-feature: Returns true if the type of a variable is mu. </font>
<font color=green>// False if anything else. I suspect there'd be a lot of</font>
<font color=green>// "typeof(variable) != mu" otherwise.</font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=green>//=====================================================================</font>
<font color=green>//</font>
<font color=green>// The Mue-Type</font>
<font color=green>// --------------</font>
<font color=green>// I struggled for a while with Mu, mainly because we may not want</font>
<font color=green>// to assign a type to mu via the assignment operator - we may want</font>
<font color=green>// to catch potential errors like this:</font>
<font color=green>//</font>
<font color=green>// mu Age = uint(5);</font>
<font color=green>// Age = string("hey man!");</font>
<font color=green>// uint total_age = other_ages + Age;</font>
<font color=green>//</font>
<font color=green>// To do so, I introduce Mue - "Mu-Explicit". This is identical to Mu,</font>
<font color=green>// with the exception that assignment of type doesn't work implicitly.</font>
<font color=green>// I actually suggest using Mue (still pronounced, like Mu, as "Moo"),</font>
<font color=green>// instead of Mu generally, because it is safer.</font>
<font color=green>//</font>
<font color=green>// Obviously, you can not use Mue as a parameter in a function (allowing</font>
<font color=green>// that was the reason Mu had to have implicit assignment of type).</font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=green>//=====================================================================</font>
<font color=green>// Real World(tm) Examples</font>
<font color=green>//=====================================================================</font>
<font color=blue>namespace</font> ksl
{
<font color=green>// if two types are equal</font>
<font color=blue>function</font> <font color=blue>boolean</font> sametype(<font color=blue>const</font> <font color=blue>mu</font>& lhs, <font color=blue>const</font> <font color=blue>mu</font>& rhs)
{
<font color=blue>return</font> <font color=blue>typeof</font>(lhs) == <font color=blue>typeof</font>(rhs);
}
<font color=green>// assumes dest and source are pointer-types. run-time failure if not.</font>
<font color=blue>function</font> <font color=blue>void</font> copy_memory(<font color=blue>mu</font> dest, <font color=blue>mu</font> source, uint size)
{
<font color=blue>for_count</font>(size) *(dest++) = *(source++);
}
<font color=green>// swap value of two things of the same type, compile-timely</font>
<font color=blue>function</font> <font color=blue>void</font> swap<<font color=blue>datatype</font> T>(T & lhs, T & rhs)
{
T temp(lhs);
lhs = rhs;
rhs = temp;
}
<font color=green>// swap value of two things of the same type, run-timely</font>
<font color=blue>function</font> <font color=blue>void</font> swap(<font color=blue>mu</font> & lhs, <font color=blue>mu</font> & rhs) <font color=blue>where</font> (sametype(lhs, rhs))
{
<font color=blue>mu</font> temp = lhs;
lhs = rhs;
rhs = temp;
}
}
<font color=green>//=====================================================================</font>
<font color=green>//</font>
<font color=green>// Classes and Datatypes</font>
<font color=green>// -----------------------</font>
<font color=green>// There is no distinction between primitives and classes, each is</font>
<font color=green>// a "datatype". Thus there is no "class" keyword. "Classes" are very</font>
<font color=green>// similar to C++/Java. Datatypes take the form of:</font>
<font color=green>//</font>
<font color=green>// datatype "name" [ < "datatype-list" > ] </font>
<font color=green>// [ [ inherits "base-list" [ where ( "boolean-expression" ) ];</font>
<font color=green>// | inherits "base-list" [ where ( "boolean-expression" ) ]</font>
<font color=green>// {</font>
<font color=green>// "member-list"</font>
<font color=green>// } ]</font>
<font color=green>//</font>
<font color=green>// Declaration</font>
<font color=green>// -------------</font>
<font color=green>// You can have either the datatype inheriting from other datatypes,</font>
<font color=green>// or the definition (ie, { and } with the methods/members in between), </font>
<font color=green>// or both. Ie, all of these are legal:</font>
<font color=green>// </font>
<font color=green>// datatype height inherits real;</font>
<font color=green>//</font>
<font color=green>// datatype Feline</font>
<font color=green>// {</font>
<font color=green>// public virtual function void Meow();</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// datatype tabby_cat inherits Pet, Feline</font>
<font color=green>// {</font>
<font color=green>// public function void Meow();</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// Validation</font>
<font color=green>// ------------</font>
<font color=green>// Dimensional analysis is provided by an expression that evaluates</font>
<font color=green>// to a boolean value, after "where". This is verified everytime a</font>
<font color=green>// non-const operation is performed.</font>
<font color=green>//</font>
<font color=green>// datatype height inherits real where (height < 2.1);</font>
<font color=green>//</font>
<font color=green>// As you can see, the ending semicolon is only required when paranthesis</font>
<font color=green>// are not used, as shown above. An involved datatype with analysis is:</font>
<font color=green>//</font>
<font color=green>// datatype Human </font>
<font color=green>// where (mName.size() <= 15 && age < 150)</font>
<font color=green>// {</font>
<font color=green>// ksl::string mName;</font>
<font color=green>// uint mAge;</font>
<font color=green>// public:</font>
<font color=green>// const function ksl::string getName() { return mName; }</font>
<font color=green>// function void setName(ksl::string name) { mName = name; }</font>
<font color=green>// const function uint getAge() { return mAge; }</font>
<font color=green>// function void setAge(uint age) { mAge = age; }</font>
<font color=green>// public:</font>
<font color=green>// real<64> Height;</font>
<font color=green>// mu Eyes;</font>
<font color=green>// }</font>
<font color=green>// </font>
<font color=green>// In this example, the validation expression will be -after- execution of</font>
<font color=green>// setName and setAge, but NOT after getName and getAge, and they are const,</font>
<font color=green>// and such garuantee that nothing will change. Furthermore, any changes to</font>
<font color=green>// the public variable Height will NOT cause a re-evaluation, as the compiler</font>
<font color=green>// can easily check that Height isn't in the validation-expression, and as</font>
<font color=green>// such can not change the value of the other variables.</font>
<font color=green>//</font>
<font color=green>// Changes to Eyes, however - will. This is because it is a mu-type variable,</font>
<font color=green>// and as such -could- take a datatype which changed the values of mName and</font>
<font color=green>// mAge, however bad that practice may be. I think this may be very inefficient,</font>
<font color=green>// so I may in the future introduce stricter limitations (somehow).</font>
<font color=green>//</font>
<font color=green>// This simple validation could also be implemented by putting the restrictions</font>
<font color=green>// on the set functions, which would indeed speed up execution, as this way, </font>
<font color=green>// any modification to Eyes wouldn't require a re-evaluation (as there is no</font>
<font color=green>// datatype-validation in place).</font>
<font color=green>//</font>
<font color=green>// Compile-Time Templating</font>
<font color=green>// -------------------------</font>
<font color=green>// Finally, compile-time types are supported through the very similar mechanism</font>
<font color=green>// to C++ templates. This allows for things like -speed-, which may be required</font>
<font color=green>// over flexibility. Such as the example of a replica of std::pair below:</font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=green>//=====================================================================</font>
<font color=green>// Real World Examples</font>
<font color=green>//=====================================================================</font>
<font color=blue>namespace</font> ksl
{
<font color=green>// compile-time pair</font>
<font color=blue>datatype</font> pair <<font color=blue>datatype</font> T, <font color=blue>datatype</font> Y>
{
<font color=blue>public</font>:
T First;
Y Second;
<font color=blue>function</font> pair(<font color=blue>const</font> T& f, <font color=blue>const</font> Y& s) : First(f), Second(s)
{
}
<font color=blue>function</font> <font color=blue>void</font> swap() <font color=blue>where</font> ( <font color=blue>typeof</font>(T) == <font color=blue>typeof</font>(Y) )
{
ksl::swap(First, Second);
}
}
<font color=green>// run-time pair</font>
<font color=blue>datatype</font> pair
{
<font color=blue>public</font>:
<font color=blue>mu</font><pair> First;
<font color=blue>mu</font><pair> Second;
<font color=blue>function</font> pair(<font color=blue>const</font> <font color=blue>mu</font>& f, <font color=blue>const</font> <font color=blue>mu</font>& s) <font color=blue>where</font> (<font color=blue>muud</font>(First) || <font color=blue>muud</font>(Second))
: First(f), Second(s)
{
}
<font color=blue>function</font> reassign(<font color=blue>const</font> <font color=blue>mu</font>& f, <font color=blue>const</font> <font color=blue>mu</font>& s)
{
First = f;
Second = s;
}
}
}
<font color=green>//=====================================================================</font>
<font color=green>//</font>
<font color=green>// Concepts</font>
<font color=green>// ----------</font>
<font color=green>// Concepts are very similar to what Bjourne is trying to introduce to </font>
<font color=green>// C++ in the next couple of decades. Concepts are not like interfaces - </font>
<font color=green>// you don't derive from them. They are similar to templated types in</font>
<font color=green>// C++, in that you just -assume- that the passed-in class has the</font>
<font color=green>// required methods/members. Those are then accessed directly.</font>
<font color=green>//</font>
<font color=green>// This means you can pass in base classes (with pure virtual functions,</font>
<font color=green>// for example), and since the methods are called directly, it will</font>
<font color=green>// properly call the derived virtual functions. That is not shown here</font>
<font color=green>// today.</font>
<font color=green>//</font>
<font color=green>// Definition</font>
<font color=green>// ------------</font>
<font color=green>// concept "name"</font>
<font color=green>// {</font>
<font color=green>// "member-list"</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// Example</font>
<font color=green>// ---------</font>
<font color=green>// concept Animal</font>
<font color=green>// {</font>
<font color=green>// const function integer getAge();</font>
<font color=green>// real<32> HappinessLevel;</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// datatype Duck</font>
<font color=green>// {</font>
<font color=green>// public:</font>
<font color=green>// const function integer getAge() { return 4; }</font>
<font color=green>// real<32> HappinessLevel;</font>
<font color=green>// private function void recogniseMate();</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>//</font>
<font color=green>// General Use</font>
<font color=green>// -------------</font>
<font color=green>// In general, we replace the "datatype" keyword in the compile-time</font>
<font color=green>// paramters with the concept name.</font>
<font color=green>//</font>
<font color=green>// function void PrintAnimalAge<Animal A>(const A& animal)</font>
<font color=green>// {</font>
<font color=green>// ksl::cout << animal.getAge() << ksl::endl;</font>
<font color=green>// }</font>
<font color=green>// </font>
<font color=green>// Singular Use</font>
<font color=green>// --------------</font>
<font color=green>// We can compact the above function, but only because it does not</font>
<font color=green>// have the concept as the return type, and because it doesn't require</font>
<font color=green>// that two parameters be the same type. Like so:</font>
<font color=green>//</font>
<font color=green>// function void PrintAnimalAge(const Animal& animal)</font>
<font color=green>// {</font>
<font color=green>// ksl::cout << animal.getAge() << ksl::endl;</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// Now, PrintAnimalAge will accept any type that "conforms" to the concept</font>
<font color=green>// of Animal - ie, it will accept Duck. You could imagine concepts as a</font>
<font color=green>// sort of more powerful type of mu - where it imposes rules on the methods</font>
<font color=green>// and members of a datatype (something mu can not express by itself).</font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=green>//=====================================================================</font>
<font color=green>// Real World Example</font>
<font color=green>//=====================================================================</font>
<font color=blue>namespace</font> ksl
{
<font color=green>// a concept that will enclose vectors, linkedlists, strings, etc</font>
<font color=blue>concept</font> container
{
<font color=blue>concept</font> iterator;
<font color=blue>concept</font> <font color=blue>const</font>_iterator;
<font color=blue>function</font> iterator begin();
<font color=blue>function</font> iterator end();
<font color=blue>const</font> <font color=blue>function</font> <font color=blue>const</font>_iterator begin();
<font color=blue>const</font> <font color=blue>function</font> <font color=blue>const</font>_iterator end();
}
<font color=green>// a non-const iterator over a container</font>
<font color=blue>concept</font> container::iterator
{
<font color=green>// definitions...</font>
}
<font color=green>// a const iterator over a container</font>
<font color=blue>concept</font> container::<font color=blue>const</font>_iterator
{
<font color=green>// definitions...</font>
}
}
<font color=green>//=====================================================================</font>
<font color=green>//</font>
<font color=green>// Expressions</font>
<font color=green>// -------------</font>
<font color=green>//</font>
<font color=green>// expression [ "expression-name" ] [ < "datatype-list" > ] ( "parameters" ) </font>
<font color=green>// [ external ( "external-variables" ) ]</font>
<font color=green>// </font>
<font color=green>// {</font>
<font color=green>// "body"</font>
<font color=green>// }</font>
<font color=green>//</font>
<font color=green>// Expressions are the method to allow for lambda functions, and for</font>
<font color=green>// a higher level of abstraction. This is allowed via the ability to</font>
<font color=green>// write anonymous expressions. Expressions effectively "replace" or </font>
<font color=green>// expand out to what is declared in the body, and allow for that sort </font>
<font color=green>// of thing to be declared within function parameters, like for_each, </font>
<font color=green>// sort and find_if.</font>
<font color=green>//</font>
<font color=green>// Compile-Time Types</font>
<font color=green>// --------------------</font>
<font color=green>// Just like everything else, expressions can be templated. However,</font>
<font color=green>// since most useful features of expressions are their, "in place"</font>
<font color=green>// functionality, this generally won't (probably) be used.</font>
<font color=green>//</font>
<font color=green>// External Variables</font>
<font color=green>// ---------------------</font>
<font color=green>// Expressions can modify variables in scope via declaring them external</font>
<font color=green>// to the expression. This is achieved via the "external" keyword.</font>
<font color=green>//</font>
<font color=green>// Comparisons to C++</font>
<font color=green>// --------------------</font>
<font color=green>// The best case for expressions lies in comparisons to C++, most</font>
<font color=green>// notably in the std::for_each, std::find_if and std::sort uses,</font>
<font color=green>// which are generally used to show inabilities in C++ (in terms</font>
<font color=green>// of lambda functions).</font>
<font color=green>//</font>
<font color=green>// To sort a vector of integers:</font>
<font color=green>// C++:</font>
<font color=green>// // look up std::less if you're unsure</font>
<font color=green>// sort(v.begin(), v.end(), std::less<int>());</font>
<font color=green>//</font>
<font color=green>// Kaleidoscope:</font>
<font color=green>// // We'd assume this expression was part of the ksl</font>
<font color=green>// // Remember: No type is an implicit mu-type</font>
<font color=green>// expression less(A, B) { A < B; }</font>
<font color=green>// // and we'd use it like so:</font>
<font color=green>// sort(v.begin(), v.end(), less);</font>
<font color=green>//</font>
<font color=green>// In this simple case, they are effectively the same, albeit shorter</font>
<font color=green>// in Kaleidoscope's case (as std::less is actually a struct). However,</font>
<font color=green>// this is a trivial example. Let us "upgrade".</font>
<font color=green>//</font>
<font color=green>// This time, we want to sort a linked-list of meshes. Unfortunately,</font>
<font color=green>// The mesh datatype doesn't have a less-than operator. So normally under</font>
<font color=green>// C++ we'd have to write either our own boolean predicate and pass that</font>
<font color=green>// into std::sort, or write an explicit instance of std::less for the </font>
<font color=green>// datatype mesh. Either way it's a struct - not fun.</font>
<font color=green>//</font>
<font color=green>// With expressions in Kaleidoscope, however, we can write it as an anonymous</font>
<font color=green>// expression:</font>
<font color=green>//</font>
<font color=green>// sort(v.begin(), v.end(), expression (lhs, rhs) { lhs.size() < rhs.size(); } );</font>
<font color=green>//</font>
<font color=green>// As you're aware now, expressions don't have to work with types, and</font>
<font color=green>// anonymous expressions generally won't.</font>
<font color=green>//</font>
<font color=green>// </font>
<font color=green>//</font>
<font color=green>// The "external" keyword is there to allow the expressions to access</font>
<font color=green>// variables within its enclosing scope. This allows for the functionality</font>
<font color=green>// that is -just- out of for_each's grasp:</font>
<font color=green>//</font>
<font color=green>// int total = 0;</font>
<font color=green>// for_each(numbers, expression (x) external(total) { total += x; } );</font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=green>//=====================================================================</font>
<font color=green>// Real World Examples</font>
<font color=green>//=====================================================================</font>
<font color=blue>namespace</font> ksl
{
<font color=green>// multi-purpose less-than expression</font>
<font color=blue>expression</font> less_than(<font color=blue>const</font> A, <font color=blue>const</font> B)
{
A < B;
}
<font color=green>// multi-purpose greater-than expression</font>
<font color=blue>expression</font> greater_than(<font color=blue>const</font> A, <font color=blue>const</font> B)
{
A > B;
}
}
<font color=green>//=====================================================================</font>
<font color=green>//</font>
<font color=green>// Jars</font>
<font color=green>// ------</font>
<font color=green>// The idea of the jar is pretty simple really - it's where we keep</font>
<font color=green>// stuff. Namely, it's where we keep datatypes, expressions and </font>
<font color=green>// functions. We do it because if we didn't have the "_jar" postfix,</font>
<font color=green>// the compiler would be unable to tell if we weren't forward</font>
<font color=green>// declaring a datatype or whatnot.</font>
<font color=green>//</font>
<font color=green>// The jars (their formal definitions) are:</font>
<font color=green>//</font>
<font color=green>// - datatype_jar "variable-name" ;</font>
<font color=green>// - expression_jar "variable-name" ;</font>
<font color=green>// - function_jar "variable-name" ;</font>
<font color=green>//</font>
<font color=green>// datatype_jar + expression_jar</font>
<font color=green>// -------------------------------</font>
<font color=green>// These two jars act identically, except with their respective fields,</font>
<font color=green>// of course. They are the jars that hold which datatype something is,</font>
<font color=green>// or which expression (yet to be covered) is. For example:</font>
<font color=green>//</font>
<font color=green>// datatype_jar j = typeof(string);</font>
<font color=green>//</font>
<font color=green>// j now equals the type of string. We can use this to type mu-types,</font>
<font color=green>// such as this example:</font>
<font color=green>//</font>
<font color=green>// mu x;</font>
<font color=green>// datatype_jar j = typeof(string);</font>
<font color=green>// x<j> = "Totally valid!";</font>
<font color=green>// j = int;</font>
<font color=green>// x<j> = 5; // more totally valid!</font>
<font color=green>//</font>
<font color=green>// I do not think this concept requires any additional explanation, save</font>
<font color=green>// the reassurance that you can pass jars around as parameters.</font>
<font color=green>//</font>
<font color=green>// function_jar</font>
<font color=green>// --------------</font>
<font color=green>// This is the equivalent of a function pointer, and as such function</font>
<font color=green>// pointers do not exist in Kaleidoscope. It has the syntax of:</font>
<font color=green>//</font>
<font color=green>// function_jar "variable-name" [ < "variable-name" > ] ;</font>
<font color=green>//</font>
<font color=green>// Non-member functions</font>
<font color=green>// ----------------------</font>
<font color=green>// They are simply assigned, like so:</font>
<font color=green>//</font>
<font color=green>// function void print(integer x) { ksl::cout << x << ksl::endl; }</font>
<font color=green>// function_jar f = print;</font>
<font color=green>// f(5); // calls the function</font>
<font color=green>//</font>
<font color=green>// As you can see, the concept of pointers in function pointers has</font>
<font color=green>// been completely eliminated. Instead, we have a jar in which you</font>
<font color=green>// can put functions.</font>
<font color=green>//</font>
<font color=green>// Member functions</font>
<font color=green>// ------------------</font>
<font color=green>// The syntax for these is wonderfully easy. As you're aware, if you</font>
<font color=green>// read the syntax above, there's an optional datatype you can apply</font>
<font color=green>// to the function_jar, in between triangle brackets. This is defaultly</font>
<font color=green>// mu, and for good reason. If it is mu, the function_jar is expecting</font>
<font color=green>// a non-member function.</font>
<font color=green>//</font>
<font color=green>// However, it can be changed to the type of datatype you're trying to</font>
<font color=green>// store. The instance of that datatype is passed in like below:</font>
<font color=green>//</font>
<font color=green>// datatype Cat { public integer getAge(integer x) { return x * 7; } }</font>
<font color=green>// Cat my_cat;</font>
<font color=green>// function_jar f<Cat> = <my_cat>::getAge();</font>
<font color=green>// f(6); // returns 42 (haha, that was a coincidence)</font>
<font color=green>//</font>
<font color=green>// As you can see, the idea behind this decision was to show that the</font>
<font color=green>// two things in the triangle brackets should match up. If you want to</font>
<font color=green>// back to non-member functions, simply:</font>
<font color=green>//</font>
<font color=green>// f<mu>;</font>
<font color=green>//</font>
<font color=green>// will do. The value of f at this point is undefined. (Possibly extensions</font>
<font color=green>// include allowing virtual functions to polymorph appropriately if the</font>
<font color=green>// type change was between two polymorphic types... tricky).</font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=green>//=====================================================================</font>
<font color=green>// Real World Examples</font>
<font color=green>//=====================================================================</font>
<font color=blue>namespace</font> ksl
{
<font color=green>// like std::for_each</font>
<font color=blue>function</font> <font color=blue>void</font> for_each(container& c, <font color=blue>expression</font>_jar expr)
{
<font color=blue>for</font> (container::iterator i = c.begin(); i != c.end(); ++i)
{
expr(*i);
}
}
<font color=green>// like std::find_if</font>
<font color=blue>function</font> C::<font color=blue>const</font>_iterator find_if<container C>(<font color=blue>const</font> C& c, <font color=blue>expression</font>_jar expr)
{
<font color=blue>for</font> (C::<font color=blue>const</font>_iterator i = c.begin(); i != c.end(); ++i)
{
<font color=blue>if</font> (expr(*i)) <font color=blue>return</font> i;
}
<font color=blue>return</font> c.end();
}
<font color=green>// performs a crappy bubble sort</font>
<font color=blue>function</font> <font color=blue>void</font> sort(container& c, <font color=blue>expression</font> E = less_than)
{
<font color=blue>for_count</font> (c.size() - 1)
{
<font color=blue>for</font> (<font color=blue>autotype</font> i = c.begin(), <font color=blue>autotype</font> j = c.next(i); j != c.end(); ++i, ++j)
{
<font color=blue>if</font> ( E(*i, *j) ) ksl::swap(i, j);
}
}
}
}
<font color=green>//=====================================================================</font>
<font color=green>//</font>
<font color=green>// Inheritance, Polymorphism, and "Monomorphism"</font>
<font color=green>// ---------------------------------------------</font>
<font color=green>// I struggled for a while about how to succinctly define the whole</font>
<font color=green>// type hierarchy thing whilst allowing for inheritance and</font>
<font color=green>// polymorphism. A good example is:</font>
<font color=green>//</font>
<font color=green>// datatype height inherits real where (height < 2.1 && height >= 0);</font>
<font color=green>//</font>
<font color=green>// Now, while real is a superset of "height" (which has limitiations), </font>
<font color=green>// "height" is not technically -inheriting- so much as it IS height,</font>
<font color=green>// but with the addition of the limitations.</font>
<font color=green>//</font>
<font color=green>// Traditional polymorphism occurs via pointers - what I term</font>
<font color=green>// "monopmorphism" does not (per se the language). Such that if you</font>
<font color=green>// had a class D that derived from B (for example, CTextureDX derives</font>
<font color=green>// from ITexture) - polymorphism works as normal:</font>
<font color=green>//</font>
<font color=green>// - dynamic_cast< > works like C++</font>
<font color=green>// - static_cast< > works like C++</font>
<font color=green>//</font>
<font color=green>// However, for situations like the "height" example, "monomorphism"</font>
<font color=green>// is possible:</font>
<font color=green>//</font>
<font color=green>// real r = height(1.23)</font>
<font color=green>//</font>
<font color=green>// It is termed "monomorphism" (and I use it in quotation marks because</font>
<font color=green>// it's probably called something else) - because it is not possible</font>
<font color=green>// with multiple inheritance, and neither is it possible if the "derived"</font>
<font color=green>// class defines any members at all. All that is allowed is dimensional</font>
<font color=green>// analysis. Thus the above definition of "height" is valid.</font>
<font color=green>//</font>
<font color=green>// Effectively, there's no morphing at all - the assignment operators</font>
<font color=green>// can be generated at compile time by the compiler. Note that an</font>
<font color=green>// inherited type will inherit all the dimensional analysis rules its</font>
<font color=green>// parent has. So:</font>
<font color=green>//</font>
<font color=green>// datatype dwarf_height inherits height where (dwarf_height > 2.1);</font>
<font color=green>//</font>
<font color=green>// would be invalid - although I'm pretty sure for all cases this is an</font>
<font color=green>// undecidable problem. The compiler should/would be able to catch 95%</font>
<font color=green>// of the mismatches, though.</font>
<font color=green>//</font>
<font color=green>// This area needs to most suggestions, guys.</font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=green>//=====================================================================</font>
<font color=green>//</font>
<font color=green>// Pointers & Garbage Collection</font>
<font color=green>// -------------------------------</font>
<font color=green>// GC is good, but for writing games, it isn't -that- good. Especially</font>
<font color=green>// Java's model for it (big sweeps every second or so). However, there</font>
<font color=green>// is definately a need for it, but it should be at the user's</font>
<font color=green>// discretion. In the form presented, there is no "default" - it all</font>
<font color=green>// depends upon the user/</font>
<font color=green>//</font>
<font color=green>// Regular Ol' Pointers</font>
<font color=green>// ----------------------</font>
<font color=green>// As per normal in C/C++.</font>
<font color=green>//</font>
<font color=green>// int * i = new int(53);</font>
<font color=green>//</font>
<font color=green>//</font>
<font color=green>// Memory Managed Pointers ("Smart Pointers")</font>
<font color=green>// --------------------------------------------</font>
<font color=green>// Pinched from C#/Managed C++:</font>
<font color=green>//</font>
<font color=green>// int ^ i = new int(53):</font>
<font color=green>//</font>
<font color=green>// There are some differences, however. Smart Pointers are garuanteed</font>
<font color=green>// to be initilaised to NULL. Furthermore, Smart Pointers are effectively</font>
<font color=green>// like boost::shared_ptrs, -except- they have the ability to avoid</font>
<font color=green>// this nasty problem:</font>
<font color=green>//</font>
<font color=green>// int ^ i = new int(53);</font>
<font color=green>// int * j = raw_pointer_cast(i);</font>
<font color=green>// int ^ k = raw_pointer_cast(j);</font>
<font color=green>//</font>
<font color=green>// In that situation, the memory pointed to by both i and k will</font>
<font color=green>// have the "reference" at "two", and not "one" (this is the required</font>
<font color=green>// behaviour - implementation doesn't matter).</font>
<font color=green>//</font>
<font color=green>//</font>
<font color=green>// raw_pointer_cast( )</font>
<font color=green>// ---------------------</font>
<font color=green>// As used above, a raw_pointer_cast converts from a Smart</font>
<font color=green>// Pointer to a Raw Pointer, and back again. They can not do that</font>
<font color=green>// implicitly, as programmers really need to be aware when they're</font>
<font color=green>// doing dangerous stuff like that.</font>
<font color=green>//</font>
<font color=green>//</font>
<font color=green>// release( )</font>
<font color=green>// ------------</font>
<font color=green>// Smart Pointers have the language-feature of having a function</font>
<font color=green>// "release", allowing them to release their data and becomming</font>
<font color=green>// NULL again:</font>
<font color=green>//</font>
<font color=green>// int ^ i; // equals NULL</font>
<font color=green>// i = new int(53);</font>
<font color=green>// i.release(); // back to NULL</font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=green>//=====================================================================</font>
<font color=green>// Real World Examples</font>
<font color=green>// - Note: I'm not happy with the syntax for const references to</font>
<font color=green>// smart pointers - but I can't think of another way.</font>
<font color=green>//=====================================================================</font>
<font color=blue>datatype</font> Mesh
{
VertexBuffer ^ mVertexBuffer;
IndexBuffer ^ mIndexBuffer;
ksl::string mName;
<font color=blue>public</font>:
<font color=blue>function</font> Mesh(<font color=blue>const</font> ksl::string& name) : mName
{
}
<font color=blue>function</font> <font color=blue>void</font> setVertexBuffer(<font color=blue>const</font> VertexBuffer^& vb)
{
mVertexBuffer = vb;
}
<font color=blue>function</font> <font color=blue>const</font> VertexBuffer^& getVertexBuffer()
{
<font color=blue>return</font> mVertexBuffer;
}
}
<font color=green>//=====================================================================</font>
<font color=green>//</font>
<font color=green>// Classes Revisited</font>
<font color=green>// -------------------</font>
<font color=green>// This simple (and not-safe) implementation of the std::vector</font>
<font color=green>// class is to demonstrate some advanced forms of typing, only briefed</font>
<font color=green>// over before.</font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=blue>namespace</font> ksl
{
<font color=blue>datatype</font> vector
{
<font color=blue>private</font>:
<font color=blue>mu</font>e _begin;
<font color=blue>mu</font>e _end;
<font color=blue>mu</font>e _cur;
<font color=green>// type we're working with atm - initially set to mu</font>
<font color=blue>datatype</font>_jar _T;
<font color=blue>public</font>:
<font color=green>//=====================================================================</font>
<font color=green>// Default Constructor</font>
<font color=green>// ---------------------</font>
<font color=green>// We do nothing - not even initialising the mu-types.</font>
<font color=green>//=====================================================================</font>
vector()
{
}
<font color=green>//=====================================================================</font>
<font color=green>// Mue-type Initialisation</font>
<font color=green>// ----------------------------</font>
<font color=green>// Note we need to explicitly define the mue-type variables, and since</font>
<font color=green>// that takes higher precedence than the constructor, everything works</font>
<font color=green>// nicely.</font>
<font color=green>//=====================================================================</font>
vector(uint<32> size, <font color=blue>datatype</font>_jar T)
: _T(T),
_begin<T*>(new T[size]), _end<T*>(_begin + size), _cur<T*>(_begin), _T(T)
{
}
<font color=green>// Simpler version</font>
vector(<font color=blue>datatype</font>_jar T)
: _T(T),
_begin<T*>(NULL), _end<T*>(NULL), _cur<T*>(NULL),
{
}
<font color=green>// Destructor - as per normal</font>
~vector()
{
delete [] _begin;
}
<font color=green>//=====================================================================</font>
<font color=green>// The following functions work properly because pointer arithmatic</font>
<font color=green>// is defined, due to _begin, _end and _cur having definitive types.</font>
<font color=green>//=====================================================================</font>
<font color=blue>inline</font> <font color=blue>function</font> <font color=blue>const</font> _T& operator [](uint<32> index) <font color=blue>const</font>
{
<font color=blue>return</font> *(_begin + index);
}
<font color=blue>inline</font> <font color=blue>function</font> _T& operator [](uint index)
{
<font color=blue>return</font> *(_begin + index);
}
<font color=blue>inline</font> <font color=blue>function</font> uint<64> size() <font color=blue>const</font>
{
<font color=blue>return</font> _end - _begin;
}
<font color=blue>inline</font> <font color=blue>function</font> <font color=blue>void</font> clear()
{
delete [] _begin;
_begin = _end = _cur = NULL;
}
<font color=green>// allow assignment to wipe our previous types (although make sure we</font>
<font color=green>// delete the held data, if any</font>
<font color=blue>inline</font> <font color=blue>function</font> <font color=blue>const</font> vector& operator = (<font color=blue>const</font> vector& v)
{
<font color=blue>if</font> (!<font color=blue>muud</font>(_T) && _begin) delete [] _begin;
<font color=green>// change over type</font>
_T<v._T>;
<font color=green>// we managed to get all three brackets on the next line</font>
_begin<_T>(new T[v.size()]);
_end<_T>(_begin + v.size());
_cur<_T>(_begin + (v._cur - v._begin));
<font color=blue>return</font> *this;
}
<font color=green>//=====================================================================</font>
<font color=green>// if we'd used the default constructor, then _T would have</font>
<font color=green>// been mu-type, meaning any datatype could be passed in as a value.</font>
<font color=green>// It also means that _T will NOT have been changed, because it is</font>
<font color=green>// not a mu-type, but rather it is a datatype_jar. Therefore we can</font>
<font color=green>// use it for comparison. We should initialise things then to the</font>
<font color=green>// type just presented.</font>
<font color=green>//===================================================================== </font>
<font color=blue>function</font> <font color=blue>const</font> _T& push_back(<font color=blue>const</font> _T& value)
{
<font color=blue>if</font> (<font color=blue>muud</font>(_T))
{
*this = vector(<font color=blue>typeof</font>(value));
}
<font color=green>// I actually have no idea if this algorithm correctly</font>
<font color=green>// pads a vector. kind of fun to write though.</font>
<font color=blue>if</font> (_cur == _end)
{
clone_<font color=blue>datatype</font><_begin> temp = _begin;
uint<64> old_size = _end - _begin;
uint<64> new_size = old_size * 1.5 + 1;
_begin = new T[new_size];
<font color=green>// just like memcpy</font>
copy_memory(_begin, temp, old_size);
_cur = begin + old_size;
_end = _begin + new_size;
}
*(_cur++) = value;
}
}
<font color=green>//=====================================================================</font>
<font color=green>//</font>
<font color=green>// D E M O N S T R A T I O N</font>
<font color=green>// -------------------------</font>
<font color=green>// Yay!</font>
<font color=green>//</font>
<font color=green>//</font>
<font color=green>// A Short Demonstration On Usage</font>
<font color=green>// --------------------------------</font>
<font color=green>// This is a simple demonstration of how the above-written classes,</font>
<font color=green>// functions, expressions, etc, could mesh together. It is pretty</font>
<font color=green>// simple - we are simply making a vector of numbers, sorting them,</font>
<font color=green>// and then displaying them.</font>
<font color=green>//</font>
<font color=green>//=====================================================================</font>
<font color=green>//=====================================================================</font>
<font color=green>// Displays a given thing</font>
<font color=green>//=====================================================================</font>
<font color=blue>expression</font> display(t)
{
ksl::cout << t << ksl::endl;
}
<font color=green>//=====================================================================</font>
<font color=green>// The Main Function</font>
<font color=green>// -------------------</font>
<font color=green>// From here on out, no more long descriptions, just example code</font>
<font color=green>//=====================================================================</font>
<font color=blue>function</font> int main()
{
ksl::vector v(<font color=blue>integer</font>);
v.push_back(75);
v.push_back(2);
v.push_back(-634);
v.push_back(231);
v.push_back(15);
v.push_back(18);
v.push_back(-77);
v.push_back(400);
<font color=green>// would cause a run-time fail, because v::mT is not const char *,</font>
<font color=green>// (see ksl::vector::push_back)</font>
<font color=green>// -------------------------------------</font>
<font color=green>// v.push_back("bork bork!");</font>
<font color=green>// display the undordered vector</font>
ksl::for_each(v, display);
<font color=green>// sort - ksl::sort takes ksl::less_than as default</font>
ksl::sort(v);
<font color=green>// display the ordered vector, this time with an anonymous expression</font>
ksl::for_each(v, <font color=blue>expression</font> (x) { ksl::cout << "E: " << x << ksl::endl; } );
<font color=green>// sort by greater than</font>
ksl::sort(v, <font color=blue>expression</font>(A, B) { A > B; } );
<font color=green>// display newly ordered vector</font>
ksl::for_each(v, display);
<font color=green>// find the position 231 is in</font>
<font color=blue>autotype</font> pos = find_if(v, <font color=blue>expression</font> (A) { A == 231 });
<font color=green>// display it - assuming vector::iterator had an "index()" method, which is really wouldn't.</font>
ksl::cout << "Position of 231: " << pos.index() << ksl::endl;
}