2017-06-23 23:53:41 +02:00
|
|
|
// Copyright 2016-2017, Pulumi Corporation
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
2017-05-18 23:51:52 +02:00
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
2017-01-14 00:00:20 +01:00
|
|
|
|
2017-04-12 19:38:12 +02:00
|
|
|
package main
|
2017-01-14 00:00:20 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
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 18:24:52 +01:00
|
|
|
"os"
|
2017-01-16 23:45:32 +01:00
|
|
|
"strings"
|
|
|
|
"unicode"
|
2017-01-14 00:00:20 +01:00
|
|
|
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
|
2017-05-18 20:38:28 +02:00
|
|
|
"github.com/pulumi/lumi/pkg/compiler/ast"
|
|
|
|
"github.com/pulumi/lumi/pkg/pack"
|
|
|
|
"github.com/pulumi/lumi/pkg/tokens"
|
|
|
|
"github.com/pulumi/lumi/pkg/util/cmdutil"
|
|
|
|
"github.com/pulumi/lumi/pkg/util/contract"
|
2017-01-14 00:00:20 +01:00
|
|
|
)
|
|
|
|
|
2017-03-09 16:43:28 +01:00
|
|
|
func newPackInfoCmd() *cobra.Command {
|
Add scaffolding for mu apply, compile, and plan
This adds scaffolding but no real functionality yet, as part of
marapongo/mu#41. I am landing this now because I need to take a
not-so-brief detour to gut and overhaul the core of the existing
compiler (parsing, semantic analysis, binding, code-gen, etc),
including merging the new pkg/pack contents back into the primary
top-level namespaces (like pkg/ast and pkg/encoding).
After that, I can begin driving the compiler to achieve the
desired effects of mu compile, first and foremost, and then plan
and apply later on.
2017-01-17 23:40:55 +01:00
|
|
|
var printAll bool
|
2017-01-14 00:00:20 +01:00
|
|
|
var printIL bool
|
|
|
|
var printSymbols bool
|
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 18:56:25 +01:00
|
|
|
var printExportedSymbols bool
|
2017-01-14 00:00:20 +01:00
|
|
|
var cmd = &cobra.Command{
|
2017-03-09 16:43:28 +01:00
|
|
|
Use: "info [packages...]",
|
|
|
|
Short: "Print information about one or more packages",
|
|
|
|
Long: "Print information about one or more packages\n" +
|
2017-02-26 20:20:14 +01:00
|
|
|
"\n" +
|
2017-03-09 16:43:28 +01:00
|
|
|
"This command prints metadata, symbol, and/or IL from one or more packages.",
|
2017-04-12 20:12:25 +02:00
|
|
|
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
|
Add scaffolding for mu apply, compile, and plan
This adds scaffolding but no real functionality yet, as part of
marapongo/mu#41. I am landing this now because I need to take a
not-so-brief detour to gut and overhaul the core of the existing
compiler (parsing, semantic analysis, binding, code-gen, etc),
including merging the new pkg/pack contents back into the primary
top-level namespaces (like pkg/ast and pkg/encoding).
After that, I can begin driving the compiler to achieve the
desired effects of mu compile, first and foremost, and then plan
and apply later on.
2017-01-17 23:40:55 +01:00
|
|
|
// If printAll is true, flip all the flags.
|
|
|
|
if printAll {
|
|
|
|
printIL = true
|
|
|
|
printSymbols = true
|
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 18:56:25 +01:00
|
|
|
printExportedSymbols = true
|
Add scaffolding for mu apply, compile, and plan
This adds scaffolding but no real functionality yet, as part of
marapongo/mu#41. I am landing this now because I need to take a
not-so-brief detour to gut and overhaul the core of the existing
compiler (parsing, semantic analysis, binding, code-gen, etc),
including merging the new pkg/pack contents back into the primary
top-level namespaces (like pkg/ast and pkg/encoding).
After that, I can begin driving the compiler to achieve the
desired effects of mu compile, first and foremost, and then plan
and apply later on.
2017-01-17 23:40:55 +01:00
|
|
|
}
|
|
|
|
|
2017-06-02 21:43:04 +02:00
|
|
|
var pkg *pack.Package
|
|
|
|
var err error
|
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 18:24:52 +01:00
|
|
|
if len(args) == 0 {
|
|
|
|
// No package specified, just load from the current directory.
|
2017-06-08 19:21:17 +02:00
|
|
|
pwd, locerr := os.Getwd()
|
|
|
|
if locerr != nil {
|
|
|
|
return locerr
|
|
|
|
}
|
2017-06-02 21:43:04 +02:00
|
|
|
if pkg, err = detectPackage(pwd); err != nil {
|
|
|
|
return err
|
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 18:24:52 +01:00
|
|
|
}
|
2017-06-02 21:43:04 +02:00
|
|
|
printPackage(pkg, printSymbols, printExportedSymbols, printIL)
|
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 18:24:52 +01:00
|
|
|
} else {
|
|
|
|
// Enumerate the list of packages, deserialize them, and print information.
|
2017-06-02 21:43:04 +02:00
|
|
|
var path string
|
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 18:24:52 +01:00
|
|
|
for _, arg := range args {
|
2017-06-02 21:43:04 +02:00
|
|
|
pkg, path = readPackageFromArg(arg)
|
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 18:24:52 +01:00
|
|
|
if pkg == nil {
|
2017-06-02 21:43:04 +02:00
|
|
|
if pkg, err = detectPackage(path); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
printPackage(pkg, printSymbols, printExportedSymbols, printIL)
|
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 18:24:52 +01:00
|
|
|
}
|
2017-01-14 20:22:26 +01:00
|
|
|
}
|
2017-01-14 00:00:20 +01:00
|
|
|
}
|
2017-03-07 14:47:42 +01:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}),
|
2017-01-14 00:00:20 +01:00
|
|
|
}
|
|
|
|
|
Add scaffolding for mu apply, compile, and plan
This adds scaffolding but no real functionality yet, as part of
marapongo/mu#41. I am landing this now because I need to take a
not-so-brief detour to gut and overhaul the core of the existing
compiler (parsing, semantic analysis, binding, code-gen, etc),
including merging the new pkg/pack contents back into the primary
top-level namespaces (like pkg/ast and pkg/encoding).
After that, I can begin driving the compiler to achieve the
desired effects of mu compile, first and foremost, and then plan
and apply later on.
2017-01-17 23:40:55 +01:00
|
|
|
cmd.PersistentFlags().BoolVarP(
|
|
|
|
&printSymbols, "all", "a", false,
|
2017-03-09 16:43:28 +01:00
|
|
|
"Print everything: the package, symbols, and IL")
|
2017-01-14 00:00:20 +01:00
|
|
|
cmd.PersistentFlags().BoolVarP(
|
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 18:56:25 +01:00
|
|
|
&printExportedSymbols, "exports", "e", false,
|
2017-01-14 00:00:20 +01:00
|
|
|
"Print just the exported symbols")
|
|
|
|
cmd.PersistentFlags().BoolVarP(
|
|
|
|
&printIL, "il", "i", false,
|
2017-03-09 16:43:28 +01:00
|
|
|
"Pretty-print the package's IL")
|
2017-01-14 00:00:20 +01:00
|
|
|
cmd.PersistentFlags().BoolVarP(
|
|
|
|
&printSymbols, "symbols", "s", false,
|
|
|
|
"Print a complete listing of all symbols, exported or otherwise")
|
|
|
|
|
|
|
|
return cmd
|
|
|
|
}
|
2017-01-16 20:47:21 +01:00
|
|
|
|
2017-01-16 23:45:32 +01:00
|
|
|
func printComment(pc *string, indent string) {
|
|
|
|
// Prints a comment header using the given indentation, wrapping at 100 lines.
|
|
|
|
if pc != nil {
|
|
|
|
prefix := "// "
|
|
|
|
maxlen := 100 - len(indent)
|
|
|
|
|
|
|
|
// For every tab, chew up 3 more chars (so each one is 4 chars wide).
|
|
|
|
for _, i := range indent {
|
|
|
|
if i == '\t' {
|
|
|
|
maxlen -= 3
|
|
|
|
}
|
|
|
|
}
|
|
|
|
maxlen -= len(prefix)
|
|
|
|
if maxlen < 40 {
|
|
|
|
maxlen = 40
|
|
|
|
}
|
|
|
|
|
|
|
|
c := make([]rune, 0)
|
|
|
|
for _, r := range *pc {
|
|
|
|
c = append(c, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
for len(c) > 0 {
|
|
|
|
fmt.Print(indent + prefix)
|
|
|
|
// Now, try to split the comment as close to maxlen-3 chars as possible (taking into account indent+"// "),
|
|
|
|
// but don't split words -- only split at whitespace characters if we can help it.
|
|
|
|
six := maxlen
|
|
|
|
for {
|
|
|
|
if len(c) <= six {
|
|
|
|
six = len(c)
|
|
|
|
break
|
|
|
|
} else if unicode.IsSpace(c[six]) {
|
|
|
|
// It's a space, set six to the first non-space character beforehand, and eix to the first
|
|
|
|
// non-space character afterwards.
|
|
|
|
for six > 0 && unicode.IsSpace(c[six-1]) {
|
|
|
|
six--
|
|
|
|
}
|
|
|
|
break
|
|
|
|
} else if six == 0 {
|
|
|
|
// We hit the start of the string and didn't find any spaces. Start over and try to find the
|
|
|
|
// first space *beyond* the start point (instead of *before*) and use that.
|
|
|
|
six = maxlen + 1
|
2017-01-17 19:00:14 +01:00
|
|
|
for six < len(c) && !unicode.IsSpace(c[six]) {
|
2017-01-16 23:45:32 +01:00
|
|
|
six++
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// We need to keep searching, back up one and try again.
|
|
|
|
six--
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print what we've got thus far, plus a newline.
|
|
|
|
fmt.Printf("%v\n", string(c[:six]))
|
|
|
|
|
|
|
|
// Now find the first non-space character beyond the split point and use that for the remainder.
|
|
|
|
eix := six
|
|
|
|
for eix < len(c) && unicode.IsSpace(c[eix]) {
|
|
|
|
eix++
|
|
|
|
}
|
|
|
|
c = c[eix:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// printPackage pretty-prints the package metadata.
|
2017-01-16 20:47:21 +01:00
|
|
|
func printPackage(pkg *pack.Package, printSymbols bool, printExports bool, printIL bool) {
|
2017-01-16 23:45:32 +01:00
|
|
|
printComment(pkg.Description, "")
|
2017-01-16 20:47:21 +01:00
|
|
|
fmt.Printf("package \"%v\" {\n", pkg.Name)
|
2017-01-16 23:45:32 +01:00
|
|
|
|
|
|
|
if pkg.Author != nil {
|
|
|
|
fmt.Printf("%vauthor \"%v\"\n", tab, *pkg.Author)
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
2017-01-16 23:45:32 +01:00
|
|
|
if pkg.Website != nil {
|
|
|
|
fmt.Printf("%vwebsite \"%v\"\n", tab, *pkg.Website)
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
2017-01-16 23:45:32 +01:00
|
|
|
if pkg.License != nil {
|
|
|
|
fmt.Printf("%vlicense \"%v\"\n", tab, *pkg.License)
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Print the dependencies:
|
2017-01-16 23:45:32 +01:00
|
|
|
fmt.Printf("%vdependencies [", tab)
|
2017-01-16 20:47:21 +01:00
|
|
|
if pkg.Dependencies != nil && len(*pkg.Dependencies) > 0 {
|
|
|
|
fmt.Printf("\n")
|
2017-02-21 21:30:31 +01:00
|
|
|
for _, dep := range pack.StableDependencies(*pkg.Dependencies) {
|
|
|
|
fmt.Printf("%v%v: \"%v\"\n", tab+tab, dep, (*pkg.Dependencies)[dep])
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
2017-01-17 18:55:58 +01:00
|
|
|
fmt.Printf("%v", tab)
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
|
|
|
fmt.Printf("]\n")
|
|
|
|
|
|
|
|
// Print the modules (just names by default, or full symbols and/or IL if requested).
|
2017-01-16 23:45:32 +01:00
|
|
|
printModules(pkg, printSymbols, printExports, printIL, tab)
|
2017-01-16 20:47:21 +01:00
|
|
|
|
|
|
|
fmt.Printf("}\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
func printModules(pkg *pack.Package, printSymbols bool, printExports bool, printIL bool, indent string) {
|
Implement dynamic loads from the environment¬
This rearranges the way dynamic loads work a bit. Previously, they¬
required an object, and did a dynamic lookup in the object's property¬
map. For real dynamic loads -- of the kind Python uses, obviously,¬
but also ECMAScript -- we need to search the "environment".
This change searches the environment by looking first in the lexical¬
scope in the current function. If a variable exists, we will use it.¬
If that misses, we then look in the module scope. If a variable exists¬
there, we will use it. Otherwise, if the variable is used in a non-lval
position, an dynamic error will be raised ("name not declared"). If
an lval, however, we will lazily allocate a slot for it.
Note that Python doesn't use block scoping in the same way that most
languages do. This behavior is simply achieved by Python not emitting
any lexically scoped blocks other than at the function level.
This doesn't perfectly achieve the scoping behavior, because we don't
yet bind every name in a way that they can be dynamically discovered.
The two obvious cases are class names and import names. Those will be
covered in a subsequent commit.
Also note that we are getting lucky here that class static/instance
variables aren't accessible in Python or ECMAScript "ambiently" like
they are in some languages (e.g., C#, Java); as a result, we don't need
to introduce a class scope in the dynamic lookup. Some day, when we
want to support such languages, we'll need to think about how to let
languages control the environment probe order; for instance, perhaps
the LoadDynamicExpression node can have an "environment" property.
2017-04-09 01:47:15 +02:00
|
|
|
if pkg.Modules != nil {
|
|
|
|
pkgtok := tokens.NewPackageToken(pkg.Name)
|
|
|
|
for _, name := range ast.StableModules(*pkg.Modules) {
|
|
|
|
mod := (*pkg.Modules)[name]
|
|
|
|
modtok := tokens.NewModuleToken(pkgtok, name)
|
|
|
|
|
|
|
|
// Print the name.
|
|
|
|
fmt.Printf("%vmodule \"%v\" {", indent, name)
|
|
|
|
|
|
|
|
// Now, if requested, print the tokens.
|
|
|
|
if printSymbols || printExports {
|
2017-04-09 03:16:10 +02:00
|
|
|
if mod.Exports != nil || mod.Members != nil {
|
Implement dynamic loads from the environment¬
This rearranges the way dynamic loads work a bit. Previously, they¬
required an object, and did a dynamic lookup in the object's property¬
map. For real dynamic loads -- of the kind Python uses, obviously,¬
but also ECMAScript -- we need to search the "environment".
This change searches the environment by looking first in the lexical¬
scope in the current function. If a variable exists, we will use it.¬
If that misses, we then look in the module scope. If a variable exists¬
there, we will use it. Otherwise, if the variable is used in a non-lval
position, an dynamic error will be raised ("name not declared"). If
an lval, however, we will lazily allocate a slot for it.
Note that Python doesn't use block scoping in the same way that most
languages do. This behavior is simply achieved by Python not emitting
any lexically scoped blocks other than at the function level.
This doesn't perfectly achieve the scoping behavior, because we don't
yet bind every name in a way that they can be dynamically discovered.
The two obvious cases are class names and import names. Those will be
covered in a subsequent commit.
Also note that we are getting lucky here that class static/instance
variables aren't accessible in Python or ECMAScript "ambiently" like
they are in some languages (e.g., C#, Java); as a result, we don't need
to introduce a class scope in the dynamic lookup. Some day, when we
want to support such languages, we'll need to think about how to let
languages control the environment probe order; for instance, perhaps
the LoadDynamicExpression node can have an "environment" property.
2017-04-09 01:47:15 +02:00
|
|
|
fmt.Printf("\n")
|
|
|
|
|
|
|
|
exports := make(map[tokens.Token]bool)
|
|
|
|
if mod.Exports != nil {
|
|
|
|
// Print the exports.
|
|
|
|
fmt.Printf("%vexports [", indent+tab)
|
|
|
|
if mod.Exports != nil && len(*mod.Exports) > 0 {
|
|
|
|
fmt.Printf("\n")
|
|
|
|
for _, exp := range ast.StableModuleExports(*mod.Exports) {
|
|
|
|
ref := (*mod.Exports)[exp].Referent.Tok
|
|
|
|
fmt.Printf("%v\"%v\" -> \"%v\"\n", indent+tab+tab, exp, ref)
|
|
|
|
exports[ref] = true
|
|
|
|
}
|
|
|
|
fmt.Printf("%v", indent+tab)
|
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 18:56:25 +01:00
|
|
|
}
|
Implement dynamic loads from the environment¬
This rearranges the way dynamic loads work a bit. Previously, they¬
required an object, and did a dynamic lookup in the object's property¬
map. For real dynamic loads -- of the kind Python uses, obviously,¬
but also ECMAScript -- we need to search the "environment".
This change searches the environment by looking first in the lexical¬
scope in the current function. If a variable exists, we will use it.¬
If that misses, we then look in the module scope. If a variable exists¬
there, we will use it. Otherwise, if the variable is used in a non-lval
position, an dynamic error will be raised ("name not declared"). If
an lval, however, we will lazily allocate a slot for it.
Note that Python doesn't use block scoping in the same way that most
languages do. This behavior is simply achieved by Python not emitting
any lexically scoped blocks other than at the function level.
This doesn't perfectly achieve the scoping behavior, because we don't
yet bind every name in a way that they can be dynamically discovered.
The two obvious cases are class names and import names. Those will be
covered in a subsequent commit.
Also note that we are getting lucky here that class static/instance
variables aren't accessible in Python or ECMAScript "ambiently" like
they are in some languages (e.g., C#, Java); as a result, we don't need
to introduce a class scope in the dynamic lookup. Some day, when we
want to support such languages, we'll need to think about how to let
languages control the environment probe order; for instance, perhaps
the LoadDynamicExpression node can have an "environment" property.
2017-04-09 01:47:15 +02:00
|
|
|
fmt.Printf("]\n")
|
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 18:56:25 +01:00
|
|
|
}
|
|
|
|
|
Implement dynamic loads from the environment¬
This rearranges the way dynamic loads work a bit. Previously, they¬
required an object, and did a dynamic lookup in the object's property¬
map. For real dynamic loads -- of the kind Python uses, obviously,¬
but also ECMAScript -- we need to search the "environment".
This change searches the environment by looking first in the lexical¬
scope in the current function. If a variable exists, we will use it.¬
If that misses, we then look in the module scope. If a variable exists¬
there, we will use it. Otherwise, if the variable is used in a non-lval
position, an dynamic error will be raised ("name not declared"). If
an lval, however, we will lazily allocate a slot for it.
Note that Python doesn't use block scoping in the same way that most
languages do. This behavior is simply achieved by Python not emitting
any lexically scoped blocks other than at the function level.
This doesn't perfectly achieve the scoping behavior, because we don't
yet bind every name in a way that they can be dynamically discovered.
The two obvious cases are class names and import names. Those will be
covered in a subsequent commit.
Also note that we are getting lucky here that class static/instance
variables aren't accessible in Python or ECMAScript "ambiently" like
they are in some languages (e.g., C#, Java); as a result, we don't need
to introduce a class scope in the dynamic lookup. Some day, when we
want to support such languages, we'll need to think about how to let
languages control the environment probe order; for instance, perhaps
the LoadDynamicExpression node can have an "environment" property.
2017-04-09 01:47:15 +02:00
|
|
|
if mod.Members != nil {
|
|
|
|
// Print the members.
|
|
|
|
for _, member := range ast.StableModuleMembers(*mod.Members) {
|
|
|
|
memtok := tokens.NewModuleMemberToken(modtok, member)
|
|
|
|
printModuleMember(memtok, (*mod.Members)[member], printExports, exports, indent+tab)
|
|
|
|
}
|
|
|
|
fmt.Printf("%v", indent)
|
Add scaffolding for mu apply, compile, and plan
This adds scaffolding but no real functionality yet, as part of
marapongo/mu#41. I am landing this now because I need to take a
not-so-brief detour to gut and overhaul the core of the existing
compiler (parsing, semantic analysis, binding, code-gen, etc),
including merging the new pkg/pack contents back into the primary
top-level namespaces (like pkg/ast and pkg/encoding).
After that, I can begin driving the compiler to achieve the
desired effects of mu compile, first and foremost, and then plan
and apply later on.
2017-01-17 23:40:55 +01:00
|
|
|
}
|
|
|
|
}
|
Implement dynamic loads from the environment¬
This rearranges the way dynamic loads work a bit. Previously, they¬
required an object, and did a dynamic lookup in the object's property¬
map. For real dynamic loads -- of the kind Python uses, obviously,¬
but also ECMAScript -- we need to search the "environment".
This change searches the environment by looking first in the lexical¬
scope in the current function. If a variable exists, we will use it.¬
If that misses, we then look in the module scope. If a variable exists¬
there, we will use it. Otherwise, if the variable is used in a non-lval
position, an dynamic error will be raised ("name not declared"). If
an lval, however, we will lazily allocate a slot for it.
Note that Python doesn't use block scoping in the same way that most
languages do. This behavior is simply achieved by Python not emitting
any lexically scoped blocks other than at the function level.
This doesn't perfectly achieve the scoping behavior, because we don't
yet bind every name in a way that they can be dynamically discovered.
The two obvious cases are class names and import names. Those will be
covered in a subsequent commit.
Also note that we are getting lucky here that class static/instance
variables aren't accessible in Python or ECMAScript "ambiently" like
they are in some languages (e.g., C#, Java); as a result, we don't need
to introduce a class scope in the dynamic lookup. Some day, when we
want to support such languages, we'll need to think about how to let
languages control the environment probe order; for instance, perhaps
the LoadDynamicExpression node can have an "environment" property.
2017-04-09 01:47:15 +02:00
|
|
|
} else {
|
|
|
|
// Print a "..." so that it's clear we're omitting information, versus the module being empty.
|
|
|
|
fmt.Printf("...")
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
Implement dynamic loads from the environment¬
This rearranges the way dynamic loads work a bit. Previously, they¬
required an object, and did a dynamic lookup in the object's property¬
map. For real dynamic loads -- of the kind Python uses, obviously,¬
but also ECMAScript -- we need to search the "environment".
This change searches the environment by looking first in the lexical¬
scope in the current function. If a variable exists, we will use it.¬
If that misses, we then look in the module scope. If a variable exists¬
there, we will use it. Otherwise, if the variable is used in a non-lval
position, an dynamic error will be raised ("name not declared"). If
an lval, however, we will lazily allocate a slot for it.
Note that Python doesn't use block scoping in the same way that most
languages do. This behavior is simply achieved by Python not emitting
any lexically scoped blocks other than at the function level.
This doesn't perfectly achieve the scoping behavior, because we don't
yet bind every name in a way that they can be dynamically discovered.
The two obvious cases are class names and import names. Those will be
covered in a subsequent commit.
Also note that we are getting lucky here that class static/instance
variables aren't accessible in Python or ECMAScript "ambiently" like
they are in some languages (e.g., C#, Java); as a result, we don't need
to introduce a class scope in the dynamic lookup. Some day, when we
want to support such languages, we'll need to think about how to let
languages control the environment probe order; for instance, perhaps
the LoadDynamicExpression node can have an "environment" property.
2017-04-09 01:47:15 +02:00
|
|
|
fmt.Printf("}\n")
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 18:56:25 +01:00
|
|
|
func printModuleMember(tok tokens.ModuleMember, member ast.ModuleMember,
|
|
|
|
exportOnly bool, exports map[tokens.Token]bool, indent string) {
|
2017-01-16 23:45:32 +01:00
|
|
|
printComment(member.GetDescription(), indent)
|
|
|
|
|
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 18:56:25 +01:00
|
|
|
if !exportOnly || exports[tokens.Token(tok)] {
|
2017-01-16 20:47:21 +01:00
|
|
|
switch member.GetKind() {
|
|
|
|
case ast.ClassKind:
|
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 18:56:25 +01:00
|
|
|
printClass(tokens.Type(tok), member.(*ast.Class), exportOnly, indent)
|
2017-01-16 20:47:21 +01:00
|
|
|
case ast.ModulePropertyKind:
|
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 18:56:25 +01:00
|
|
|
printModuleProperty(tok, member.(*ast.ModuleProperty), indent)
|
2017-01-16 20:47:21 +01:00
|
|
|
case ast.ModuleMethodKind:
|
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 18:56:25 +01:00
|
|
|
printModuleMethod(tok, member.(*ast.ModuleMethod), indent)
|
2017-01-16 20:47:21 +01:00
|
|
|
default:
|
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 18:56:25 +01:00
|
|
|
contract.Failf("Unexpected ModuleMember kind: %v (tok %v)\n", member.GetKind(), tok)
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 18:56:25 +01:00
|
|
|
func printClass(tok tokens.Type, class *ast.Class, exportOnly bool, indent string) {
|
|
|
|
fmt.Printf("%vclass \"%v\"", indent, tok.Name())
|
2017-01-16 20:47:21 +01:00
|
|
|
|
|
|
|
var mods []string
|
|
|
|
if class.Sealed != nil && *class.Sealed {
|
|
|
|
mods = append(mods, "sealed")
|
|
|
|
}
|
|
|
|
if class.Abstract != nil && *class.Abstract {
|
|
|
|
mods = append(mods, "abstract")
|
|
|
|
}
|
|
|
|
if class.Record != nil && *class.Record {
|
|
|
|
mods = append(mods, "record")
|
|
|
|
}
|
|
|
|
if class.Interface != nil && *class.Interface {
|
|
|
|
mods = append(mods, "interface")
|
|
|
|
}
|
2017-05-25 21:11:59 +02:00
|
|
|
if class.Attributes != nil {
|
|
|
|
for _, att := range *class.Attributes {
|
|
|
|
mods = append(mods, "@"+att.Decorator.Tok.String())
|
|
|
|
}
|
|
|
|
}
|
2017-06-07 19:52:03 +02:00
|
|
|
fmt.Print(modString(mods))
|
2017-01-16 20:47:21 +01:00
|
|
|
|
|
|
|
if class.Extends != nil {
|
2017-01-21 20:48:58 +01:00
|
|
|
fmt.Printf("\n%vextends %v", indent+tab+tab, string(class.Extends.Tok))
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
|
|
|
if class.Implements != nil {
|
|
|
|
for _, impl := range *class.Implements {
|
2017-01-21 20:48:58 +01:00
|
|
|
fmt.Printf("\n%vimplements %v", indent+tab+tab, string(impl.Tok))
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Printf(" {")
|
|
|
|
if class.Members != nil {
|
|
|
|
fmt.Printf("\n")
|
2017-01-16 21:02:33 +01:00
|
|
|
for _, member := range ast.StableClassMembers(*class.Members) {
|
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 18:56:25 +01:00
|
|
|
memtok := tokens.NewClassMemberToken(tok, member)
|
|
|
|
printClassMember(memtok, (*class.Members)[member], exportOnly, indent+tab)
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
2017-06-07 19:52:03 +02:00
|
|
|
fmt.Print(indent)
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
|
|
|
fmt.Printf("}\n")
|
|
|
|
}
|
|
|
|
|
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 18:56:25 +01:00
|
|
|
func printClassMember(tok tokens.ClassMember, member ast.ClassMember, exportOnly bool, indent string) {
|
2017-01-16 23:45:32 +01:00
|
|
|
printComment(member.GetDescription(), indent)
|
|
|
|
|
2017-01-16 20:47:21 +01:00
|
|
|
acc := member.GetAccess()
|
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 18:56:25 +01:00
|
|
|
if !exportOnly || (acc != nil && *acc == tokens.PublicAccessibility) {
|
2017-01-16 20:47:21 +01:00
|
|
|
switch member.GetKind() {
|
|
|
|
case ast.ClassPropertyKind:
|
2017-05-25 21:11:59 +02:00
|
|
|
printClassProperty(tok.Name(), member.(*ast.ClassProperty), indent)
|
2017-01-16 20:47:21 +01:00
|
|
|
case ast.ClassMethodKind:
|
2017-05-25 21:11:59 +02:00
|
|
|
printClassMethod(tok.Name(), member.(*ast.ClassMethod), indent)
|
2017-01-16 20:47:21 +01:00
|
|
|
default:
|
Begin merging MuPackage/MuIL into the compiler
This is the first change of many to merge the MuPack/MuIL formats
into the heart of the "compiler".
In fact, the entire meaning of the compiler has changed, from
something that took metadata and produced CloudFormation, into
something that takes MuPack/MuIL as input, and produces a MuGL
graph as output. Although this process is distinctly different,
there are several aspects we can reuse, like workspace management,
dependency resolution, and some amount of name binding and symbol
resolution, just as a few examples.
An overview of the compilation process is available as a comment
inside of the compiler.Compile function, although it is currently
unimplemented.
The relationship between Workspace and Compiler has been semi-
inverted, such that all Compiler instances require a Workspace
object. This is more natural anyway and moves some of the detection
logic "outside" of the Compiler. Similarly, Options has moved to
a top-level package, so that Workspace and Compiler may share
access to it without causing package import cycles.
Finally, all that templating crap is gone. This alone is cause
for mass celebration!
2017-01-18 02:04:15 +01:00
|
|
|
contract.Failf("Unexpected ClassMember kind: %v\n", member.GetKind())
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-25 21:11:59 +02:00
|
|
|
func printClassProperty(name tokens.ClassMemberName, prop *ast.ClassProperty, indent string) {
|
2017-01-16 20:47:21 +01:00
|
|
|
var mods []string
|
|
|
|
if prop.Access != nil {
|
|
|
|
mods = append(mods, string(*prop.Access))
|
|
|
|
}
|
|
|
|
if prop.Static != nil && *prop.Static {
|
|
|
|
mods = append(mods, "static")
|
|
|
|
}
|
|
|
|
if prop.Readonly != nil && *prop.Readonly {
|
|
|
|
mods = append(mods, "readonly")
|
|
|
|
}
|
2017-05-25 21:11:59 +02:00
|
|
|
if prop.Attributes != nil {
|
|
|
|
for _, att := range *prop.Attributes {
|
|
|
|
mods = append(mods, "@"+att.Decorator.Tok.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Printf("%vproperty \"%v\"%v", indent, name, modString(mods))
|
2017-01-16 20:47:21 +01:00
|
|
|
if prop.Type != nil {
|
2017-01-21 21:25:59 +01:00
|
|
|
fmt.Printf(": %v", prop.Type.Tok)
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
2017-05-25 21:11:59 +02:00
|
|
|
|
|
|
|
if prop.Getter != nil || prop.Setter != nil {
|
|
|
|
fmt.Printf(" {\n")
|
|
|
|
if prop.Getter != nil {
|
|
|
|
printClassMethod(tokens.ClassMemberName("get"), prop.Getter, indent+" ")
|
|
|
|
}
|
|
|
|
if prop.Setter != nil {
|
|
|
|
printClassMethod(tokens.ClassMemberName("set"), prop.Setter, indent+" ")
|
|
|
|
}
|
|
|
|
fmt.Printf("%v}\n", indent)
|
|
|
|
} else {
|
|
|
|
fmt.Printf("\n")
|
|
|
|
}
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
|
|
|
|
2017-05-25 21:11:59 +02:00
|
|
|
func printClassMethod(name tokens.ClassMemberName, meth *ast.ClassMethod, indent string) {
|
2017-01-16 20:47:21 +01:00
|
|
|
var mods []string
|
|
|
|
if meth.Access != nil {
|
|
|
|
mods = append(mods, string(*meth.Access))
|
|
|
|
}
|
|
|
|
if meth.Static != nil && *meth.Static {
|
|
|
|
mods = append(mods, "static")
|
|
|
|
}
|
|
|
|
if meth.Sealed != nil && *meth.Sealed {
|
|
|
|
mods = append(mods, "sealed")
|
|
|
|
}
|
|
|
|
if meth.Abstract != nil && *meth.Abstract {
|
|
|
|
mods = append(mods, "abstract")
|
|
|
|
}
|
2017-05-25 21:11:59 +02:00
|
|
|
if meth.Attributes != nil {
|
|
|
|
for _, att := range *meth.Attributes {
|
|
|
|
mods = append(mods, "@"+att.Decorator.Tok.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Printf("%vmethod \"%v\"%v: %v\n", indent, name, modString(mods), funcSig(meth))
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
|
|
|
|
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 18:56:25 +01:00
|
|
|
func printModuleMethod(tok tokens.ModuleMember, meth *ast.ModuleMethod, indent string) {
|
|
|
|
fmt.Printf("%vmethod \"%v\": %v\n", indent, tok.Name(), funcSig(meth))
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
|
|
|
|
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 18:56:25 +01:00
|
|
|
func printModuleProperty(tok tokens.ModuleMember, prop *ast.ModuleProperty, indent string) {
|
2017-01-16 20:47:21 +01:00
|
|
|
var mods []string
|
|
|
|
if prop.Readonly != nil && *prop.Readonly {
|
|
|
|
mods = append(mods, "readonly")
|
|
|
|
}
|
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 18:56:25 +01:00
|
|
|
fmt.Printf("%vproperty \"%v\"%v", indent, tok.Name(), modString(mods))
|
2017-01-16 20:47:21 +01:00
|
|
|
if prop.Type != nil {
|
2017-01-21 21:25:59 +01:00
|
|
|
fmt.Printf(": %v", prop.Type.Tok)
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
|
|
|
fmt.Printf("\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
func modString(mods []string) string {
|
|
|
|
if len(mods) == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
s := " ["
|
|
|
|
for i, mod := range mods {
|
|
|
|
if i > 0 {
|
|
|
|
s += ", "
|
|
|
|
}
|
|
|
|
s += mod
|
|
|
|
}
|
|
|
|
s += "]"
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2017-01-16 23:45:32 +01:00
|
|
|
// spaces returns a string with the given number of spaces.
|
|
|
|
func spaces(num int) string {
|
|
|
|
return strings.Repeat(" ", num)
|
|
|
|
}
|
|
|
|
|
|
|
|
// tab is a tab represented as spaces, since some consoles have ridiculously wide true tabs.
|
|
|
|
var tab = spaces(4)
|
|
|
|
|
2017-01-16 20:47:21 +01:00
|
|
|
func funcSig(fun ast.Function) string {
|
|
|
|
sig := "("
|
|
|
|
|
|
|
|
// To create a signature, first concatenate the parameters.
|
|
|
|
params := fun.GetParameters()
|
|
|
|
if params != nil {
|
|
|
|
for i, param := range *params {
|
|
|
|
if i > 0 {
|
|
|
|
sig += ", "
|
|
|
|
}
|
|
|
|
sig += string(param.Name.Ident)
|
2017-05-25 21:11:59 +02:00
|
|
|
|
|
|
|
var mods []string
|
|
|
|
if param.Attributes != nil {
|
|
|
|
for _, att := range *param.Attributes {
|
|
|
|
mods = append(mods, "@"+att.Decorator.Tok.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sig += modString(mods)
|
|
|
|
|
2017-01-16 20:47:21 +01:00
|
|
|
if param.Type != nil {
|
2017-01-21 20:48:58 +01:00
|
|
|
sig += ": " + string(param.Type.Tok)
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sig += ")"
|
|
|
|
|
|
|
|
// And then the return type, if present.
|
|
|
|
ret := fun.GetReturnType()
|
|
|
|
if ret != nil {
|
2017-01-21 20:48:58 +01:00
|
|
|
sig += ": " + string(ret.Tok)
|
2017-01-16 20:47:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return sig
|
|
|
|
}
|