93 lines
2.5 KiB
Go
93 lines
2.5 KiB
Go
|
package parse
|
||
|
|
||
|
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
|
||
|
}
|