Sunday, March 13, 2011

Platform Dependent Versus Universal Numeric Types

Implementing numeric types in a programming language is surprisingly difficult.  At this time, Crack supports Universal Numeric Types (UNTs - for example int32, uint64, byte) and Platform Dependant Numeric Types (PDNTs - int, uint, float).  The PDNT names are just aliases for the corresponding UNTs in the target compiler - so for example, if the C++ compiler you use to build crack has a 32 bit int, Crack's "int" will be an alias for "int32".  Additionally, the philosophy behind implicit type conversions is to allow them only if they do not result in loss of precision.

This overall approach is not without its problems:

  • It makes Crack code platform-dependent because there are expressions that will work on one platform but will result in a compile-time error on another platform.  For example, "int32 i = int(v);" works on platforms with 32 bit integers, but breaks on platforms with 64 bit integers.
  • You end up writing a lot of explicit type conversions in places where you really don't care that much (like when using a signed integer value for a function argument of type "uint").  For a scripting language valuing terse syntax, this is kind of lame.
So after some discussion on IRC, we've decided to change our approach a little bit.  The general philosophy now is that you should use a PDNT in situations where you care about performance or interoperability with C/C++ code and you should use a UNT in situations where you care about precision.  The manifestations of this decision are:

  • PDNTs will be promiscuous: any numeric type will implicitly convert to any PDNT type.  So "int i = float64(v);" will be perfectly legal on any platform.
  • There will be max-size, min-size assumptions about PDNTs.  In particular, they will all be at least 32 bits but no more than 64 bits in size.  Like everything else in the language, these assumptions are subject to change across major versions of the language.
  • UNTs will continue to apply the strict conversion rules.  However, because of the min-size/max-size assumptions, certain conversions from PDNTs will always be legal.  An example of this is "int64 i = int(v)".
There's still a platform dependency problem here because expressions like "int i = int64(v);" will vary in behavior at runtime depending on the size of an integer on the platform.  So we've essentially converted a compile-time portability issue to a runtime portability issue :-/.

To mitigate this effect, there will be a warning flag that allows you to identify the places where you could potentially lose precision with something like this.  We are also considering allowing the generation of a runtime check that would throw an exception if specific values will be truncated.

This change will probably go into the language in Crack 0.5.  For anyone interested, the formal proposal is at http://code.google.com/p/crack-language/wiki/PlatformDependentNumericTypes

Tuesday, March 8, 2011

AOT for Crack 0.4

One of the trademarks of a scripting language is of course: instant results. You edit your script and execute it immediately, skipping the traditional compile step with its associated build files, etc.

True to scripting language ideals, Crack executes scripts immediately using LLVM's excellent Just In Time (JIT) compiler to handle the heavywork of converting the compiled crack code to native instructions so that it can run both immediately and as fast as possible.


weyrick@mozek:~/crack$ cat hello.crk 
import crack.io cout;
cout `hello JIT\n`;
weyrick@mozek:~/crack$ crack hello.crk 
hello JIT


But what if you find yourself wishing for a native binary, just this once? You know, something like this instead:


weyrick@mozek:~/crack$ crackc hello.crk 
weyrick@mozek:~/crack$ ./hello 
hello JIT


What's a scripter to do? Well with Crack at least, you'll be in luck. Crack 0.4 will include an Ahead Of Time (AOT) mode which will create native binaries just like the example above. All imported crack modules will be included in the binary (something like a static link of the crack modules, although the binary itself is not statically linked). The annotation system (and macros) work for AOT binaries. We also have plans for full DWARF debugging information, which will allow source level debugging with tools like gdb.

Crack 0.4 is tentatively scheduled for release about the same time as LLVM 2.9 (beginning of April).


Friday, January 14, 2011

Crack 0.3 released

We are pleased to announce the release of crack 0.3.  The new version features the following enhancements:

  • Added support for extensions
    • Added a module to that can generate bindings for many C APIs
    • converted the runtime, GTK and PCRE modules to use the extension API
    • added (undocumented) support for SDL and OpenGL
  • Added the annotations subsystem
    • Used annotations to implement macros
    • Added the @static, @final, @FILE and @LINE built-in annotations
  • Added the math module
  • Added macro based generic containers.
  • Added the "for" statement (both C and iterator styles, so "for (x :in collection)" now works)
  • Aggregate type variables now default to null if no initializer is given.
That last item means that if you actually have written some crack code, we've probably broken it.  See the appendix of the manual for info on how to deal :-)

Dig in and send us feedback!

Sunday, December 12, 2010

Annotations

An exciting new feature for 0.3 (currently working in the latest repository code) is "annotations."  Annotations allow you to extend the compiler with the language.  For example if I have module A:

import crack.compiler CrackContext;

void myann(CrackContext ctx) {
    ctx.inject('import crack.io cout; cout `hello world`;'.buffer);
}
And module B:


@import A myann;
@myann


The code injected from myann()would be injected into the token stream of module B.


There are a number of things that you can do from an annotation, you can read tokens, put them back, set parser callbacks, and create other annotations.  You can't yet do introspection (examine classes or functions or other compile-time objects) but that feature will be coming.


We currently use annotations to implement compiler-level macros:


import crack.exp.ann define;

@define attr(type, name) {
    type name;
    type get_$$name() { return name; }
    void set_$$name(type newVal) { name = newVal; }
} 
class Foo {
@attr(String, bar)
}


To use a macro like this from another module, you need to export it:
import crack.exp.ann define, export, exporter;
@exporter  # import all of the stuff we need to define exporter functions

@define attr(type, name) {
    type name;
    type get_$$name() { return name; }
    void set_$$name(type newVal) { name = newVal; }
}

@export attr

From another module we can import the macro as an annotation:

@import attrmod attr;

class Foo {
    @attr(String, bar);
}

Tuesday, November 16, 2010

Extension API

Crack currently has a bona-fide extension API in the trunk.  See http://code.google.com/p/crack-language/wiki/ModuleExtensionAPI for details.

The short story is: you can now define functions, types and methods from a shared library, and then import that library as if it were a normal Crack module.

Wednesday, October 6, 2010

Crack 0.2.1 Released

In the spirit of "release early, release often," We've just released crack 0.2.1.  This release is mostly 0.2 with the changes necessary for LLVM 2.8.  We also fixed the library distribution rule and made a few minor fixes to the documentation.

Sunday, October 3, 2010

Crack 0.2 Released

We're happy to announce the release of Crack 0.2.  The main change in this release was under the covers: we refactored some significant portions of the code in the interest of streamlining future development.  However, we also fixed a lot of bugs and added a lot of basic enhancements that go a long way towards making Crack a usable language.

With 0.2, Crack now provides the complete set of C operators.  We also added our first map implementation (red-black trees), File IO libraries, and libraries for socket programming.

So please dig in and let us know what you think!