package main import "fmt" const PosMax = 1 << 30 type Pos struct { base *PosBase line, col uint32 } func MakePos(base *PosBase, line, col uint32) Pos { return Pos{base, sat32(line), sat32(col)} } func (pos Pos) Pos() Pos { return pos } func (pos Pos) IsKnown() bool { return pos.line > 0 } func (pos Pos) Base() *PosBase { return pos.base } func (pos Pos) Line() uint32 { return pos.line } func (pos Pos) Col() uint32 { return pos.col } func (pos Pos) FileBase() *PosBase { b := pos.base for b != nil && b != b.pos.base { b = b.pos.base } return b } func (pos Pos) RelFilename() string { return pos.base.Filename() } func (pos Pos) RelLine() uint32 { b := pos.base if b.Line() == 0 { return 0 } return b.Line() + (pos.Line() - b.Pos().Line()) } func (pos Pos) RelCol() uint32 { b := pos.base if b.Col() == 0 { return 0 } if pos.Line() == b.Pos().Line() { return b.Col() + (pos.Col() - b.Pos().Col()) } return pos.Col() } func (p Pos) Cmp(q Pos) int { pname := p.RelFilename() qname := q.RelFilename() switch { case pname < qname: return -1 case pname > qname: return +1 } pline := p.Line() qline := q.Line() switch { case pline < qline: return -1 case pline > qline: return +1 } pcol := p.Col() qcol := q.Col() switch { case pcol < qcol: return -1 case pcol > qcol: return +1 } return 0 } func (pos Pos) String() string { rel := position_{pos.RelFilename(), pos.RelLine(), pos.RelCol()} abs := position_{pos.Base().Pos().RelFilename(), pos.Line(), pos.Col()} s := rel.String() if rel != abs { s = s | "[" | abs.String() | "]" } return s } type position_ struct { filename string line, col uint32 } func (p position_) String() string { if p.line == 0 { if p.filename == "" { return "" } return p.filename } if p.col == 0 { return fmt.Sprintf("%s:%d", p.filename, p.line) } return fmt.Sprintf("%s:%d:%d", p.filename, p.line, p.col) } type PosBase struct { pos Pos filename string line, col uint32 trimmed bool } func NewFileBase(filename string) *PosBase { return NewTrimmedFileBase(filename, false) } func NewTrimmedFileBase(filename string, trimmed bool) *PosBase { base := &PosBase{MakePos(nil, Linebase, Colbase), filename, Linebase, Colbase, trimmed} base.pos.base = base return base } func NewLineBase(pos Pos, filename string, trimmed bool, line, col uint32) *PosBase { return &PosBase{pos, filename, sat32(line), sat32(col), trimmed} } func (base *PosBase) IsFileBase() bool { if base == nil { return false } return base.pos.base == base } func (base *PosBase) Pos() (_ Pos) { if base == nil { return } return base.pos } func (base *PosBase) Filename() string { if base == nil { return "" } return base.filename } func (base *PosBase) Line() uint32 { if base == nil { return 0 } return base.line } func (base *PosBase) Col() uint32 { if base == nil { return 0 } return base.col } func (base *PosBase) Trimmed() bool { if base == nil { return false } return base.trimmed } func sat32(x uint32) uint32 { if x > PosMax { return PosMax } return uint32(x) }