This includes break, continue, and while, which are trivial. It also
includes do/while, which requires a simple rewrite during the lowering,
since MuIL only supports the one simple kind of loop.
This adds a few missing statement AST kinds:
* ReturnStatement to `return` from a function with an optional expression.
* ThrowStatement to `throw` an exception object.
* EmptyStatement to represent, well, a statement without anything to it.
This change refactors a few things:
* Eliminate the `mujs.il` package; instead, introduce `mujs.ast`.
* Split `mujs.ast` into many different files, to organize things a
little more cleanly into expressions, nodes, sources, and statements.
* Move most definitions from the `mujs.pack` package into `mujs.ast`,
into the definitions file. This ensures that all definitions are true
AST nodes, meaning they will preserve source location information.
This change adds a simple compiler module that hosts TypeScript and
compiles a program. The compile function takes a path and optional options
data structure; the path can be one of three things: 1) a path to a single `*.ts`
file, in which case it, and it alone, is compiled; 2) a path to a `tsconfig.json`
file, in which case it is loaded, parsed, and used to drive compilation just
like the `tsc` command line driver; or 3) a path to a directory containing a
`tsconfig.json` file, in which case it is discovered and used just like #2.
There is also a new command line driver that just blindly passes arguments to
this compiler API, and prints the results. Next up, this will begin lowering and
transforming the resulting TypeScript AST to MuPack and MuIL.
Note that I've reorganized the source code slightly, so that instead of just
`src/*`, we now have `lib/*` for the library, `cmd/*` for any CLI drivers, and,
soon, `test/*` for test cases.
This is an initial implementation of the MuIL AST.
The AST has been intentionally pared back to the bare essentials, leaving the
task of lowering to the higher level MetaMu compilers. For example, there is
only a single conditional statement, a single looping construct, etc.
It is in the MuJS compiler package so that we can begin lowering TypeScript
ASTs to this format and serializing them. We will need separate Go projections
of these shapes in order to deserialize and evaluate programs to produce MuGL
from within the runtime/evaluation engine.
I'm sure the shape of these things will need to change -- and there are a
handful of open questions -- however, this is a start...
This change adopts some standard aspects of classes, like statics, abstract,
and virtuals (sealing). It also recognizes records and interfaces as explicit
annotations on classes (both of which can be called "conjurable"); they were
already being treated specially with respect to structural/duck conversions,
and I always hate inferring defining properties of types like this, since simple
edits can completely change the type's semantics and break consumers of it.
This change articulates Mu's packaging format, MuPack, along with its
corresponding intermediate language and type system, MuIL. This is very
much a work in progress.
This documents our latest thinking on Mu languages. At a high level,
there are three classes of language at play:
1. Mu Metadata Languages (MuML): these are the high-level language
subsets that a programmer uses to specify Mu modules, etc. Examples
include MuJS, MuPy, MuRu, and MyGo, each representing a deterministic
subset of JavaScript, Python, Ruby, and Go, respectively.
2. Mu Intermediate Language (MuIL): this is the intermediate form that
all of the above compile down to. It is capable of representing
computations like functions, conditionals, and basic expressions like
string concatenation, etc. This is fully statically analyzable and
can be used to create deterministic plans and topology graphs.
3. Mu Graph Language (MuGL): this is the "final" form in which any Mu
service topology is represented. It never contains computations and
is merely a metadata description of services-as-nodes, dependencies-as-
edges, and all known properties. In the planning form, it may contain
"holes" because output properties aren't known until execution has
occurred, while in the actual applied form, those holes have been
plugged. MuGLs can be diffed, and a MuGL can be generated from an
existing live environment (for bootstrapping and/or drift analysis).
There are several TODOs in here, but this is braindump of where we're at.
This splits the overall example rack service into many sub-services.
This leads to a much cleaner factoring of the code. Note that there are
some missing properties -- it's hard to eyeball this without a real compiler.
But the essence of the example is pretty spot on.
I personally prefer this syntax. It is more "declarative" and, particularly
because we support true assignments, I feel like it's more representative of
what's going on. It's easier to scan for real imperative assignments. This
approach also eliminates a single horizontal character per assignment.
In this change, ":= new" declares a new resource.
In addition, we change arrays to be Go-like, in that zero-initialized
ones can be appended to safely, such that declaring a new instance is
done merely by stating "var arr: T[]".