Commit e97c8a59 authored by Robert Griesemer's avatar Robert Griesemer

[dev.inline] cmd/compile/internal/syntax: simplified position code

Reviewed in and cherry-picked from https://go-review.googlesource.com/#/c/33805/.

Change-Id: I859d9bd5f2256ca78f7b24b330290f7ae600854d
Reviewed-on: https://go-review.googlesource.com/34234Reviewed-by: default avatarMatthew Dempsky <mdempsky@google.com>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 32bf2829
......@@ -6,10 +6,11 @@
package syntax
import "fmt"
import "strconv"
// A Pos encodes a source position consisting of a (line, column) number pair
// and a position base.
// and a position base. A zero Pos is a ready to use "unknown" position (empty
// filename, and unknown line and column number).
//
// The (line, column) values refer to a position in a file independent of any
// position base ("absolute" position). They start at 1, and they are unknown
......@@ -33,47 +34,36 @@ func MakePos(base *PosBase, line, col uint) Pos {
}
// Filename returns the name of the actual file containing this position.
func (p *Pos) Filename() string {
if b := p.base; b != nil {
return b.pos.RelFilename()
}
return ""
}
func (p *Pos) Filename() string { return p.base.Pos().RelFilename() }
// Base returns the position base.
func (p *Pos) Base() *PosBase { return p.base }
// RelFilename returns the filename recorded with the position's base.
func (p *Pos) RelFilename() string {
if b := p.base; b != nil {
return b.filename
}
return ""
}
func (p *Pos) RelFilename() string { return p.base.Filename() }
// RelLine returns the line number relative to the positions's base.
func (p *Pos) RelLine() uint {
var line0 uint
if b := p.base; b != nil {
line0 = b.line - p.base.pos.Line()
}
return line0 + p.Line()
}
func (p *Pos) RelLine() uint { b := p.base; return b.Line() + p.Line() - b.Pos().Line() }
func (p *Pos) String() string {
b := p.base
if b == nil {
return p.lico.String()
}
if b == b.pos.base {
// base is file base
return fmt.Sprintf("%s:%s", b.filename, p.lico.String())
if b == b.Pos().base {
// base is file base (incl. nil)
return posString(b.Filename(), p.Line(), p.Col())
}
// base is relative
return fmt.Sprintf("%s:%s[%s]", b.filename, licoString(p.RelLine(), p.Col()), b.pos.String())
return posString(b.Filename(), p.RelLine(), p.Col()) + "[" + b.Pos().String() + "]"
}
// posString formats a (filename, line, col) tuple as a printable position.
func posString(filename string, line, col uint) string {
s := filename + ":" + strconv.FormatUint(uint64(line), 10)
if col != 0 {
s += ":" + strconv.FormatUint(uint64(col), 10)
}
return s
}
// ----------------------------------------------------------------------------
......@@ -91,9 +81,12 @@ type PosBase struct {
// NewFileBase returns a new *PosBase for a file with the given filename.
func NewFileBase(filename string) *PosBase {
base := &PosBase{filename: filename}
base.pos = MakePos(base, 0, 0)
return base
if filename != "" {
base := &PosBase{filename: filename}
base.pos = MakePos(base, 0, 0)
return base
}
return nil
}
// NewLinePragmaBase returns a new *PosBase for a line pragma of the form
......@@ -106,7 +99,7 @@ func NewLinePragmaBase(pos Pos, filename string, line uint) *PosBase {
var noPos Pos
// Pos returns the position at which base is located.
// If b == nil, the result is the empty position.
// If b == nil, the result is the zero position.
func (b *PosBase) Pos() *Pos {
if b != nil {
return &b.pos
......@@ -144,29 +137,21 @@ type lico uint32
// information as line numbers grow bigger; similar to what gcc
// does.)
const (
lineW, lineM = 24, 1<<lineW - 1
colW, colM = 32 - lineW, 1<<colW - 1
lineBits, lineMax = 24, 1<<lineBits - 1
colBits, colMax = 32 - lineBits, 1<<colBits - 1
)
func makeLico(line, col uint) lico {
if line > lineM {
if line > lineMax {
// cannot represent line, use max. line so we have some information
line = lineM
line = lineMax
}
if col > colM {
if col > colMax {
// cannot represent column, use 0 to indicate unknown column
col = 0
}
return lico(line<<colW | col)
return lico(line<<colBits | col)
}
func (x lico) Line() uint { return uint(x) >> colW }
func (x lico) Col() uint { return uint(x) & colM }
func (x lico) String() string { return licoString(x.Line(), x.Col()) }
func licoString(line, col uint) string {
if col == 0 {
return fmt.Sprintf("%d", line)
}
return fmt.Sprintf("%d:%d", line, col)
}
func (x lico) Line() uint { return uint(x) >> colBits }
func (x lico) Col() uint { return uint(x) & colMax }
......@@ -10,6 +10,7 @@ import (
)
func TestPos(t *testing.T) {
f0 := NewFileBase("")
f1 := NewFileBase("f1")
f2 := NewLinePragmaBase(Pos{}, "f2", 10)
f3 := NewLinePragmaBase(MakePos(f1, 10, 1), "f3", 100)
......@@ -27,10 +28,11 @@ func TestPos(t *testing.T) {
relFilename string
relLine uint
}{
{Pos{}, "0", "", 0, 0, "", 0},
{MakePos(nil, 2, 3), "2:3", "", 2, 3, "", 2},
{Pos{}, ":0", "", 0, 0, "", 0},
{MakePos(nil, 2, 3), ":2:3", "", 2, 3, "", 2},
{MakePos(f0, 2, 3), ":2:3", "", 2, 3, "", 2},
{MakePos(f1, 1, 1), "f1:1:1", "f1", 1, 1, "f1", 1},
{MakePos(f2, 7, 10), "f2:16:10[0]", "", 7, 10, "f2", 16},
{MakePos(f2, 7, 10), "f2:16:10[:0]", "", 7, 10, "f2", 16},
{MakePos(f3, 12, 7), "f3:101:7[f1:10:1]", "f1", 12, 7, "f3", 101},
{MakePos(f4, 25, 1), "f4:114:1[f3:99:1[f1:10:1]]", "f3", 25, 1, "f4", 114}, // doesn't occur in Go code
} {
......@@ -66,20 +68,20 @@ func TestLico(t *testing.T) {
string string
line, col uint
}{
{0, "0", 0, 0},
{makeLico(0, 0), "0", 0, 0},
{makeLico(0, 1), "0:1", 0, 1},
{makeLico(1, 0), "1", 1, 0},
{makeLico(1, 1), "1:1", 1, 1},
{makeLico(2, 3), "2:3", 2, 3},
{makeLico(lineM, 1), fmt.Sprintf("%d:1", lineM), lineM, 1},
{makeLico(lineM+1, 1), fmt.Sprintf("%d:1", lineM), lineM, 1}, // line too large, stick with max. line
{makeLico(1, colM), fmt.Sprintf("1:%d", colM), 1, colM},
{makeLico(1, colM+1), "1", 1, 0}, // column too large
{makeLico(lineM+1, colM+1), fmt.Sprintf("%d", lineM), lineM, 0},
{0, ":0", 0, 0},
{makeLico(0, 0), ":0", 0, 0},
{makeLico(0, 1), ":0:1", 0, 1},
{makeLico(1, 0), ":1", 1, 0},
{makeLico(1, 1), ":1:1", 1, 1},
{makeLico(2, 3), ":2:3", 2, 3},
{makeLico(lineMax, 1), fmt.Sprintf(":%d:1", lineMax), lineMax, 1},
{makeLico(lineMax+1, 1), fmt.Sprintf(":%d:1", lineMax), lineMax, 1}, // line too large, stick with max. line
{makeLico(1, colMax), fmt.Sprintf(":1:%d", colMax), 1, colMax},
{makeLico(1, colMax+1), ":1", 1, 0}, // column too large
{makeLico(lineMax+1, colMax+1), fmt.Sprintf(":%d", lineMax), lineMax, 0},
} {
x := test.x
if got := x.String(); got != test.string {
if got := posString("", x.Line(), x.Col()); got != test.string {
t.Errorf("%s: got %q", test.string, got)
}
}
......
......@@ -577,7 +577,7 @@ func (s *scanner) lineComment() {
}
nstr := text[i+1:]
n, err := strconv.Atoi(nstr)
if err != nil || n <= 0 || n > lineM {
if err != nil || n <= 0 || n > lineMax {
s.error_at(s.line0, s.col0-uint(len(nstr)), "invalid line number: "+nstr)
return
}
......
......@@ -323,7 +323,7 @@ func TestScanErrors(t *testing.T) {
{`//line foo:123abc`, "invalid line number: 123abc", 1, 12},
{`/**///line foo:x`, "invalid line number: x", 1, 16},
{`//line foo:0`, "invalid line number: 0", 1, 12},
{fmt.Sprintf(`//line foo:%d`, lineM+1), fmt.Sprintf("invalid line number: %d", lineM+1), 1, 12},
{fmt.Sprintf(`//line foo:%d`, lineMax+1), fmt.Sprintf("invalid line number: %d", lineMax+1), 1, 12},
// former problem cases
{"package p\n\n\xef", "invalid UTF-8 encoding", 3, 1},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment