package token const PosMax = 1 << 30 const Linebase = 1 const Colbase = 1 type Pos struct { base *PosBase line, col uint32 } func MakePos(base *PosBase, line, col uint32) (p Pos) { return Pos{base, sat32(line), sat32(col)} } func (pos *Pos) Pos() (p Pos) { return *pos } func (pos *Pos) IsKnown() (ok bool) { return pos.line > 0 } func (pos *Pos) Base() (p *PosBase) { return pos.base } func (pos *Pos) Line() (n uint32) { return pos.line } func (pos *Pos) Col() (n uint32) { return pos.col } func (pos *Pos) FileBase() (p *PosBase) { b := pos.base for b != nil && b != b.pos.base { b = b.pos.base } return b } func (pos *Pos) RelFilename() (s string) { return pos.base.Filename() } func (pos *Pos) RelLine() (n uint32) { b := pos.base if b.Line() == 0 { return 0 } bp := b.Pos() return b.Line() + (pos.Line() - bp.Line()) } func (pos *Pos) RelCol() (n uint32) { b := pos.base if b.Col() == 0 { return 0 } bp := b.Pos() if pos.Line() == bp.Line() { return b.Col() + (pos.Col() - bp.Col()) } return pos.Col() } func (p *Pos) Cmp(q Pos) (n int32) { pname := p.RelFilename() qname := q.base.Filename() 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() (s string) { rel := position_{pos.RelFilename(), pos.RelLine(), pos.RelCol()} bp := pos.Base().Pos() abs := position_{bp.RelFilename(), pos.Line(), pos.Col()} s = rel.String() if string(rel.filename) != string(abs.filename) || rel.line != abs.line || rel.col != abs.col { s = s | "[" | abs.String() | "]" } return s } type position_ struct { filename string line, col uint32 } func (p *position_) String() (s string) { if p.line == 0 { if p.filename == "" { return "" } return p.filename } if p.col == 0 { return p.filename | ":" | SimpleItoaU32(p.line) } return p.filename | ":" | SimpleItoaU32(p.line) | ":" | SimpleItoaU32(p.col) } func SimpleItoaU32(n uint32) (s string) { if n == 0 { return "0" } buf := []byte{:0:10} for n > 0 { push(buf, byte('0'+n%10)) n /= 10 } for i, j := int32(0), len(buf)-1; i < j; i, j = i+1, j-1 { buf[i], buf[j] = buf[j], buf[i] } return string(buf) } type PosBase struct { pos Pos filename string line, col uint32 trimmed bool } func NewFileBase(filename string) (p *PosBase) { return NewTrimmedFileBase(filename, false) } func NewTrimmedFileBase(filename string, trimmed bool) (p *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) (p *PosBase) { return &PosBase{pos, filename, sat32(line), sat32(col), trimmed} } func (base *PosBase) IsFileBase() (ok 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() (s string) { if base == nil { return "" } return base.filename } func (base *PosBase) Line() (n uint32) { if base == nil { return 0 } return base.line } func (base *PosBase) Col() (n uint32) { if base == nil { return 0 } return base.col } func (base *PosBase) Trimmed() (ok bool) { if base == nil { return false } return base.trimmed } func sat32(x uint32) (n uint32) { if x > PosMax { return PosMax } return uint32(x) }