file.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. // Package file encapsulates the file abstractions used by the ast & parser.
  2. //
  3. package file
  4. import (
  5. "fmt"
  6. "strings"
  7. )
  8. // Idx is a compact encoding of a source position within a file set.
  9. // It can be converted into a Position for a more convenient, but much
  10. // larger, representation.
  11. type Idx int
  12. // Position describes an arbitrary source position
  13. // including the filename, line, and column location.
  14. type Position struct {
  15. Filename string // The filename where the error occurred, if any
  16. Offset int // The src offset
  17. Line int // The line number, starting at 1
  18. Column int // The column number, starting at 1 (The character count)
  19. }
  20. // A Position is valid if the line number is > 0.
  21. func (self *Position) isValid() bool {
  22. return self.Line > 0
  23. }
  24. // String returns a string in one of several forms:
  25. //
  26. // file:line:column A valid position with filename
  27. // line:column A valid position without filename
  28. // file An invalid position with filename
  29. // - An invalid position without filename
  30. //
  31. func (self *Position) String() string {
  32. str := self.Filename
  33. if self.isValid() {
  34. if str != "" {
  35. str += ":"
  36. }
  37. str += fmt.Sprintf("%d:%d", self.Line, self.Column)
  38. }
  39. if str == "" {
  40. str = "-"
  41. }
  42. return str
  43. }
  44. // FileSet
  45. // A FileSet represents a set of source files.
  46. type FileSet struct {
  47. files []*File
  48. last *File
  49. }
  50. // AddFile adds a new file with the given filename and src.
  51. //
  52. // This an internal method, but exported for cross-package use.
  53. func (self *FileSet) AddFile(filename, src string) int {
  54. base := self.nextBase()
  55. file := &File{
  56. name: filename,
  57. src: src,
  58. base: base,
  59. }
  60. self.files = append(self.files, file)
  61. self.last = file
  62. return base
  63. }
  64. func (self *FileSet) nextBase() int {
  65. if self.last == nil {
  66. return 1
  67. }
  68. return self.last.base + len(self.last.src) + 1
  69. }
  70. func (self *FileSet) File(idx Idx) *File {
  71. for _, file := range self.files {
  72. if idx <= Idx(file.base+len(file.src)) {
  73. return file
  74. }
  75. }
  76. return nil
  77. }
  78. // Position converts an Idx in the FileSet into a Position.
  79. func (self *FileSet) Position(idx Idx) *Position {
  80. position := &Position{}
  81. for _, file := range self.files {
  82. if idx <= Idx(file.base+len(file.src)) {
  83. offset := int(idx) - file.base
  84. src := file.src[:offset]
  85. position.Filename = file.name
  86. position.Offset = offset
  87. position.Line = 1 + strings.Count(src, "\n")
  88. if index := strings.LastIndex(src, "\n"); index >= 0 {
  89. position.Column = offset - index
  90. } else {
  91. position.Column = 1 + len(src)
  92. }
  93. }
  94. }
  95. return position
  96. }
  97. type File struct {
  98. name string
  99. src string
  100. base int // This will always be 1 or greater
  101. }
  102. func NewFile(filename, src string, base int) *File {
  103. return &File{
  104. name: filename,
  105. src: src,
  106. base: base,
  107. }
  108. }
  109. func (fl *File) Name() string {
  110. return fl.name
  111. }
  112. func (fl *File) Source() string {
  113. return fl.src
  114. }
  115. func (fl *File) Base() int {
  116. return fl.base
  117. }