I hope to “finish” the proposal this weekend before we begin ripping it apart. Feel free to look at it and get a feel for what I've tried to compile.
The LOLCode language so far has either had only static variables, or required the use of a garbage collector. This proposal formally specifies a Garbage Collection Strategy. All variables are merely references to locations in memory. It is assumed that when a variable is no longer referenced, that variable's allocated space will be freed sometime in the future, or on program exit.
Here's a few changes to how Declaring variables works.
I HAS A <variable> ITZ <value>
This instantiates and initializes a variable. If the value is a literal, the variable is initialized to the appropriate object type (YARN, TROOF, NOOB, NUMBR, NUMBAR). If the value is an identifier or expression, the variable is initialized to the resulting expression.
I HAS A <variable> ITZ A <type>
This instatiates a variable, and initializes it to a default value.
Built-In default values:
- YARN - ””
- TROOF - FAIL
- NUMBR - 0
- NUMBAR - 0.0
- NOOB - NOOB
I HAS A <variable>
This instatiates a variable and initializes it to NOOB. It is shorthand for I HAS A <variable> ITZ NOOB
Function types are declared/initialized using the HOW DUZ I / IF U SAY SO blocks, however they behave the same as variables. For example:
HOW DUZ I var YR stuff BTW implement IF U SAY SO I HAS A var ITZ 0 BTW Throws an error AS var is already taken var R 0 BTW FUNIKSHUN var no longer exists, it's now NUMBR-0
<variable> R NOOB
This above code ensures that a variable no longer references anything. The reference still exists on the current scope and still requires a small amount of memory. If this was the last reference to an object, it will be garbage collected in the future.
All primitive types are considered Immutable. All built in operations return new objects instead of references to old objects. The exceptions to this rule are WIN, FAIL and NOOB. Every TROOF reference is either the WIN or FAIL object. Every NOOB reference is ot the NOOB instance.
The SRS operation can be used to interpret a YARN (or something castable to a YARN) as an identifier. This operator may be used anywhere that a regular identifier is expected.
I HAS A var ITZ 0
Is the same as:
I HAS A name ITZ "var" I HAS A SRS name ITZ 0
The A becomes optional in variable declaration so:
I HAS A SRS name ITZ 0
is the same as
I HAS SRS name ITZ 0
Functions have a new function call syntax to ensure appropriate grammatical parsing (and proper english verb tense):
I IZ <funkshun> (YR <argument> (AN YR <argument>) * ) ? MKAY
The I parameter is used to distingish a function call on the current namespace vs. a function call on a bukkit (defined below).
The MKAY has been added to help disambiguate function calls.
Creating functions is done with a new HOW IZ I operator instead of HOW DUZ I.
HOW IZ I <function> (YR <param> (AN YR <param>)*)? ::code-block:: IF U SAY SO
BUKKITs are the container type. They may hold NUMBRs, NUMBARs, TROOFs, YARNs, functions (FUNKSHUN), and other BUKKITS. Each entity within a BUKKIT may be indexed by a NUMBR or a YARN. These indices, whether NUMBRs or YARNs, referring to functions, variables, or other BUKKITs, are generically called “slots”.
To create an empty object within the current object's scope:
I HAS A <object> ITZ A BUKKIT
This object will have the default behavior of all bukkits.
One of the most obvious thing to do with a bukkit it place something in a slot.
<object> HAS A <slotname> ITZ <expression>
A slot may be declared/initialized more than once, however doing so only changes the value the slot references.
This places the value returned from expression (could be another object) into the slot identified by slotname. The slot name may be any identifier (or SRS BIZNUS cast). Note: This identifier may be a function. Example:
HOW IZ I blogin YR stuff VISIBLE stuff IF U SAY SO <object> HAS A blogin ITZ blogin
The function definition syntax has changed as well. To declare a function inside a bukkit's slot
HOW IZ <object> <slot> (YR <argument>)* ( <statements> )* IF U SAY SO
So, the above code becomes
HOW IZ <object> blogin YR stuff VISIBLE stuff IF U SAY SO
Functions operate differently in the context of bukkits. When a function is called from an object, some scope rules and variable resolution change.
When an identifier is used in a function, the variable is looked up in the following manner:
- The function namespace
- The calling object's namespace (if called from object)
- The “global” namespace
IT is always looked up from global namespace
The function namespace is made up of all arguments and any variable declared using the identifier “I” it resides within the function's namespace.
HOW IZ I fooin YR bar BTW bar is on function namespace I HAS A bar2 BTW bar2 is on the function namespace IF U SAY SO
ME is an identifer used to access the calling object of a function. If there is no calling object, access to ME throws an exception. Define This exception
Declaring a variable on the calling objects namespace is done as follows:
HOW IZ I fooin YR bar ME HAS A bar2 BTW bar2 is now a slot on calling object IF U SAY SO
ME can also be used to explicitly use a slot variable vs. a function namespace variable.
HOW IZ I fooin YR bar ME'Z bar R bar BTW sets calling object's bar slot to bar value IF U SAY SO
There is an alternate way to define a new object/bukkit
O HAI IM <object> [IM LIEK <parent>] <code-block> KTHX
Anything “I” inside the codeblock actually refers to <object>. This can simplify syntax, eg:
O HAI IM pokeman I HAS A name ITZ "pikachu" HOW IZ I pikachuin YR face BTW DEFINE IF U SAY SO KTHX
Identifiers within the O HAI block are looked up via slot-access first. If they are not found, the global scope is then searched. If that fails, then an error is thrown.
Bukkit slots are accessed using the slot operator ”-”.
<object> 'Z <slotname>
or indirectly using the Srs operator
<object> 'Z SRS <expression>
Slot access is very important to function calls. The new function call syntax is as follows.
To call a function on an object:
<object> IZ <slotname> (YR <variable> (AN YR <variable)*)? MKAY
combined with the Srs operator allows the following:
HOW IZ I getin YR object AN YR varName I HAS A funcName ITZ SMOOSH "get" AN varName MKAY FOUND YR object IZ SRS funcName MKAY IF U SAY SO
This will call get<varName> on object.
Every bukkit contains a few slots that have special meaning
TODO - Figure out what else may be needed
parent refers to a bukkit's “parent” object and is described below.
omgwtf refers to a method that is called when slot access fails. This method should return a variable (that will be placed in the unknown slot) or throw an exception. The default implementation of canhas is to always throw an exception.
izmakin refers to a method that will be run after a bukkit is fully prototyped but before the prototyping method returns. This allows a bukkit creator to perform some logic every time that bukkit is prototyped, and guarantees a “well-formed” bukkit.
To create an object based upon an existing object:
I HAS A <object> ITZ LIEK A <parent>
Behavior of this sort of inheritance is described further below.
To define inheritance using alternate syntax, do the following.
O HAI IM <object> [IM LIEK <parent>] <code block> KTHX
Inheritance implies a few things, one of which is inheritance of slots (described below). Another thing inheritance does is automatically create a “parent” slot on the new object. The “parent” slot refers to the object that this object was inherited from, or its prototype. The parent slot is treated specially by the Bukkit. An interesting side effect of this is that a Bukkit may change its “parent”/“prototype” by changing its parent slot. More on this later.
Declaring a variable within the current object adds that variable to the object.
Accessing a variable from within the current object looks for that variable within the current object. If it is not found, it searches for the variable within the parent object (using the parent slot), and on up the chain of parents until it reaches an object where the parent slot is NOOB or it reaches a parent object is has already searched before.
Assigning a variable within the object first searches for it within the current object. If it has been declared within the current object, then it is set. If that fails, it attempts to access it within the parent object. Search continues in up the chain of parents. If the variable name is found up the inheritance chain, then that variable is declared and created within the current object (where the search started), and the value is set. If the variable search fails and the variable was never previously assigned, then it's a declaration error.
In this way, a child object has all of the values and methods of its ancestors, unless it replaces them within itself.
No matter where a FUNKSHUN is stored in a slot, during a Slot-Access Function call, the Function obtains variables from the object it was accessed from.
TODO - This needs to be reworded
<object> IZ <functionSlotName> MKAY
In this case, the function will pull variables from <object>.
HOW IZ I funkin YR shun ? VISIBLE SMOOSH prefix AN shun MKAY IF U SAY SO O HAI IM parentClass I HAS A prefix ITZ "parentClass-" I HAS A funkin ITZ funkin BTW Pulls funk from global scope KTHX O HAI IM testClass IM LIEK parentClass I HAS A prefix ITZ "testClass-" KTHX parentClass IZ funkin YR "HAI" MKAY BTW parentClass-HAI testClass IZ funkin YR "HAI" MKAY BTW testClass-HAI
LOLCode supports a form of multiple-inheritance (I'm calling mixin-inheritance) via a new use of the SMOOSH operator.
I HAS A <object> ITZ A <parent> SMOOSH <mixin> (AN <mixin>)*
O HAI IM <object> IM LIEK <parent> SMOOSH <mixin> (AN <mixin>)* <statement-block> KTHX
A mixin may be any bukkit type. When declaring a new object using mixins, All slots defined on the mixin are copied into the newly construct bukkit in reverse order of declaration. So
I HAS A ZipFileRiver ITZ A River SMOOSH FileStuffz AN ZipStuffz
This copies all slots from ZipStuffz into ZipFileRiver, then all slots from FileStuffz into ZipFileRiver, then replaces the parent slot with a reference to River.
Mixin-Inheritance is static. It can only pull in slots that are defined when the mixin takes place. If the FileStuffz or ZipStuffz objects change after the ZipFileRiver object is defined, the ZipFileRiver class does not see the change.
Here's a method of performing Mixin-Inheritance after a bukkit has been created.
BTW burger is on the stack I HAS A cheezburger ITZ A burger SMOOSH cheeze BTW Let's do the same thing, but assume cheezburger2 already exists BTW Makes sure all of cheeze and its parent slots are copied into slice I HAS A slice ITZ A bukkit SMOOSH cheeze slice'Z parent R burger'Z parent BTW slice & burger are now commons sub-classes cheezburger2'Z parent R slice BTW cheezburger2 now has same functionality as cheezburger
These classes represent ideas for an LOLCode standard library of classes for use when Bukkits are first implement. These classes represent functionality and reference implementation and are by no means what should be distributed with LOLCode runtimes.
CAN HAS lolstd-collections-pile
O HAI IM pile I HAS A length IZ 0 HOW IZ I pushin YR item ME HAS SRS length ITZ item length R SUM OF length AN 1 IF U SAY SO HOW IZ I popin length R DIFF OF length AN 1 BTW Do bounds checking... I HAS A mom ITZ length ME'Z SRS length R NOOB FOUND YR mom IF U SAY SO KTHX
I HAS A fishBarrel ITZ A pile fishBarrel IZ pushin YR "halibut" MKAY fishBarrel IZ pushin YR "trout" MKAY fishBarrel IZ pushin YR "salmon" MKAY VISIBLE fishBarrel IZ popin MKAY BTW prints "salmon"
O HAI IM River KTHX
This are imported by default as they are a core type of the language, however, they will have extra slot-like functions on them.
- LONGNESS - This returns the lenght fo the YARN.
I HAS A thread ITZ "HAI WURLD!" VISIBLE thread'Z LONGNESS BTW Displays "10"
TODO - Define.