parser.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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. )
  36. // A Mode value is a set of flags (or 0). They control optional parser functionality.
  37. type Mode uint
  38. const (
  39. IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking)
  40. )
  41. type _parser struct {
  42. str string
  43. length int
  44. base int
  45. chr rune // The current character
  46. chrOffset int // The offset of current character
  47. offset int // The offset after current character (may be greater than 1)
  48. idx file.Idx // The index of token
  49. token token.Token // The token
  50. literal string // The literal of the token, if any
  51. scope *_scope
  52. insertSemicolon bool // If we see a newline, then insert an implicit semicolon
  53. implicitSemicolon bool // An implicit semicolon exists
  54. errors ErrorList
  55. recover struct {
  56. // Scratch when trying to seek to the next statement, etc.
  57. idx file.Idx
  58. count int
  59. }
  60. mode Mode
  61. file *file.File
  62. }
  63. func _newParser(filename, src string, base int) *_parser {
  64. return &_parser{
  65. chr: ' ', // This is set so we can start scanning by skipping whitespace
  66. str: src,
  67. length: len(src),
  68. base: base,
  69. file: file.NewFile(filename, src, base),
  70. }
  71. }
  72. func newParser(filename, src string) *_parser {
  73. return _newParser(filename, src, 1)
  74. }
  75. func ReadSource(filename string, src interface{}) ([]byte, error) {
  76. if src != nil {
  77. switch src := src.(type) {
  78. case string:
  79. return []byte(src), nil
  80. case []byte:
  81. return src, nil
  82. case *bytes.Buffer:
  83. if src != nil {
  84. return src.Bytes(), nil
  85. }
  86. case io.Reader:
  87. var bfr bytes.Buffer
  88. if _, err := io.Copy(&bfr, src); err != nil {
  89. return nil, err
  90. }
  91. return bfr.Bytes(), nil
  92. }
  93. return nil, errors.New("invalid source")
  94. }
  95. return ioutil.ReadFile(filename)
  96. }
  97. // ParseFile parses the source code of a single JavaScript/ECMAScript source file and returns
  98. // the corresponding ast.Program node.
  99. //
  100. // If fileSet == nil, ParseFile parses source without a FileSet.
  101. // If fileSet != nil, ParseFile first adds filename and src to fileSet.
  102. //
  103. // The filename argument is optional and is used for labelling errors, etc.
  104. //
  105. // src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8.
  106. //
  107. // // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
  108. // program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0)
  109. //
  110. func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode) (*ast.Program, error) {
  111. str, err := ReadSource(filename, src)
  112. if err != nil {
  113. return nil, err
  114. }
  115. {
  116. str := string(str)
  117. base := 1
  118. if fileSet != nil {
  119. base = fileSet.AddFile(filename, str)
  120. }
  121. parser := _newParser(filename, str, base)
  122. parser.mode = mode
  123. return parser.parse()
  124. }
  125. }
  126. // ParseFunction parses a given parameter list and body as a function and returns the
  127. // corresponding ast.FunctionLiteral node.
  128. //
  129. // The parameter list, if any, should be a comma-separated list of identifiers.
  130. //
  131. func ParseFunction(parameterList, body string) (*ast.FunctionLiteral, error) {
  132. src := "(function(" + parameterList + ") {\n" + body + "\n})"
  133. parser := _newParser("", src, 1)
  134. program, err := parser.parse()
  135. if err != nil {
  136. return nil, err
  137. }
  138. return program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral), nil
  139. }
  140. func (self *_parser) slice(idx0, idx1 file.Idx) string {
  141. from := int(idx0) - self.base
  142. to := int(idx1) - self.base
  143. if from >= 0 && to <= len(self.str) {
  144. return self.str[from:to]
  145. }
  146. return ""
  147. }
  148. func (self *_parser) parse() (*ast.Program, error) {
  149. self.next()
  150. program := self.parseProgram()
  151. if false {
  152. self.errors.Sort()
  153. }
  154. return program, self.errors.Err()
  155. }
  156. func (self *_parser) next() {
  157. self.token, self.literal, self.idx = self.scan()
  158. }
  159. func (self *_parser) optionalSemicolon() {
  160. if self.token == token.SEMICOLON {
  161. self.next()
  162. return
  163. }
  164. if self.implicitSemicolon {
  165. self.implicitSemicolon = false
  166. return
  167. }
  168. if self.token != token.EOF && self.token != token.RIGHT_BRACE {
  169. self.expect(token.SEMICOLON)
  170. }
  171. }
  172. func (self *_parser) semicolon() {
  173. if self.token != token.RIGHT_PARENTHESIS && self.token != token.RIGHT_BRACE {
  174. if self.implicitSemicolon {
  175. self.implicitSemicolon = false
  176. return
  177. }
  178. self.expect(token.SEMICOLON)
  179. }
  180. }
  181. func (self *_parser) idxOf(offset int) file.Idx {
  182. return file.Idx(self.base + offset)
  183. }
  184. func (self *_parser) expect(value token.Token) file.Idx {
  185. idx := self.idx
  186. if self.token != value {
  187. self.errorUnexpectedToken(self.token)
  188. }
  189. self.next()
  190. return idx
  191. }
  192. func lineCount(str string) (int, int) {
  193. line, last := 0, -1
  194. pair := false
  195. for index, chr := range str {
  196. switch chr {
  197. case '\r':
  198. line += 1
  199. last = index
  200. pair = true
  201. continue
  202. case '\n':
  203. if !pair {
  204. line += 1
  205. }
  206. last = index
  207. case '\u2028', '\u2029':
  208. line += 1
  209. last = index + 2
  210. }
  211. pair = false
  212. }
  213. return line, last
  214. }
  215. func (self *_parser) position(idx file.Idx) file.Position {
  216. position := file.Position{}
  217. offset := int(idx) - self.base
  218. str := self.str[:offset]
  219. position.Filename = self.file.Name()
  220. line, last := lineCount(str)
  221. position.Line = 1 + line
  222. if last >= 0 {
  223. position.Column = offset - last
  224. } else {
  225. position.Column = 1 + len(str)
  226. }
  227. return position
  228. }