Implement diag.Diagable on MuIL AST nodes

This ensures that source context information flows automatically from
MuIL AST nodes to the various diag-related functions.
This commit is contained in:
joeduffy 2017-01-17 18:01:11 -08:00
parent 3ff9e83f63
commit 2964bf6ad0
4 changed files with 36 additions and 11 deletions

View file

@ -4,8 +4,8 @@ package diag
// Pos represents a position in a file. // Pos represents a position in a file.
type Pos struct { type Pos struct {
Ln int Line int // a 1-based line number
Col int Column int // a 0-based column number
} }
// EmptyPos may be used when no position is needed. // EmptyPos may be used when no position is needed.
@ -13,19 +13,19 @@ var EmptyPos = Pos{0, 0}
// IsEmpty returns true if the Pos information is missing. // IsEmpty returns true if the Pos information is missing.
func (pos Pos) IsEmpty() bool { func (pos Pos) IsEmpty() bool {
return pos.Ln == 0 && pos.Col == 0 return pos.Line == 0 && pos.Column == 0
} }
// Location represents a region spanning two positions in a file. // Location represents a region spanning two positions in a file.
type Location struct { type Location struct {
From Pos Start Pos // a starting position.
To Pos End *Pos // an ending position; if nil, represents a point.
} }
// EmptyLocation may be used when no position information is available. // EmptyLocation may be used when no position information is available.
var EmptyLocation = Location{EmptyPos, EmptyPos} var EmptyLocation = Location{EmptyPos, nil}
// IsEmpty returns true if the Location information is missing. // IsEmpty returns true if the Location information is missing.
func (loc Location) IsEmpty() bool { func (loc Location) IsEmpty() bool {
return loc.From.IsEmpty() && loc.To.IsEmpty() return loc.Start.IsEmpty() && (loc.End == nil || loc.End.IsEmpty())
} }

View file

@ -114,9 +114,9 @@ func (d *defaultSink) Stringify(diag *Diag, prefix string, args ...interface{})
if diag.Loc != nil && !diag.Loc.IsEmpty() { if diag.Loc != nil && !diag.Loc.IsEmpty() {
buffer.WriteRune(':') buffer.WriteRune(':')
buffer.WriteString(strconv.Itoa(diag.Loc.From.Ln)) buffer.WriteString(strconv.Itoa(diag.Loc.Start.Line))
buffer.WriteRune(':') buffer.WriteRune(':')
buffer.WriteString(strconv.Itoa(diag.Loc.From.Col)) buffer.WriteString(strconv.Itoa(diag.Loc.Start.Column))
} }
buffer.WriteString(": ") buffer.WriteString(": ")
} }

View file

@ -11,6 +11,7 @@
package ast package ast
import ( import (
"github.com/marapongo/mu/pkg/diag"
"github.com/marapongo/mu/pkg/symbols" "github.com/marapongo/mu/pkg/symbols"
) )
@ -30,10 +31,34 @@ type node struct {
Loc *Location `json:"loc,omitempty"` Loc *Location `json:"loc,omitempty"`
} }
var _ diag.Diagable = (*node)(nil)
func (node *node) nd() {} func (node *node) nd() {}
func (node *node) GetKind() NodeKind { return node.Kind } func (node *node) GetKind() NodeKind { return node.Kind }
func (node *node) GetLoc() *Location { return node.Loc } func (node *node) GetLoc() *Location { return node.Loc }
func (node *node) Where() (*diag.Document, *diag.Location) {
// TODO: consider caching Document objects; allocating one per Node is wasteful.
// TODO: for development scenarios, it would be really great to recover the original source file text for purposes
// of the diag.Document part. Doing so would give nice error messages tied back to the original source code
// for any errors associated with the AST. Absent that, we will simply return nil.
if node.Loc == nil {
return nil, nil
} else {
var doc *diag.Document
if node.Loc.File != nil {
doc = diag.NewDocument(*node.Loc.File)
}
var end *diag.Pos
if node.Loc.End != nil {
end = &diag.Pos{int(node.Loc.End.Line), int(node.Loc.End.Column)}
}
return doc, &diag.Location{
diag.Pos{int(node.Loc.Start.Line), int(node.Loc.Start.Column)}, end,
}
}
}
// Identifier represents a simple string token associated with its source location context. // Identifier represents a simple string token associated with its source location context.
type Identifier struct { type Identifier struct {
node node

View file

@ -11,6 +11,6 @@ type Location struct {
// Position consists of a 1-indexed `line` number and a 0-indexed `column` number. // Position consists of a 1-indexed `line` number and a 0-indexed `column` number.
type Position struct { type Position struct {
Line int64 `json:"line"` // >= 1 Line int64 `json:"line"` // a 1-based line number
Column int64 `json:"column"` // >= 0 Column int64 `json:"column"` // a 0-based column number
} }