This change begins to lay the groundwork for doing semantic analysis and lowering to the cloud target's representation. In particular: * Split the mu/schema package. There is now mu/ast which contains the core types and mu/encoding which concerns itself with JSON and YAML serialization. * Notably I am *not* yet introducing a second AST form. Instead, we will keep the parse tree and AST unified for the time being. I envision very little difference between them -- at least for now -- and so this keeps things simpler, at the expense of two downsides: 1) the trees will be mutable (which turns out to be a good thing for performance), and 2) some fields will need to be ignored during de/serialization. We can always revisit this later when and if the need to split them arises. * Add a binder phase. It is currently a no-op.
116 lines
3 KiB
Go
116 lines
3 KiB
Go
// Copyright 2016 Marapongo, Inc. All rights reserved.
|
|
|
|
package diag
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
|
|
"github.com/golang/glog"
|
|
)
|
|
|
|
// Sink facilitates pluggable diagnostics messages.
|
|
type Sink interface {
|
|
// Count fetches the total number of diagnostics issued (errors plus warnings).
|
|
Count() int
|
|
// Errors fetches the number of errors issued.
|
|
Errors() int
|
|
// Warnings fetches the number of warnings issued.
|
|
Warnings() int
|
|
|
|
// Error issues a new error diagnostic.
|
|
Errorf(diag *Diag, args ...interface{})
|
|
// Warning issues a new warning diagnostic.
|
|
Warningf(diag *Diag, args ...interface{})
|
|
|
|
// Stringify stringifies a diagnostic in the usual way (e.g., "error: MU123: Mu.yaml:7:39: error goes here\n").
|
|
Stringify(diag *Diag, prefix string, args ...interface{}) string
|
|
}
|
|
|
|
// DefaultDiags returns a default sink that simply logs output to stderr/stdout.
|
|
func DefaultSink(pwd string) Sink {
|
|
return &defaultSink{pwd: pwd}
|
|
}
|
|
|
|
const DefaultSinkIDPrefix = "MU"
|
|
const DefaultSinkErrorPrefix = "error"
|
|
const DefaultSinkWarningPrefix = "warning"
|
|
|
|
// defaultSink is the default sink which logs output to stderr/stdout.
|
|
type defaultSink struct {
|
|
pwd string // an optional present working directory to which output paths will be relative to.
|
|
errors int // the number of errors that have been issued.
|
|
warnings int // the number of warnings that have been issued.
|
|
}
|
|
|
|
func (d *defaultSink) Count() int {
|
|
return d.errors + d.warnings
|
|
}
|
|
|
|
func (d *defaultSink) Errors() int {
|
|
return d.errors
|
|
}
|
|
|
|
func (d *defaultSink) Warnings() int {
|
|
return d.warnings
|
|
}
|
|
|
|
func (d *defaultSink) Errorf(diag *Diag, args ...interface{}) {
|
|
msg := d.Stringify(diag, DefaultSinkErrorPrefix, args...)
|
|
if glog.V(3) {
|
|
glog.V(3).Infof("defaultSink::Error(%v)", msg)
|
|
}
|
|
fmt.Fprintf(os.Stderr, msg)
|
|
}
|
|
|
|
func (d *defaultSink) Warningf(diag *Diag, args ...interface{}) {
|
|
msg := d.Stringify(diag, DefaultSinkWarningPrefix, args...)
|
|
if glog.V(4) {
|
|
glog.V(4).Infof("defaultSink::Warning(%v)", msg)
|
|
}
|
|
fmt.Fprintf(os.Stdout, msg)
|
|
}
|
|
|
|
func (d *defaultSink) Stringify(diag *Diag, prefix string, args ...interface{}) string {
|
|
var buffer bytes.Buffer
|
|
|
|
buffer.WriteString(prefix)
|
|
buffer.WriteString(": ")
|
|
|
|
if diag.ID > 0 {
|
|
buffer.WriteString(DefaultSinkIDPrefix)
|
|
buffer.WriteString(strconv.Itoa(int(diag.ID)))
|
|
buffer.WriteString(": ")
|
|
}
|
|
|
|
if diag.Doc != nil {
|
|
file := diag.Doc.File
|
|
if d.pwd != "" {
|
|
// If a PWD is available, try to create a relative path.
|
|
rel, err := filepath.Rel(d.pwd, file)
|
|
if err == nil {
|
|
file = rel
|
|
}
|
|
}
|
|
buffer.WriteString(file)
|
|
|
|
if diag.Loc != nil && !diag.Loc.IsEmpty() {
|
|
buffer.WriteRune(':')
|
|
buffer.WriteString(strconv.Itoa(diag.Loc.From.Ln))
|
|
buffer.WriteRune(':')
|
|
buffer.WriteString(strconv.Itoa(diag.Loc.From.Col))
|
|
}
|
|
buffer.WriteString(": ")
|
|
}
|
|
|
|
buffer.WriteString(fmt.Sprintf(diag.Message, args...))
|
|
buffer.WriteRune('\n')
|
|
|
|
// TODO: support Clang-style caret diagnostics; e.g., see http://clang.llvm.org/diagnostics.html.
|
|
|
|
return buffer.String()
|
|
}
|