diff --git a/pkg/diag/pos.go b/pkg/diag/pos.go index 101674292..70d1e0721 100644 --- a/pkg/diag/pos.go +++ b/pkg/diag/pos.go @@ -4,8 +4,8 @@ package diag // Pos represents a position in a file. type Pos struct { - Ln int - Col int + Line int // a 1-based line number + Column int // a 0-based column number } // 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. 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. type Location struct { - From Pos - To Pos + Start Pos // a starting position. + End *Pos // an ending position; if nil, represents a point. } // 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. func (loc Location) IsEmpty() bool { - return loc.From.IsEmpty() && loc.To.IsEmpty() + return loc.Start.IsEmpty() && (loc.End == nil || loc.End.IsEmpty()) } diff --git a/pkg/diag/sink.go b/pkg/diag/sink.go index d7c3f39a0..e5da3ac47 100644 --- a/pkg/diag/sink.go +++ b/pkg/diag/sink.go @@ -114,9 +114,9 @@ func (d *defaultSink) Stringify(diag *Diag, prefix string, args ...interface{}) if diag.Loc != nil && !diag.Loc.IsEmpty() { buffer.WriteRune(':') - buffer.WriteString(strconv.Itoa(diag.Loc.From.Ln)) + buffer.WriteString(strconv.Itoa(diag.Loc.Start.Line)) buffer.WriteRune(':') - buffer.WriteString(strconv.Itoa(diag.Loc.From.Col)) + buffer.WriteString(strconv.Itoa(diag.Loc.Start.Column)) } buffer.WriteString(": ") } diff --git a/pkg/pack/ast/nodes.go b/pkg/pack/ast/nodes.go index 9e3165970..5cb1b49e3 100644 --- a/pkg/pack/ast/nodes.go +++ b/pkg/pack/ast/nodes.go @@ -11,6 +11,7 @@ package ast import ( + "github.com/marapongo/mu/pkg/diag" "github.com/marapongo/mu/pkg/symbols" ) @@ -30,10 +31,34 @@ type node struct { Loc *Location `json:"loc,omitempty"` } +var _ diag.Diagable = (*node)(nil) + func (node *node) nd() {} func (node *node) GetKind() NodeKind { return node.Kind } 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. type Identifier struct { node diff --git a/pkg/pack/ast/source.go b/pkg/pack/ast/source.go index b5ec23136..f3345ee56 100644 --- a/pkg/pack/ast/source.go +++ b/pkg/pack/ast/source.go @@ -11,6 +11,6 @@ type Location struct { // Position consists of a 1-indexed `line` number and a 0-indexed `column` number. type Position struct { - Line int64 `json:"line"` // >= 1 - Column int64 `json:"column"` // >= 0 + Line int64 `json:"line"` // a 1-based line number + Column int64 `json:"column"` // a 0-based column number }