Commit graph

532 commits

Author SHA1 Message Date
joeduffy
d3ce3cd9c6 Implement a coco husk ls command
This command is handy for development, so I whipped up a quick implementation.
All it does is print all known husks with their associated deployment time
and resource count (if any, or "n/a" for initialized husks with no deployments).
2017-02-26 13:06:33 -08:00
joeduffy
ace693290f Fix the directionality of delete edges 2017-02-26 12:05:49 -08:00
joeduffy
44783cffb7 Don't overwrite unmarshaled deployment info 2017-02-26 12:00:00 -08:00
joeduffy
ff3f2232db Only use args if non-nil 2017-02-26 11:53:02 -08:00
joeduffy
1bdd24395c Recognize TextUnmarshaler and use it
This change recognizes TextUnmarshaler during object mapping, and
will defer to it when we have a string but are assigning to a
non-string target that implements the interface.
2017-02-26 11:51:38 -08:00
joeduffy
2f60a414c7 Reorganize deployment commands
As part of pulumi/coconut#94 -- adding targeting capabilities -- I've
decided to (yet again) reorganize the deployment commands a bit.  This
makes targets ("husks") more of a first class thing.

Namely, you must first initialize a husk before using it:

    $ coco husk init staging
    Coconut husk 'staging' initialized; ready for deployments

Eventually, this is when you will be given a choice to configure it.
Afterwards, you can perform deployments.  The first one is like a create,
but subsequent ones just figure out the right thing to do and do it:

    $ ... make some changes ...
    $ coco husk deploy staging
    ... standard deployment progress spew ...

Finally, should you want to teardown an entire environment:

    $ coco husk destroy staging
    ... standard deletion progress spew for all resources ...
    Coconut husk 'staging' has been destroyed!
2017-02-26 11:20:14 -08:00
joeduffy
2fec5f74d5 Make DeepEquals nullary logic match Diff 2017-02-25 18:24:12 -08:00
joeduffy
977b16b2cc Add basic targeting capability
This change partially implements pulumi/coconut#94, by adding the
ability to name targets during creation and reuse those names during
deletion and update.  This simplifies the management of deployment
records, checkpoints, and snapshots.

I've opted to call these things "husks" (perhaps going overboard with
joy after our recent renaming).  The basic idea is that for any
executable Nut that will be deployed, you have a nutpack/ directory
whose layout looks roughly as follows:

    nutpack/
        bin/
            Nutpack.json
            ... any other compiled artifacts ...
        husks/
            ... one snapshot per husk ...

For example, if we had a stage and prod husk, we would have:

    nutpack/
        bin/...
        husks/
            prod.json
            stage.json

In the prod.json and stage.json files, we'd have the most recent
deployment record for that environment.  These would presumably get
checked in and versioned along with the overall Nut, so that we
can use Git history for rollbacks, etc.

The create, update, and delete commands look in the right place for
these files automatically, so you don't need to manually supply them.
2017-02-25 09:24:52 -08:00
joeduffy
fbb56ab5df Coconut! 2017-02-25 07:25:33 -08:00
joeduffy
e0440ad312 Print step op labels 2017-02-24 17:44:54 -08:00
joeduffy
b43c374905 Fix a few more things about updates
* Eliminate some superfluous "\n"s.

* Remove the redundant properties stored on AWS resources.

* Compute array diff lengths properly (+1).

* Display object property changes from null to non-null as
  adds; and from non-null to null as deletes.

* Fix a boolean expression from ||s to &&s.  (Bone-headed).
2017-02-24 17:02:02 -08:00
joeduffy
53cf9f8b60 Tidy up a few things
* Print a pretty message if the plan has nothing to do:

        "info: nothing to do -- resources are up to date"

* Add an extra validation step after reading in a snapshot,
  so that we detect more errors sooner.  For example, I've
  fed in the wrong file several times, and it just chugs
  along as though it were actually a snapshot.

* Skip printing nulls in most plan outputs.  These just
  clutter up the output.
2017-02-24 16:44:46 -08:00
joeduffy
877fa131eb Detect duplicate object names
This change detects duplicate object names (monikers) and issues a nice
error message with source context include.  For example:

    index.ts(260,22): error MU2006: Duplicate objects with the same name:
        prod::ec2instance:index::aws:ec2/securityGroup:SecurityGroup::group

The prior code asserted and failed abruptly, whereas this actually points
us to the offending line of code:

    let group1 = new aws.ec2.SecurityGroup("group", { ... });
    let group2 = new aws.ec2.SecurityGroup("group", { ... });
                 ^^^^^^^^^^^^^^^^^^^^^^^^^
2017-02-24 16:03:06 -08:00
joeduffy
14e3f19437 Implement name property in AWS provider/library 2017-02-24 15:41:56 -08:00
joeduffy
c120f62964 Redo object monikers
This change overhauls the way we do object monikers.  The old mechanism,
generating monikers using graph paths, was far too brittle and prone to
collisions.  The new approach mixes some amount of "automatic scoping"
plus some "explicit naming."  Although there is some explicitness, this
is arguably a good thing, as the monikers will be relatable back to the
source more readily by developers inspecting the graph and resource state.

Each moniker has four parts:

    <Namespace>::<AllocModule>::<Type>::<Name>

wherein each element is the following:

    <Namespace>     The namespace being deployed into
    <AllocModule>   The module in which the object was allocated
    <Type>          The type of the resource
    <Name>          The assigned name of the resource

The <Namespace> is essentially the deployment target -- so "prod",
"stage", etc -- although it is more general purpose to allow for future
namespacing within a target (e.g., "prod/customer1", etc); for now
this is rudimentary, however, see marapongo/mu#94.

The <AllocModule> is the token for the code that contained the 'new'
that led to this object being created.  In the future, we may wish to
extend this to also track the module under evaluation.  (This is a nice
aspect of monikers; they can become arbitrarily complex, so long as
they are precise, and not prone to false positives/negatives.)

The <Name> warrants more discussion.  The resource provider is consulted
via a new gRPC method, Name, that fetches the name.  How the provider
does this is entirely up to it.  For some resource types, the resource
may have properties that developers must set (e.g., `new Bucket("foo")`);
for other providers, perhaps the resource intrinsically has a property
that explicitly and uniquely qualifies the object (e.g., AWS SecurityGroups,
via `new SecurityGroup({groupName: "my-sg"}`); and finally, it's conceivable
that a provider might auto-generate the name (e.g., such as an AWS Lambda
whose name could simply be a hash of the source code contents).

This should overall produce better results with respect to moniker
collisions, ability to match resources, and the usability of the system.
2017-02-24 14:50:02 -08:00
joeduffy
9dc75da159 Diff and colorize update outputs
This change implements detailed object diffing for puposes of displaying
(and colorizing) updated properties during an update deployment.
2017-02-23 19:03:22 -08:00
joeduffy
c4d1f60a7e Eliminate a superfluous map allocation 2017-02-23 15:05:30 -08:00
joeduffy
86bfe5961d Implement updates
This change is a first whack at implementing updates.

Creation and deletion plans are pretty straightforward; we just take
a single graph, topologically sort it, and perform the operations in
the right order.  For creation, this is in dependency order (things
that are depended upon must be created before dependents); for deletion,
this is in reverse-dependency order (things that depend on others must
be deleted before dependencies).  These are just special cases of the more
general idea of performing DAG operations in dependency order.

Updates must work in terms of this more general notion.  For example:

* It is an error to delete a resource while another refers to it; thus,
  resources are deleted after deleting dependents, or after updating
  dependent properties that reference the resource to new values.

* It is an error to depend on a create a resource before it is created;
  thus, resources must be created before dependents are created, and/or
  before updates to existing resource properties that would cause them
  to refer to the new resource.

Of course, all of this is tangled up in a graph of dependencies.  As a
result, we must create a DAG of the dependencies between creates, updates,
and deletes, and then topologically sort this DAG, in order to determine
the proper order of update operations.

