file.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // Package file encapsulates the file abstractions used by the ast & parser.
  2. package file
  3. import (
  4. "fmt"
  5. "strings"
  6. "gopkg.in/sourcemap.v1"
  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 (p *Position) isValid() bool {
  22. return p.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. func (p *Position) String() string {
  31. str := p.Filename
  32. if p.isValid() {
  33. if str != "" {
  34. str += ":"
  35. }
  36. str += fmt.Sprintf("%d:%d", p.Line, p.Column)
  37. }
  38. if str == "" {
  39. str = "-"
  40. }
  41. return str
  42. }
  43. // A FileSet represents a set of source files.
  44. type FileSet struct {
  45. last *File
  46. files []*File
  47. }
  48. // AddFile adds a new file with the given filename and src.
  49. //
  50. // This an internal method, but exported for cross-package use.
  51. func (fs *FileSet) AddFile(filename, src string) int {
  52. base := fs.nextBase()
  53. file := &File{
  54. name: filename,
  55. src: src,
  56. base: base,
  57. }
  58. fs.files = append(fs.files, file)
  59. fs.last = file
  60. return base
  61. }
  62. func (fs *FileSet) nextBase() int {
  63. if fs.last == nil {
  64. return 1
  65. }
  66. return fs.last.base + len(fs.last.src) + 1
  67. }
  68. // File returns the File at idx or nil if not found.
  69. func (fs *FileSet) File(idx Idx) *File {
  70. for _, file := range fs.files {
  71. if idx <= Idx(file.base+len(file.src)) {
  72. return file
  73. }
  74. }
  75. return nil
  76. }
  77. // Position converts an Idx in the FileSet into a Position.
  78. func (fs *FileSet) Position(idx Idx) *Position {
  79. for _, file := range fs.files {
  80. if idx <= Idx(file.base+len(file.src)) {
  81. return file.Position(idx - Idx(file.base))
  82. }
  83. }
  84. return nil
  85. }
  86. // File represents a file to parse.
  87. type File struct {
  88. sm *sourcemap.Consumer
  89. name string
  90. src string
  91. base int
  92. }
  93. // NewFile returns a new file with the given filename, src and base.
  94. func NewFile(filename, src string, base int) *File {
  95. return &File{
  96. name: filename,
  97. src: src,
  98. base: base,
  99. }
  100. }
  101. // WithSourceMap sets the source map of fl.
  102. func (fl *File) WithSourceMap(sm *sourcemap.Consumer) *File {
  103. fl.sm = sm
  104. return fl
  105. }
  106. // Name returns the name of fl.
  107. func (fl *File) Name() string {
  108. return fl.name
  109. }
  110. // Source returns the source of fl.
  111. func (fl *File) Source() string {
  112. return fl.src
  113. }
  114. // Base returns the base of fl.
  115. func (fl *File) Base() int {
  116. return fl.base
  117. }
  118. // Position returns the position at idx or nil if not valid.
  119. func (fl *File) Position(idx Idx) *Position {
  120. position := &Position{}
  121. offset := int(idx) - fl.base
  122. if offset >= len(fl.src) || offset < 0 {
  123. return nil
  124. }
  125. src := fl.src[:offset]
  126. position.Filename = fl.name
  127. position.Offset = offset
  128. position.Line = strings.Count(src, "\n") + 1
  129. if index := strings.LastIndex(src, "\n"); index >= 0 {
  130. position.Column = offset - index
  131. } else {
  132. position.Column = len(src) + 1
  133. }
  134. if fl.sm != nil {
  135. if f, _, l, c, ok := fl.sm.Source(position.Line, position.Column); ok {
  136. position.Filename, position.Line, position.Column = f, l, c
  137. }
  138. }
  139. return position
  140. }