parser.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /*
  2. Package parser implements a parser for JavaScript.
  3. import (
  4. "github.com/dop251/goja/parser"
  5. )
  6. Parse and return an AST
  7. filename := "" // A filename is optional
  8. src := `
  9. // Sample xyzzy example
  10. (function(){
  11. if (3.14159 > 0) {
  12. console.log("Hello, World.");
  13. return;
  14. }
  15. var xyzzy = NaN;
  16. console.log("Nothing happens.");
  17. return xyzzy;
  18. })();
  19. `
  20. // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
  21. program, err := parser.ParseFile(nil, filename, src, 0)
  22. Warning
  23. The parser and AST interfaces are still works-in-progress (particularly where
  24. node types are concerned) and may change in the future.
  25. */
  26. package parser
  27. import (
  28. "bytes"
  29. "errors"
  30. "io"
  31. "io/ioutil"
  32. "github.com/dop251/goja/ast"
  33. "github.com/dop251/goja/file"
  34. "github.com/dop251/goja/token"
  35. "github.com/dop251/goja/unistring"
  36. )
  37. // A Mode value is a set of flags (or 0). They control optional parser functionality.
  38. type Mode uint
  39. const (
  40. IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking)
  41. )
  42. type options struct {
  43. disableSourceMaps bool
  44. sourceMapLoader func(path string) ([]byte, error)
  45. }
  46. // Option represents one of the options for the parser to use in the Parse methods. Currently supported are:
  47. // WithDisableSourceMaps and WithSourceMapLoader.
  48. type Option func(*options)
  49. // WithDisableSourceMaps is an option to disable source maps support. May save a bit of time when source maps
  50. // are not in use.
  51. func WithDisableSourceMaps(opts *options) {
  52. opts.disableSourceMaps = true
  53. }
  54. // WithSourceMapLoader is an option to set a custom source map loader. The loader will be given a path or a
  55. // URL from the sourceMappingURL. If sourceMappingURL is not absolute it is resolved relatively to the name
  56. // of the file being parsed. Any error returned by the loader will fail the parsing.
  57. // Note that setting this to nil does not disable source map support, there is a default loader which reads
  58. // from the filesystem. Use WithDisableSourceMaps to disable source map support.
  59. func WithSourceMapLoader(loader func(path string) ([]byte, error)) Option {
  60. return func(opts *options) {
  61. opts.sourceMapLoader = loader
  62. }
  63. }
  64. type _parser struct {
  65. str string
  66. length int
  67. base int
  68. chr rune // The current character
  69. chrOffset int // The offset of current character
  70. offset int // The offset after current character (may be greater than 1)
  71. idx file.Idx // The index of token
  72. token token.Token // The token
  73. literal string // The literal of the token, if any
  74. parsedLiteral unistring.String
  75. scope *_scope
  76. insertSemicolon bool // If we see a newline, then insert an implicit semicolon
  77. implicitSemicolon bool // An implicit semicolon exists
  78. errors ErrorList
  79. recover struct {
  80. // Scratch when trying to seek to the next statement, etc.
  81. idx file.Idx
  82. count int
  83. }
  84. mode Mode
  85. opts options
  86. file *file.File
  87. }
  88. func _newParser(filename, src string, base int, opts ...Option) *_parser {
  89. p := &_parser{
  90. chr: ' ', // This is set so we can start scanning by skipping whitespace
  91. str: src,
  92. length: len(src),
  93. base: base,
  94. file: file.NewFile(filename, src, base),
  95. }
  96. for _, opt := range opts {
  97. opt(&p.opts)
  98. }
  99. return p
  100. }
  101. func newParser(filename, src string) *_parser {
  102. return _newParser(filename, src, 1)
  103. }
  104. func ReadSource(filename string, src interface{}) ([]byte, error) {
  105. if src != nil {
  106. switch src := src.(type) {
  107. case string:
  108. return []byte(src), nil
  109. case []byte:
  110. return src, nil
  111. case *bytes.Buffer:
  112. if src != nil {
  113. return src.Bytes(), nil
  114. }
  115. case io.Reader:
  116. var bfr bytes.Buffer
  117. if _, err := io.Copy(&bfr, src); err != nil {
  118. return nil, err
  119. }
  120. return bfr.Bytes(), nil
  121. }
  122. return nil, errors.New("invalid source")
  123. }
  124. return ioutil.ReadFile(filename)
  125. }
  126. // ParseFile parses the source code of a single JavaScript/ECMAScript source file and returns
  127. // the corresponding ast.Program node.
  128. //
  129. // If fileSet == nil, ParseFile parses source without a FileSet.
  130. // If fileSet != nil, ParseFile first adds filename and src to fileSet.
  131. //
  132. // The filename argument is optional and is used for labelling errors, etc.
  133. //
  134. // src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8.
  135. //
  136. // // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
  137. // program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0)
  138. //
  139. func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode, options ...Option) (*ast.Program, error) {
  140. str, err := ReadSource(filename, src)
  141. if err != nil {
  142. return nil, err
  143. }
  144. {
  145. str := string(str)
  146. base := 1
  147. if fileSet != nil {
  148. base = fileSet.AddFile(filename, str)
  149. }
  150. parser := _newParser(filename, str, base, options...)
  151. parser.mode = mode
  152. return parser.parse()
  153. }
  154. }
  155. // ParseFunction parses a given parameter list and body as a function and returns the
  156. // corresponding ast.FunctionLiteral node.
  157. //
  158. // The parameter list, if any, should be a comma-separated list of identifiers.
  159. //
  160. func ParseFunction(parameterList, body string, options ...Option) (*ast.FunctionLiteral, error) {
  161. src := "(function(" + parameterList + ") {\n" + body + "\n})"
  162. parser := _newParser("", src, 1, options...)
  163. program, err := parser.parse()
  164. if err != nil {
  165. return nil, err
  166. }
  167. return program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral), nil
  168. }
  169. func (self *_parser) slice(idx0, idx1 file.Idx) string {
  170. from := int(idx0) - self.base
  171. to := int(idx1) - self.base
  172. if from >= 0 && to <= len(self.str) {
  173. return self.str[from:to]
  174. }
  175. return ""
  176. }
  177. func (self *_parser) parse() (*ast.Program, error) {
  178. self.next()
  179. program := self.parseProgram()
  180. if false {
  181. self.errors.Sort()
  182. }
  183. return program, self.errors.Err()
  184. }
  185. func (self *_parser) next() {
  186. self.token, self.literal, self.parsedLiteral, self.idx = self.scan()
  187. }
  188. func (self *_parser) optionalSemicolon() {
  189. if self.token == token.SEMICOLON {
  190. self.next()
  191. return
  192. }
  193. if self.implicitSemicolon {
  194. self.implicitSemicolon = false
  195. return
  196. }
  197. if self.token != token.EOF && self.token != token.RIGHT_BRACE {
  198. self.expect(token.SEMICOLON)
  199. }
  200. }
  201. func (self *_parser) semicolon() {
  202. if self.token != token.RIGHT_PARENTHESIS && self.token != token.RIGHT_BRACE {
  203. if self.implicitSemicolon {
  204. self.implicitSemicolon = false
  205. return
  206. }
  207. self.expect(token.SEMICOLON)
  208. }
  209. }
  210. func (self *_parser) idxOf(offset int) file.Idx {
  211. return file.Idx(self.base + offset)
  212. }
  213. func (self *_parser) expect(value token.Token) file.Idx {
  214. idx := self.idx
  215. if self.token != value {
  216. self.errorUnexpectedToken(self.token)
  217. }
  218. self.next()
  219. return idx
  220. }
  221. func (self *_parser) position(idx file.Idx) file.Position {
  222. return self.file.Position(int(idx) - self.base)
  223. }