To do this, we slightly generalize the existing graph infrastructure,
while also specializing two kinds of graphs; the existing one becomes a
heapstate.ObjectGraph, while this new one is resource.planGraph (internal).
2017-02-23 14:56:23 -08:00
joeduffy
43432babc5 Wait for SecurityGroups to become active 2017-02-23 10:18:37 -08:00
joeduffy
d9e6d8a207 Add a simple retry.Until package/function
This change introduces a simple retry package which will be used in
resource providers in order to perform waits, backoffs, etc.  It's
pretty basic right now but leverages the fancy new Golang context
support for deadlines and timeouts.  Soon we will layer AWS-specific
acceptors, etc. atop this more general purpose thing.
2017-02-22 19:16:46 -08:00
joeduffy
f00b146481 Echo resource provider outputs
This change introduces a new informational message category to the
overall diagnostics infrastructure, and then wires up the resource
provider plugins stdout/stderr streams to it.  In particular, a
write to stdout implies an informational message, whereas a write to
stderr implies an error.  This is just a very simple and convenient
way for plugins to provide progress reporting; eventually we may
need something more complex, due to parallel evaluation of resource
graphs, however I hope we don't have to deviate too much from this.
2017-02-22 18:53:36 -08:00
joeduffy
8610a70ca4 Implement stable resource ordering
This change adds custom serialization and deserialization to preserve
the ordering of resources in snapshot files.  Unfortunately, because
Go maps are unordered, the results are scrambled (actually, it turns out,
Go will sort by the key).  An alternative would be to resort the results
after reading in the file, or storing as an array, but this would be a
change to the MuGL file format and is less clear than simply keying each
resource by its moniker as we are doing today.
2017-02-22 17:33:08 -08:00
joeduffy
ae99e957f9 Fix a few messages and assertions 2017-02-22 14:43:08 -08:00
joeduffy
9c2013baf0 Implement resource snapshot deserialization 2017-02-22 14:32:03 -08:00
joeduffy
8d71771391 Repivot plan/apply commands; prepare for updates
This change repivots the plan/apply commands slightly.  This is largely
in preparation for performing deletes and updates of existing environments.

The old way was slightly confusing and made things appear more "magical"
than they actually are.  Namely, different things are needed for different
kinds of deployment operations, and trying to present them each underneath
a single pair of CLI commands just leads to weird modality and options.

The new way is to offer three commands: create, update, and delete.  Each
does what it says on the tin: create provisions a new environment, update
makes resource updates to an existing one, and delete tears down an existing
one entirely.  The arguments are what make this interesting: create demands
a MuPackage to evaluate (producing the new desired state snapshot), update
takes *both* an existing snapshot file plus a MuPackage to evaluate (producing
the new desired state snapshot to diff against the existing one), and delete
merely takes an existing snapshot file and no MuPackage, since all it must
do is tear down an existing known environment.

Replacing the plan functionality is the --dry-run (-n) flag that may be
passed to any of the above commands.  This will print out the plan without
actually performing any opterations.

All commands produce serializable resource files in the MuGL file format,
and attempt to do smart things with respect to backups, etc., to support the
intended "Git-oriented" workflow of the pure CLI dev experience.
2017-02-22 11:21:26 -08:00
joeduffy
26cac1af3a Move colors into a central location
Per Eric's suggestion, this moves the colors we use into a central
location so that it'll be easier someday down the road to reconfigure
and/or disable them, etc.  This does not include a --no-colors option
although we should really include this soon before it gets too hairy.
2017-02-21 18:49:51 -08:00
joeduffy
d4911ad6f6 Implement snapshot MuGL
This change adds support for serializing snapshots in MuGL, per the
design document in docs/design/mugl.md.  At the moment, it is only
exposed from the `mu plan` command, which allows you to specify an
output location using `mu plan --output=file.json` (or `-o=file.json`
for short).  This serializes the snapshot with monikers, resources,
and so on.  Deserialization is not yet supported; that comes next.
2017-02-21 18:31:43 -08:00
joeduffy
0efb8bdd69 Fix a few things
* Specify MinCount/MaxCount when creating an EC2 instance.  These
  are required properties on the RunInstances API.

* Only attempt to unmarshal egressArray/ingressArray when non-nil.

* Remember the context object on the instanceProvider.

* Move the moniker and object maps into the shared context object.

* Marshal object monikers as the resource IDs to which they refer,
  since monikers are useless on "the other side" of the RPC boundary.
  This ensures that, for example, the AWS provider gets IDs it can use.

* Add some paranoia assertions.
2017-02-20 13:55:09 -08:00
joeduffy
81158d0fc2 Make property logic nil-sensitive
...and add some handy plugin-oriented logging.
2017-02-20 13:27:31 -08:00
joeduffy
a9085ece0f Trace plugin STDOUT/STDERR 2017-02-20 12:34:15 -08:00
joeduffy
f8cba5d752 Add interface members to prototypes
This change does two things:

First, and foremost, it adds interface members to prototypes.  This
ensures that interface members are available statically on all objects
of types that implement those interfaces.

Second, we permit dynamic loads through LoadLocationExpression when
the `this` object is `dynamic`.  This is a convenient shortcut compared
to creating different LoadDynamicExpressions when the `this` happens to
be of a dynamic type.
2017-02-20 12:20:59 -08:00
joeduffy
276b6c253d Implement a basic AWS resource provider
This commit includes a basic AWS resource provider.  Mostly it is just
scaffolding, however, it also includes prototype implementations for EC2
instance and security group resource creation operations.
2017-02-20 11:18:47 -08:00
joeduffy
dbd1721ced Properly detect "missing file from PATH" os/exec errors 2017-02-19 12:23:26 -08:00
joeduffy
3618837092 Add plan apply progress reporting 2017-02-19 11:58:04 -08:00
joeduffy
bfe659017f Implement the mu apply command
This change implements `mu apply`, by driving compilation, evaluation,
planning, and then walking the plan and evaluating it.  This is the bulk
of marapongo/mu#21, except that there's a ton of testing/hardening to
perform, in addition to things like progress reporting.
2017-02-19 11:41:05 -08:00
joeduffy
09c01dd942 Implement resource provider plugins
This change adds basic support for discovering, loading, binding to,
and invoking RPC methods on, resource provider plugins.

In a nutshell, we add a new context object that will share cached
state such as loaded plugins and connections to them.  It will be
a policy decision in server scenarios how much state to share and
between whom.  This context also controls per-resource context
allocation, which in the future will allow us to perform structured
cancellation and teardown amongst entire groups of requests.

Plugins are loaded based on their name, and can be found in one of
two ways: either simply by having them on your path (with a name of
"mu-ressrv-<pkg>", where "<pkg>" is the resource package name with
any "/"s replaced with "_"s); or by placing them in the standard
library installation location, which need not be on the path for this
to work (since we know precisely where to look).

If we find a protocol, we will load it as a child process.

The protocol for plugins is that they will choose a port on their
own -- to eliminate races that'd be involved should Mu attempt to
pre-pick one for them -- and then write that out as the first line
to STDOUT (terminated by a "\n").  This is the only STDERR/STDOUT
that Mu cares about; from there, the plugin is free to write all it
pleases (e.g., for logging, debugging purposes, etc).

