file.go 3.3 KB

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