2016-11-16 02:42:22 +01:00
|
|
|
// Copyright 2016 Marapongo, Inc. All rights reserved.
|
|
|
|
|
|
|
|
package compiler
|
|
|
|
|
|
|
|
import (
|
2016-11-16 19:00:52 +01:00
|
|
|
"github.com/blang/semver"
|
2016-11-16 20:09:45 +01:00
|
|
|
"github.com/golang/glog"
|
2016-11-16 19:00:52 +01:00
|
|
|
|
2016-11-16 18:29:44 +01:00
|
|
|
"github.com/marapongo/mu/pkg/ast"
|
2016-11-16 02:42:22 +01:00
|
|
|
"github.com/marapongo/mu/pkg/diag"
|
|
|
|
"github.com/marapongo/mu/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
// PTAnalyzer knows how to walk and validate parse trees.
|
|
|
|
type PTAnalyzer interface {
|
2016-11-16 20:09:45 +01:00
|
|
|
Visitor
|
2016-11-16 02:42:22 +01:00
|
|
|
|
|
|
|
// Analyze checks the validity of an entire parse tree (starting with a top-level Stack).
|
2016-11-16 18:29:44 +01:00
|
|
|
Analyze(doc *diag.Document, stack *ast.Stack)
|
2016-11-16 02:42:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewPTAnalayzer allocates a new PTAnalyzer associated with the given Compiler.
|
|
|
|
func NewPTAnalyzer(c Compiler) PTAnalyzer {
|
|
|
|
return &ptAnalyzer{c: c}
|
|
|
|
}
|
|
|
|
|
|
|
|
type ptAnalyzer struct {
|
|
|
|
c Compiler
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *ptAnalyzer) Diag() diag.Sink {
|
|
|
|
return a.c.Diag()
|
|
|
|
}
|
|
|
|
|
2016-11-16 18:29:44 +01:00
|
|
|
func (a *ptAnalyzer) Analyze(doc *diag.Document, stack *ast.Stack) {
|
2016-11-16 20:09:45 +01:00
|
|
|
glog.Infof("Parsetree analyzing Mu Stack: %v", stack.Name)
|
|
|
|
if glog.V(2) {
|
|
|
|
defer func() {
|
|
|
|
glog.V(2).Infof("Parsetree analysis for Mu Stack %v completed w/ %v warnings and %v errors",
|
|
|
|
stack.Name, a.Diag().Warnings(), a.Diag().Errors())
|
|
|
|
}()
|
2016-11-16 02:42:22 +01:00
|
|
|
}
|
2016-11-16 20:45:41 +01:00
|
|
|
|
|
|
|
// Use an InOrderVisitor to walk the tree in-order; this handles determinism for us.
|
|
|
|
v := NewInOrderVisitor(a, nil)
|
|
|
|
v.VisitStack(doc, stack)
|
2016-11-16 20:09:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a *ptAnalyzer) VisitMetadata(doc *diag.Document, kind string, meta *ast.Metadata) {
|
2016-11-16 20:51:50 +01:00
|
|
|
// Decorate the AST with contextual information so subsequent passes can operate context-free.
|
|
|
|
meta.Kind = kind
|
|
|
|
|
2016-11-16 20:09:45 +01:00
|
|
|
// Metadata names are required.
|
|
|
|
if meta.Name == "" {
|
|
|
|
a.Diag().Errorf(errors.MissingMetadataName.WithDocument(doc), kind)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Metadata versions must be valid semantic versions (and specifically, not ranges). In other words, we need
|
|
|
|
// a concrete version number like "1.3.9-beta2" and *not* a range like ">1.3.9".
|
|
|
|
// TODO: should we require a version number?
|
|
|
|
if meta.Version != "" {
|
|
|
|
if _, err := semver.Parse(string(meta.Version)); err != nil {
|
|
|
|
a.Diag().Errorf(errors.IllegalMetadataSemVer.WithDocument(doc), kind, meta.Version)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *ptAnalyzer) VisitStack(doc *diag.Document, stack *ast.Stack) {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *ptAnalyzer) VisitParameter(doc *diag.Document, name string, param *ast.Parameter) {
|
2016-11-16 20:51:50 +01:00
|
|
|
// Decorate the AST with contextual information so subsequent passes can operate context-free.
|
|
|
|
param.Name = name
|
2016-11-16 20:09:45 +01:00
|
|
|
}
|
|
|
|
|
2016-11-16 22:11:58 +01:00
|
|
|
func (a *ptAnalyzer) VisitDependency(doc *diag.Document, name ast.Name, dep *ast.Dependency) {
|
2016-11-16 20:09:45 +01:00
|
|
|
// Dependency versions must be valid semantic versions *or* ranges.
|
|
|
|
// TODO: should we require dependencies to have versions?
|
|
|
|
ver := *dep
|
|
|
|
if ver != "" {
|
|
|
|
if _, err := semver.ParseRange(string(ver)); err != nil {
|
|
|
|
a.Diag().Errorf(errors.IllegalDependencySemVer.WithDocument(doc), name, ver)
|
2016-11-16 19:00:52 +01:00
|
|
|
}
|
|
|
|
}
|
2016-11-16 02:42:22 +01:00
|
|
|
}
|
2016-11-17 02:30:03 +01:00
|
|
|
|
|
|
|
func (a *ptAnalyzer) VisitServices(doc *diag.Document, svcs *ast.Services) {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *ptAnalyzer) VisitService(doc *diag.Document, name ast.Name, public bool, svc *ast.Service) {
|
|
|
|
// Decorate the AST with contextual information so subsequent passes can operate context-free.
|
|
|
|
svc.Name = name
|
|
|
|
svc.Public = public
|
|
|
|
}
|