Afterwards, we then bind our gRPC connection to that port, and create
a typed resource provider client.  The CRUD operations that get driven
by plan application are then simple wrappers atop the underlying gRPC
calls.  For now, we interpret all errors as catastrophic; in the near
future, we will probably want to introduce a "structured error"
mechanism in the gRPC interface for "transactional errors"; that is,
errors for which the server was able to recover to a safe checkpoint,
which can be interpreted as ResourceOK rather than ResourceUnknown.
2017-02-19 11:08:06 -08:00
joeduffy
6c25ff5cba Drive plan application
This moves us one step closer from planning to application (marapongo/mu#21).
Namely, we now drive the right resource provider operations in response to
the plan's steps.  Those providers, however, are still empty shells.
2017-02-18 11:54:24 -08:00
joeduffy
9621aa7201 Implement deletion plans
This change adds a flag to `plan` so that we can create deletion plans:

    $ mu plan --delete

This will have an equivalent in the `apply` command, achieving the ability
to delete entire sets of resources altogether (see marapongo/mu#58).
2017-02-18 10:33:36 -08:00
joeduffy
6f42e1134b Create object monikers
This change introduces object monikers.  These are unique, serializable
names that refer to resources created during the execution of a MuIL
program.  They are pretty darned ugly at the moment, but at least they
serve their desired purpose.  I suspect we will eventually want to use
more information (like edge "labels" (variable names and what not)),
but this should suffice for the time being.  The names right now are
particularly sensitive to simple refactorings.

This is enough for marapongo/mu#69 during the current sprint, although
I will keep the work item (in a later sprint) to think more about how
to make these more stable.  I'd prefer to do that with a bit of
experience under our belts first.
2017-02-18 10:22:04 -08:00
joeduffy
d9ee2429da Begin resource modeling and planning
This change introduces a new package, pkg/resource, that will form
the foundation for actually performing deployment plans and applications.

It contains the following key abstractions:

* resource.Provider is a wrapper around the CRUD operations exposed by
  underlying resource plugins.  It will eventually defer to resource.Plugin,
  which itself defers -- over an RPC interface -- to the actual plugin, one
  per package exposing resources.  The provider will also understand how to
  load, cache, and overall manage the lifetime of each plugin.

* resource.Resource is the actual resource object.  This is created from
  the overall evaluation object graph, but is simplified.  It contains only
  serializable properties, for example.  Inter-resource references are
  translated into serializable monikers as part of creating the resource.

* resource.Moniker is a serializable string that uniquely identifies
  a resource in the Mu system.  This is in contrast to resource IDs, which
  are generated by resource providers and generally opaque to the Mu
  system.  See marapongo/mu#69 for more information about monikers and some
  of their challenges (namely, designing a stable algorithm).

* resource.Snapshot is a "snapshot" taken from a graph of resources.  This
  is a transitive closure of state representing one possible configuration
  of a given environment.  This is what plans are created from.  Eventually,
  two snapshots will be diffable, in order to perform incremental updates.
  One way of thinking about this is that a snapshot of the old world's state
  is advanced, one step at a time, until it reaches a desired snapshot of
  the new world's state.

* resource.Plan is a plan for carrying out desired CRUD operations on a target
  environment.  Each plan consists of zero-to-many Steps, each of which has
  a CRUD operation type, a resource target, and a next step.  This is an
  enumerator because it is possible the plan will evolve -- and introduce new
  steps -- as it is carried out (hence, the Next() method).  At the moment, this
  is linearized; eventually, we want to make this more "graph-like" so that we
  can exploit available parallelism within the dependencies.

There are tons of TODOs remaining.  However, the `mu plan` command is functioning
with these new changes -- including colorization FTW -- so I'm landing it now.

This is part of marapongo/mu#38 and marapongo/mu#41.
2017-02-17 12:31:48 -08:00
joeduffy
28a13a28cb Slightly prettify the plan command
This just makes it a little easier to see what's going on.
2017-02-16 17:32:13 -08:00
joeduffy
3a04e7e72b Adjust this on PropertyValues too 2017-02-16 16:59:11 -08:00
joeduffy
394bec544a Adjust prototype pointers; bind super correctly
This change fixes two aspects of binding names to objects.  First, we need
to adjust pointers from prototype functions, since they will have the wrong
"this" (it points to the prototype instance and not the actual object).
Second, we need to bind `super` accesses using the correct prototype object.
2017-02-16 15:55:28 -08:00
joeduffy
110ecdc734 Emit Tokens in Symbol Stringer.String functions
It's generally more useful to have the fully qualified symbol token in logs,
error messages, etc.  So this is a better default than name.
2017-02-16 15:53:55 -08:00
joeduffy
092fe384c8 Fix intrinsic token 2017-02-16 08:23:04 -08:00
joeduffy
231d697181 Flip the order of comparison for static cast checking 2017-02-16 07:38:10 -08:00
joeduffy
79569b9044 Rename serialized name from "alternative" to "alternate" 2017-02-16 07:37:40 -08:00
joeduffy
af45bfd53f Fix up a few things
* Improve some logging and assertions.

* Initialize the prototype map (it was always empty before).

* Actually memoize the prototype objects in this map.

* During class initialization, ensure the base class is also initialized.

* Trigger class initialization anytime a property or function is loaded
  from that class.  Similarly, trigger module initialization on loads too.

* Treat For/ForIn/ForOfStatements as legal parents for local variables.
  This ensures for loops at the top-level in a module have their variables
  treated as locals, rather than module properties.  Add a test also.

* Track source locations for more nodes.

* Mark auto-generated constructors as public so that they may be called.

* Fix the naming of class init methods; it should be ".init", not ".ctor".

* Clone the loop test cases into both function and module top-level variants.
2017-02-16 06:48:39 -08:00
joeduffy
c6f77ae3d6 Implement for loops
This change introduces a for loop node to the AST, for much the same
reasons we elevated switch nodes to the AST, and lowers MuJS for loops.
2017-02-16 05:38:01 -08:00
joeduffy
563fad29ec Add 1st class switch support
One guiding principle for what makes it into the MuIL AST is that
the gap between source language and AST should not be too great; the
projection of switch statements from MuJS into MuIL clearly violated
that principle, particularly considering that the logic wasn't even
right due to the incorrect emulation of conditional breaks.

Instead of digging deeper into the hole, I've encoded switch logic
in the AST, and implemented support in the evaluator.
2017-02-16 04:58:04 -08:00
joeduffy
6dcdf9e884 Fix a few flubs
* Add a TODO as a reminder to implement number toString formatting.

* Change the Loreley delimiters to something obscure ("<{%" and "%}>")
  to avoid conflicting with actual characters we might use in messages.
  Also, make the assertions more descriptive should Loreley fail.

* Rip out a debug.PrintStack() in verbose logging.

* Check the underlying pointer's object type for +=, not the pointer type.
2017-02-16 04:15:07 -08:00
joeduffy
84c3f0f9bc Support string concatenation with += operator 2017-02-15 18:51:37 -08:00
joeduffy
71a22509d7 Only bind non-nil function types 2017-02-15 18:44:30 -08:00
joeduffy
a6aa17e2b7 Make variable types required
This change requires variable types in the MuIL.  It is up to the MetaMu
compiler to pick a default if it so chooses (object, dynamic, etc.)
2017-02-15 18:42:24 -08:00
joeduffy
20d9c3e9ca Add verbose AST walk tracing 2017-02-15 18:28:13 -08:00
joeduffy
64554621cf Implement switch statements 2017-02-15 18:19:24 -08:00
joeduffy
2af011d6ab Implement TypeOf expressions 2017-02-15 15:58:46 -08:00
joeduffy
2898747db4 Implement intrinsic function machinery
This change implements intrinsic function support in the runtime.

The basic idea is to swap out references to MuIL functions with
runtime-implemented functionality, for operations that cannot be
expressed in the MuIL-subset.  For example, this change includes
two such cases: 1) a mu.runtime.isFunction(obj) function to check if
the target object is a function; and 2) a
mu.runtime.dynamicInvoke(obj,obj,obj[]) function to dynamically
invoke a target object.

