A Language (temporarily) called Prefix

Tuesday, August 16th, 2011     #javascript #everything

For the time being, I've decided to call this language Prefix. I'm not particularly fond of it, but I just need a name - anything's got to be better than [something]. As for the project itself, static constructors and enumerated types are now working.

This post is about Prefix a modern language that compiles to JavaScript. A language where C# is the inspiration - not the goal. Read more on the Prefix Project Page.

Static Constructors

Nothing too exciting here - they're static constructors and they work.

The main challenge with this was sensibly determining the order in which to invoke them. The .NET runtime runs static constructors the first time a class is used, but that seemed an unreasonable amount of overhead. To resolve this the compiler looks as the dependencies between classes (ie: class coupling) and tries to order things accordingly. If it can't resolve the order through dependencies (say two classes depend on each other), the constructors are run in declaration order.

{{C#}}
extern dynamic Console;

// This test is to check the ordering of static constructors.

// Because class `Foo` is coupled to class `Bar` through it's `_coupled` 
// property, Bar's static constructor should be invoked first.


class Foo
{
 static Foo()
    {
       Console.WriteLine("Foo");
   }


   Bar _coupled;
}

class Bar
{
    static Bar()
    {
       Console.WriteLine("Bar");
   }
}

Console.WriteLine("Hello World");
{{JavaScript}}
(function() {

// class Foo
var Foo = function()
{

};

Foo.prototype._coupled = null;

// static Foo.Foo()
Foo.$cctor = function()
{
 Console.WriteLine("Foo");
};



// class Bar
var Bar = function()
{

};

// static Bar.Bar()
Bar.$cctor = function()
{
   Console.WriteLine("Bar");
};



// Call static constructors
Bar.$cctor();
Foo.$cctor();

Console.WriteLine("Hello World");

})();

Enumerated Types

I thought enumerated types - aka enum's - would be reasonably trivial to implement but there's quite a bit of detail in this area. This is how I've decided to implement them:

  • There's no runtime type information - currently I'm thinking that Prefix won't support any runtime type information. For enums, this means there's no ToString method to convert the value to a string representation. (I could add this but it would require boxing the value to an object and it just seemed overkill).
  • The enum type is generated as a JavaScript object literal if it's a debug build (for readabiliy), or if the enum is exported (for usability obviously).
  • From JavaScript code you can access exported enum values as typename.membername just like in C#. eg: DayofWeek.Monday
  • In release builds the code generated by the compiler doesn't use the enum names - rather it inserts the enum's numerical value directly into the generated code - for performance and code size minimization.
  • All correct operator logic is implemented. eg: you can subtract two enum values, but you can't add them, you can add an enum value and integer, etc...
  • When declaring an enum member value the expression can reference other constant values, including other types.

eg:

{{C#}}
extern dynamic Console;

public enum MyEnum
{
    a = 10,
 b = a+10,
   c,
  d = MyOtherEnum.a
}

public enum MyOtherEnum
{
  a = 100
}


Console.WriteLine((int)MyEnum.a);
Console.WriteLine((int)MyEnum.b);
Console.WriteLine((int)MyEnum.c);
Console.WriteLine((int)MyEnum.d);
{{JavaScript}}
// Exported entities
var MyEnum, MyOtherEnum;

(function() {

// enum MyEnum
MyEnum = 
{
 a: 10,
  b: 20,
  c: 21,
  d: 100
};

// enum MyOtherEnum
MyOtherEnum = 
{
  a: 100
};

Console.WriteLine(MyEnum.a);
Console.WriteLine(MyEnum.b);
Console.WriteLine(MyEnum.c);
Console.WriteLine(MyEnum.d);

})();

Improved Code Generation of Automatically Implemented Properties

I've made some tweaks to the way automatically implemented properties are accessed. If the property is automatically implemented, and it's not virtual, abstract or override generated code no longer uses the accessor methods. This should make for more readable and more efficient JavaScript code. I've also removed the $ that used to be added to the name of the backing field.

In this example note how the accessor methods are still generated, but where the property is used the generated code directly uses the backing variable.

So why generate the accessor methods at all? Mainly because they'll be needed once interfaces are supported, but also to ensure the consistency of exported classes - ie: all properties should be accessible through the accessor methods.

{{C#}}
extern dynamic Console;
class MyClass
{
 public string SimpleProperty
    {
       get;
        set;
    }
}

var x = new MyClass();

x.SimpleProperty = "Hello World";

Console.WriteLine(x.SimpleProperty);
{{JavaScript}}
(function() {

// class MyClass
var MyClass = function()
{

};

MyClass.prototype.SimpleProperty = null;

// System.String MyClass.get_SimpleProperty()
MyClass.prototype.get_SimpleProperty = function()
{
   return this.SimpleProperty;
};

// System.String MyClass.set_SimpleProperty(System.String value)
MyClass.prototype.set_SimpleProperty = function(value)
{
    this.SimpleProperty = value;
    return value;
};


var x=new MyClass();
x.SimpleProperty = "Hello World";
Console.WriteLine(x.SimpleProperty);

})();

A Quick Note

I'm well aware that so far this project has been very heavily focused on the C# style side of things. Although it may appear as though I'm simply building a C# to JavaScript compiler, this is not the case. I plan to shift focus to the interoperability and language features closer to the JavaScript side of things once the major strongly typed features are complete.

« Older - Prefix - Support for Interfaces Newer - A Language Called [something] - Code Path Analysis »