Godeps, vendor: convert dependency management to trash (#3198)

This commit converts the dependency management from Godeps to the vendor
folder, also switching the tool from godep to trash. Since the upstream tool
lacks a few features proposed via a few PRs, until those PRs are merged in
(if), use github.com/karalabe/trash.

You can update dependencies via trash --update.

All dependencies have been updated to their latest version.

Parts of the build system are reworked to drop old notions of Godeps and
invocation of the go vet command so that it doesn't run against the vendor
folder, as that will just blow up during vetting.

The conversion drops OpenCL (and hence GPU mining support) from ethash and our
codebase. The short reasoning is that there's noone to maintain and having
opencl libs in our deps messes up builds as go install ./... tries to build
them, failing with unsatisfied link errors for the C OpenCL deps.

golang.org/x/net/context is not vendored in. We expect it to be fetched by the
user (i.e. using go get). To keep ci.go builds reproducible the package is
"vendored" in build/_vendor.
This commit is contained in:
Péter Szilágyi
2016-10-28 20:05:01 +03:00
committed by Felix Lange
parent 7770304576
commit 289b30715d
886 changed files with 23194 additions and 32856 deletions

4
vendor/github.com/robertkrimen/otto/parser/Makefile generated vendored Normal file
View File

@ -0,0 +1,4 @@
.PHONY: test
test:
go test

View File

@ -0,0 +1,190 @@
# parser
--
import "github.com/robertkrimen/otto/parser"
Package parser implements a parser for JavaScript.
import (
"github.com/robertkrimen/otto/parser"
)
Parse and return an AST
filename := "" // A filename is optional
src := `
// Sample xyzzy example
(function(){
if (3.14159 > 0) {
console.log("Hello, World.");
return;
}
var xyzzy = NaN;
console.log("Nothing happens.");
return xyzzy;
})();
`
// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
program, err := parser.ParseFile(nil, filename, src, 0)
### Warning
The parser and AST interfaces are still works-in-progress (particularly where
node types are concerned) and may change in the future.
## Usage
#### func ParseFile
```go
func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode) (*ast.Program, error)
```
ParseFile parses the source code of a single JavaScript/ECMAScript source file
and returns the corresponding ast.Program node.
If fileSet == nil, ParseFile parses source without a FileSet. If fileSet != nil,
ParseFile first adds filename and src to fileSet.
The filename argument is optional and is used for labelling errors, etc.
src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST
always be in UTF-8.
// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0)
#### func ParseFunction
```go
func ParseFunction(parameterList, body string) (*ast.FunctionLiteral, error)
```
ParseFunction parses a given parameter list and body as a function and returns
the corresponding ast.FunctionLiteral node.
The parameter list, if any, should be a comma-separated list of identifiers.
#### func ReadSource
```go
func ReadSource(filename string, src interface{}) ([]byte, error)
```
#### func TransformRegExp
```go
func TransformRegExp(pattern string) (string, error)
```
TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern.
re2 (Go) cannot do backtracking, so the presence of a lookahead (?=) (?!) or
backreference (\1, \2, ...) will cause an error.
re2 (Go) has a different definition for \s: [\t\n\f\r ]. The JavaScript
definition, on the other hand, also includes \v, Unicode "Separator, Space",
etc.
If the pattern is invalid (not valid even in JavaScript), then this function
returns the empty string and an error.
If the pattern is valid, but incompatible (contains a lookahead or
backreference), then this function returns the transformation (a non-empty
string) AND an error.
#### type Error
```go
type Error struct {
Position file.Position
Message string
}
```
An Error represents a parsing error. It includes the position where the error
occurred and a message/description.
#### func (Error) Error
```go
func (self Error) Error() string
```
#### type ErrorList
```go
type ErrorList []*Error
```
ErrorList is a list of *Errors.
#### func (*ErrorList) Add
```go
func (self *ErrorList) Add(position file.Position, msg string)
```
Add adds an Error with given position and message to an ErrorList.
#### func (ErrorList) Err
```go
func (self ErrorList) Err() error
```
Err returns an error equivalent to this ErrorList. If the list is empty, Err
returns nil.
#### func (ErrorList) Error
```go
func (self ErrorList) Error() string
```
Error implements the Error interface.
#### func (ErrorList) Len
```go
func (self ErrorList) Len() int
```
#### func (ErrorList) Less
```go
func (self ErrorList) Less(i, j int) bool
```
#### func (*ErrorList) Reset
```go
func (self *ErrorList) Reset()
```
Reset resets an ErrorList to no errors.
#### func (ErrorList) Sort
```go
func (self ErrorList) Sort()
```
#### func (ErrorList) Swap
```go
func (self ErrorList) Swap(i, j int)
```
#### type Mode
```go
type Mode uint
```
A Mode value is a set of flags (or 0). They control optional parser
functionality.
```go
const (
IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking)
)
```
--
**godocdown** http://github.com/robertkrimen/godocdown

9
vendor/github.com/robertkrimen/otto/parser/dbg.go generated vendored Normal file
View File

@ -0,0 +1,9 @@
// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) for github.com/robertkrimen/dbg
package parser
import (
Dbg "github.com/robertkrimen/otto/dbg"
)
var dbg, dbgf = Dbg.New()

175
vendor/github.com/robertkrimen/otto/parser/error.go generated vendored Normal file
View File

@ -0,0 +1,175 @@
package parser
import (
"fmt"
"sort"
"github.com/robertkrimen/otto/file"
"github.com/robertkrimen/otto/token"
)
const (
err_UnexpectedToken = "Unexpected token %v"
err_UnexpectedEndOfInput = "Unexpected end of input"
err_UnexpectedEscape = "Unexpected escape"
)
// UnexpectedNumber: 'Unexpected number',
// UnexpectedString: 'Unexpected string',
// UnexpectedIdentifier: 'Unexpected identifier',
// UnexpectedReserved: 'Unexpected reserved word',
// NewlineAfterThrow: 'Illegal newline after throw',
// InvalidRegExp: 'Invalid regular expression',
// UnterminatedRegExp: 'Invalid regular expression: missing /',
// InvalidLHSInAssignment: 'Invalid left-hand side in assignment',
// InvalidLHSInForIn: 'Invalid left-hand side in for-in',
// MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
// NoCatchOrFinally: 'Missing catch or finally after try',
// UnknownLabel: 'Undefined label \'%0\'',
// Redeclaration: '%0 \'%1\' has already been declared',
// IllegalContinue: 'Illegal continue statement',
// IllegalBreak: 'Illegal break statement',
// IllegalReturn: 'Illegal return statement',
// StrictModeWith: 'Strict mode code may not include a with statement',
// StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode',
// StrictVarName: 'Variable name may not be eval or arguments in strict mode',
// StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode',
// StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
// StrictFunctionName: 'Function name may not be eval or arguments in strict mode',
// StrictOctalLiteral: 'Octal literals are not allowed in strict mode.',
// StrictDelete: 'Delete of an unqualified identifier in strict mode.',
// StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode',
// AccessorDataProperty: 'Object literal may not have data and accessor property with the same name',
// AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name',
// StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode',
// StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode',
// StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode',
// StrictReservedWord: 'Use of future reserved word in strict mode'
// A SyntaxError is a description of an ECMAScript syntax error.
// An Error represents a parsing error. It includes the position where the error occurred and a message/description.
type Error struct {
Position file.Position
Message string
}
// FIXME Should this be "SyntaxError"?
func (self Error) Error() string {
filename := self.Position.Filename
if filename == "" {
filename = "(anonymous)"
}
return fmt.Sprintf("%s: Line %d:%d %s",
filename,
self.Position.Line,
self.Position.Column,
self.Message,
)
}
func (self *_parser) error(place interface{}, msg string, msgValues ...interface{}) *Error {
idx := file.Idx(0)
switch place := place.(type) {
case int:
idx = self.idxOf(place)
case file.Idx:
if place == 0 {
idx = self.idxOf(self.chrOffset)
} else {
idx = place
}
default:
panic(fmt.Errorf("error(%T, ...)", place))
}
position := self.position(idx)
msg = fmt.Sprintf(msg, msgValues...)
self.errors.Add(position, msg)
return self.errors[len(self.errors)-1]
}
func (self *_parser) errorUnexpected(idx file.Idx, chr rune) error {
if chr == -1 {
return self.error(idx, err_UnexpectedEndOfInput)
}
return self.error(idx, err_UnexpectedToken, token.ILLEGAL)
}
func (self *_parser) errorUnexpectedToken(tkn token.Token) error {
switch tkn {
case token.EOF:
return self.error(file.Idx(0), err_UnexpectedEndOfInput)
}
value := tkn.String()
switch tkn {
case token.BOOLEAN, token.NULL:
value = self.literal
case token.IDENTIFIER:
return self.error(self.idx, "Unexpected identifier")
case token.KEYWORD:
// TODO Might be a future reserved word
return self.error(self.idx, "Unexpected reserved word")
case token.NUMBER:
return self.error(self.idx, "Unexpected number")
case token.STRING:
return self.error(self.idx, "Unexpected string")
}
return self.error(self.idx, err_UnexpectedToken, value)
}
// ErrorList is a list of *Errors.
//
type ErrorList []*Error
// Add adds an Error with given position and message to an ErrorList.
func (self *ErrorList) Add(position file.Position, msg string) {
*self = append(*self, &Error{position, msg})
}
// Reset resets an ErrorList to no errors.
func (self *ErrorList) Reset() { *self = (*self)[0:0] }
func (self ErrorList) Len() int { return len(self) }
func (self ErrorList) Swap(i, j int) { self[i], self[j] = self[j], self[i] }
func (self ErrorList) Less(i, j int) bool {
x := &self[i].Position
y := &self[j].Position
if x.Filename < y.Filename {
return true
}
if x.Filename == y.Filename {
if x.Line < y.Line {
return true
}
if x.Line == y.Line {
return x.Column < y.Column
}
}
return false
}
func (self ErrorList) Sort() {
sort.Sort(self)
}
// Error implements the Error interface.
func (self ErrorList) Error() string {
switch len(self) {
case 0:
return "no errors"
case 1:
return self[0].Error()
}
return fmt.Sprintf("%s (and %d more errors)", self[0].Error(), len(self)-1)
}
// Err returns an error equivalent to this ErrorList.
// If the list is empty, Err returns nil.
func (self ErrorList) Err() error {
if len(self) == 0 {
return nil
}
return self
}

1005
vendor/github.com/robertkrimen/otto/parser/expression.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

866
vendor/github.com/robertkrimen/otto/parser/lexer.go generated vendored Normal file
View File

@ -0,0 +1,866 @@
package parser
import (
"bytes"
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"unicode"
"unicode/utf8"
"github.com/robertkrimen/otto/ast"
"github.com/robertkrimen/otto/file"
"github.com/robertkrimen/otto/token"
)
type _chr struct {
value rune
width int
}
var matchIdentifier = regexp.MustCompile(`^[$_\p{L}][$_\p{L}\d}]*$`)
func isDecimalDigit(chr rune) bool {
return '0' <= chr && chr <= '9'
}
func digitValue(chr rune) int {
switch {
case '0' <= chr && chr <= '9':
return int(chr - '0')
case 'a' <= chr && chr <= 'f':
return int(chr - 'a' + 10)
case 'A' <= chr && chr <= 'F':
return int(chr - 'A' + 10)
}
return 16 // Larger than any legal digit value
}
func isDigit(chr rune, base int) bool {
return digitValue(chr) < base
}
func isIdentifierStart(chr rune) bool {
return chr == '$' || chr == '_' || chr == '\\' ||
'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' ||
chr >= utf8.RuneSelf && unicode.IsLetter(chr)
}
func isIdentifierPart(chr rune) bool {
return chr == '$' || chr == '_' || chr == '\\' ||
'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' ||
'0' <= chr && chr <= '9' ||
chr >= utf8.RuneSelf && (unicode.IsLetter(chr) || unicode.IsDigit(chr))
}
func (self *_parser) scanIdentifier() (string, error) {
offset := self.chrOffset
parse := false
for isIdentifierPart(self.chr) {
if self.chr == '\\' {
distance := self.chrOffset - offset
self.read()
if self.chr != 'u' {
return "", fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr))
}
parse = true
var value rune
for j := 0; j < 4; j++ {
self.read()
decimal, ok := hex2decimal(byte(self.chr))
if !ok {
return "", fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr))
}
value = value<<4 | decimal
}
if value == '\\' {
return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value))
} else if distance == 0 {
if !isIdentifierStart(value) {
return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value))
}
} else if distance > 0 {
if !isIdentifierPart(value) {
return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value))
}
}
}
self.read()
}
literal := string(self.str[offset:self.chrOffset])
if parse {
return parseStringLiteral(literal)
}
return literal, nil
}
// 7.2
func isLineWhiteSpace(chr rune) bool {
switch chr {
case '\u0009', '\u000b', '\u000c', '\u0020', '\u00a0', '\ufeff':
return true
case '\u000a', '\u000d', '\u2028', '\u2029':
return false
case '\u0085':
return false
}
return unicode.IsSpace(chr)
}
// 7.3
func isLineTerminator(chr rune) bool {
switch chr {
case '\u000a', '\u000d', '\u2028', '\u2029':
return true
}
return false
}
func (self *_parser) scan() (tkn token.Token, literal string, idx file.Idx) {
self.implicitSemicolon = false
for {
self.skipWhiteSpace()
idx = self.idxOf(self.chrOffset)
insertSemicolon := false
switch chr := self.chr; {
case isIdentifierStart(chr):
var err error
literal, err = self.scanIdentifier()
if err != nil {
tkn = token.ILLEGAL
break
}
if len(literal) > 1 {
// Keywords are longer than 1 character, avoid lookup otherwise
var strict bool
tkn, strict = token.IsKeyword(literal)
switch tkn {
case 0: // Not a keyword
if literal == "true" || literal == "false" {
self.insertSemicolon = true
tkn = token.BOOLEAN
return
} else if literal == "null" {
self.insertSemicolon = true
tkn = token.NULL
return
}
case token.KEYWORD:
tkn = token.KEYWORD
if strict {
// TODO If strict and in strict mode, then this is not a break
break
}
return
case
token.THIS,
token.BREAK,
token.THROW, // A newline after a throw is not allowed, but we need to detect it
token.RETURN,
token.CONTINUE,
token.DEBUGGER:
self.insertSemicolon = true
return
default:
return
}
}
self.insertSemicolon = true
tkn = token.IDENTIFIER
return
case '0' <= chr && chr <= '9':
self.insertSemicolon = true
tkn, literal = self.scanNumericLiteral(false)
return
default:
self.read()
switch chr {
case -1:
if self.insertSemicolon {
self.insertSemicolon = false
self.implicitSemicolon = true
}
tkn = token.EOF
case '\r', '\n', '\u2028', '\u2029':
self.insertSemicolon = false
self.implicitSemicolon = true
self.comments.AtLineBreak()
continue
case ':':
tkn = token.COLON
case '.':
if digitValue(self.chr) < 10 {
insertSemicolon = true
tkn, literal = self.scanNumericLiteral(true)
} else {
tkn = token.PERIOD
}
case ',':
tkn = token.COMMA
case ';':
tkn = token.SEMICOLON
case '(':
tkn = token.LEFT_PARENTHESIS
case ')':
tkn = token.RIGHT_PARENTHESIS
insertSemicolon = true
case '[':
tkn = token.LEFT_BRACKET
case ']':
tkn = token.RIGHT_BRACKET
insertSemicolon = true
case '{':
tkn = token.LEFT_BRACE
case '}':
tkn = token.RIGHT_BRACE
insertSemicolon = true
case '+':
tkn = self.switch3(token.PLUS, token.ADD_ASSIGN, '+', token.INCREMENT)
if tkn == token.INCREMENT {
insertSemicolon = true
}
case '-':
tkn = self.switch3(token.MINUS, token.SUBTRACT_ASSIGN, '-', token.DECREMENT)
if tkn == token.DECREMENT {
insertSemicolon = true
}
case '*':
tkn = self.switch2(token.MULTIPLY, token.MULTIPLY_ASSIGN)
case '/':
if self.chr == '/' {
if self.mode&StoreComments != 0 {
literal := string(self.readSingleLineComment())
self.comments.AddComment(ast.NewComment(literal, self.idx))
continue
}
self.skipSingleLineComment()
continue
} else if self.chr == '*' {
if self.mode&StoreComments != 0 {
literal = string(self.readMultiLineComment())
self.comments.AddComment(ast.NewComment(literal, self.idx))
continue
}
self.skipMultiLineComment()
continue
} else {
// Could be division, could be RegExp literal
tkn = self.switch2(token.SLASH, token.QUOTIENT_ASSIGN)
insertSemicolon = true
}
case '%':
tkn = self.switch2(token.REMAINDER, token.REMAINDER_ASSIGN)
case '^':
tkn = self.switch2(token.EXCLUSIVE_OR, token.EXCLUSIVE_OR_ASSIGN)
case '<':
tkn = self.switch4(token.LESS, token.LESS_OR_EQUAL, '<', token.SHIFT_LEFT, token.SHIFT_LEFT_ASSIGN)
case '>':
tkn = self.switch6(token.GREATER, token.GREATER_OR_EQUAL, '>', token.SHIFT_RIGHT, token.SHIFT_RIGHT_ASSIGN, '>', token.UNSIGNED_SHIFT_RIGHT, token.UNSIGNED_SHIFT_RIGHT_ASSIGN)
case '=':
tkn = self.switch2(token.ASSIGN, token.EQUAL)
if tkn == token.EQUAL && self.chr == '=' {
self.read()
tkn = token.STRICT_EQUAL
}
case '!':
tkn = self.switch2(token.NOT, token.NOT_EQUAL)
if tkn == token.NOT_EQUAL && self.chr == '=' {
self.read()
tkn = token.STRICT_NOT_EQUAL
}
case '&':
if self.chr == '^' {
self.read()
tkn = self.switch2(token.AND_NOT, token.AND_NOT_ASSIGN)
} else {
tkn = self.switch3(token.AND, token.AND_ASSIGN, '&', token.LOGICAL_AND)
}
case '|':
tkn = self.switch3(token.OR, token.OR_ASSIGN, '|', token.LOGICAL_OR)
case '~':
tkn = token.BITWISE_NOT
case '?':
tkn = token.QUESTION_MARK
case '"', '\'':
insertSemicolon = true
tkn = token.STRING
var err error
literal, err = self.scanString(self.chrOffset - 1)
if err != nil {
tkn = token.ILLEGAL
}
default:
self.errorUnexpected(idx, chr)
tkn = token.ILLEGAL
}
}
self.insertSemicolon = insertSemicolon
return
}
}
func (self *_parser) switch2(tkn0, tkn1 token.Token) token.Token {
if self.chr == '=' {
self.read()
return tkn1
}
return tkn0
}
func (self *_parser) switch3(tkn0, tkn1 token.Token, chr2 rune, tkn2 token.Token) token.Token {
if self.chr == '=' {
self.read()
return tkn1
}
if self.chr == chr2 {
self.read()
return tkn2
}
return tkn0
}
func (self *_parser) switch4(tkn0, tkn1 token.Token, chr2 rune, tkn2, tkn3 token.Token) token.Token {
if self.chr == '=' {
self.read()
return tkn1
}
if self.chr == chr2 {
self.read()
if self.chr == '=' {
self.read()
return tkn3
}
return tkn2
}
return tkn0
}
func (self *_parser) switch6(tkn0, tkn1 token.Token, chr2 rune, tkn2, tkn3 token.Token, chr3 rune, tkn4, tkn5 token.Token) token.Token {
if self.chr == '=' {
self.read()
return tkn1
}
if self.chr == chr2 {
self.read()
if self.chr == '=' {
self.read()
return tkn3
}
if self.chr == chr3 {
self.read()
if self.chr == '=' {
self.read()
return tkn5
}
return tkn4
}
return tkn2
}
return tkn0
}
func (self *_parser) chrAt(index int) _chr {
value, width := utf8.DecodeRuneInString(self.str[index:])
return _chr{
value: value,
width: width,
}
}
func (self *_parser) _peek() rune {
if self.offset+1 < self.length {
return rune(self.str[self.offset+1])
}
return -1
}
func (self *_parser) read() {
if self.offset < self.length {
self.chrOffset = self.offset
chr, width := rune(self.str[self.offset]), 1
if chr >= utf8.RuneSelf { // !ASCII
chr, width = utf8.DecodeRuneInString(self.str[self.offset:])
if chr == utf8.RuneError && width == 1 {
self.error(self.chrOffset, "Invalid UTF-8 character")
}
}
self.offset += width
self.chr = chr
} else {
self.chrOffset = self.length
self.chr = -1 // EOF
}
}
// This is here since the functions are so similar
func (self *_RegExp_parser) read() {
if self.offset < self.length {
self.chrOffset = self.offset
chr, width := rune(self.str[self.offset]), 1
if chr >= utf8.RuneSelf { // !ASCII
chr, width = utf8.DecodeRuneInString(self.str[self.offset:])
if chr == utf8.RuneError && width == 1 {
self.error(self.chrOffset, "Invalid UTF-8 character")
}
}
self.offset += width
self.chr = chr
} else {
self.chrOffset = self.length
self.chr = -1 // EOF
}
}
func (self *_parser) readSingleLineComment() (result []rune) {
for self.chr != -1 {
self.read()
if isLineTerminator(self.chr) {
return
}
result = append(result, self.chr)
}
// Get rid of the trailing -1
result = result[:len(result)-1]
return
}
func (self *_parser) readMultiLineComment() (result []rune) {
self.read()
for self.chr >= 0 {
chr := self.chr
self.read()
if chr == '*' && self.chr == '/' {
self.read()
return
}
result = append(result, chr)
}
self.errorUnexpected(0, self.chr)
return
}
func (self *_parser) skipSingleLineComment() {
for self.chr != -1 {
self.read()
if isLineTerminator(self.chr) {
return
}
}
}
func (self *_parser) skipMultiLineComment() {
self.read()
for self.chr >= 0 {
chr := self.chr
self.read()
if chr == '*' && self.chr == '/' {
self.read()
return
}
}
self.errorUnexpected(0, self.chr)
}
func (self *_parser) skipWhiteSpace() {
for {
switch self.chr {
case ' ', '\t', '\f', '\v', '\u00a0', '\ufeff':
self.read()
continue
case '\r':
if self._peek() == '\n' {
self.comments.AtLineBreak()
self.read()
}
fallthrough
case '\u2028', '\u2029', '\n':
if self.insertSemicolon {
return
}
self.comments.AtLineBreak()
self.read()
continue
}
if self.chr >= utf8.RuneSelf {
if unicode.IsSpace(self.chr) {
self.read()
continue
}
}
break
}
}
func (self *_parser) skipLineWhiteSpace() {
for isLineWhiteSpace(self.chr) {
self.read()
}
}
func (self *_parser) scanMantissa(base int) {
for digitValue(self.chr) < base {
self.read()
}
}
func (self *_parser) scanEscape(quote rune) {
var length, base uint32
switch self.chr {
//case '0', '1', '2', '3', '4', '5', '6', '7':
// Octal:
// length, base, limit = 3, 8, 255
case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"', '\'', '0':
self.read()
return
case '\r', '\n', '\u2028', '\u2029':
self.scanNewline()
return
case 'x':
self.read()
length, base = 2, 16
case 'u':
self.read()
length, base = 4, 16
default:
self.read() // Always make progress
return
}
var value uint32
for ; length > 0 && self.chr != quote && self.chr >= 0; length-- {
digit := uint32(digitValue(self.chr))
if digit >= base {
break
}
value = value*base + digit
self.read()
}
}
func (self *_parser) scanString(offset int) (string, error) {
// " ' /
quote := rune(self.str[offset])
for self.chr != quote {
chr := self.chr
if chr == '\n' || chr == '\r' || chr == '\u2028' || chr == '\u2029' || chr < 0 {
goto newline
}
self.read()
if chr == '\\' {
if quote == '/' {
if self.chr == '\n' || self.chr == '\r' || self.chr == '\u2028' || self.chr == '\u2029' || self.chr < 0 {
goto newline
}
self.read()
} else {
self.scanEscape(quote)
}
} else if chr == '[' && quote == '/' {
// Allow a slash (/) in a bracket character class ([...])
// TODO Fix this, this is hacky...
quote = -1
} else if chr == ']' && quote == -1 {
quote = '/'
}
}
// " ' /
self.read()
return string(self.str[offset:self.chrOffset]), nil
newline:
self.scanNewline()
err := "String not terminated"
if quote == '/' {
err = "Invalid regular expression: missing /"
self.error(self.idxOf(offset), err)
}
return "", errors.New(err)
}
func (self *_parser) scanNewline() {
if self.chr == '\r' {
self.read()
if self.chr != '\n' {
return
}
}
self.read()
}
func hex2decimal(chr byte) (value rune, ok bool) {
{
chr := rune(chr)
switch {
case '0' <= chr && chr <= '9':
return chr - '0', true
case 'a' <= chr && chr <= 'f':
return chr - 'a' + 10, true
case 'A' <= chr && chr <= 'F':
return chr - 'A' + 10, true
}
return
}
}
func parseNumberLiteral(literal string) (value interface{}, err error) {
// TODO Is Uint okay? What about -MAX_UINT
value, err = strconv.ParseInt(literal, 0, 64)
if err == nil {
return
}
parseIntErr := err // Save this first error, just in case
value, err = strconv.ParseFloat(literal, 64)
if err == nil {
return
} else if err.(*strconv.NumError).Err == strconv.ErrRange {
// Infinity, etc.
return value, nil
}
err = parseIntErr
if err.(*strconv.NumError).Err == strconv.ErrRange {
if len(literal) > 2 && literal[0] == '0' && (literal[1] == 'X' || literal[1] == 'x') {
// Could just be a very large number (e.g. 0x8000000000000000)
var value float64
literal = literal[2:]
for _, chr := range literal {
digit := digitValue(chr)
if digit >= 16 {
goto error
}
value = value*16 + float64(digit)
}
return value, nil
}
}
error:
return nil, errors.New("Illegal numeric literal")
}
func parseStringLiteral(literal string) (string, error) {
// Best case scenario...
if literal == "" {
return "", nil
}
// Slightly less-best case scenario...
if !strings.ContainsRune(literal, '\\') {
return literal, nil
}
str := literal
buffer := bytes.NewBuffer(make([]byte, 0, 3*len(literal)/2))
for len(str) > 0 {
switch chr := str[0]; {
// We do not explicitly handle the case of the quote
// value, which can be: " ' /
// This assumes we're already passed a partially well-formed literal
case chr >= utf8.RuneSelf:
chr, size := utf8.DecodeRuneInString(str)
buffer.WriteRune(chr)
str = str[size:]
continue
case chr != '\\':
buffer.WriteByte(chr)
str = str[1:]
continue
}
if len(str) <= 1 {
panic("len(str) <= 1")
}
chr := str[1]
var value rune
if chr >= utf8.RuneSelf {
str = str[1:]
var size int
value, size = utf8.DecodeRuneInString(str)
str = str[size:] // \ + <character>
} else {
str = str[2:] // \<character>
switch chr {
case 'b':
value = '\b'
case 'f':
value = '\f'
case 'n':
value = '\n'
case 'r':
value = '\r'
case 't':
value = '\t'
case 'v':
value = '\v'
case 'x', 'u':
size := 0
switch chr {
case 'x':
size = 2
case 'u':
size = 4
}
if len(str) < size {
return "", fmt.Errorf("invalid escape: \\%s: len(%q) != %d", string(chr), str, size)
}
for j := 0; j < size; j++ {
decimal, ok := hex2decimal(str[j])
if !ok {
return "", fmt.Errorf("invalid escape: \\%s: %q", string(chr), str[:size])
}
value = value<<4 | decimal
}
str = str[size:]
if chr == 'x' {
break
}
if value > utf8.MaxRune {
panic("value > utf8.MaxRune")
}
case '0':
if len(str) == 0 || '0' > str[0] || str[0] > '7' {
value = 0
break
}
fallthrough
case '1', '2', '3', '4', '5', '6', '7':
// TODO strict
value = rune(chr) - '0'
j := 0
for ; j < 2; j++ {
if len(str) < j+1 {
break
}
chr := str[j]
if '0' > chr || chr > '7' {
break
}
decimal := rune(str[j]) - '0'
value = (value << 3) | decimal
}
str = str[j:]
case '\\':
value = '\\'
case '\'', '"':
value = rune(chr)
case '\r':
if len(str) > 0 {
if str[0] == '\n' {
str = str[1:]
}
}
fallthrough
case '\n':
continue
default:
value = rune(chr)
}
}
buffer.WriteRune(value)
}
return buffer.String(), nil
}
func (self *_parser) scanNumericLiteral(decimalPoint bool) (token.Token, string) {
offset := self.chrOffset
tkn := token.NUMBER
if decimalPoint {
offset--
self.scanMantissa(10)
goto exponent
}
if self.chr == '0' {
offset := self.chrOffset
self.read()
if self.chr == 'x' || self.chr == 'X' {
// Hexadecimal
self.read()
if isDigit(self.chr, 16) {
self.read()
} else {
return token.ILLEGAL, self.str[offset:self.chrOffset]
}
self.scanMantissa(16)
if self.chrOffset-offset <= 2 {
// Only "0x" or "0X"
self.error(0, "Illegal hexadecimal number")
}
goto hexadecimal
} else if self.chr == '.' {
// Float
goto float
} else {
// Octal, Float
if self.chr == 'e' || self.chr == 'E' {
goto exponent
}
self.scanMantissa(8)
if self.chr == '8' || self.chr == '9' {
return token.ILLEGAL, self.str[offset:self.chrOffset]
}
goto octal
}
}
self.scanMantissa(10)
float:
if self.chr == '.' {
self.read()
self.scanMantissa(10)
}
exponent:
if self.chr == 'e' || self.chr == 'E' {
self.read()
if self.chr == '-' || self.chr == '+' {
self.read()
}
if isDecimalDigit(self.chr) {
self.read()
self.scanMantissa(10)
} else {
return token.ILLEGAL, self.str[offset:self.chrOffset]
}
}
hexadecimal:
octal:
if isIdentifierStart(self.chr) || isDecimalDigit(self.chr) {
return token.ILLEGAL, self.str[offset:self.chrOffset]
}
return tkn, self.str[offset:self.chrOffset]
}