These will be used most immediately to implement ECMAScript toString
functionality in the MuJS runtime library, however it will also come
in handy shortly to implement things like printf (marapongo/mu#86),
string and array functions (marapongo/mu#80), and, eventually, the
ECMAScript-specific operators and behavior (marapongo/mu#61).
2017-02-15 15:35:52 -08:00
joeduffy
42ef7914ea Look up base types during static member binding 2017-02-15 13:06:54 -08:00
joeduffy
33384d1deb Do not bind alias definitions 2017-02-15 12:57:36 -08:00
joeduffy
6719a6070a Generalize default modules
This change generalizes the support for default modules (added as
part of marapongo/mu#57), to use an "alias" table.  This is just a
table of alternative module names to the real defining module name.
The default module is now just represented as a mapping from the
special default module token, ".default", to the actual defining
module.  This generalization was necessary to support Node.js-style
alternative names for modules, like "lib" mapping to "lib/index".
2017-02-15 12:53:36 -08:00
joeduffy
a40db0e6a0 Permit throwing anything
Previously, I had thought we would ask MetaMu compilers to map their
own exception/error types to ours.  That causes some complexity, however,
in that the exception types in each language (if they even exist) are
"different".  So we would need to wrap things, etc., which seems cumbersome.

Partly I had done this after having been burned on the CLR by permitting
throws of any types; e.g., C++/CLI could throw integers, which would rip
through C# code unknowingly, because all C# throws had to derive from the
Exception base class.  This actually caused some security issues!

But, we're in a different place, and a different time.  The first three
languages I envision supporting -- ECMAScript, Python, and Ruby -- each
permit you to throw anything.  And the only other languages I am seriously
contemplating right now -- Go -- doesn't even have exceptions.

So this change backs off and is less opinionated about what is thrown.
Instead, we track stack traces, etc., in the unwind information, and happily
propagate whatever object is thrown.
2017-02-15 10:04:33 -08:00
joeduffy
973dfe1f22 Map class names to their prototype objects 2017-02-15 08:57:28 -08:00
joeduffy
25d626ed50 Implement prototype chaining
This change more accurately implements ECMAScript prototype chains.
This includes using the prototype chain to lookup properties when
necessary, and copying them down upon writes.

This still isn't 100% faithful -- for example, classes and
constructor functions should be represented as real objects with
the prototype link -- so that examples like those found here will
work: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor.
I've updated marapongo/mu#70 with additional details about this.

I'm sure we'll be forced to fix this as we encounter more "dynamic"
JavaScript.  (In fact, it would be interesting to start running the
pre-ES6 output of TypeScript through the compiler as test cases.)

See http://www.ecma-international.org/ecma-262/6.0/#sec-objects
for additional details on the prototype chaining semantics.
2017-02-14 16:12:01 -08:00
joeduffy
1e62872ef3 Simplify property initialization
The complexity of the last round of property initialization code was
starting to bug me.  I've switched away from lazily initializing them
and now eagerly initialize at the proper times: module initialization,
class initialization (for statics), and object allocation (for instances).
This includes the logic around readonly and freezing.  The code is a
lot simpler now and actually enforces some invariants that we'd like to
enforce, like not silently adding new properties when a supposedly static
member load is happening (all such accesses should be dynamic ones).
2017-02-14 08:40:20 -08:00
joeduffy
722e963f89 Rename tempmark to visiting and mark to visited
Better variable names, per Eric's code review feedback.
2017-02-13 14:41:20 -08:00
joeduffy
b47445490b Implement a very rudimentary plan command
This simply topologically sorts the resulting MuGL graph and, in
a super lame way, prints out the resources and their properties.
2017-02-13 14:26:46 -08:00
joeduffy
7befbe151b Properly enforce readonly properties
This change accurately enforces readonly properties.  Namely, they
should not be written to anywhere outside of the respective initializer,
but writes must be allowed within them.  This means the module initializer
for module properties, the class initializer for statics, and the object
constructor for instance properties.
2017-02-13 13:29:05 -08:00
joeduffy
295db4faac Push and pop class/module context during evaluation
Now that some name lookups are context-sensitive (namely, whether
or not we should use the export or member table for inter vs. intra
module token references), we need to faithfully track the context.
2017-02-13 11:58:10 -08:00
joeduffy
a668db2e2c Properly chase exports
This change fixes up some issues in the big export refactoring;
namely, we need to chase down references one link at a time during
binding, before they settle.  This is because an export might depend
on another export, which might depend on ...
2017-02-13 10:54:11 -08:00
joeduffy
32960be0fb Use export tables
This change redoes the way module exports are represented.  The old
mechanism -- although laudible for its attempt at consistency -- was
wrong.  For example, consider this case:

    let v = 42;
    export { v };

The old code would silently add *two* members, both with the name "v",
one of which would be dropped since the entries in the map collided.

It would be easy enough just to detect collisions, and update the
above to mark "v" as public, when the export was encountered.  That
doesn't work either, as the following two examples demonstrate:

    let v = 42;
    export { v as w };
    let x = w; // error!

This demonstrates:

    * Exporting "v" with a different name, "w" to consumers of the
      module.  In particular, it should not be possible for module
      consumers to access the member through the name "v".

    * An inability to access the exported name "w" from within the
      module itself.  This is solely for external consumption.

Because of this, we will use an export table approach.  The exports
live alongside the members, and we are smart about when to consult
the export table, versus the member table, during name binding.
2017-02-13 09:56:25 -08:00
joeduffy
b1f96964ac Implement array l-values
This commit implements array l-values (for dynamic loads only, since we do
not yet ever produce static ones).  Note that there are some ECMAScript
compliance noteworthies about this change, which are captured in comments.

Namely, we are emulating ECMAScript's ability to index into an array
anywhere (except for negative numbers, which is a problem).  This is in
contrast to the usual approach of throwing an error on out-of-bounds access,
which will crop up when we move on to other languages like Python.  And yet we
are usuing a real array as the backing store, which can cause problems with
some real ECMAScript programs that use sparse arrays and expect the "bag of
properties" approach to keep memory usage reasonable.

The work item marapongo/mu#70 tracks this among other things.
2017-02-13 07:23:00 -08:00
joeduffy
1af9cd720b Use 1-based column numbers 2017-02-13 06:44:48 -08:00
joeduffy
f0181d8933 Emit more array types; permit more dynamic conversions
This change plucks array element types out of more AST nodes, tightening
up the MuIL that MuJS produces.  It also adds a few cases where we should
be emitting "object", and permits more dynamic conversions (which are
technically unsafe, but necessary for the sort of duck typing we anticipate).
2017-02-13 06:39:50 -08:00
joeduffy
d82adefd38 Implement casts 2017-02-13 05:56:39 -08:00
joeduffy
55deb13100 Implement static properties in the runtime 2017-02-13 05:45:28 -08:00
joeduffy
de7d8c7a90 Tolerate duplicate objects
In select few cases (right now, just nulls and boolean true/false),
we use predefined object constants to avoid allocating wastefully.
In the future, it's possible we will do this for other types, like
interned strings.  So, the graph generator should tolerate this.
2017-02-13 04:00:33 -08:00
joeduffy
4a1a117d4e Create arrays of the right type; return the object 2017-02-12 13:42:53 -08:00
joeduffy
9375f38ba1 Don't store nulls 2017-02-12 13:36:34 -08:00
joeduffy
bf6f6db089 Track all objects in MuGL
This change starts tracking all objects in our MuGL graph.  The reason is
that resources can be buried deep within a nest of objects, and unless we
do "on the fly" reachability analysis, we can't know a priori whether any
given object will be of interest or not.  So, we track 'em all.  For large
programs, this would obviously create space leak problems, so we'll
eventually, I assume, want to prune the graph at some point.

With this change, the EC2instance example produces a (gigantic) graph!
2017-02-12 13:11:53 -08:00
joeduffy
3f2f444bd7 Use the string *value*
This fixes a bug where we used the Stringer string, rather than the
underlying string literal value, when doing dynamic lookups.
2017-02-12 12:18:13 -08:00
joeduffy
edb2fae7ba Register the local variable symbol, not its type 2017-02-12 11:49:19 -08:00
joeduffy
d6405a7694 Dump eval state in sorted order
This eliminates some non-determinism in the output, due to Go's
randomized map enumeration order.
2017-02-12 10:21:37 -08:00
joeduffy
36b4a6f848 Implement stack traces
This change implements stack traces.  This is primarily so that we
can print out a full stack trace in the face of an unhandled exception,
and is done simply by recording the full trace during evaluation
alongside the existing local variable scopes.
2017-02-12 09:38:19 -08:00
joeduffy
a16bb714e4 Implement dynamic loading
This change implements dynamic loads in the binder and evaluator.
2017-02-12 08:22:44 -08:00
joeduffy
a8812bdbf0 Use strings for property keys
In dynamic scenarios, property keys could be arbitrary strings, including
invalid identifiers.  This change reflects that in the runtime representation
of objects and also in the object literal and overall indexing logic.
2017-02-11 15:45:37 -08:00
joeduffy
b2d43e1c59 Permit dynamic to flow through to eval
This change "kicks the can" of dealing with `dynamic` down the road.
Namely, the binder will be permissive about letting dynamic objects
flow through to the evaluation phase.  At that point, the evaluator
will be responsible for performing duck typing, etc.
2017-02-11 08:39:45 -08:00
joeduffy
7a2d2b4cb1 Typecheck new expressions 2017-02-11 08:28:47 -08:00
joeduffy
dc9f91c3cf Support dynamic object literals
This change introduces support for dynamic object literals.  If the
type of a literal is dynamic, then the property initializers for that
literal are naked strings; if the type of a literal is anything else,
however, we expect that the property names are full blown member tokens.
2017-02-11 08:20:06 -08:00
joeduffy
0802399a8c Distinguish between object and dynamic (fka any)
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.
2017-02-10 16:44:18 -08:00
joeduffy
fa69ed43f4 Tweak a couple error messages 2017-02-10 15:18:30 -08:00
joeduffy
11b7880547 Further reshuffle Protobufs; generate JavaScript code
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/.
2017-02-10 09:28:46 -08:00
joeduffy
6b60852c76 Generate Golang Protobuf/gRPC code
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.
2017-02-10 09:08:06 -08:00
joeduffy
20452a99e1 Add Protobuf definitions for resource providers 2017-02-10 08:55:26 -08:00
joeduffy
6e472f8ab4 Colorize Mu output
This change adds colorization to the core Mu tool's output, similar
to what we added to MuJS in
cf6bbd460d.
2017-02-09 17:26:49 -08:00
joeduffy
c333bf6f01 Tag an object moniker TODO with marapongo/mu#76 2017-02-09 16:02:45 -08:00
joeduffy
79f8b1bef7 Implement a DOT graph converter
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).
2017-02-09 15:56:15 -08:00
joeduffy
c26ec0aa65 Parent the predefined Resource class to the mu/resource module 2017-02-09 15:06:02 -08:00
joeduffy
c0b0a87b07 Use tokens when printing initialized modules/classes 2017-02-09 15:04:26 -08:00
joeduffy
c2897d5b70 Fix HasBaseName's logic 2017-02-09 15:01:23 -08:00
joeduffy
1b2ec4eebd Add some handy logging to graph generation 2017-02-09 15:01:08 -08:00
joeduffy
9fb8819375 Fix a possible nil deref in verbose logging 2017-02-09 14:52:33 -08:00
joeduffy
e14ca5f4ff Manually print the stack-trace when --logtostderr is enabled
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.
2017-02-09 14:50:41 -08:00
joeduffy
b241ea3795 Fix a few messages (print the token value, not pointer address) 2017-02-09 14:35:45 -08:00
joeduffy
eb7bfcf355 Implement new expression evaluation 2017-02-09 14:32:44 -08:00
joeduffy
3eb72b62d5 Introduce an <error> type
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).
2017-02-09 12:58:34 -08:00
joeduffy
a91f3e6050 Fix more phasing issues
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.
2017-02-09 12:24:02 -08:00
joeduffy
330a6c3d25 Probe for Mupacks, not Mufiles, for dependencies 2017-02-09 11:23:27 -08:00
joeduffy
e72cf059fb Fix up dependency probing
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.
2017-02-09 11:01:02 -08:00
joeduffy
be7dcec123 Support sugared "*" semvers
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.
2017-02-09 10:48:18 -08:00
joeduffy
2f968b778c Permit any for object literals
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.
2017-02-08 17:03:38 -08:00
joeduffy
7f094a3054 Chase exports
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.
2017-02-08 16:19:25 -08:00
joeduffy
69b23ca2ea Implement structured token binding
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.
2017-02-08 14:10:16 -08:00
joeduffy
f9c02a12a3 Improve some token-related assertion messages 2017-02-08 10:12:09 -07:00
joeduffy
5fb638a9f9 Add some quotes to a few error messages 2017-02-08 09:57:54 -07:00
joeduffy
31ff4e96a6 Add missing ClassMemberToken case 2017-02-08 09:56:10 -07:00
joeduffy
93c6c936c3 Permit nil typemap entries
Previously we asserted that typemap entries are never nil.  It turns
out we represent "void" as the absence of a type in MuIL, and so we need
to permit these for constructors, etc.
2017-02-08 09:53:29 -07:00
joeduffy
130838b1b1 Walk arguments in calls and function invokes 2017-02-08 09:53:00 -07:00
joeduffy
ebb2b018ad Bind class member definitions in a separate pass
This change further rearranges the phasing of binding to account for
the fact that class definitions may freely reference exports, which won't
be known until after binding class names.  Therefore, we bind class names
first and then the definitions (function signatures and variables).

This brings the AWS MuPackage's number of verification errors down to 84
from 136.
2017-02-08 08:14:08 -07:00
joeduffy
13872b77c7 Use stable map enumeration in the binder 2017-02-07 09:58:23 -07:00
joeduffy
6c26693fea Move default to a property on package, not a bit on module
The old method of specifying a default module for a package was using
a bit on the module definition itself.  The new method is to specify the
module name as part of the package definition itself.

The new approach makes more sense for a couple reasons.  1) it doesn't
make sense to have more than one "default" for a given package, and yet
the old model didn't prevent this; the new one prevents it by construction.
2) The defaultness of a module is really an aspect of the package, not the
module, who is generally unaware of the package containing it.

