What's the recommended way to implement the part-1?

Starting implementing the compiler(part 1).

What’s the recommended way to implement it?
Follow the part 1 of the book and write those parsers and implementations out?

Though i did peek into provided implementation of the compiler.ts I feel like copying it would be a poor way to really learn the details…

Some notes:

  1. building Docker-ized environment since I rather not install all the tooling on the host machine and i’m on OSX.
  2. 5.3 Interface: missing argument name: src: Source in
interface Parser<T> {
    parse(Source): ParseResult<T> | null;
} 

maybe combinator from the book Ch 5.13 doesn’t compile:

static maybe<U>( parser: Parser<U>): Parser<U | null> {
   return parser.or(constant(null));
}

Has to be

static maybe<U>( parser: Parser<U | null>): Parser<U | null> {
   return parser.or(constant(null));
}

One example where generics makes things quite confusing… :confused:

More compilation errors:

src/compiler.ts:240:14 - error TS7006: Parameter 'pattern' implicitly has an 'any' type.

240 let token = (pattern) => regexp(pattern).bind((value) => ignored.and(constant(value)));
                 ~~~~~~~

fixed with

let token = (pattern:RegExp) => regexp(pattern).bind((value) => ignored.and(constant(value)));

Thanks for posting these! I made the mistake of using the TypeScript compiler with the default settings when no tsconfig.json file is found. These settings are extremely relaxed, and the warnings above do not show up. I have fixed the issues in the repo but not in the book yet (I will be working on that). So, if in doubt, see the code in the repo:

Fortunately, there are not many places with these kinds of problems.

I had some trouble setting the toolchain on OSX, so using Docker is preferred if you can. On OSX, neither brew nor MacPorts has the cross-compilation tools packaged, so the only way seems to be built from source. So using some Linux environment like Docker or WSL on Windows is much easier.

The new ARM-based macs should run the 32-bit ARM binaries natively, but assembling them might be tricky. So far, I haven’t got my hands on one of these systems.

Looking forward to seeing how your compiler unfolds!

1 Like

You likely know about these, still i’m posting here for the record

  1. let ASSIGN = token(/=/y).map((_) => Assign); is missing in the book
  2. Number is a javascript type so there’s collision a with Number:AST
  3. equals(other: AST) miss returned type equals(other: AST):boolean
  4. interface AST { equals(AST): boolean;} missing the arg name

Finished parser.
Overall impression: combinators are quite hard to “combine” due to nested/callback-like structure and it takes time to really have the intuition on when to use which(like bind vs map) etc…

I’m curious to compare it to Ocaml version to see if it language makes a difference.

1 Like

@gmarik appreciated!

Yes, combinators are a bit special. It kind of takes time to get used to this way of writing code. In many ways, it is similar to writing async code, which also takes time to get into.

I tried several approaches with parsing. Recursive descent parser could be simpler, but requires a lot of code (by volume), so really hard to fit in a small book. And my goal was to include all the necessary code for part-1.

The OCaml version that I put in the repo does not have a parser, but I have similar parser combinators in OCaml in another project:

1 Like

@gmarik do you have a link to your repo? It would be enlightening to have a look.

Regarding Number. You are allowed to use names like Number in a JavaScript module (which is in its own scope), but not in a global context. A source file is considered to be a module if it has import or export statements in it. That’s why the part-1 compiler has a dummy export {} at the end:

Without it, the source would be considered non-module (and thus, global scope), and Number would clash with the built-in Number class. However, in a module context, it is allowed.

I’m making a note to myself to mention this one way or another in the book. I was hoping that it is now so common to use JS modules that this would never show up, but it’s not the case.

However, in a module context, it is allowed.

Fair enough, though, if one follows the book, there’s a token NUMBER which gets renamed to INTEGER in your compiler.ts. So I renamed corresponding number parser/token to Integer as well which also fixes the type “collision”.

My repo https://github.com/gmarik/c2afs

1 Like

Once i’m done with Typescript version I’d like to implement it in Go and Ocaml…

2 Likes

Second, computer memory has several levels of caches, usually re- ferred to as L1–L3. And even if the fastest cache uses the same technology as registers, there could still be a cache miss. But such a cache miss can never happen in the case of registers.

I’m not sure about the point being made in this paragraph… I’m assuming that it’s performance, but it’s quite indirect that it makes me wonder if it’s something else that I may not be getting…

Would be nice to have it stated explicitly, ie like this:

Second, performance: computer memory has several levels of caches, …

1 Like