344
vendor/github.com/robertkrimen/otto/parser/parser.go generated vendored Normal file
View File

@ -0,0 +1,344 @@
/*
Package parser implements a parser for JavaScript.
import (
"github.com/robertkrimen/otto/parser"
)
Parse and return an AST
filename := "" // A filename is optional
src := `
// Sample xyzzy example
(function(){
if (3.14159 > 0) {
console.log("Hello, World.");
return;
}
var xyzzy = NaN;
console.log("Nothing happens.");
return xyzzy;
})();
`
// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
program, err := parser.ParseFile(nil, filename, src, 0)
Warning
The parser and AST interfaces are still works-in-progress (particularly where
node types are concerned) and may change in the future.
*/
package parser
import (
"bytes"
"encoding/base64"
"errors"
"io"
"io/ioutil"
"github.com/robertkrimen/otto/ast"
"github.com/robertkrimen/otto/file"
"github.com/robertkrimen/otto/token"
"gopkg.in/sourcemap.v1"
)
// A Mode value is a set of flags (or 0). They control optional parser functionality.
type Mode uint
const (
IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking)
StoreComments // Store the comments from source to the comments map
)
type _parser struct {
str string
length int
base int
chr rune // The current character
chrOffset int // The offset of current character
offset int // The offset after current character (may be greater than 1)
idx file.Idx // The index of token
token token.Token // The token
literal string // The literal of the token, if any
scope *_scope
insertSemicolon bool // If we see a newline, then insert an implicit semicolon
implicitSemicolon bool // An implicit semicolon exists
errors ErrorList
recover struct {
// Scratch when trying to seek to the next statement, etc.
idx file.Idx
count int
}
mode Mode
file *file.File
comments *ast.Comments
}
type Parser interface {
Scan() (tkn token.Token, literal string, idx file.Idx)
}
func _newParser(filename, src string, base int, sm *sourcemap.Consumer) *_parser {
return &_parser{
chr: ' ', // This is set so we can start scanning by skipping whitespace
str: src,
length: len(src),
base: base,
file: file.NewFile(filename, src, base).WithSourceMap(sm),
comments: ast.NewComments(),
}
}
// Returns a new Parser.
func NewParser(filename, src string) Parser {
return _newParser(filename, src, 1, nil)
}
func ReadSource(filename string, src interface{}) ([]byte, error) {
if src != nil {
switch src := src.(type) {
case string:
return []byte(src), nil
case []byte:
return src, nil
case *bytes.Buffer:
if src != nil {
return src.Bytes(), nil
}
case io.Reader:
var bfr bytes.Buffer
if _, err := io.Copy(&bfr, src); err != nil {
return nil, err
}
return bfr.Bytes(), nil
}
return nil, errors.New("invalid source")
}
return ioutil.ReadFile(filename)
}
func ReadSourceMap(filename string, src interface{}) (*sourcemap.Consumer, error) {
if src == nil {
return nil, nil
}
switch src := src.(type) {
case string:
return sourcemap.Parse(filename, []byte(src))
case []byte:
return sourcemap.Parse(filename, src)
case *bytes.Buffer:
if src != nil {
return sourcemap.Parse(filename, src.Bytes())
}
case io.Reader:
var bfr bytes.Buffer
if _, err := io.Copy(&bfr, src); err != nil {
return nil, err
}
return sourcemap.Parse(filename, bfr.Bytes())
case *sourcemap.Consumer:
return src, nil
}
return nil, errors.New("invalid sourcemap type")
}
func ParseFileWithSourceMap(fileSet *file.FileSet, filename string, javascriptSource, sourcemapSource interface{}, mode Mode) (*ast.Program, error) {
src, err := ReadSource(filename, javascriptSource)
if err != nil {
return nil, err
}
if sourcemapSource == nil {
lines := bytes.Split(src, []byte("\n"))
lastLine := lines[len(lines)-1]
if bytes.HasPrefix(lastLine, []byte("//# sourceMappingURL=data:application/json")) {
bits := bytes.SplitN(lastLine, []byte(","), 2)
if len(bits) == 2 {
if d, err := base64.StdEncoding.DecodeString(string(bits[1])); err == nil {
sourcemapSource = d
}
}
}
}
sm, err := ReadSourceMap(filename, sourcemapSource)
if err != nil {
return nil, err
}
base := 1
if fileSet != nil {
base = fileSet.AddFile(filename, string(src))
}
parser := _newParser(filename, string(src), base, sm)
parser.mode = mode
program, err := parser.parse()
program.Comments = parser.comments.CommentMap
return program, err
}
// ParseFile parses the source code of a single JavaScript/ECMAScript source file and returns
// the corresponding ast.Program node.
//
// If fileSet == nil, ParseFile parses source without a FileSet.
// If fileSet != nil, ParseFile first adds filename and src to fileSet.
//
// The filename argument is optional and is used for labelling errors, etc.
//
// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8.
//
// // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
// program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0)
//
func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode) (*ast.Program, error) {
return ParseFileWithSourceMap(fileSet, filename, src, nil, mode)
}
// ParseFunction parses a given parameter list and body as a function and returns the
// corresponding ast.FunctionLiteral node.
//
// The parameter list, if any, should be a comma-separated list of identifiers.
//
func ParseFunction(parameterList, body string) (*ast.FunctionLiteral, error) {
src := "(function(" + parameterList + ") {\n" + body + "\n})"
parser := _newParser("", src, 1, nil)
program, err := parser.parse()
if err != nil {
return nil, err
}
return program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral), nil
}
// Scan reads a single token from the source at the current offset, increments the offset and
// returns the token.Token token, a string literal representing the value of the token (if applicable)
// and it's current file.Idx index.
func (self *_parser) Scan() (tkn token.Token, literal string, idx file.Idx) {
return self.scan()
}
func (self *_parser) slice(idx0, idx1 file.Idx) string {
from := int(idx0) - self.base
to := int(idx1) - self.base
if from >= 0 && to <= len(self.str) {
return self.str[from:to]
}
return ""
}
func (self *_parser) parse() (*ast.Program, error) {
self.next()
program := self.parseProgram()
if false {
self.errors.Sort()
}
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(program, self.comments.FetchAll(), ast.TRAILING)
}
return program, self.errors.Err()
}
func (self *_parser) next() {
self.token, self.literal, self.idx = self.scan()
}
func (self *_parser) optionalSemicolon() {
if self.token == token.SEMICOLON {
self.next()
return
}
if self.implicitSemicolon {
self.implicitSemicolon = false
return
}
if self.token != token.EOF && self.token != token.RIGHT_BRACE {
self.expect(token.SEMICOLON)
}
}
func (self *_parser) semicolon() {
if self.token != token.RIGHT_PARENTHESIS && self.token != token.RIGHT_BRACE {
if self.implicitSemicolon {
self.implicitSemicolon = false
return
}
self.expect(token.SEMICOLON)
}
}
func (self *_parser) idxOf(offset int) file.Idx {
return file.Idx(self.base + offset)
}
func (self *_parser) expect(value token.Token) file.Idx {
idx := self.idx
if self.token != value {
self.errorUnexpectedToken(self.token)
}
self.next()
return idx
}
func lineCount(str string) (int, int) {
line, last := 0, -1
pair := false
for index, chr := range str {
switch chr {
case '\r':
line += 1
last = index
pair = true
continue
case '\n':
if !pair {
line += 1
}
last = index
case '\u2028', '\u2029':
line += 1
last = index + 2
}
pair = false
}
return line, last
}
func (self *_parser) position(idx file.Idx) file.Position {
position := file.Position{}
offset := int(idx) - self.base
str := self.str[:offset]
position.Filename = self.file.Name()
line, last := lineCount(str)
position.Line = 1 + line
if last >= 0 {
position.Column = offset - last
} else {
position.Column = 1 + len(str)
}
return position
}

