fspl/errors/position.go

93 lines
2.5 KiB
Go

package errors
import "fmt"
import "strings"
// Position stores the position of something within a file. For memory's sake,
// positions in the same file should be created using the same File string, and
// positions on the same line should be created using the same Line string.
type Position struct {
File string // The name of the file
Line string // the text of the line the position is on
Row int // Line number, starting at 0
Start int // Starting column number, starting at 0
End int // Terminating column number, starting at 0
}
// String returns a string representation of the position of the form:
// FILE:ROW+1:START+1
// Note that the row and column numbers as displayed are increased by one from
// their normal zero-indexed state, because most editors display row and column
// numbers starting at 1:1.
func (pos Position) String () string {
return fmt.Sprintf("%s:%d:%d", pos.File, pos.Row + 1, pos.Start + 1)
}
// Format produces a formatted printout of where the position is in its file.
func (pos Position) Format () string {
line := formatTabs(pos.Line)
output := fmt.Sprintf("%-4d | %s\n ", pos.Row + 1, line)
start := pos.Start
end := pos.End
if end <= start { end = start + 1 }
start = getXInTabbedString(pos.Line, start)
end = getXInTabbedString(pos.Line, end)
for index := range line {
if index >= end { break }
if index >= start{
output += "^"
} else {
output += " "
}
}
return output
}
func getXInTabbedString (tabbed string, column int) int {
var x int
for index, ch := range tabbed {
if index >= column { return x }
if ch == '\t' {
nextStop := ((x / 8) + 1) * 8
x = nextStop - 1
}
x ++
}
return x
}
func formatTabs (tabbed string) string {
var result string
var x int
for _, ch := range tabbed {
if ch == '\t' {
nextStop := ((x / 8) + 1) * 8
result += strings.Repeat(" ", nextStop - x)
x = nextStop - 1
} else {
result += string(ch)
}
x ++
}
return result
}
// Union attempts to return the union of two positions. Since a position cannot
// span multiple lines or files, the resulting position will be on the same line
// and in the same file as the method reciever.
func (pos Position) Union (other Position) Position {
switch {
case other.File != pos.File:
case other.Row < pos.Row: pos.Start = 0
case other.Row > pos.Row: pos.End = len(pos.Line)
default:
if other.Start < pos.Start { pos.Start = other.Start }
if other.End > pos.End { pos.End = other.End }
}
return pos
}