This change simplifies certain types while lowering to MuIL, fixing
a number of assertion failures that cropped up after my tightening of
the type checking (namely, asserting rather than silently logging).
This comes in the following forms:
1) StringLiteral, NumberLiteral, and BooleanLiteral types can simply
become String, Number, and Boolean, respectively. This loses some
static type-checking (e.g., ensuring that values are within range),
however at least the physical runtime type will match.
2) EnumLiteral types can simply adopt their base types, with similar
downsides and caveats to the other literal types (range widening).
3) Any union types that contain Undefined or Null types can be simplified
to omit those types. If, after filtering them out, we are left with
a single type (admitting the recursive case of multiple XLiterals that
simplified down to the exact same primitive type), we will eliminate
the union altogether and just use the underlying simple type. This
handles the case of optional interface properties, e.g. `T?`, which are
internally represented in TypeScript as `T|undefined` union types.
There are two follow up work items for later on:
* marapongo/mu#64 tracks support for non-nullability. At the moment we
are tossing away any `|undefined` or `|null` unions, including `T?`,
because MuIL currently permits nullability everywhere, but we want to
consider honoring these annotations (eventually).
* marapongo/mu#82 tracks support for union types. This is how we intended
to support true enum types, so tossing away this information is a bit
unfortunate. But this is a feature for another day.
This change prepares to mimick the TypeScript behavior of `any` which,
it turns out, is closer to Python and Ruby duck typing than what we have
here. It also introduces `object` to mean "the base of all types".
MuJS has been updated to map `any` to `dynamic`.
At this point, the conversion logic now distinguishes between NoConversion,
ImplicitConversion, and AutoCastConversion cases, the latter being a case
in which `dynamic` is the source and so a duck-type-style cast must occur.
Right now we do not honor the AutoCastConversion case anywhere, so we still
have not achieved the duck-typing semantics of the source language.
This is part of fixing marapongo/mu#79.
This change eliminates the AWS package's cloudformation.expandTags
functionality. The primary reason is that we haven't yet implemented
string/array intrinsics (see marapongo/mu#80), and so the presence of
this routine was preventing verification of the AWS library. But, it
turns out, the expandTags routine was violating our "zero artistic
license" principle for the foundational cloud packages. Namely, it
was auto-adding the Name=<name> tag, when the name property was set,
capturing an idiomatic AWS resource pattern. Instead of doing that,
we will just project tags in their raw form, and add such conveniences
(possibly) add a higher level in the layer cake.
After a bit more thinking, we will create new SDK packages for each
of the languages we wish to support writing resource providers in.
This is where the RPC goo will live, so I have created a new sdk/
directory, moved the Protobuf/gRPC definitions underneath sdk/proto/,
and put the generated code into sdk/go/ and sdk/js/.
This change moves the RPC definitions to the pkg/murpc package,
underneath the proto folder. This is to ensure that the resulting
Protobufs get a good package name "murpc" that is unique within the
overall toolchain. It also includes a `generate.sh` script that
can be used to manually regenerate client/server code. Finally,
we are actually checking in the generated files underneath pkg/murpc.
This restructures the examples directory a bit, into three buckets:
* basic/: simplistic examples, like hello world and whatnot.
* conversions/: actual conversions from existing samples (with the source cited).
* scenarios/: more complex examples that demonstrate various features of the system.
This change adds a --dot option to the eval command, which will simply
output the MuGL graph using the DOT language. This allows you to use
tools like Graphviz to inspect the resulting graph, including using the
`dot` command to generate images (like PNGs and whatnot).
For example, the simple MuGL program:
class C extends mu.Resource {...}
class B extends mu.Resource {...}
class A extends mu.Resource {
private b: B;
private c: C;
constructor() {
this.b = new B();
this.c = new C();
}
}
let a = new A();
Results in the following DOT file, from `mu eval --dot`:
strict digraph {
Resource0 [label="A"];
Resource0 -> {Resource1 Resource2}
Resource1 [label="B"];
Resource2 [label="C"];
}
Eventually the auto-generated ResourceN identifiers will go away in
favor of using true object monikers (marapongo/mu#76).
Glog doesn't actually print out the stack traces for all goroutines,
when --logtostderr is enabled, the same way it normally does. This
makes debugging more complex in some cases. So, we'll manually do it.
Turns out we hadn't been transforming interface heritage clauses --
extends and implements -- like we were classes. This change fixes
that. As a result, we're down to 14 verification errors for AWS.
This change renames the old Error type to Exception -- more consistent
with our AST, etc. nodes anyway -- and introduces a new Error type ("<error>")
to use when something during typechecking or binding fails.
The old way led to errors like:
error: MU504: tags.ts:32:18: Symbol 'Tag:push' not found
error: MU522: tags.ts:32:8: Cannot invoke a non-function; 'any' is not a function
This is because of cascading errors during type-checking; the symbol not found
error means we cannot produce the right type for the function invoke that
consumes it. But the 'any' part is weird. Instead, this new change produces:
error: MU504: tags.ts:32:18: Symbol 'Tag:push' not found
error: MU522: tags.ts:32:8: Cannot invoke a non-function; '<error>' is not a function
It's slightly better. And furthermore, it gives us a leg to stand on someday
should we decide to get smarter about detecting cascades and avoiding issuing
the secondary error messages (we can just check for the Error type).
This change also accepts MuPackages during MuJS dependency resolution.
The old logic wasn't quite right, because it would skip over MuPackage
files, and keep probing upwards, possibly binding to the wrong, pre-
compilation, Mufile. Note that we must still probe for both Mupackages
and Mufiles, because of intra-package dependencies.
After this change, the AWS MuPackage is down to 28 verification errors.
This change fixes a few more phasing issues in the compiler. Namely,
it now splits all passes into three distinct phases:
1. Declarations: simply populating names.
2. Definitions: chasing down any references to other names from those
declared entities. For instance, base classes, other modules, etc.
3. Bodies: fully type-checking everything else, which will depend
upon both declarations and definitions being fully present.
This changes a few things with dependency probing:
1) Probe for Mupack files, not Mufiles.
2) Substitute defaults in the PackageURL before probing.
3) Trace the full search paths when an import fails to resolve.
This will help diagnose dependency resolution issues.
This adds support for sugared "*" semvers, which is a shortcut for
">=0.0.0" (in other words, any version matches). This is a minor part
of marapongo/mu#18. I'm just doing it now as a convenience for dev
scenarios, as we start to do real packages and dependencies between them.
This change:
* Renames the Mu standard library NPM package from "mu" to "@mu/mu".
This reflects the convention that MuPackages published on NPM will,
at least initially, be published under the "@mu" scope. This
indicates that a package is intended to be consumed by Mu programs.
* Adds an NPM peerDependency on the Mu package from the AWS package.
* Adds a Mu dependency on the Mu package from the AWS MuPackage.
To prepare a MuPackage for consumption by another MuPackage, we need
to save the definition files output by the TypeScript compiler. In
theory, we could regenerate these definitions from the MuPackage itself;
in fact, when we tackle cross-language, this will be necessary. For
now, however, it's much simpler to simply save the definition outputs
alongside the Mupack.json package metadata.
Note that we do not save "code" outputs. The MuIL format encodes all
computations and so the only thing we need for dependent packages is
the header files to compile against; all "link" and runtime dependencies
are satisfied by the MuIL format and Mu toolchain.
There are a variety of ways to specify a MuJS output location, including
the command line --out (-o for short) flag. If that isn't present, we simply
dump the Mupack.json file in the current working directory. However, in order
to prepare Mupackage files for packaging, distribution, and reuse, we actually
want to store the Mupack.json file alongside all the other package assets (like
TypeScript header files). So it makes sense to recognize the outDir, if present,
in the tsconfig.json file, and use it as the default if not otherwise specified.
This change permits object literals with the type of `any`; although
they will typically get coerced to record/interface types prior to use,
there is no reason to ban `any`. In fact, it is the idiomatic way of
encoding "objects as bags of properties" that is common to dynamic
languages like JavaScript, Python, etc.
This changes transformIdentifierExpression to call through to the
general purpose function that understands how to transform any Symbol
reference into a fully qualified token suitable for serialization.
This change recognizes identifier expressions that mention the null and
undefined constants, transforming them into null literals. Note that
marapongo/mu#70 tracks mimicking the subtle differences between
undefined and null in ECMAScript.
This change adds support for loading from export locations. All we
need to do is keep chasing the referent pointer until we bottom out
on an actual method, property, etc.
In the event that we're loading a property straight from a module,
we needn't actually load the module at all; instead, we refer to it
using a fully qualified token.
In many cases, property access expressions interact with types. For
example, the expression `o.j.bar()` likely accesses variable `o`, then
its property `j`, then `j`'s method `bar`. In other cases, however,
property access elements are actually modules. For example, in:
import * as o from "o";
o.j.bar();
This change recognizes this situation and emits code correctly.
This change fixes a whole host of issues with our current token binding
logic. There are two primary aspects of this change:
First, the prior token syntax was ambiguous, due to our choice of
delimiter characters. For instance, "/" could be used both as a module
member delimiter, in addition to being a valid character for sub-modules.
The result is that we could not look at a token and know for certain
which kind it is. There was also some annoyance with "." being the
delimiter for class members in addition to being the leading character
for special names like ".this", ".super", and ".ctor". Now, we just use
":" as the delimiter character for everything. The result is unambiguous.
Second, the simplistic token table lookup really doesn't work. This is
for three reasons: 1) decorated types like arrays, maps, pointers, and
functions shouldn't need token lookup in the classical sense; 2) largely
because of decorated naming, the mapping of token pieces to symbolic
information isn't straightforward and requires parsing; 3) default modules
need to be expanded and the old method only worked for simple cases and,
in particular, would not work when combined with decorated names.