2016-11-16 22:11:58 +01:00
|
|
|
// Copyright 2016 Marapongo, Inc. All rights reserved.
|
|
|
|
|
|
|
|
package ast
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
// NameDelimiter is what delimits Namespace and Name parts.
|
|
|
|
const NameDelimiter = "/"
|
|
|
|
|
Implement dependency resolution
This change includes logic to resolve dependencies declared by stacks. The design
is described in https://github.com/marapongo/mu/blob/master/docs/deps.md.
In summary, each stack may declare dependencies, which are name/semver pairs. A
new structure has been introduced, ast.Ref, to distinguish between ast.Names and
dependency names. An ast.Ref includes a protocol, base part, and a name part (the
latter being an ast.Name); for example, in "https://hub.mu.com/mu/container/",
"https://" is the protocol, "hub.mu.com/" is the base, and "mu/container" is the
name. This is used to resolve URL-like names to package manager-like artifacts.
The dependency resolution phase happens after parsing, but before semantic analysis.
This is because dependencies are "source-like" in that we must load and parse all
dependency metadata files. We stick the full transitive closure of dependencies
into a map attached to the compiler to avoid loading dependencies multiple times.
Note that, although dependencies prohibit cycles, this forms a DAG, meaning multiple
inbound edges to a single stack may come from multiple places.
From there, we rely on ordinary visitation to deal with dependencies further.
This includes inserting symbol entries into the symbol table, mapping names to the
loaded stacks, during the first phase of binding so that they may be found
subsequently when typechecking during the second phase and beyond.
2016-11-21 20:19:25 +01:00
|
|
|
// Simple extracts the name portion of a Name (dropping any Namespace).
|
|
|
|
func (nm Name) Simple() Name {
|
2016-11-16 22:11:58 +01:00
|
|
|
ix := strings.LastIndex(string(nm), NameDelimiter)
|
|
|
|
if ix == -1 {
|
|
|
|
return nm
|
|
|
|
}
|
|
|
|
return nm[ix+1:]
|
|
|
|
}
|
|
|
|
|
Implement dependency resolution
This change includes logic to resolve dependencies declared by stacks. The design
is described in https://github.com/marapongo/mu/blob/master/docs/deps.md.
In summary, each stack may declare dependencies, which are name/semver pairs. A
new structure has been introduced, ast.Ref, to distinguish between ast.Names and
dependency names. An ast.Ref includes a protocol, base part, and a name part (the
latter being an ast.Name); for example, in "https://hub.mu.com/mu/container/",
"https://" is the protocol, "hub.mu.com/" is the base, and "mu/container" is the
name. This is used to resolve URL-like names to package manager-like artifacts.
The dependency resolution phase happens after parsing, but before semantic analysis.
This is because dependencies are "source-like" in that we must load and parse all
dependency metadata files. We stick the full transitive closure of dependencies
into a map attached to the compiler to avoid loading dependencies multiple times.
Note that, although dependencies prohibit cycles, this forms a DAG, meaning multiple
inbound edges to a single stack may come from multiple places.
From there, we rely on ordinary visitation to deal with dependencies further.
This includes inserting symbol entries into the symbol table, mapping names to the
loaded stacks, during the first phase of binding so that they may be found
subsequently when typechecking during the second phase and beyond.
2016-11-21 20:19:25 +01:00
|
|
|
// Namespace extracts the namespace portion of a Name (dropping the Name); this may be empty.
|
|
|
|
func (nm Name) Namespace() Name {
|
2016-11-16 22:11:58 +01:00
|
|
|
ix := strings.LastIndex(string(nm), NameDelimiter)
|
|
|
|
if ix == -1 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return nm[:ix]
|
|
|
|
}
|
Implement dependency resolution
This change includes logic to resolve dependencies declared by stacks. The design
is described in https://github.com/marapongo/mu/blob/master/docs/deps.md.
In summary, each stack may declare dependencies, which are name/semver pairs. A
new structure has been introduced, ast.Ref, to distinguish between ast.Names and
dependency names. An ast.Ref includes a protocol, base part, and a name part (the
latter being an ast.Name); for example, in "https://hub.mu.com/mu/container/",
"https://" is the protocol, "hub.mu.com/" is the base, and "mu/container" is the
name. This is used to resolve URL-like names to package manager-like artifacts.
The dependency resolution phase happens after parsing, but before semantic analysis.
This is because dependencies are "source-like" in that we must load and parse all
dependency metadata files. We stick the full transitive closure of dependencies
into a map attached to the compiler to avoid loading dependencies multiple times.
Note that, although dependencies prohibit cycles, this forms a DAG, meaning multiple
inbound edges to a single stack may come from multiple places.
From there, we rely on ordinary visitation to deal with dependencies further.
This includes inserting symbol entries into the symbol table, mapping names to the
loaded stacks, during the first phase of binding so that they may be found
subsequently when typechecking during the second phase and beyond.
2016-11-21 20:19:25 +01:00
|
|
|
|
|
|
|
// DefaultRefBase is the base part used if a Ref doesn't specify one explicitly.
|
|
|
|
const DefaultRefBase = "hub.mu.com/"
|
|
|
|
|
|
|
|
// Base extracts the base portion of a Ref.
|
|
|
|
func (r Ref) Base() string {
|
|
|
|
return r.parse().Base
|
|
|
|
}
|
|
|
|
|
|
|
|
// Name extracts the name portion of a Ref (including the Namespace).
|
|
|
|
func (r Ref) Name() Name {
|
|
|
|
return r.parse().Name
|
|
|
|
}
|
|
|
|
|
|
|
|
type refParts struct {
|
|
|
|
Proto string // the protocol (e.g., "https://").
|
|
|
|
Base string // the base part of the URL (e.g., "mu.hub.com/").
|
|
|
|
Name Name // the name part of the URL (e.g., "mu/container").
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r Ref) parse() refParts {
|
|
|
|
s := string(r)
|
|
|
|
parts := refParts{}
|
|
|
|
|
|
|
|
// Look for the leading protocol, if any.
|
|
|
|
pix := strings.Index(s, "://")
|
|
|
|
if pix != -1 {
|
|
|
|
// Remember it and then strip it off for subsequent parsing.
|
|
|
|
parts.Proto = s[:pix]
|
|
|
|
s = s[pix+1:]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now look to see if there is a dot, indicating a base part.
|
|
|
|
bix := strings.Index(s, ".")
|
|
|
|
if bix == -1 {
|
|
|
|
// No base seems to be here; populate it with the default ref base.
|
|
|
|
// TODO(joe): this might be questionable; e.g., domain-less hosts will require a trailing period.
|
|
|
|
parts.Base = DefaultRefBase
|
|
|
|
} else {
|
|
|
|
// A base exists; look for a slash (indicating the name), and capture everything up to it.
|
|
|
|
six := strings.Index(s[bix+1:], NameDelimiter)
|
|
|
|
if six == -1 {
|
|
|
|
parts.Base = s
|
|
|
|
s = ""
|
|
|
|
} else {
|
|
|
|
rest := bix + 1 + six
|
|
|
|
parts.Base = s[:rest]
|
|
|
|
s = s[rest+1:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Anything remaining at this point represents the name.
|
|
|
|
parts.Name = Name(s)
|
|
|
|
|
|
|
|
return parts
|
|
|
|
}
|