The other reason is that I'm auditing the code for nondeterministic map
enumerations, and this came up, which simply pushed me over the edge.
2017-02-06 18:33:31 -07:00
joeduffy
4cf8654bf1 Fix a few lint issues 2017-02-06 18:08:22 -07:00
joeduffy
deb015a15d Implement multi-pass module binding
The prior module binding logic was naive.  It turns out that, because of inter-module
references within the same package, the order of binding certain modules and its members
within a package matters.  This leads to a more Java/C#/JavaScript/etc. multi-pass model,
versus a classical single-pass C/C++ model (which requires forward declarations).

To do this, we bind things in this order within a package:

* First, we add an entry for each module.  It is largely empty, but at least names resolve.

* Second, we bind all imports.  Since all modules are known, all inter-module tokens resolve.

* Next, we can bind class symbols.  Note that we must do this prior to module properties,
  methods, and exports, since they might be referenced and need to exist first.  As before,
  we do not yet bind function bodies, since those can reference anything.

* Now we bind all module "members", meaning any module scoped properties and methods.  Again,
  we do not bind function bodies just yet.  This is just a symbolic binding step.

* Next, we can finally bind exports.  Because exports may reference any members, all of the
  above must have been done first.  But all possibly exported symbols are now resolved.

* Finally, we can bind function bodies.
2017-02-03 19:57:53 -08:00
joeduffy
15ba2949dc Add a mu verify command
This adds a `mu verify` command that simply runs the verification
pass against a MuPackage and its MuIL.  This is handy for compiler
authors to verify that the right stuff is getting emitted.
2017-02-03 19:27:37 -08:00
joeduffy
f6c96c2c86 Bind and validate function calls 2017-02-02 17:11:58 -08:00
joeduffy
a5b7a35629 Fix up some error messages 2017-02-02 16:58:53 -08:00
joeduffy
f6c5e9d30a Add a couple log statements to graph generation 2017-02-02 11:18:39 -08:00
joeduffy
f5faf058c0 Check in a missing test file
...and also fix up .gitignore to only ignore /mu/ -- the binary created
when you `go build` -- and not the Mu project file (needed for this test).
2017-02-01 19:41:13 -08:00
joeduffy
054d928170 Add a very basic graph pretty printer
This is pretty worthless, but will help me debug some issues locally.
Eventually we want MuGL to be fully serializable, including the option
to emit DOT files.
2017-02-01 09:07:30 -08:00
joeduffy
2a2dfdc5c0 Tolerate nil this for variable assignments 2017-02-01 08:59:59 -08:00
joeduffy
301f246f25 Invoke the OnVariableAssign interpreter hook
This change actually invokes the OnVariableAssign interpreter hook
at the right places.  (Renamed from OnAssignProperty, as it will now
handle all variable assignments, and not just properties.)  This
requires tracking a bit more information about l-values so that we
can accurately convey the target object and symbol associated with
the assignment (resulting in the new "location" struct type).
2017-02-01 08:56:29 -08:00
joeduffy
5594d23f84 Implement graph generation
This change lowers the information collected about resource allocations
and dependencies into the MuGL graph representation.

As part of this, we've moved pkg/compiler/eval out into its own top-level
package, pkg/eval, and split up its innards into a smaller sub-package,
pkg/eval/rt, that contains the Object and Pointer abstractions.  This
permits the graph generation logic to use it without introducing cycles.
2017-02-01 08:08:21 -08:00
joeduffy
24ea62bc78 Begin tracking graph dependencies
This change refactors the interpreter hooks into a first class interface
with many relevant event handlers (including enter/leave functions for
packages, modules, and functions -- something necessary to generate object
monikers).  It also includes a rudimentary start for tracking actual object
allocations and their dependencies, a step towards creating a MuGL graph.
2017-01-31 17:42:06 -08:00
joeduffy
63accbc794 Introduce an Allocator abstraction
This change adds a new Allocator type that will handle all object allocations
that occur during evaluation.  This allows us to hook into interesting lifecycle
events that are required in order to create a MuGL resource graph.
2017-01-31 15:28:00 -08:00
joeduffy
f050b567aa Rename GetPropertyPointer to GetPropertyAddr
This change mirrors the same change we made for local variable scope
addressing.  GetPropertyPointer is all about getting the property's
address; instead of GetPropertyPointer, which could be confused as
meaning the property's value is a pointer, we will call this method
GetPropertyAddr instead.
2017-01-28 10:54:16 -08:00
joeduffy
5336122511 Dump eval state afterwards; also rename a few things
This change dumps the evaluation state after evaluation completes, at
log-level 5.  This includes which modules and classes were initialized,
in addition to the values for all global variables.

In addition to this, we rename a few things:

* Rename Object's Data field to Value.

* Rename the Object.T() methods to Object.TValue().  This more clearly
  indicates what they are doing (i.e., fetching the value from the object)
  and also avoids object.String() conflicting with fmt.Stringer's String().

* Rename Reference to Pointer, so it's consistent with everything else.

* Rename the GetValueReference/InitValueReference/etc. family of methods
  to GetValueAddr/InitValueAddr/etc., since this reflects what they are
  actually doing: manipulating a variable slot's address.
2017-01-28 10:51:30 -08:00
joeduffy
e92229f3a7 Minor evaluation fixes
* Assert that module/class initializer functions returned nil.

* Evaluate the LHS as an l-value for binary assignment operators.

