GAMS
Find a file
2024-02-04 20:22:31 -08:00
.cargo [oden] Upgrade cargo 2023-09-11 20:40:28 -07:00
fine [fine] Clean up warnings 2024-02-04 20:22:31 -08:00
game [oden][game] Multiple screens, logging, pre/post, bluescreen 2023-09-11 20:41:11 -07:00
oden-js [quickjs][oden][oden-js] Source maps 2023-09-21 01:51:55 -05:00
oden-js-sys [quickjs][oden][oden-js] Source maps 2023-09-21 01:51:55 -05:00
src [quickjs][oden][oden-js] Source maps 2023-09-21 01:51:55 -05:00
types [oden] Move scaling entirely into JavaScript 2023-09-02 09:58:58 -07:00
.gitignore [fine] The name is official 2024-01-02 16:50:21 -08:00
Cargo.lock [fine] Assignments! 2024-01-19 19:08:17 -08:00
Cargo.toml [fine] Fix project 2024-01-03 06:18:20 -08:00
README.md [oden] Explain myself. 2023-09-16 22:56:27 -07:00
tsconfig.json [oden] Time, path searching, game directory 2023-06-30 16:24:54 -07:00

ODEN: A small TypeScript coding environment

This is just a silly little project which connects a JS interpreter with WebGL and init/update/draw loop in Rust. You can think about this maybe like Pico-8 except not constrained, or LÖVE but with JS instead of lua.

Why?

  1. I want types and type hints and type checking for my little scripts, they're useful. Lua doesn't have an equivalent of what TypeScript has, except maybe LUAU but that's incompatible with LÖVE, so what would the point be?

  2. I want WebGL because I would like to be able to (easily) run things in multiple places. I like that you can play Pico-8 games on the web. Seems like it would be nice to make that work too.

Why not?

  1. No operator overloading in TypeScript. I'm going to get irritated by this quickly I think. QuickJS actually supports operator overloading but TypeScript doesn't and patching operator overloading into the type-checker looks very difficult.

Details

JavaScript

I'm using QuickJS, lightly patched, with my own bindings.

QuickJS

The easy alternative to QuickJS is to use Deno Core, which binds V8. Unfortunately, Deno Core has kind of a wild architecture, where it goes through flatbuf for all its communications, and keeps the JavaScript engine at arm's length. It doesn't expose things like "make a new native class." And while it feels "possible" to use (see https://deno.com/blog/roll-your-own-javascript-runtime-pt2), it was not quite as easy to use for what I wanted to do as you might like.

QuickJS may be more or less abandoned by Fabrice Bellard, but also it does what I need it to.

(But it makes me sad: there are millions of dollars and uncountable engineering-hours in V8. V8 is one of the best dynamic language runtimes ever constructed by man. It would be really nice to be able to take advantage of it.)

Lightly Patched

See above about being abandoned: we have small patches in place to e.g. expose information about modules and whatnot.

My Own Bindings

The other bindings are maddeningly incomplete, and don't expose features I want to expose out of QuickJS.

TypeScript

I'm using deno_ast to parse typescript at load time and "transpile" to javascript before loading. deno_ast is built on swc, which I really really don't like very much, but my other options are:

  1. Actually use the typescript compiler. This is a neat idea but involves loading the compiler into QuickJS (which requires some patches to tsc since qjs is not node). Also the typescript compiler is SLOW (especially when run in QJS) and finicky. But this would be a full-fidelity option for sure.

  2. Use tree-sitter to parse the typescript and strip the types out myself. The tree-sitter parser is fast and gives me everything with full fidelity. This also has the nice side-effect of letting me edit the source file in place, so source maps aren't a problem. The downside is that finding every part of TS that has to be cut out to make JS is time consuming and error prone and I don't want to do it, particularly.

  3. Parse Typescript myself. I can write a parser, sure, doesn't bother me, but the syntax for typescript is not formally documented anywhere. And it has the same problem as #2 when it comes time to emit JS.

So for now, I stick with deno_ast. It does what I need it to do.