A Language Called [something] - more language features

Wednesday, August 10th, 2011     #javascript #everything

I still haven't decided on a name for this project, but the compiler itself is coming along nicely and now supports static members, virtual properties, the using statement and more...

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 Members

The compiler now supports static methods, properties and variables. In the generated JavaScript, they're stored on the class function object.

{{C#}}
extern dynamic Console;

class MyClass
{
 public static string MessageFunction()
  {
       return "Hello World (Function)";
    }

   public static string MessageProperty
    {
       get
     {
           return "Hello World (Property)";
        }
   }

   public static string MessageVariable = "Hello World (Variable)";
}

Console.WriteLine(MyClass.MessageFunction());
Console.WriteLine(MyClass.MessageProperty);
Console.WriteLine(MyClass.MessageVariable);
{{JavaScript}}
(function() {

// class MyClass
var MyClass = function()
{
};

MyClass.MessageVariable = "Hello World (Variable)"
// System.String MyClass.MessageFunction()
MyClass.MessageFunction = function()
{
 return "Hello World (Function)";
};

// System.String MyClass.get_MessageProperty()
MyClass.get_MessageProperty = function()
{
   return "Hello World (Property)";
};


Console.WriteLine(MyClass.MessageFunction());
Console.WriteLine(MyClass.get_MessageProperty());
Console.WriteLine(MyClass.MessageVariable);

})();

Virtual Properties

Properties can now be declared virtual or abstract and be overridden in derived classes:

{{C#}}
extern dynamic Console;

class BaseClass
{
   public virtual string prop
  {
       get
     {
           return "Base";
      }
   }
}

class DerivedClass : BaseClass
{
   public override string prop
 {
       get
     {
           return "Derived";
       }
   }
}

BaseClass bc = new DerivedClass();
Console.WriteLine(bc.prop);
{{JavaScript}}
(function() {

// class BaseClass
var BaseClass = function()
{

};

// System.String BaseClass.get_prop()
BaseClass.prototype.get_prop = function()
{
   return "Base";
};


// class DerivedClass
var DerivedClass = function()
{

};

DerivedClass.prototype = new BaseClass();

// System.String DerivedClass.get_prop()
DerivedClass.prototype.get_prop = function()
{
 return "Derived";
};


var bc=new DerivedClass();
Console.WriteLine(bc.get_prop());

})();

Support for base and this in Constructors

The base keyword already worked in constructor base calls but is now also supported for calling base methods from overridden methods (including virtual overridden properties).

I've also added support for the this keyword for calling one constructor from another on the same class. eg:

{{C#}}
extern dynamic Console;

class Fruit
{
   public Fruit() : this("Apple")
  {
       Console.WriteLine("Fruit");
 }

   public Fruit(string name)
   {
       Console.WriteLine("Fruit " + name);
 }
}

var x = new Fruit();
{{JavaScript}}
(function() {

// class Fruit
var Fruit = function()
{

};

// Fruit.Fruit()
var Fruit$1 = function()
{
 Fruit$2("Apple");
   Console.WriteLine("Fruit");
};


// Fruit.Fruit(System.String name)
var Fruit$2 = function(name)
{
    Console.WriteLine("Fruit " + name);
};


Fruit.prototype = new Base();

Fruit$1.prototype = Fruit.prototype;
Fruit$2.prototype = Fruit.prototype;

var x=new Fruit$1();

})();

Initialization of Reference Type Member Variables

The compiler now moves object reference members variable initializers from the prototype to the class constructor. Previously all member variables were initialized by setting a default value in the prototype, but this obviously doesn't work for reference types where each class instance should get it's own member instance.

{{C#}}
extern dynamic Console;

class Bar
{
}

class Foo
{
  public Bar _bar = new Bar();
} 
{{JavaScript}}
(function() {

// class Bar
var Bar = function()
{

};

// class Foo
var Foo = function()
{
 this._bar = new Bar();

};

Foo.prototype._bar = null;
})();

Support for using Statements

Not to be confused with using clauses (which haven't been implemented yet), using statements are now implemented.

Unlike C# where the expression used in the using statement must implement the IDisposable interface, in this language the object must simply implement a method Dispose() that takes no arguments. It can even be a dynamic, but the object better have a Dispose method at runtime.

Note that the point of implementing the using statement is not so much for resource management, but to provide some form of deterministic cleanup. I often use this technique to wrap pieces of code that have a definite start/end - to automatically do the end part. For example in the compiler itself I use a disposable object to track a stack of code scopes as the code is compiled.

In JavaScript, the using statement is implemented as a temporary variable and a try/finally code block:

{{C#}}
extern dynamic Console;

class Foo
{
 public void Dispose()
   {
       Console.WriteLine("Foo.Dispose");
   }
}

using (var x = new Foo())
{
    Console.WriteLine("In using clause");
}
{{JavaScript}}
(function() {

// class Foo
var Foo = function()
{

};

//  Foo.Dispose()
Foo.prototype.Dispose = function()
{
  Console.WriteLine("Foo.Dispose");
};


var $temp;
var x;
$temp = x = new Foo();
try
{
   Console.WriteLine("In using clause");
}
finally
{
  $temp.Dispose();
}

})();

which produces this output:

In using clause
Foo.Dispose

Note that a temp variable is always used to hold the object to be disposed. In the above example the compiler could have generated x.Dispose(), but if the user code changed the x variable to point to something else inside the using clause the wrong object would end up getting disposed.

Other

Protection Level Access Consistency Checks

The compiler now checks for inconsistent protection access levels. so if you declare a public function that expects or returns a private type, you'll get an error.

Lots of Minor Improvements

There's been a ton of other minor improvements - everything from about 100 new unit tests to support for @"raw strings" and lots of bug fixes.

Major Internal Redesign

Aside from the above, the compiler has had a major redesign. The design that came from MiniME was really too simple for this project. The new design is much cleaner, much easier to work with and after considerable effort now passes the current set of 224 unit tests.

« Older - A Language Called [something] - Code Path Analysis Newer - A Language Called [something] - The Story So Far - Part II »