git @ Cat's Eye Technologies Castile / master TODO.md
master

Tree @master (Download .tar.gz)

TODO.md @masterview rendered · raw · history · blame

TODO
----

### Implementation ###

Line number reporting for Context errors and Syntax errors.

Name mangling for compilers (prepend with `_` most likely.)

And literal characters in strings, especially `'` and `"`.

Figure out a way to do `input`, `read`, and `write` with node.js backend.

Implement `int`, `chr`, `ord` for Ruby, JavaScript, stackmac, C.

Better indentation in the JavaScript backend.

TaggedValue -> just a tuple.

stackmac: store tagged values as two values on the stack.
and void types in unions of (void, X) should only be one value.
(structs are still boxed though)

Implement garbage collection of some sort in the C backend.  Either that
or implement some kind of resource-awareness in the language itself.

Other backends (Python? Java? CIL? Scheme?)

Test framework: collect the backend-independent tests into a single
file, and only test it once.  Run all the *other* tests on every
backend.

### Design ###

Don't output final value.  Command-line arguments passed to `main`.  (`sysmain`?)

Automatic type promotion (upcasting), e.g. using an integer where
integer|string is expected (as e.g. a function argument) is fine,
an `as integer|string` should be automatically inserted.

Lua-esque `:` operator: `a:b(c)` -> `a.b(a, c)`

Type promotion with higher precedence?  So that it can be used at toplevel.

Should there be closures as well as function values?

Should there be an expression form of `if`?

Should there be an expression form of `=`?  (`:=`?)

Block scope in blocks; if the first assignment to a variable occurs inside
a block, its scope is that block.  It cannot be seen after that block closes.
(Shadowing is not possible; if the first assignment was before, that outer
variable is what gets updated.)

### Object Orientation ###

Castile will likely not have any "real" object-oriented features.  Probably,
only nominal subtyping, with a Lua-like method call operator.  Meaning
something like this:

    struct counter {
      value: integer;
      bump: counter, integer -> counter
    }
    
    struct named_counter < counter {
      name: string;
    }

    forward bump: counter, integer -> counter

    bump = fun(c: counter, v: integer) {
      return make counter(value=c.value + v, bump=bump;
    }

    new_counter = fun(init) {
      return make counter(value=init, bump=bump);
    }

    new_named_counter = fun(init) {
      g = make named_counter(value=init, bump=bump, name="Jeremy");
      return g:bump(5);
    }

`bump` can operate on a `named_counter` type, but only because it
is declared to be a subtype of `counter`, so its fields are known to be
a superset of `counter`'s fields.  The `bump` method itself is not defined
within either structure, and the `:` syntax is used to pass the object as
the first argument.

### Memory Management ###

Some notes on memory management.

Some properties of Castile have some potential for simplifying memory
management.  For example, structures are immutable.  I do not expect to
be able to get away with avoiding garbage collection completely, but I
suspect there to be ways to tie reference counting to execution in a
"nice" fashion.  Although this will likely still be difficult.

I'd like Castile's memory allocation system to be based on the following
idea:

Every time you call a function of type `A -> B`, you have created an
object of type `B`.

If, in some function, your argument `a` to the call of the function of type
`A -> B` was your last use of `a`, you have destroyed an object of type `a`.
(Unless objects of type `B` contain objects of type `A`.)

If you call a function of type `A -> A`, you haven't created or destroyed
any objects.  (Even if you discard the argument and allocate a new `A` to
be returned, you can just "allocate" the new `A` over the old one.)

I don't know enough about linear types to know how well that can be
expressed with them.

Suppose you are `f`, a function `A -> B`, and `B` contains `A`.  You
actually ignore the `A` given to you, and call another function `g` to
allocate a new `B` containing the new `A`.

It would seem that the caller of `f` must pass "this is the last use of
the argument" to you, and that you must pass "it's ok to overwrite
my `A` with your new `A`" to `g`.  This is complicated enough communication
that it would probably be best modelled as a global dictionary of some sort,
rather than each function telling each other function what's up.  For
example, at the point of the last use of the `A` just before calling `f`, the
address of that `A` could be placed on a list of "newly available `A`s."