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
}
// 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
}
// ----------------------------------------------------------------------------
// 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
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
// 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 }
)
func TestPos(t *testing.T) {
+ f0 := NewFileBase("")
f1 := NewFileBase("f1")
f2 := NewLinePragmaBase(Pos{}, "f2", 10)
f3 := NewLinePragmaBase(MakePos(f1, 10, 1), "f3", 100)
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
} {
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)
}
}