A Language Called [something] - The Story So Far - Part II

Saturday, August 6th, 2011     #javascript #everything

Part II of describing what's been implemented in this language compiler I'm working on.

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.

Automatic 'this' references

Because this language is strongly typed, the compiler knows when to automatically insert this references when accessing member variables, methods and properties.

{{C#}}
extern dynamic Console;

class Foo
{
 public string fn1()
 {
       return fn2();
   }

   string fn2()
    {
       return prop1;
   }

   string prop1
    {
       get
     {
           return _field1;
     }
   }

   string _field1 = "Hello World";
}

Console.WriteLine(new Foo().fn1());
{{JavaScript}}
(function() {

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

Foo.prototype._field1 = "Hello World"

// System.String Foo.fn1()
Foo.prototype.fn1 = function()
{
    return this.fn2();
};

// System.String Foo.fn2()
Foo.prototype.fn2 = function()
{
   return this.get_prop1();
};

// System.String Foo.get_prop1()
Foo.prototype.get_prop1 = function()
{
 return this._field1;
};


Console.WriteLine(new Foo().fn1());

})();

(This was actually shown in yesterday's post but I neglected to mention it)

Explicit Casts

Explicit casts convert an object reference between two different types without performing a runtime check.

The compiler only allows casting between types that could be possible. So you can't cast an int to string and you can only cast between class if one derives from the other.

{{C#}}
class Foo
{
}

class Bar : Foo
{
}

Foo x;
Bar y = (Bar)x;
{{JavaScript}}
(function() {

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


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

Bar.prototype = new Foo();

var x;
var y=x;

})();

Note:

  • explicit casts are a compile time only operation - there's no JavaScript code actually associated with them.
  • explicit casts are a little different to C# which does do a runtime check and will throw an exception if the cast fails.
  • you can always explicitly cast to and from dynamic, so if you really really need to cast something that doesn't make sense to the compiler you can. eg: ((string)((dynamic)23))

Actually, there is one case where code is generated on an explicit cast - when casting a double to int, in which case it's rounded:

{{C#}}
extern dynamic Console;

double x=23.9;
int y = (int)x;
Console.WriteLine(y.toString());
{{JavaScript}}
(function() {

var x=23.9;
var y=((x)|0);
Console.WriteLine(y.toString());

})();

Dynamic Casts with as and is

A dynamic cast converts an object reference from one type to another, but uses JavaScript's instanceof operator to check the conversion and produces null if the object is of the wrong type. Dynamic casts use the as keyword:

{{C#}}
extern dynamic Console;

class Foo
{
}

class Bar : Foo
{
}

Foo x = new Bar();
Bar y = x as Bar;
Console.WriteLine((y!=null).toString());
{{JavaScript}}
(function() {

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


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

Bar.prototype = new Foo();


var x=new Bar();
var y=(x instanceof Bar) ? x : null;
Console.WriteLine((y!==null).toString());

})();

The is is essentially the same as the JavaScript instanceof operator, it checks if an instance is of a particular type.

{{C#}}
extern dynamic Console;

class Foo
{
}

class Bar : Foo
{
}

Foo x = new Bar();
Console.WriteLine((x is Bar).toString());
{{JavaScript}}
(function() {

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


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

Bar.prototype = new Foo();


var x=new Bar();
Console.WriteLine((x instanceof Bar).toString());

})();

Inline JavaScript

This is a bit of a hack, but it's there and works. You can inline just about any JavaScript by quoting it in backticks. This is not a free form text pass through though. In the context of the input program, the backtick quoted JavaScript is treated as an expression element that returns something typed dynamic.

This lets you embed plain JavaScript just about anywhere and is great for testing while I'm still building other parts of the language:

{{C#}}
// Use `backticks` to embed Javascript expressions and statements directly into
// the generated output.  Highly discouraged, and will be eventually removed
// but useful for testing and until we support more stuff natively.

// JavaScript array
dynamic x=`["Hello", "World"]`;

// JavaScript code
`
for (var i in x)
{
  Console.WriteLine(x[i]);
}
`;
{{JavaScript}}
(function() {

var x=["Hello", "World"];

for (var i in x)
{
   Console.WriteLine(x[i]);
}
;

})();

Access Protection

Most of the class member access protection is working. This includes the keywords:

  • private
  • protected
  • public
  • internal

Note that non-public protection levels aren't enforced outside the language. In other words they're a compile time only directive that the compiler enforces with respect to other code in the same compilation. So, private members can definitely be accessed from external JavaScript (if you let the object out of your compiled script), or by casting an object reference to dynamic.

That said, these keywords are excellent hints for minification and the compiler will obfuscate these symbols. So you can't reliably get around this restriction from code, but you wouldn't rely on this for a banking application.

Protection levels on classes and global functions are parsed, but not implemented correctly yet.

Namespaces and Nested Classes

Namespaces and Nested Classes are supported. Note that the naming of these is properly maintained in the generated JavaScript code.

{{C#}}
extern dynamic Console;

namespace MyCoolLibrary
{
   class Outer
 {
       class Inner
     {
           public void test()
          {
               Console.WriteLine("Hello World");
           }
       }
   }
}

var x = new MyCoolLibrary.Outer.Inner();
x.test();
{{JavaScript}}
(function() {

// namespace MyCoolLibrary
var MyCoolLibrary = new function()
{
    // class MyCoolLibrary.Outer
    var Outer = function()
  {
   };
  
    this.Outer = Outer;
 
    // class MyCoolLibrary.Outer.Inner
  Outer.Inner = function()
    {
   };
  
    //  MyCoolLibrary.Outer.Inner.test()
    Outer.Inner.prototype.test = function()
 {
       Console.WriteLine("Hello World");
   };
  
    
}();
var x=new MyCoolLibrary.Outer.Inner();
x.test();

})();

That's It So Far

I think that covers most of what's implemented so far. There's 150+ (and growing) unit tests and while I know there are some issues I think it's reasonably solid.

« Older - A Language Called [something] - more language features Newer - A Language Called [something] - The Story So Far »