Plinth Isn’t Dead!

Update 2018-02-26: This was written several years ago, and Plinth development is essentially dead now. (In retrospect, although it was true at the time, this was a bad title and it’s been annoying me ever since.)


I thought I’d write a status update, since it’s been almost four months since I last posted anything here.

Firstly, Plinth isn’t dead! I have been working on it, albeit slightly more slowly than I’d have liked, for the last four months. At first, that was because Generics took me a while to get right, as it required major structural changes throughout the compiler. More recently, I’ve just been away on a couple of holidays. For the last few days, though, development has sped up significantly.

Now, since this is a status update, here’s a list of all of the major things I’ve added since my last post:

Generics [commit]

This took much longer than it should have – partly because it changed so much of the compiler’s data structure, but also because code generation for it was much more difficult than I expected (see last post).

A Test Framework [commits: 1 2 3 4]

There is now a suite of tests that covers everything up to name resolution. I still need to write tests for: cycle checking, type checking, control flow checking, and exception checking.

If you want to run all of the tests, just run `ant run-tests` from the project root directory.

A new array-initialisation syntax [commit]

Before this change, it was impossible to create an array of anything that didn’t have a default value, unless you knew its size in advance. Here’s an example:

[]string foo = new [n]string;

^ This is impossible – you can’t create an array of not-null strings unless you know all of their values beforehand. Here’s what you could do before:

[]string foo = new []string {"foo", "bar"}; // only has two elements, not n
[]?string bar = new [n]?string;             // all elements are nullable

But now, with this new syntax, you can create them much more easily:

[]string foo = new [n]string("hello"); // initialises all of the strings to "hello"

Or, if you define a function somewhere first:

string makeString(uint index)
{
  return "string number " + index;
}
[]string foo = new [n]string(makeString);

This will become much easier to use once closures are added, which will hopefully be soon.

Wildcard types [commits: 1 2]

Wildcards are useful if you want to store an object but don’t care about its generic type arguments. For example, a List<?> can only store one type inside it, but we don’t care what that type is. We can’t store new elements in it, but we can retrieve them as objects.
Adding wildcards required a lot of changes to the way run-time type information works, mainly because of cases like this one:

ArrayList<?> list = new ArrayList<Bar>();
List<? extends Foo> list2 = cast<List<? extends Foo>> list;

To do this sort of check, we need to go through all of the super-types of the value being tested (list) for the target type (List), and then make sure the constraints on the wildcard type argument encompass the actual type argument we get from the object’s run-time type information. This gets especially complicated when you cast to a type like List<? extends Foo super Bar>, or when you have multiple type arguments, so the runtime’s code for checking this is a bit long (although that’s partly because it’s written in LLVM bitcode).

A copyright notice [commit]

This should have been added a while ago, but the code is now officially under the three-clause BSD license, so you can use it for almost anything you want.

Array bounds checking [commit]

Now, if you index past the end of an array, you won’t get a segmentation fault any more – an IndexError will be thrown instead. Also, IndexError has been added to the standard library.

Various standard-library improvements [commits: 1 2 3 4 5 6]

Some new methods have been added to string, such as trim() which trims all unicode whitespace characters.
string now has a read-only property called “length” instead of a method, which will be the standard way of exposing the length of something in the standard library (no size() methods on Lists!).
Also, the stdin::readLine() method can now throw EOFException.

ArrayList<T> and LinkedList<T> implementations [commits: 1 2 3]

These both implement a new List<T> interface, which extends Iterable<T>.
ArrayList<T> is actually implemented as a ring buffer in an array. I’m not sure why this isn’t more standard in ArrayList implementations, but it makes insertions to and deletions from the start of the list O(1) with hardly any overhead.

For-each loops [commit]

These loops are extremely flexible: they don’t just iterate through arrays and Iterables, they also work on integers (signed and unsigned) and Iterators. This means you can do things like this:

for uint x in 5
{
  stdout::print(" " + x); // prints: 0 1 2 3 4
}
for short y in value
{
  stdout::print(" " + y);
  // if value == -5, prints: 0 -1 -2 -3 -4
  // if value == 0, prints nothing
  // if value == 3, prints: 0 1 2
}

And this:

Iterator<Foo> iter = list.iterator();
if iter.hasNext()
{
  iter.next(); // skip the first element
}
for Foo foo in iter
{
  stdout::println(foo); // starts at the second element of the list
}

Auto-assign parameters [commit]

These are something I’ve wanted Java to have for a while, but they’re purely syntactic sugar. Basically, they let you define a constructor/method(/property setter) parameter which, instead of creating a local variable, just assigns to a field. It’s easier to show it than to describe it. Instead of writing this:

class Item
{
  string name;
  string description;
  uint price;
  create(string name, string description, uint price)
  {
    this.name = name;
    this.description = description;
    this.price = price;
  }
  // ... getters and setters ...
}

You can write this (also, you can use properties instead of writing getters and setters!):

class Item
{
  property string name;
  property string description;
  property uint price;
  create(@name, @description, @price);
}

The auto-assign parameters get assigned just before the body of whatever you’re calling.
With this change, when you’re using an auto-assign parameter a body is added automatically, so you can put a semicolon instead of an empty body.
You can also do some cool things like:

void setHalfPrice(@price)
{
  price /= 2;
}

This works because the parameter doesn’t create a local variable, so when you say “price” you’re actually referencing the property.

 

That’s as far as I’ve got right now. Hopefully I’ll add default arguments and varargs soon. However, I do need to do some preparation for job interviews, so I’m not sure when my next post will be.

One thought on “Plinth Isn’t Dead!

  1. Adam Chainz

    Tests! Brilliant. Nice format for them too. Auto create initializer is a good idea too – saw something similar done in Python with metaclasses the other week.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.