how directly can we translate

how directly can we translate
Photo by Ling App / Unsplash

Initially I intended to have multiple backends but the path of least resistance is to use C as the backend for now and to come back to the idea of multiple backends later.

Given that - just what do I need to do to kick start things; how much effort is required? Let's start with some simple code:

FibonacciState :: {a, b: Integer = 1}.
[self next] [:FibonacciState -> return: Integer |
  next := a.
  a := b.
  b := next + a.
  return next].

generator: FibonacciState.
{Range | start: 1, stop: 100} -> generator -> stdout.

Let's see if we can translate it line by line:

struct FibonacciState {a: int, b: int};
int FibonacciState_next_int(struct FibonacciState* self);
void Program_start();

int FibonacciState_next_int(struct FibonacciState* self) {
  int next = self->a;
  self->a = self->b;
  self->b = next + a;
  return next;
}

void Program_start(void) {
  struct FibonacciState generator;
  generator.a = 1;
  generator.b = 1;
  
  struct Range tmp1;
  tmp1.start = 1;
  tmp1.stop = 100;

  int tmp2 = tmp1.start;
  while(tmp2 <= tmp1.stop) {
    int tmp3 = FibonacciState_next_int(&generator);
    write_IO_int(Console_stdout, tmp3);
    tmp2++;
  }
}

Hm. Not too bad. Let's stick with a fairly strict C subset to keep things simple. Methods are translated to ArgType1_MethodName_ArgType2_ArgType3_..._ReturnType which makes them easy to call when embeddeding STZ in to a C program.

I took some liberties here and kept things simplified by mapping Integer to a straight C int. That's probably not how it'll work in the end but it's a close enough facsimile to get started.

I also inlined all the blocks as described in a previous post to simplify execution. Writing blocks you pass around in C is a bit painful after all. The → methods in STZ are a bit more complex than this straight translation too but not so much that it wouldn't look roughly like what we ended up with.

All the types in the example are 1:1 mappings to type definitions; no generics means no extra compilation to resolve the types ahead of time. Something still needs to call Program_start(); to make this program work.

I'm also not sure we can use while() like that and have it map correctly to STZ. Really we should use a goto label to simulate a tail-call:

int tmp2 = tmp1.start;
loop:
  int tmp3 = FibonnaciState_next_int(&generator);
  write_IO_int(stdout, tmp3);
  if (tmp2 <= tmp1.stop) {
    tmp2++;
    goto loop;
  }

Next time I will try and dig in to the → or next> methods to see how we build up our i/o framework and how it translates in to inlined blocks that ultimately end up with simple C code. Hopefully simple C code.