* Also consider two nulls to be equal, always.
2017-01-28 09:21:29 -08:00
joeduffy
68a01aeab8 Implement binary operator evaluation 2017-01-28 09:02:12 -08:00
joeduffy
d685850800 Implement unary operator evaluation; and l-values
This change implements unary operator evaluation, including prefix/postfix
assignment operators.  Doing this required implementing l-values properly
in the interpreter; namely, load location and dereference, when used in an
l-value position, result in a pointer to the location, while otherwise they
(implicitly, in load location's case) deference the location and yield a value.
2017-01-27 16:42:32 -08:00
joeduffy
c8044b66ce Fix up a bunch of golint errors 2017-01-27 15:42:39 -08:00
joeduffy
32e7f3f9a8 Fix a few go vet issues 2017-01-27 15:14:19 -08:00
joeduffy
e8e1272987 Encapsulate Unwind's innards
This change uses an enum rather than set of bools for unwind's inner
state, makes its fields private, and adds accessor methods.  This makes
it easier to protect invariants and wonky states like being a break and
a throw simultaneously.  Now that Unwind is part of the public API (vs
being private as it used to), this is really a requirement (and was
obviously a good idea regardless).  Thanks to Eric for CR feedback.
2017-01-27 15:01:22 -08:00
joeduffy
94bbd390cc Run class and module initializers 2017-01-27 14:47:26 -08:00
joeduffy
6f23e29ed5 Add some fails
This change adds some fail statements into places that aren't yet
implemented during binding and evaluation so I can qucikly catch
stuff that isn't working as expected yet.
2017-01-27 14:08:24 -08:00
joeduffy
ebe40e4be6 Make alternative features less alternative
Three minor blocking issues fixed, discovered as I am running tests
locally to stress the interpretr in a few different ways:

* Properly initiaize the local scope.

* Log pkg.Name(), not pkg.Name, so we get a string and not an address.

* Don't dereference uw after doing a call, when it could be nil.
2017-01-27 14:01:36 -08:00
joeduffy
df9246dfa1 Bind unary and binary operators 2017-01-27 13:51:36 -08:00
joeduffy
349e98abbd Populate this/super in instance method activation frames
This change populates the special `this` and `super` variables in
instance method activation frames.  This occurs both during binding --
so that we can properly typecheck and verify references to them -- and
during evaluation -- so we can actually operate on their values.
2017-01-26 18:14:49 -08:00
joeduffy
34e9eec4fb Initialize the binder's scope 2017-01-26 17:19:12 -08:00
joeduffy
6ea444fd7c Replace tree-walk-style type binding with token map lookups
Now that we have introduced a full blown token map -- new as of just
a few changes ago -- we can start using it for all of our symbol binding.
This also addresses some order-dependent issues, like intra-module
references looking up symbols that have been registered in the token map
but not necessarily stored in the relevant parent symbols just yet.

Plus, frankly, it's much simpler and uses a hashmap lookup instead of
a fairly complex recursive tree walk.

I've kept the tree walk case, however, to improve diagnostics upon
failure.  This allows us to tell developers, for example, that the reason
a binding failed was due to a missing package.
2017-01-26 16:49:38 -08:00
joeduffy
9455483d27 Reintroduce a handful of binder tests
This change adds a few binder tests, and fixes package dependency
enumeration to be deterministic by using stable map enumeration.
2017-01-26 16:07:21 -08:00
joeduffy
289a0d405d Revive some compiler tests
This change revives some compiler tests that are still lingering around
from the old architecture, before our latest round of ship burning.

It also fixes up some bugs uncovered during this:

* Don't claim that a symbol's kind is incorrect in the binder error
  message when it wasn't found.  Instead, say that it was missing.

* Do not attempt to compile if an error was issued during workspace
  resolution and/or loading of the Mufile.  This leads to trying to
  load an empty path and badness quickly ensues (crash).

* Issue an error if the Mufile wasn't found (this got lost apparently).

* Rename the ErrorMissingPackageName message to ErrorInvalidPackageName,
  since missing names are now caught by our new fancy decoder that
  understands required versus optional fields.  We still need to guard
  against illegal characters in the name, including the empty string "".

* During decoding, reject !src.IsValid elements.  This represents the
  zero value and should be treated equivalently to a missing field.

* Do not permit empty strings "" as Names or QNames.  The old logic
  accidentally permitted them because regexp.FindString("") == "", no
  matter the regex!

* Move the TestDiagSink abstraction to a new pkg/util/testutil package,
  allowing us to share this common code across multiple package tests.

* Fix up a few messages that needed tidying or to use Infof vs. Info.

The binder tests -- deleted in this -- are about to come back, however,
I am splitting up the changes, since this represents a passing fixed point.
2017-01-26 15:30:08 -08:00
joeduffy
177b3ea5c7 Invoke Name() function when spewing diagnostics
Otherwise, we just print its address, which is kinda silly.
2017-01-26 08:31:43 -08:00
joeduffy
281805311c Perform package and module evaluation
This change looks up the main module from a package, and that module's
entrypoint, when performing evaluation.  In any case, the arguments are
validated and bound to the resulting function's parameters.
2017-01-25 18:38:53 -08:00
joeduffy
2ec7452bf7 Delete a garbage file 2017-01-25 17:59:37 -08:00
joeduffy
215afa6faf Clean up errors
This change eliminates all traces of the old, legacy errors and warnings.
It also renumbers everything so that we don't have any "gaps".  Finally,
it introduces a factory method for new errors and warnings, private to
this package, that ensures we don't accidentally register duplicate IDs.
2017-01-25 17:58:16 -08:00
joeduffy
8af53d5f69 Burn the ships
This change deletes a bunch of old legacy code.  I've tagged the
prior changelist as "0.1" in case we need to go back and recover
some of the code (as I expect some of the constraint type logic
and AWS-specific code to come in handy down the road).

🔥 🔥 🔥
2017-01-25 17:32:57 -08:00
joeduffy
3ec402753e Implement function invocation 2017-01-25 17:22:35 -08:00
joeduffy
12d4bee318 Add todos referencing marapongo/mu#56 2017-01-25 17:16:17 -08:00
joeduffy
e0843d6ba0 Add support for loading locations
This adds support for LoadLocationExpression, which will be key to
getting virtually everything else working, including class and module
member accesses, local variable gets/sets, function calls, and more.

In particular, an extra level of indirection is added to the globals,
locals, and properties maps.  This new thing is called a *Reference,
and it is simply a mutable slot that contains an *Object.  Instead
of storing and retrieving objects directly, this extra level of
indirection enables us to create *Object instances that themselves
simply refer to other objects (for subsequent loads and stores).

For now, we do not permit overwriting of method properties.  This
is a future work item (see marapongo/mu#56).
2017-01-25 17:11:36 -08:00
joeduffy
cff883211b Implement function calls in the interpreter
This change adds support for function calls.  We do not yet wire it
into the various function call expressions, however, that should be
relatively trivial after this change.  (It is dependent on figuring
out the runtime representation of load location objects and subsequent
uses of them).  This change also updates the scoping logic to respect
"activation frame" style of lexical scoping, where an inner function
as a result of a call should not have access to the callee context.

This change also includes logic to detect unhandled exceptions and to
print an error as a result.  Eventually, we will want stack traces here.
2017-01-25 15:04:47 -08:00
joeduffy
86cb0367ef Evaluate object literals 2017-01-25 14:08:49 -08:00
joeduffy
61aacf19b9 Break from loops when unwind != nil 2017-01-25 14:00:23 -08:00
joeduffy
18de260ecc Fix a typo 2017-01-25 13:59:33 -08:00
joeduffy
43556de6d7 Populate exception variable value in catch blocks 2017-01-25 13:58:48 -08:00
joeduffy
240a08b73e Introduce lexical scoping for variable values 2017-01-25 13:14:51 -08:00
joeduffy
cb8af34c93 Eliminate the scope-based symbol table
This change eliminates the scope-based symbol table.  Because we now
require that all module, type, function, and variable elements are
encoded as fully qualified tokens, there is no need for the scope-based
lookups.  Instead, the languages themselves decide how the names bind
to locations and just encode that information directly.

The scope is still required for local variables, however, since those
don't have a well-defined "fixed" notion of name.  This is also how
we will ensure the evaluator stores values correctly -- including
discarding them -- in a lexically scoped manner.
2017-01-25 10:51:04 -08:00
joeduffy
5bdb535c54 Begin doing evaluation
This change checks in an enormously rudimentary interpreter.  There is
a lot left to do, as evidenced by the countless TODOs scattered throughout
pkg/compiler/eval/eval.go.  Nevertheless, the scaffolding and some important
pieces are included in this change.

In particular, to evaluate a package, we must locate its entrypoint and then,
using the results of binding, systematically walk the full AST.  As we do
so, we will assert that aspects of the AST match the expected shape,
including symbols and their types, and produce value objects for expressions.

An *unwind structure is used for returns, throws, breaks, and continues (both
labeled and unlabeled).  Each statement or expression visitation may optionally
return one and its presence indicates that control flow transfer is occurring.
The visitation logic then needs to propagate this; usually just by bailing out
of the current context immediately, but sometimes -- as is the case with
TryCatchBlock statements, for instance -- the unwind is manipulated in more
"interesting" ways.

An *Object structure is used for expressions yielding values.  This is a
"runtime object" in our system and is comprised of three things: 1) a Type
(essentially its v-table), 2) an optional data pointer (for tagged primitives),
and 3) an optional bag of properties (for complex object property values).
I am still on the fence about whether to unify the data representations.