358
vendor/github.com/robertkrimen/otto/parser/regexp.go generated vendored Normal file
View File

@ -0,0 +1,358 @@
package parser
import (
"bytes"
"fmt"
"strconv"
)
type _RegExp_parser struct {
str string
length int
chr rune // The current character
chrOffset int // The offset of current character
offset int // The offset after current character (may be greater than 1)
errors []error
invalid bool // The input is an invalid JavaScript RegExp
goRegexp *bytes.Buffer
}
// TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern.
//
// re2 (Go) cannot do backtracking, so the presence of a lookahead (?=) (?!) or
// backreference (\1, \2, ...) will cause an error.
//
// re2 (Go) has a different definition for \s: [\t\n\f\r ].
// The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc.
//
// If the pattern is invalid (not valid even in JavaScript), then this function
// returns the empty string and an error.
//
// If the pattern is valid, but incompatible (contains a lookahead or backreference),
// then this function returns the transformation (a non-empty string) AND an error.
func TransformRegExp(pattern string) (string, error) {
if pattern == "" {
return "", nil
}
// TODO If without \, if without (?=, (?!, then another shortcut
parser := _RegExp_parser{
str: pattern,
length: len(pattern),
goRegexp: bytes.NewBuffer(make([]byte, 0, 3*len(pattern)/2)),
}
parser.read() // Pull in the first character
parser.scan()
var err error
if len(parser.errors) > 0 {
err = parser.errors[0]
}
if parser.invalid {
return "", err
}
// Might not be re2 compatible, but is still a valid JavaScript RegExp
return parser.goRegexp.String(), err
}
func (self *_RegExp_parser) scan() {
for self.chr != -1 {
switch self.chr {
case '\\':
self.read()
self.scanEscape(false)
case '(':
self.pass()
self.scanGroup()
case '[':
self.pass()
self.scanBracket()
case ')':
self.error(-1, "Unmatched ')'")
self.invalid = true
self.pass()
default:
self.pass()
}
}
}
// (...)
func (self *_RegExp_parser) scanGroup() {
str := self.str[self.chrOffset:]
if len(str) > 1 { // A possibility of (?= or (?!
if str[0] == '?' {
if str[1] == '=' || str[1] == '!' {
self.error(-1, "re2: Invalid (%s) <lookahead>", self.str[self.chrOffset:self.chrOffset+2])
}
}
}
for self.chr != -1 && self.chr != ')' {
switch self.chr {
case '\\':
self.read()
self.scanEscape(false)
case '(':
self.pass()
self.scanGroup()
case '[':
self.pass()
self.scanBracket()
default:
self.pass()
continue
}
}
if self.chr != ')' {
self.error(-1, "Unterminated group")
self.invalid = true
return
}
self.pass()
}
// [...]
func (self *_RegExp_parser) scanBracket() {
for self.chr != -1 {
if self.chr == ']' {
break
} else if self.chr == '\\' {
self.read()
self.scanEscape(true)
continue
}
self.pass()
}
if self.chr != ']' {
self.error(-1, "Unterminated character class")
self.invalid = true
return
}
self.pass()
}
// \...
func (self *_RegExp_parser) scanEscape(inClass bool) {
offset := self.chrOffset
var length, base uint32
switch self.chr {
case '0', '1', '2', '3', '4', '5', '6', '7':
var value int64
size := 0
for {
digit := int64(digitValue(self.chr))
if digit >= 8 {
// Not a valid digit
break
}
value = value*8 + digit
self.read()
size += 1
}
if size == 1 { // The number of characters read
_, err := self.goRegexp.Write([]byte{'\\', byte(value) + '0'})
if err != nil {
self.errors = append(self.errors, err)
}
if value != 0 {
// An invalid backreference
self.error(-1, "re2: Invalid \\%d <backreference>", value)
}
return
}
tmp := []byte{'\\', 'x', '0', 0}
if value >= 16 {
tmp = tmp[0:2]
} else {
tmp = tmp[0:3]
}
tmp = strconv.AppendInt(tmp, value, 16)
_, err := self.goRegexp.Write(tmp)
if err != nil {
self.errors = append(self.errors, err)
}
return
case '8', '9':
size := 0
for {
digit := digitValue(self.chr)
if digit >= 10 {
// Not a valid digit
break
}
self.read()
size += 1
}
err := self.goRegexp.WriteByte('\\')
if err != nil {
self.errors = append(self.errors, err)
}
_, err = self.goRegexp.WriteString(self.str[offset:self.chrOffset])
if err != nil {
self.errors = append(self.errors, err)
}
self.error(-1, "re2: Invalid \\%s <backreference>", self.str[offset:self.chrOffset])
return
case 'x':
self.read()
length, base = 2, 16
case 'u':
self.read()
length, base = 4, 16
case 'b':
if inClass {
_, err := self.goRegexp.Write([]byte{'\\', 'x', '0', '8'})
if err != nil {
self.errors = append(self.errors, err)
}
self.read()
return
}
fallthrough
case 'B':
fallthrough
case 'd', 'D', 's', 'S', 'w', 'W':
// This is slightly broken, because ECMAScript
// includes \v in \s, \S, while re2 does not
fallthrough
case '\\':
fallthrough
case 'f', 'n', 'r', 't', 'v':
err := self.goRegexp.WriteByte('\\')
if err != nil {
self.errors = append(self.errors, err)
}
self.pass()
return
case 'c':
self.read()
var value int64
if 'a' <= self.chr && self.chr <= 'z' {
value = int64(self.chr) - 'a' + 1
} else if 'A' <= self.chr && self.chr <= 'Z' {
value = int64(self.chr) - 'A' + 1
} else {
err := self.goRegexp.WriteByte('c')
if err != nil {
self.errors = append(self.errors, err)
}
return
}
tmp := []byte{'\\', 'x', '0', 0}
if value >= 16 {
tmp = tmp[0:2]
} else {
tmp = tmp[0:3]
}
tmp = strconv.AppendInt(tmp, value, 16)
_, err := self.goRegexp.Write(tmp)
if err != nil {
self.errors = append(self.errors, err)
}
self.read()
return
default:
// $ is an identifier character, so we have to have
// a special case for it here
if self.chr == '$' || !isIdentifierPart(self.chr) {
// A non-identifier character needs escaping
err := self.goRegexp.WriteByte('\\')
if err != nil {
self.errors = append(self.errors, err)
}
} else {
// Unescape the character for re2
}
self.pass()
return
}
// Otherwise, we're a \u.... or \x...
valueOffset := self.chrOffset
var value uint32
{
length := length
for ; length > 0; length-- {
digit := uint32(digitValue(self.chr))
if digit >= base {
// Not a valid digit
goto skip
}
value = value*base + digit
self.read()
}
}
if length == 4 {
_, err := self.goRegexp.Write([]byte{
'\\',
'x',
'{',
self.str[valueOffset+0],
self.str[valueOffset+1],
self.str[valueOffset+2],
self.str[valueOffset+3],
'}',
})
if err != nil {
self.errors = append(self.errors, err)
}
} else if length == 2 {
_, err := self.goRegexp.Write([]byte{
'\\',
'x',
self.str[valueOffset+0],
self.str[valueOffset+1],
})
if err != nil {
self.errors = append(self.errors, err)
}
} else {
// Should never, ever get here...
self.error(-1, "re2: Illegal branch in scanEscape")
goto skip
}
return
skip:
_, err := self.goRegexp.WriteString(self.str[offset:self.chrOffset])
if err != nil {
self.errors = append(self.errors, err)
}
}
func (self *_RegExp_parser) pass() {
if self.chr != -1 {
_, err := self.goRegexp.WriteRune(self.chr)
if err != nil {
self.errors = append(self.errors, err)
}
}
self.read()
}
// TODO Better error reporting, use the offset, etc.
func (self *_RegExp_parser) error(offset int, msg string, msgValues ...interface{}) error {
err := fmt.Errorf(msg, msgValues...)
self.errors = append(self.errors, err)
return err
}