The hokiest aspect of this change is the scoping and value management.  I am
trying to avoid needing to implement any sort of "garbage collection", which
means our bag-of-values approach will not work.  Instead, we will need to
arrange for scopes to be erected and discarded in the correct order during
evaluation.  I will probably tackle that next, along with fleshing out the
many missing statement and expression cases (...and tests, of course).
2017-01-25 10:13:06 -08:00
joeduffy
7c1010ba72 Rearrange binding context storage of symbols 2017-01-25 09:29:34 -08:00
joeduffy
c5aea187c2 Creater a binder.Context structure
This change introduces a binder.Context structure, with a
*core.Context embedded, that carries additional semantic information
forward to future passes in the compiler.  In particular, this is how
the evaluation/interpretation phase will gain access to types, scopes,
and symbols.
2017-01-24 10:29:37 -08:00
joeduffy
f3c69b671a Memoize decorated types
This change memoizes decorated types to avoid creating boatloads
of redundant symbols at runtime.  We want it to be the case that you
can create instances of decorated types as needed throughout the
compiler without worry for excess garbage (e.g., arrays, pointers, etc).
2017-01-24 07:07:49 -08:00
joeduffy
11d3b2f9c4 Adjust the AST walker to make anonymous walking easier
During evaluation, we need to perform very delicate walking of the AST,
and are likely to use the ability to substitute visitors "in situ."  As
a result, I'd like to make anonymous visitors easier to produce.  This
change permits both single function forms of Visit/After, in addition to
anonymous structures that pair up a Visit and an After, to produce a Visitor.
2017-01-24 06:54:53 -08:00
joeduffy
5260ff9313 Add a crazy recursive parsing test...and fix some bugs
This change completes my testing of decorator parsing for now.  It tests the token
`*[]map[string]map[()*(bool,string,test/package:test/module/Crazy)number][][]test/package:test/module/Crazy`.

This turned up some bugs, most notably in the way we returned the "full" token for
the parsed types.  We need to extract the subset of the token consumed by the parsing
routine, rather than the entire thing.  To do this, we introduce a tokenBuffer type
that allows for convenient parsing of tokens (eating, advancing, extraction, etc).
2017-01-24 05:25:08 -08:00
joeduffy
8bdc81a4e1 Test function type token parsing
This change adds tests for basic function type token parsing.  It also
fixes two bugs around eating tokens and remainders.
2017-01-23 17:20:47 -08:00
joeduffy
4e78f10280 Add some decorated token tests
This isn't comprehensive yet, however it caught two bugs:

1. parseNextType should operate on "rest" in most cases, not "tok".

2. We must eat the "]" map separator before moving on to the element type.
2017-01-23 15:14:04 -08:00
joeduffy
049cc7dbc8 Implement decorated token parsing and binding
Part of the token grammar permits so-called "decorated" types.  These
are tokens that are pointer, array, map, or function types.  For example:

* `*any`: a pointer to anything.
* `[]string`: an array of primitive strings.
* `map[string]number`: a map from strings to numbers.
* `(string,string)bool`: a function with two string parameters and a
      boolean return type.
* `[]aws:s3/Bucket`: an array of objects whose class is `Bucket` from
      the package `aws` and its module `s3`.

This change introduces this notion into the parsing and handling of
type tokens.  In particular, it uses recursive parsing to handle complex
nested structures, and the binder.bindTypeToken routine has been updated
to call out to these as needed, in order to produce the correct symbol.
2017-01-23 14:48:55 -08:00
joeduffy
d56f640d5f Bind object initializers 2017-01-23 12:51:13 -08:00
joeduffy
a7ccc1b052 Add optional properties 2017-01-23 11:14:24 -08:00
joeduffy
5f0a1e970b Refactor binding logic
This changes a few things around binding logic, as part of eliminating
all of the legacy logic and weaving it into the new codebase:

* Give Scopes access to the Context object.  Related, add a TryRegister
  method to Scope that is like RequireRegister, except that instead of
  fail-fast upon encountering a duplicate entry, it will issue an error.

* Move all typecheck visitation functions out of the big honkin' switch
  and into their own member functions.  As this stuff gets more complex,
  having everything in one routine was starting to irk my sensibilities.

* Validate that packages have names.

* Store both the package symbol, plus the canonicalized URL used to
  resolve it, in the package map.  This will help us verify that versions
  match for multiple package references resolving to the same symbol.

* Add nice inquiry methods to the Class symbol (Sealed, Abstract, Record,
  Interface) that simplify accessing the modifiers on the underlying node.
2017-01-23 10:58:45 -08:00
joeduffy
25efb734da Move compiler options to pkg/compiler/core
The options structure will be shared between multiple passes of
compilation, including evaluation and graph generation.  Therefore,
it must not be in the pkg/compile package, else we would create
package cycles.  Now that the options structure is barebones --
and, in particular, no more "backend" settings pollute it -- this
refactoring actually works.
2017-01-23 08:10:49 -08:00
joeduffy
d7e04dc42e Add a pointer type; make load location expressions produce it
This change adds a pointer type, so that we can turn load location
expressions into proper l-values.
2017-01-23 08:00:09 -08:00
joeduffy
1e1a70345e Record and validate labeled statements and jumps 2017-01-23 07:25:05 -08:00
joeduffy
b724857ae1 Properly enforce accessibility
This change fixes a few mistakes in the old way of checking accessibility,
and adds support for protected class visibility.
2017-01-22 10:09:55 -08:00
joeduffy
7c5241978e Perform member lookup and binding 2017-01-22 09:45:58 -08:00
joeduffy
d907e358ad Assert that all expressions have types
This is a bit of paranoia, however, an invariant of the typechecker
is that, after the final pass, all expression nodes are assigned a
type.  This assertion checks that this is true.
2017-01-21 14:39:20 -08:00
joeduffy
9fe2f28e7b Add walker cases for token nodes 2017-01-21 14:39:05 -08:00
joeduffy
bd231ff624 Implement fmt.Stringer on token types 2017-01-21 12:25:59 -08:00
joeduffy
4dc082a2df Use 1st class token AST nodes
Instead of serializing simple token strings into the AST -- in place of things
like type references, module references, export references, etc. -- we now use
1st class AST nodes.  This ensures that source context flows with the tokens
as we bind them, etc., and also cleans up a few inconsistencies (like using an
ast.Identifier for NewExpression -- clearly wrong since this the resulting
MuIL is meant to contain fully bound semantic references).
2017-01-21 11:48:58 -08:00
joeduffy
3518dce92f Test token manipulation
This change includes some tests for token parsing and conversions.  It
also fixes a bug where we treated Type tokens like ClassMembers, when
we ought to have been treating them like ModuleMembers.
2017-01-21 11:08:25 -08:00
joeduffy
02d7ba2417 Add type conversion tests 2017-01-21 10:20:47 -08:00
joeduffy
a58cd7e2a0 Begin typechecking
This change performs typechecking during binding.  This is less about
typechecking per se -- since higher level languages will have presumably
given us well-typed IL -- and more about preparing the AST so that we
can evaluate the fully bound nodes to produce a MuGL graph.  It also
serves as a "verifier" for the incoming MuIL, however.

This is clearly incomplete, as the dozens of TODOs will make obvious.
But it's a clean checkpoint that does enough interesting typechecking
that I am landing it now.
2017-01-21 09:08:35 -08:00
joeduffy
74980ce339 Use stable map enumeration for member walking 2017-01-20 17:34:11 -08:00
joeduffy
c182d71c08 Begin binding function bodies
This change begins to bind function bodies.  This must be done as a
second pass over the AST, because dependencies between modules, and
even intra-module dependencies, might refer to top-level symbols like
types, variables, and functions, and so must be established first.

At the moment, the only node kind we handle is ast.Block, which
merely pushes and pops lexical scopes; however, the next step is to
implement the AST node-specific visitation logic for all statement
and expression nodes.

I've also rearranged how Scopes work to be a little easier to use.
The Scope type now remembers the **Scope slot in which it is rooted,
so that we can simply call Push and Pop on Scopes and have the right
thing happen.
2017-01-20 14:04:13 -08:00
joeduffy
3f8a317724 Use symbol factories
This introduces symbol factory methods to make creating them
less error prone.  In particular, we hadn't been wiring up parents
properly (since they came in after the initial symbol shape).
Now with the factory methods, we'll be reforced to visit creation
sites whenever adding new required elements to symbol types.
2017-01-20 13:02:51 -08:00
joeduffy
2c0266c9e4 Clean up package URL logic
This change rearranges the old way we dealt with URLs.  In the old system,
virtually every reference to an element, including types, was fully qualified
with a possible URL-like reference.  (The old pkg/tokens/Ref type.)  In the
new model, only dependency references are URL-like.  All maps and references
within the MuPack/MuIL format are token and name based, using the new
pkg/tokens/Token and pkg/tokens/Name family of related types.

As such, this change renames Ref to PackageURLString, and RefParts to
PackageURL.  (The convenient name is given to the thing with "more" structure,
since we prefer to deal with structured types and not strings.)  It moves
out of the pkg/tokens package and into pkg/pack, since it is exclusively
there to support package resolution.  Similarly, the Version, VersionSpec,
and related types move out of pkg/tokens and into pkg/pack.

This change cleans up the various binder, package, and workspace logic.
Most of these changes are a natural fallout of this overall restructuring,
although in a few places we remained sloppy about the difference between
Token, Name, and URL.  Now the type system supports these distinctions and
forces us to be more methodical about any conversions that take place.
2017-01-20 11:46:36 -08:00
joeduffy
729af81e44 Move all cloud switching to mu/x MuPackage
In the old system, the core runtime/toolset understood that we are targeting
specific cloud providers at a very deep level.  In fact, the whole code-generation
phase of the compiler was based on it.

In the new system, this difference is less of a "special" concern, and more of
a general one of mapping MuIL objects to resource providers, and letting *them*
gather up any configuration they need in a more general purpose way.

Therefore, most of this stuff can go.  I've merged in a small amount of it to
the mu/x MuPackage, since that has to switch on cloud IaaS and CaaS providers in
order to decide what kind of resources to provision.  For example, it has a
mu.x.Cluster stack type that itself provisions a lot of the barebone essential
resources, like a virtual private cloud and its associated networking components.

I suspect *some* knowledge of this will surface again as we implement more
runtime presence (discovery, etc).  But for the time being, it's a distraction
getting the core model running.  I've retained some of the old AWS code in the
new pkg/resource/providers/aws package, in case I want to reuse some of it when
implementing our first AWS resource providers.  (Although we won't be using
CloudFormation, some of the name generation code might be useful.)  So, the
ships aren't completely burned to the ground, but they are certainly on 🔥.
2017-01-20 09:46:59 -08:00
joeduffy
7b0dc8ee8d Overhaul names versus tokens
I was sloppy in my use of names versus tokens in the original AST.
Now that we're actually binding things to concrete symbols, etc., we
need to be more precise.  In particular, names are just identifiers
that must be "interpreted" in a given lexical context for them to
make any sense; whereas, tokens stand alone and can be resolved without
context other than the set of imported packages, modules, and overall
module structure.  As such, names are much simpler than tokens.

As explained in the comments, tokens.Names are simple identifiers:

    Name = [A-Za-z_][A-Za-z0-9_]*

and tokens.QNames are fully qualified identifiers delimited by "/":

    QName = [ <Name> "/" ]* <Name>

The legal grammar for a token depends on the subset of symbols that
token is meant to represent.  However, the most general case, that
accepts all specializations of tokens, is roughly as follows:

    Token       = <Name> |
                  <PackageName>
                    [ ":" <ModuleName>
                        [ "/" <ModuleMemberName>
                            [ "." <Class MemberName> ]
                        ]
                    ]

where:

    PackageName         = <QName>
    ModuleName          = <QName>
    ModuleMemberName    = <Name>
    ClassMemberName     = <Name>

Please refer to the comments in pkg/tokens/tokens.go for more details.
2017-01-19 17:57:20 -08:00
joeduffy
35fddddb78 Bind packages and modules
This change implements a significant amount of the top-level package
and module binding logic, including module and class members.  It also
begins whittling away at the legacy binder logic (which I expect will
disappear entirely in the next checkin).

The scope abstraction has been rewritten in terms of the new tokens
and symbols layers.  Each scope has a symbol table that associates
names with bound symbols, which can be used during lookup.  This
accomplishes lexical scoping of the symbol names, by pushing and
popping at the appropriate times.  I envision all name resolution to
happen during this single binding pass so that we needn't reconstruct
lexical scoping more than once.

Note that we need to do two passes at the top-level, however.  We
must first bind module-level member names to their symbols *before*
we bind any method bodies, otherwise legal intra-module references
might turn up empty-handed during this binding pass.

There is also a type table that associates types with ast.Nodes.
This is how we avoid needing a complete shadow tree of nodes, and/or
avoid needing to mutate the nodes in place.  Every node with a type
gets an entry in the type table.  For example, variable declarations,
expressions, and so on, each get an entry.  This ensures that we can
access type symbols throughout the subsequent passes without needing
to reconstruct scopes or emulating lexical scoping (as described above).

This is a work in progress, so there are a number of important TODOs
in there associated with symbol table management and body binding.
2017-01-19 13:37:47 -08:00
joeduffy
5aff453cc1 Add rudimentary symbol abstractions
This massages the symbol layer to reflect more closely what we need.

There is a Symbol interface.  It is an interface because it's polymorphic
and we'll need to switch on type tests throughout the code a fair bit.

In addition to the Symbol interface, there are three other interfaces:

* ModuleMember, for any Module member symbols.

* ClassMember, for any Class member symbols.

* Type, to permit polymorphic treatment of Classes and built-in types.

There are concrete symbols for Module, ModuleProperty, ModuleMethod,
Class, ClassProperty, and ClassMethod.  These map directly to the
corresponding AST abstractions and simply permit us to annotate those
AST nodes with some semantic information and, more importantly, to
inject them into the symbol table as we perform binding/typechecking.

Class implements the Type abstraction.

There is also a primitive node with four constant types, AnyType,
BoolType, NumberType, and StringType, each of which is registered in
an export Primitives map, keyed by their name/keyword/token.  These
of course implement the Type abstraction.

Finally, there are ArrayType and MapType symbols, which also implement
Type.  They wrap other types as keys/elements.

I'm peeling off this part from my gigantic pending change, since this is
mostly standalone, and ideally leads to more independent chunks.
2017-01-19 12:02:49 -08:00
joeduffy
25632886c8 Begin overhauling semantic phases
This change further merges the new AST and MuPack/MuIL formats and
abstractions into the core of the compiler.  A good amount of the old
code is gone now; I decided against ripping it all out in one fell
swoop so that I can methodically check that we are preserving all
relevant decisions and/or functionality we had in the old model.

The changes are too numerous to outline in this commit message,
however, here are the noteworthy ones:

    * Split up the notion of symbols and tokens, resulting in:

        - pkg/symbols for true compiler symbols (bound nodes)
        - pkg/tokens for name-based tokens, identifiers, constants

    * Several packages move underneath pkg/compiler:

        - pkg/ast becomes pkg/compiler/ast
        - pkg/errors becomes pkg/compiler/errors
        - pkg/symbols becomes pkg/compiler/symbols

    * pkg/ast/... becomes pkg/compiler/legacy/ast/...

    * pkg/pack/ast becomes pkg/compiler/ast.

    * pkg/options goes away, merged back into pkg/compiler.

    * All binding functionality moves underneath a dedicated
      package, pkg/compiler/binder.  The legacy.go file contains
      cruft that will eventually go away, while the other files
      represent a halfway point between new and old, but are
      expected to stay roughly in the current shape.

    * All parsing functionality is moved underneath a new
      pkg/compiler/metadata namespace, and we adopt new terminology
      "metadata reading" since real parsing happens in the MetaMu
      compilers.  Hence, Parser has become metadata.Reader.

    * In general phases of the compiler no longer share access to
      the actual compiler.Compiler object.  Instead, shared state is
      moved to the core.Context object underneath pkg/compiler/core.

    * Dependency resolution during binding has been rewritten to
      the new model, including stashing bound package symbols in the
      context object, and detecting import cycles.

    * Compiler construction does not take a workspace object.  Instead,
      creation of a workspace is entirely hidden inside of the compiler's
      constructor logic.

    * There are three Compile* functions on the Compiler interface, to
      support different styles of invoking compilation: Compile() auto-
      detects a Mu package, based on the workspace; CompilePath(string)
      loads the target as a Mu package and compiles it, regardless of
      the workspace settings; and, CompilePackage(*pack.Package) will
      compile a pre-loaded package AST, again regardless of workspace.

    * Delete the _fe, _sema, and parsetree phases.  They are no longer
      relevant and the functionality is largely subsumed by the above.

...and so very much more.  I'm surprised I ever got this to compile again!
2017-01-18 12:18:37 -08:00
joeduffy
259135c15d Add a visitation API
This change introduces a new visitation API to the new MuIL AST.
The ast.Walk API takes an ast.Visitor implementation and walks the
tree in depth-first order, invoking the visitor along the way.

The visitor gets to choose whether to continue visitation (by returning
a non-nil visitor object), or to stop it (by returning nil).  The
visitation will proceed with that returned visitor, so that a visitor
can "swap out" the visitor used for child nodes if needed.

At the end, the PostVisit function is called, for any clean up logic.

Finally, the ast.Inspector type is available as a simple way of consing
up visitors simply using a function that returns a bool indicating
whether visitation should continue.
2017-01-18 07:56:53 -08:00