44
vendor/github.com/robertkrimen/otto/parser/scope.go generated vendored Normal file
View File

@ -0,0 +1,44 @@
package parser
import (
"github.com/robertkrimen/otto/ast"
)
type _scope struct {
outer *_scope
allowIn bool
inIteration bool
inSwitch bool
inFunction bool
declarationList []ast.Declaration
labels []string
}
func (self *_parser) openScope() {
self.scope = &_scope{
outer: self.scope,
allowIn: true,
}
}
func (self *_parser) closeScope() {
self.scope = self.scope.outer
}
func (self *_scope) declare(declaration ast.Declaration) {
self.declarationList = append(self.declarationList, declaration)
}
func (self *_scope) hasLabel(name string) bool {
for _, label := range self.labels {
if label == name {
return true
}
}
if self.outer != nil && !self.inFunction {
// Crossing a function boundary to look for a label is verboten
return self.outer.hasLabel(name)
}
return false
}

938
vendor/github.com/robertkrimen/otto/parser/statement.go generated vendored Normal file
View File

@ -0,0 +1,938 @@
package parser
import (
"github.com/robertkrimen/otto/ast"
"github.com/robertkrimen/otto/token"
)
func (self *_parser) parseBlockStatement() *ast.BlockStatement {
node := &ast.BlockStatement{}
// Find comments before the leading brace
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.LEADING)
self.comments.Unset()
}
node.LeftBrace = self.expect(token.LEFT_BRACE)
node.List = self.parseStatementList()
if self.mode&StoreComments != 0 {
self.comments.Unset()
self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.FINAL)
self.comments.AfterBlock()
}
node.RightBrace = self.expect(token.RIGHT_BRACE)
// Find comments after the trailing brace
if self.mode&StoreComments != 0 {
self.comments.ResetLineBreak()
self.comments.CommentMap.AddComments(node, self.comments.Fetch(), ast.TRAILING)
}
return node
}
func (self *_parser) parseEmptyStatement() ast.Statement {
idx := self.expect(token.SEMICOLON)
return &ast.EmptyStatement{Semicolon: idx}
}
func (self *_parser) parseStatementList() (list []ast.Statement) {
for self.token != token.RIGHT_BRACE && self.token != token.EOF {
statement := self.parseStatement()
list = append(list, statement)
}
return
}
func (self *_parser) parseStatement() ast.Statement {
if self.token == token.EOF {
self.errorUnexpectedToken(self.token)
return &ast.BadStatement{From: self.idx, To: self.idx + 1}
}
if self.mode&StoreComments != 0 {
self.comments.ResetLineBreak()
}
switch self.token {
case token.SEMICOLON:
return self.parseEmptyStatement()
case token.LEFT_BRACE:
return self.parseBlockStatement()
case token.IF:
return self.parseIfStatement()
case token.DO:
statement := self.parseDoWhileStatement()
self.comments.PostProcessNode(statement)
return statement
case token.WHILE:
return self.parseWhileStatement()
case token.FOR:
return self.parseForOrForInStatement()
case token.BREAK:
return self.parseBreakStatement()
case token.CONTINUE:
return self.parseContinueStatement()
case token.DEBUGGER:
return self.parseDebuggerStatement()
case token.WITH:
return self.parseWithStatement()
case token.VAR:
return self.parseVariableStatement()
case token.FUNCTION:
return self.parseFunctionStatement()
case token.SWITCH:
return self.parseSwitchStatement()
case token.RETURN:
return self.parseReturnStatement()
case token.THROW:
return self.parseThrowStatement()
case token.TRY:
return self.parseTryStatement()
}
var comments []*ast.Comment
if self.mode&StoreComments != 0 {
comments = self.comments.FetchAll()
}
expression := self.parseExpression()
if identifier, isIdentifier := expression.(*ast.Identifier); isIdentifier && self.token == token.COLON {
// LabelledStatement
colon := self.idx
if self.mode&StoreComments != 0 {
self.comments.Unset()
}
self.next() // :
label := identifier.Name
for _, value := range self.scope.labels {
if label == value {
self.error(identifier.Idx0(), "Label '%s' already exists", label)
}
}
var labelComments []*ast.Comment
if self.mode&StoreComments != 0 {
labelComments = self.comments.FetchAll()
}
self.scope.labels = append(self.scope.labels, label) // Push the label
statement := self.parseStatement()
self.scope.labels = self.scope.labels[:len(self.scope.labels)-1] // Pop the label
exp := &ast.LabelledStatement{
Label: identifier,
Colon: colon,
Statement: statement,
}
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(exp, labelComments, ast.LEADING)
}
return exp
}
self.optionalSemicolon()
statement := &ast.ExpressionStatement{
Expression: expression,
}
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(statement, comments, ast.LEADING)
}
return statement
}
func (self *_parser) parseTryStatement() ast.Statement {
var tryComments []*ast.Comment
if self.mode&StoreComments != 0 {
tryComments = self.comments.FetchAll()
}
node := &ast.TryStatement{
Try: self.expect(token.TRY),
Body: self.parseBlockStatement(),
}
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(node, tryComments, ast.LEADING)
self.comments.CommentMap.AddComments(node.Body, self.comments.FetchAll(), ast.TRAILING)
}
if self.token == token.CATCH {
catch := self.idx
if self.mode&StoreComments != 0 {
self.comments.Unset()
}
self.next()
self.expect(token.LEFT_PARENTHESIS)
if self.token != token.IDENTIFIER {
self.expect(token.IDENTIFIER)
self.nextStatement()
return &ast.BadStatement{From: catch, To: self.idx}
} else {
identifier := self.parseIdentifier()
self.expect(token.RIGHT_PARENTHESIS)
node.Catch = &ast.CatchStatement{
Catch: catch,
Parameter: identifier,
Body: self.parseBlockStatement(),
}
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(node.Catch.Body, self.comments.FetchAll(), ast.TRAILING)
}
}
}
if self.token == token.FINALLY {
if self.mode&StoreComments != 0 {
self.comments.Unset()
}
self.next()
if self.mode&StoreComments != 0 {
tryComments = self.comments.FetchAll()
}
node.Finally = self.parseBlockStatement()
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(node.Finally, tryComments, ast.LEADING)
}
}
if node.Catch == nil && node.Finally == nil {
self.error(node.Try, "Missing catch or finally after try")
return &ast.BadStatement{From: node.Try, To: node.Body.Idx1()}
}
return node
}
func (self *_parser) parseFunctionParameterList() *ast.ParameterList {
opening := self.expect(token.LEFT_PARENTHESIS)
if self.mode&StoreComments != 0 {
self.comments.Unset()
}
var list []*ast.Identifier
for self.token != token.RIGHT_PARENTHESIS && self.token != token.EOF {
if self.token != token.IDENTIFIER {
self.expect(token.IDENTIFIER)
} else {
identifier := self.parseIdentifier()
list = append(list, identifier)
}
if self.token != token.RIGHT_PARENTHESIS {
if self.mode&StoreComments != 0 {
self.comments.Unset()
}
self.expect(token.COMMA)
}
}
closing := self.expect(token.RIGHT_PARENTHESIS)
return &ast.ParameterList{
Opening: opening,
List: list,
Closing: closing,
}
}
func (self *_parser) parseParameterList() (list []string) {
for self.token != token.EOF {
if self.token != token.IDENTIFIER {
self.expect(token.IDENTIFIER)
}
list = append(list, self.literal)
self.next()
if self.token != token.EOF {
self.expect(token.COMMA)
}
}
return
}
func (self *_parser) parseFunctionStatement() *ast.FunctionStatement {
var comments []*ast.Comment
if self.mode&StoreComments != 0 {
comments = self.comments.FetchAll()
}
function := &ast.FunctionStatement{
Function: self.parseFunction(true),
}
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(function, comments, ast.LEADING)
}
return function
}
func (self *_parser) parseFunction(declaration bool) *ast.FunctionLiteral {
node := &ast.FunctionLiteral{
Function: self.expect(token.FUNCTION),
}
var name *ast.Identifier
if self.token == token.IDENTIFIER {
name = self.parseIdentifier()
if declaration {
self.scope.declare(&ast.FunctionDeclaration{
Function: node,
})
}
} else if declaration {
// Use expect error handling
self.expect(token.IDENTIFIER)
}
if self.mode&StoreComments != 0 {
self.comments.Unset()
}
node.Name = name
node.ParameterList = self.parseFunctionParameterList()
self.parseFunctionBlock(node)
node.Source = self.slice(node.Idx0(), node.Idx1())
return node
}
func (self *_parser) parseFunctionBlock(node *ast.FunctionLiteral) {
{
self.openScope()
inFunction := self.scope.inFunction
self.scope.inFunction = true
defer func() {
self.scope.inFunction = inFunction
self.closeScope()
}()
node.Body = self.parseBlockStatement()
node.DeclarationList = self.scope.declarationList
}
}
func (self *_parser) parseDebuggerStatement() ast.Statement {
idx := self.expect(token.DEBUGGER)
node := &ast.DebuggerStatement{
Debugger: idx,
}
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.TRAILING)
}
self.semicolon()
return node
}
func (self *_parser) parseReturnStatement() ast.Statement {
idx := self.expect(token.RETURN)
var comments []*ast.Comment
if self.mode&StoreComments != 0 {
comments = self.comments.FetchAll()
}
if !self.scope.inFunction {
self.error(idx, "Illegal return statement")
self.nextStatement()
return &ast.BadStatement{From: idx, To: self.idx}
}
node := &ast.ReturnStatement{
Return: idx,
}
if !self.implicitSemicolon && self.token != token.SEMICOLON && self.token != token.RIGHT_BRACE && self.token != token.EOF {
node.Argument = self.parseExpression()
}
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
}
self.semicolon()
return node
}
func (self *_parser) parseThrowStatement() ast.Statement {
var comments []*ast.Comment
if self.mode&StoreComments != 0 {
comments = self.comments.FetchAll()
}
idx := self.expect(token.THROW)
if self.implicitSemicolon {
if self.chr == -1 { // Hackish
self.error(idx, "Unexpected end of input")
} else {
self.error(idx, "Illegal newline after throw")
}
self.nextStatement()
return &ast.BadStatement{From: idx, To: self.idx}
}
node := &ast.ThrowStatement{
Argument: self.parseExpression(),
}
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
}
self.semicolon()
return node
}
func (self *_parser) parseSwitchStatement() ast.Statement {
var comments []*ast.Comment
if self.mode&StoreComments != 0 {
comments = self.comments.FetchAll()
}
self.expect(token.SWITCH)
if self.mode&StoreComments != 0 {
comments = append(comments, self.comments.FetchAll()...)
}
self.expect(token.LEFT_PARENTHESIS)
node := &ast.SwitchStatement{
Discriminant: self.parseExpression(),
Default: -1,
}
self.expect(token.RIGHT_PARENTHESIS)
if self.mode&StoreComments != 0 {
comments = append(comments, self.comments.FetchAll()...)
}
self.expect(token.LEFT_BRACE)
inSwitch := self.scope.inSwitch
self.scope.inSwitch = true
defer func() {
self.scope.inSwitch = inSwitch
}()
for index := 0; self.token != token.EOF; index++ {
if self.token == token.RIGHT_BRACE {
self.next()
break
}
clause := self.parseCaseStatement()
if clause.Test == nil {
if node.Default != -1 {
self.error(clause.Case, "Already saw a default in switch")
}
node.Default = index
}
node.Body = append(node.Body, clause)
}
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
}
return node
}
func (self *_parser) parseWithStatement() ast.Statement {
var comments []*ast.Comment
if self.mode&StoreComments != 0 {
comments = self.comments.FetchAll()
}
self.expect(token.WITH)
var withComments []*ast.Comment
if self.mode&StoreComments != 0 {
withComments = self.comments.FetchAll()
}
self.expect(token.LEFT_PARENTHESIS)
node := &ast.WithStatement{
Object: self.parseExpression(),
}
self.expect(token.RIGHT_PARENTHESIS)
if self.mode&StoreComments != 0 {
//comments = append(comments, self.comments.FetchAll()...)
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
self.comments.CommentMap.AddComments(node, withComments, ast.WITH)
}
node.Body = self.parseStatement()
return node
}
func (self *_parser) parseCaseStatement() *ast.CaseStatement {
node := &ast.CaseStatement{
Case: self.idx,
}
var comments []*ast.Comment
if self.mode&StoreComments != 0 {
comments = self.comments.FetchAll()
self.comments.Unset()
}
if self.token == token.DEFAULT {
self.next()
} else {
self.expect(token.CASE)
node.Test = self.parseExpression()
}
if self.mode&StoreComments != 0 {
self.comments.Unset()
}
self.expect(token.COLON)
for {
if self.token == token.EOF ||
self.token == token.RIGHT_BRACE ||
self.token == token.CASE ||
self.token == token.DEFAULT {
break
}
consequent := self.parseStatement()
node.Consequent = append(node.Consequent, consequent)
}
// Link the comments to the case statement
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
}
return node
}
func (self *_parser) parseIterationStatement() ast.Statement {
inIteration := self.scope.inIteration
self.scope.inIteration = true
defer func() {
self.scope.inIteration = inIteration
}()
return self.parseStatement()
}
func (self *_parser) parseForIn(into ast.Expression) *ast.ForInStatement {
// Already have consumed "<into> in"
source := self.parseExpression()
self.expect(token.RIGHT_PARENTHESIS)
body := self.parseIterationStatement()
forin := &ast.ForInStatement{
Into: into,
Source: source,
Body: body,
}
return forin
}
func (self *_parser) parseFor(initializer ast.Expression) *ast.ForStatement {
// Already have consumed "<initializer> ;"
var test, update ast.Expression
if self.token != token.SEMICOLON {
test = self.parseExpression()
}
if self.mode&StoreComments != 0 {
self.comments.Unset()
}
self.expect(token.SEMICOLON)
if self.token != token.RIGHT_PARENTHESIS {
update = self.parseExpression()
}
self.expect(token.RIGHT_PARENTHESIS)
body := self.parseIterationStatement()
forstatement := &ast.ForStatement{
Initializer: initializer,
Test: test,
Update: update,
Body: body,
}
return forstatement
}
func (self *_parser) parseForOrForInStatement() ast.Statement {
var comments []*ast.Comment
if self.mode&StoreComments != 0 {
comments = self.comments.FetchAll()
}
idx := self.expect(token.FOR)
var forComments []*ast.Comment
if self.mode&StoreComments != 0 {
forComments = self.comments.FetchAll()
}
self.expect(token.LEFT_PARENTHESIS)
var left []ast.Expression
forIn := false
if self.token != token.SEMICOLON {
allowIn := self.scope.allowIn
self.scope.allowIn = false
if self.token == token.VAR {
var_ := self.idx
var varComments []*ast.Comment
if self.mode&StoreComments != 0 {
varComments = self.comments.FetchAll()
self.comments.Unset()
}
self.next()
list := self.parseVariableDeclarationList(var_)
if len(list) == 1 && self.token == token.IN {
if self.mode&StoreComments != 0 {
self.comments.Unset()
}
self.next() // in
forIn = true
left = []ast.Expression{list[0]} // There is only one declaration
} else {
left = list
}
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(left[0], varComments, ast.LEADING)
}
} else {
left = append(left, self.parseExpression())
if self.token == token.IN {
self.next()
forIn = true
}
}
self.scope.allowIn = allowIn
}
if forIn {
switch left[0].(type) {
case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression, *ast.VariableExpression:
// These are all acceptable
default:
self.error(idx, "Invalid left-hand side in for-in")
self.nextStatement()
return &ast.BadStatement{From: idx, To: self.idx}
}
forin := self.parseForIn(left[0])
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(forin, comments, ast.LEADING)
self.comments.CommentMap.AddComments(forin, forComments, ast.FOR)
}
return forin
}
if self.mode&StoreComments != 0 {
self.comments.Unset()
}
self.expect(token.SEMICOLON)
initializer := &ast.SequenceExpression{Sequence: left}
forstatement := self.parseFor(initializer)
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(forstatement, comments, ast.LEADING)
self.comments.CommentMap.AddComments(forstatement, forComments, ast.FOR)
}
return forstatement
}
func (self *_parser) parseVariableStatement() *ast.VariableStatement {
var comments []*ast.Comment
if self.mode&StoreComments != 0 {
comments = self.comments.FetchAll()
}
idx := self.expect(token.VAR)
list := self.parseVariableDeclarationList(idx)
statement := &ast.VariableStatement{
Var: idx,
List: list,
}
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(statement, comments, ast.LEADING)
self.comments.Unset()
}
self.semicolon()
return statement
}
func (self *_parser) parseDoWhileStatement() ast.Statement {
inIteration := self.scope.inIteration
self.scope.inIteration = true
defer func() {
self.scope.inIteration = inIteration
}()
var comments []*ast.Comment
if self.mode&StoreComments != 0 {
comments = self.comments.FetchAll()
}
self.expect(token.DO)
var doComments []*ast.Comment
if self.mode&StoreComments != 0 {
doComments = self.comments.FetchAll()
}
node := &ast.DoWhileStatement{}
if self.token == token.LEFT_BRACE {
node.Body = self.parseBlockStatement()
} else {
node.Body = self.parseStatement()
}
self.expect(token.WHILE)
var whileComments []*ast.Comment
if self.mode&StoreComments != 0 {
whileComments = self.comments.FetchAll()
}
self.expect(token.LEFT_PARENTHESIS)
node.Test = self.parseExpression()
self.expect(token.RIGHT_PARENTHESIS)
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
self.comments.CommentMap.AddComments(node, doComments, ast.DO)
self.comments.CommentMap.AddComments(node, whileComments, ast.WHILE)
}
return node
}
func (self *_parser) parseWhileStatement() ast.Statement {
var comments []*ast.Comment
if self.mode&StoreComments != 0 {
comments = self.comments.FetchAll()
}
self.expect(token.WHILE)
var whileComments []*ast.Comment
if self.mode&StoreComments != 0 {
whileComments = self.comments.FetchAll()
}
self.expect(token.LEFT_PARENTHESIS)
node := &ast.WhileStatement{
Test: self.parseExpression(),
}
self.expect(token.RIGHT_PARENTHESIS)
node.Body = self.parseIterationStatement()
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
self.comments.CommentMap.AddComments(node, whileComments, ast.WHILE)
}
return node
}
func (self *_parser) parseIfStatement() ast.Statement {
var comments []*ast.Comment
if self.mode&StoreComments != 0 {
comments = self.comments.FetchAll()
}
self.expect(token.IF)
var ifComments []*ast.Comment
if self.mode&StoreComments != 0 {
ifComments = self.comments.FetchAll()
}
self.expect(token.LEFT_PARENTHESIS)
node := &ast.IfStatement{
Test: self.parseExpression(),
}
self.expect(token.RIGHT_PARENTHESIS)
if self.token == token.LEFT_BRACE {
node.Consequent = self.parseBlockStatement()
} else {
node.Consequent = self.parseStatement()
}
if self.token == token.ELSE {
self.next()
node.Alternate = self.parseStatement()
}
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
self.comments.CommentMap.AddComments(node, ifComments, ast.IF)
}
return node
}
func (self *_parser) parseSourceElement() ast.Statement {
statement := self.parseStatement()
//self.comments.Unset()
return statement
}
func (self *_parser) parseSourceElements() []ast.Statement {
body := []ast.Statement(nil)
for {
if self.token != token.STRING {
break
}
body = append(body, self.parseSourceElement())
}
for self.token != token.EOF {
body = append(body, self.parseSourceElement())
}
return body
}
func (self *_parser) parseProgram() *ast.Program {
self.openScope()
defer self.closeScope()
return &ast.Program{
Body: self.parseSourceElements(),
DeclarationList: self.scope.declarationList,
File: self.file,
}
}
func (self *_parser) parseBreakStatement() ast.Statement {
var comments []*ast.Comment
if self.mode&StoreComments != 0 {
comments = self.comments.FetchAll()
}
idx := self.expect(token.BREAK)
semicolon := self.implicitSemicolon
if self.token == token.SEMICOLON {
semicolon = true
self.next()
}
if semicolon || self.token == token.RIGHT_BRACE {
self.implicitSemicolon = false
if !self.scope.inIteration && !self.scope.inSwitch {
goto illegal
}
breakStatement := &ast.BranchStatement{
Idx: idx,
Token: token.BREAK,
}
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(breakStatement, comments, ast.LEADING)
self.comments.CommentMap.AddComments(breakStatement, self.comments.FetchAll(), ast.TRAILING)
}
return breakStatement
}
if self.token == token.IDENTIFIER {
identifier := self.parseIdentifier()
if !self.scope.hasLabel(identifier.Name) {
self.error(idx, "Undefined label '%s'", identifier.Name)
return &ast.BadStatement{From: idx, To: identifier.Idx1()}
}
self.semicolon()
breakStatement := &ast.BranchStatement{
Idx: idx,
Token: token.BREAK,
Label: identifier,
}
if self.mode&StoreComments != 0 {
self.comments.CommentMap.AddComments(breakStatement, comments, ast.LEADING)
}
return breakStatement
}
self.expect(token.IDENTIFIER)
illegal:
self.error(idx, "Illegal break statement")
self.nextStatement()
return &ast.BadStatement{From: idx, To: self.idx}
}
func (self *_parser) parseContinueStatement() ast.Statement {
idx := self.expect(token.CONTINUE)
semicolon := self.implicitSemicolon
if self.token == token.SEMICOLON {
semicolon = true
self.next()
}
if semicolon || self.token == token.RIGHT_BRACE {
self.implicitSemicolon = false
if !self.scope.inIteration {
goto illegal
}
return &ast.BranchStatement{
Idx: idx,
Token: token.CONTINUE,
}
}
if self.token == token.IDENTIFIER {
identifier := self.parseIdentifier()
if !self.scope.hasLabel(identifier.Name) {
self.error(idx, "Undefined label '%s'", identifier.Name)
return &ast.BadStatement{From: idx, To: identifier.Idx1()}
}
if !self.scope.inIteration {
goto illegal
}
self.semicolon()
return &ast.BranchStatement{
Idx: idx,
Token: token.CONTINUE,
Label: identifier,
}
}
self.expect(token.IDENTIFIER)
illegal:
self.error(idx, "Illegal continue statement")
self.nextStatement()
return &ast.BadStatement{From: idx, To: self.idx}
}
// Find the next statement after an error (recover)
func (self *_parser) nextStatement() {
for {
switch self.token {
case token.BREAK, token.CONTINUE,
token.FOR, token.IF, token.RETURN, token.SWITCH,
token.VAR, token.DO, token.TRY, token.WITH,
token.WHILE, token.THROW, token.CATCH, token.FINALLY:
// Return only if parser made some progress since last
// sync or if it has not reached 10 next calls without
// progress. Otherwise consume at least one token to
// avoid an endless parser loop
if self.idx == self.recover.idx && self.recover.count < 10 {
self.recover.count++
return
}
if self.idx > self.recover.idx {
self.recover.idx = self.idx
self.recover.count = 0
return
}
// Reaching here indicates a parser bug, likely an
// incorrect token list in this function, but it only
// leads to skipping of possibly correct code if a
// previous error is present, and thus is preferred
// over a non-terminating parse.
case token.EOF:
return
}
self.next()
}
}