From 6777531a2d3367ef3cee933d7a36945eecb37090 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Fri, 19 Feb 2016 12:06:58 +0100 Subject: [PATCH] console: seed random number generator --- Godeps/Godeps.json | 2 +- .../robertkrimen/otto/ast/comments.go | 92 ++++++ .../github.com/robertkrimen/otto/ast/node.go | 8 + .../robertkrimen/otto/builtin_math.go | 8 +- .../robertkrimen/otto/cmpl_parse.go | 3 + .../src/github.com/robertkrimen/otto/otto.go | 4 + .../robertkrimen/otto/parser/expression.go | 285 ++++++++++++++-- .../robertkrimen/otto/parser/lexer.go | 47 +++ .../robertkrimen/otto/parser/parser.go | 87 ++++- .../robertkrimen/otto/parser/statement.go | 306 +++++++++++++++++- .../github.com/robertkrimen/otto/runtime.go | 1 + jsre/jsre.go | 18 ++ 12 files changed, 815 insertions(+), 46 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/robertkrimen/otto/ast/comments.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index dcf0a60cb..2858ef0c9 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -84,7 +84,7 @@ }, { "ImportPath": "github.com/robertkrimen/otto", - "Rev": "c21072f61b64b51ea58138ccacf0a85d54b9f07c" + "Rev": "53221230c215611a90762720c9042ac782ef74ee" }, { "ImportPath": "github.com/syndtr/goleveldb/leveldb", diff --git a/Godeps/_workspace/src/github.com/robertkrimen/otto/ast/comments.go b/Godeps/_workspace/src/github.com/robertkrimen/otto/ast/comments.go new file mode 100644 index 000000000..227e34ecb --- /dev/null +++ b/Godeps/_workspace/src/github.com/robertkrimen/otto/ast/comments.go @@ -0,0 +1,92 @@ +package ast + +import ( + "fmt" + "github.com/robertkrimen/otto/file" +) + +// CommentPosition determines where the comment is in a given context +type CommentPosition int + +const ( + _ CommentPosition = iota + LEADING // Before the pertinent expression + TRAILING // After the pertinent expression + KEY // After a key or keyword + COLON // After a colon in a field declaration + FINAL // Final comments in a block, not belonging to a specific expression or the comment after a trailing , in an array or object literal + TBD +) + +// Comment contains the data of the comment +type Comment struct { + Begin file.Idx + Text string + Position CommentPosition +} + +// String returns a stringified version of the position +func (cp CommentPosition) String() string { + switch cp { + case LEADING: + return "Leading" + case TRAILING: + return "Trailing" + case KEY: + return "Key" + case COLON: + return "Colon" + case FINAL: + return "Final" + default: + return "???" + } +} + +// String returns a stringified version of the comment +func (c Comment) String() string { + return fmt.Sprintf("Comment: %v", c.Text) +} + +// CommentMap is the data structure where all found comments are stored +type CommentMap map[Node][]*Comment + +// AddComment adds a single comment to the map +func (cm CommentMap) AddComment(node Node, comment *Comment) { + list := cm[node] + list = append(list, comment) + + cm[node] = list +} + +// AddComments adds a slice of comments, given a node and an updated position +func (cm CommentMap) AddComments(node Node, comments []*Comment, position CommentPosition) { + for _, comment := range comments { + comment.Position = position + cm.AddComment(node, comment) + } +} + +// Size returns the size of the map +func (cm CommentMap) Size() int { + size := 0 + for _, comments := range cm { + size += len(comments) + } + + return size +} + +// MoveComments moves comments with a given position from a node to another +func (cm CommentMap) MoveComments(from, to Node, position CommentPosition) { + for i, c := range cm[from] { + if c.Position == position { + cm.AddComment(to, c) + + // Remove the comment from the "from" slice + cm[from][i] = cm[from][len(cm[from])-1] + cm[from][len(cm[from])-1] = nil + cm[from] = cm[from][:len(cm[from])-1] + } + } +} diff --git a/Godeps/_workspace/src/github.com/robertkrimen/otto/ast/node.go b/Godeps/_workspace/src/github.com/robertkrimen/otto/ast/node.go index eb46f8601..8a651dc2f 100644 --- a/Godeps/_workspace/src/github.com/robertkrimen/otto/ast/node.go +++ b/Godeps/_workspace/src/github.com/robertkrimen/otto/ast/node.go @@ -86,6 +86,11 @@ type ( Identifier Identifier } + EmptyExpression struct { + Begin file.Idx + End file.Idx + } + FunctionLiteral struct { Function file.Idx Name *Identifier @@ -185,6 +190,7 @@ func (*BracketExpression) _expressionNode() {} func (*CallExpression) _expressionNode() {} func (*ConditionalExpression) _expressionNode() {} func (*DotExpression) _expressionNode() {} +func (*EmptyExpression) _expressionNode() {} func (*FunctionLiteral) _expressionNode() {} func (*Identifier) _expressionNode() {} func (*NewExpression) _expressionNode() {} @@ -399,6 +405,7 @@ func (self *BracketExpression) Idx0() file.Idx { return self.Left.Idx0() } func (self *CallExpression) Idx0() file.Idx { return self.Callee.Idx0() } func (self *ConditionalExpression) Idx0() file.Idx { return self.Test.Idx0() } func (self *DotExpression) Idx0() file.Idx { return self.Left.Idx0() } +func (self *EmptyExpression) Idx0() file.Idx { return self.Begin } func (self *FunctionLiteral) Idx0() file.Idx { return self.Function } func (self *Identifier) Idx0() file.Idx { return self.Idx } func (self *NewExpression) Idx0() file.Idx { return self.New } @@ -447,6 +454,7 @@ func (self *BracketExpression) Idx1() file.Idx { return self.RightBracket + func (self *CallExpression) Idx1() file.Idx { return self.RightParenthesis + 1 } func (self *ConditionalExpression) Idx1() file.Idx { return self.Test.Idx1() } func (self *DotExpression) Idx1() file.Idx { return self.Identifier.Idx1() } +func (self *EmptyExpression) Idx1() file.Idx { return self.End } func (self *FunctionLiteral) Idx1() file.Idx { return self.Body.Idx1() } func (self *Identifier) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Name)) } func (self *NewExpression) Idx1() file.Idx { return self.RightParenthesis + 1 } diff --git a/Godeps/_workspace/src/github.com/robertkrimen/otto/builtin_math.go b/Godeps/_workspace/src/github.com/robertkrimen/otto/builtin_math.go index a9f4a55c1..7ce90c339 100644 --- a/Godeps/_workspace/src/github.com/robertkrimen/otto/builtin_math.go +++ b/Godeps/_workspace/src/github.com/robertkrimen/otto/builtin_math.go @@ -117,7 +117,13 @@ func builtinMath_pow(call FunctionCall) Value { } func builtinMath_random(call FunctionCall) Value { - return toValue_float64(rand.Float64()) + var v float64 + if call.runtime.random != nil { + v = call.runtime.random() + } else { + v = rand.Float64() + } + return toValue_float64(v) } func builtinMath_round(call FunctionCall) Value { diff --git a/Godeps/_workspace/src/github.com/robertkrimen/otto/cmpl_parse.go b/Godeps/_workspace/src/github.com/robertkrimen/otto/cmpl_parse.go index e758a5230..f1e002d39 100644 --- a/Godeps/_workspace/src/github.com/robertkrimen/otto/cmpl_parse.go +++ b/Godeps/_workspace/src/github.com/robertkrimen/otto/cmpl_parse.go @@ -82,6 +82,9 @@ func (cmpl *_compiler) parseExpression(in ast.Expression) _nodeExpression { identifier: in.Identifier.Name, } + case *ast.EmptyExpression: + return nil + case *ast.FunctionLiteral: name := "" if in.Name != nil { diff --git a/Godeps/_workspace/src/github.com/robertkrimen/otto/otto.go b/Godeps/_workspace/src/github.com/robertkrimen/otto/otto.go index 2ec033cbc..613533082 100644 --- a/Godeps/_workspace/src/github.com/robertkrimen/otto/otto.go +++ b/Godeps/_workspace/src/github.com/robertkrimen/otto/otto.go @@ -363,6 +363,10 @@ func (self Otto) SetDebuggerHandler(fn func(vm *Otto)) { self.runtime.debugger = fn } +func (self Otto) SetRandomSource(fn func() float64) { + self.runtime.random = fn +} + // Context is a structure that contains information about the current execution // context. type Context struct { diff --git a/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/expression.go b/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/expression.go index 8baf22f7c..a23a7279a 100644 --- a/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/expression.go +++ b/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/expression.go @@ -12,10 +12,14 @@ func (self *_parser) parseIdentifier() *ast.Identifier { literal := self.literal idx := self.idx self.next() - return &ast.Identifier{ + comments := self.findComments(false) + exp := &ast.Identifier{ Name: literal, Idx: idx, } + + self.commentMap.AddComments(exp, comments, ast.TRAILING) + return exp } func (self *_parser) parsePrimaryExpression() ast.Expression { @@ -196,11 +200,20 @@ func (self *_parser) parseVariableDeclarationList(var_ file.Idx) []ast.Expressio var list []ast.Expression for { - list = append(list, self.parseVariableDeclaration(&declarationList)) + comments := self.findComments(false) + + decl := self.parseVariableDeclaration(&declarationList) + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(decl, comments, ast.LEADING) + self.commentMap.AddComments(decl, self.findComments(false), ast.TRAILING) + } + + list = append(list, decl) if self.token != token.COMMA { break } self.next() + } self.scope.declare(&ast.VariableDeclaration{ @@ -211,10 +224,13 @@ func (self *_parser) parseVariableDeclarationList(var_ file.Idx) []ast.Expressio return list } -func (self *_parser) parseObjectPropertyKey() (string, string) { +func (self *_parser) parseObjectPropertyKey() (string, string, []*ast.Comment) { idx, tkn, literal := self.idx, self.token, self.literal value := "" self.next() + + comments := self.findComments(false) + switch tkn { case token.IDENTIFIER: value = literal @@ -238,15 +254,14 @@ func (self *_parser) parseObjectPropertyKey() (string, string) { value = literal } } - return literal, value + return literal, value, comments } func (self *_parser) parseObjectProperty() ast.Property { - - literal, value := self.parseObjectPropertyKey() + literal, value, comments := self.parseObjectPropertyKey() if literal == "get" && self.token != token.COLON { idx := self.idx - _, value := self.parseObjectPropertyKey() + _, value, _ := self.parseObjectPropertyKey() parameterList := self.parseFunctionParameterList() node := &ast.FunctionLiteral{ @@ -261,7 +276,7 @@ func (self *_parser) parseObjectProperty() ast.Property { } } else if literal == "set" && self.token != token.COLON { idx := self.idx - _, value := self.parseObjectPropertyKey() + _, value, _ := self.parseObjectPropertyKey() parameterList := self.parseFunctionParameterList() node := &ast.FunctionLiteral{ @@ -277,63 +292,128 @@ func (self *_parser) parseObjectProperty() ast.Property { } self.expect(token.COLON) + comments2 := self.findComments(false) - return ast.Property{ + exp := ast.Property{ Key: value, Kind: "value", Value: self.parseAssignmentExpression(), } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(exp.Value, comments, ast.KEY) + self.commentMap.AddComments(exp.Value, comments2, ast.COLON) + } + return exp } func (self *_parser) parseObjectLiteral() ast.Expression { var value []ast.Property idx0 := self.expect(token.LEFT_BRACE) + + var comments2 []*ast.Comment for self.token != token.RIGHT_BRACE && self.token != token.EOF { + + // Leading comments for object literal + comments := self.findComments(false) property := self.parseObjectProperty() + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(property.Value, comments, ast.LEADING) + self.commentMap.AddComments(property.Value, comments2, ast.LEADING) + } value = append(value, property) if self.token == token.COMMA { self.next() + + // Find leading comments after trailing comma + comments2 = self.findComments(false) continue } } idx1 := self.expect(token.RIGHT_BRACE) - return &ast.ObjectLiteral{ + exp := &ast.ObjectLiteral{ LeftBrace: idx0, RightBrace: idx1, Value: value, } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(exp, comments2, ast.FINAL) + } + self.consumeComments(exp, ast.FINAL) + + return exp } func (self *_parser) parseArrayLiteral() ast.Expression { - idx0 := self.expect(token.LEFT_BRACKET) + var comments2 []*ast.Comment + var comments []*ast.Comment var value []ast.Expression for self.token != token.RIGHT_BRACKET && self.token != token.EOF { + // Find leading comments for both empty and non-empty expressions + comments = self.findComments(false) + if self.token == token.COMMA { self.next() - value = append(value, nil) + + // This kind of comment requires a special empty expression node. + empty := &ast.EmptyExpression{self.idx, self.idx} + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(empty, comments, ast.LEADING) + self.commentMap.AddComments(empty, comments2, ast.LEADING) + } + + value = append(value, empty) + + // This comment belongs to the following expression, or trailing + comments2 = self.findComments(false) + continue } - value = append(value, self.parseAssignmentExpression()) + + exp := self.parseAssignmentExpression() + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(exp, comments, ast.LEADING) + self.commentMap.AddComments(exp, comments2, ast.LEADING) + } + + value = append(value, exp) if self.token != token.RIGHT_BRACKET { self.expect(token.COMMA) } + + // This comment belongs to the following expression, or trailing + comments2 = self.findComments(false) } idx1 := self.expect(token.RIGHT_BRACKET) - return &ast.ArrayLiteral{ + array := &ast.ArrayLiteral{ LeftBracket: idx0, RightBracket: idx1, Value: value, } + + // This is where comments after a possible trailing comma are added + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(array, comments2, ast.FINAL) + } + + return array } func (self *_parser) parseArgumentList() (argumentList []ast.Expression, idx0, idx1 file.Idx) { idx0 = self.expect(token.LEFT_PARENTHESIS) if self.token != token.RIGHT_PARENTHESIS { for { - argumentList = append(argumentList, self.parseAssignmentExpression()) + comments := self.findComments(false) + exp := self.parseAssignmentExpression() + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(exp, comments, ast.LEADING) + } + argumentList = append(argumentList, exp) if self.token != token.COMMA { break } @@ -346,12 +426,17 @@ func (self *_parser) parseArgumentList() (argumentList []ast.Expression, idx0, i func (self *_parser) parseCallExpression(left ast.Expression) ast.Expression { argumentList, idx0, idx1 := self.parseArgumentList() - return &ast.CallExpression{ + exp := &ast.CallExpression{ Callee: left, LeftParenthesis: idx0, ArgumentList: argumentList, RightParenthesis: idx1, } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(exp, self.findComments(false), ast.TRAILING) + } + return exp } func (self *_parser) parseDotMember(left ast.Expression) ast.Expression { @@ -402,6 +487,11 @@ func (self *_parser) parseNewExpression() ast.Expression { node.LeftParenthesis = idx0 node.RightParenthesis = idx1 } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node, self.findComments(false), ast.TRAILING) + } + return node } @@ -414,6 +504,10 @@ func (self *_parser) parseLeftHandSideExpression() ast.Expression { left = self.parsePrimaryExpression() } + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(left, self.findComments(false), ast.TRAILING) + } + for { if self.token == token.PERIOD { left = self.parseDotMember(left) @@ -442,6 +536,10 @@ func (self *_parser) parseLeftHandSideExpressionAllowCall() ast.Expression { left = self.parsePrimaryExpression() } + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(left, self.findComments(false), ast.TRAILING) + } + for { if self.token == token.PERIOD { left = self.parseDotMember(left) @@ -476,12 +574,18 @@ func (self *_parser) parsePostfixExpression() ast.Expression { self.nextStatement() return &ast.BadExpression{From: idx, To: self.idx} } - return &ast.UnaryExpression{ + exp := &ast.UnaryExpression{ Operator: tkn, Idx: idx, Operand: operand, Postfix: true, } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(exp, self.findComments(false), ast.TRAILING) + } + + return exp } return operand @@ -496,16 +600,30 @@ func (self *_parser) parseUnaryExpression() ast.Expression { tkn := self.token idx := self.idx self.next() - return &ast.UnaryExpression{ + + comments := self.findComments(false) + + exp := &ast.UnaryExpression{ Operator: tkn, Idx: idx, Operand: self.parseUnaryExpression(), } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(exp.Operand, comments, ast.LEADING) + } + return exp case token.INCREMENT, token.DECREMENT: tkn := self.token idx := self.idx self.next() + + comments := self.findComments(false) + operand := self.parseUnaryExpression() + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(operand, comments, ast.LEADING) + } switch operand.(type) { case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: default: @@ -531,11 +649,18 @@ func (self *_parser) parseMultiplicativeExpression() ast.Expression { self.token == token.REMAINDER { tkn := self.token self.next() + + comments := self.findComments(false) + left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(left.(*ast.BinaryExpression).Right, comments, ast.LEADING) + } } return left @@ -548,11 +673,18 @@ func (self *_parser) parseAdditiveExpression() ast.Expression { for self.token == token.PLUS || self.token == token.MINUS { tkn := self.token self.next() + + comments := self.findComments(false) + left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(left.(*ast.BinaryExpression).Right, comments, ast.LEADING) + } } return left @@ -566,11 +698,18 @@ func (self *_parser) parseShiftExpression() ast.Expression { self.token == token.UNSIGNED_SHIFT_RIGHT { tkn := self.token self.next() + + comments := self.findComments(false) + left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(left.(*ast.BinaryExpression).Right, comments, ast.LEADING) + } } return left @@ -590,31 +729,55 @@ func (self *_parser) parseRelationalExpression() ast.Expression { case token.LESS, token.LESS_OR_EQUAL, token.GREATER, token.GREATER_OR_EQUAL: tkn := self.token self.next() - return &ast.BinaryExpression{ + + comments := self.findComments(false) + + exp := &ast.BinaryExpression{ Operator: tkn, Left: left, Right: self.parseRelationalExpression(), Comparison: true, } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(exp.Right, comments, ast.LEADING) + } + return exp case token.INSTANCEOF: tkn := self.token self.next() - return &ast.BinaryExpression{ + + comments := self.findComments(false) + + exp := &ast.BinaryExpression{ Operator: tkn, Left: left, Right: self.parseRelationalExpression(), } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(exp.Right, comments, ast.LEADING) + } + return exp case token.IN: if !allowIn { return left } tkn := self.token self.next() - return &ast.BinaryExpression{ + + comments := self.findComments(false) + + exp := &ast.BinaryExpression{ Operator: tkn, Left: left, Right: self.parseRelationalExpression(), } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(exp.Right, comments, ast.LEADING) + } + return exp } return left @@ -628,12 +791,19 @@ func (self *_parser) parseEqualityExpression() ast.Expression { self.token == token.STRICT_EQUAL || self.token == token.STRICT_NOT_EQUAL { tkn := self.token self.next() + + comments := self.findComments(false) + left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), Comparison: true, } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(left.(*ast.BinaryExpression).Right, comments, ast.LEADING) + } } return left @@ -646,11 +816,18 @@ func (self *_parser) parseBitwiseAndExpression() ast.Expression { for self.token == token.AND { tkn := self.token self.next() + + comments := self.findComments(false) + left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(left.(*ast.BinaryExpression).Right, comments, ast.LEADING) + } } return left @@ -663,11 +840,18 @@ func (self *_parser) parseBitwiseExclusiveOrExpression() ast.Expression { for self.token == token.EXCLUSIVE_OR { tkn := self.token self.next() + + comments := self.findComments(false) + left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(left.(*ast.BinaryExpression).Right, comments, ast.LEADING) + } } return left @@ -680,11 +864,18 @@ func (self *_parser) parseBitwiseOrExpression() ast.Expression { for self.token == token.OR { tkn := self.token self.next() + + comments := self.findComments(false) + left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(left.(*ast.BinaryExpression).Right, comments, ast.LEADING) + } } return left @@ -697,11 +888,18 @@ func (self *_parser) parseLogicalAndExpression() ast.Expression { for self.token == token.LOGICAL_AND { tkn := self.token self.next() + + comments := self.findComments(false) + left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(left.(*ast.BinaryExpression).Right, comments, ast.LEADING) + } } return left @@ -714,11 +912,18 @@ func (self *_parser) parseLogicalOrExpression() ast.Expression { for self.token == token.LOGICAL_OR { tkn := self.token self.next() + + comments := self.findComments(false) + left = &ast.BinaryExpression{ Operator: tkn, Left: left, Right: next(), } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(left.(*ast.BinaryExpression).Right, comments, ast.LEADING) + } } return left @@ -729,13 +934,29 @@ func (self *_parser) parseConditionlExpression() ast.Expression { if self.token == token.QUESTION_MARK { self.next() + + // Comments before the consequence + comments1 := self.findComments(false) + consequent := self.parseAssignmentExpression() + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(consequent, comments1, ast.LEADING) + } + self.expect(token.COLON) - return &ast.ConditionalExpression{ + + // Comments before the alternate + comments2 := self.findComments(false) + exp := &ast.ConditionalExpression{ Test: left, Consequent: consequent, Alternate: self.parseAssignmentExpression(), } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(exp.Alternate, comments2, ast.LEADING) + } + return exp } return left @@ -783,17 +1004,30 @@ func (self *_parser) parseAssignmentExpression() ast.Expression { self.nextStatement() return &ast.BadExpression{From: idx, To: self.idx} } - return &ast.AssignExpression{ + + comments := self.findComments(false) + + exp := &ast.AssignExpression{ Left: left, Operator: operator, Right: self.parseAssignmentExpression(), } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(exp.Right, comments, ast.LEADING) + } + + return exp } return left } func (self *_parser) parseExpression() ast.Expression { + + comments := self.findComments(false) + statementComments := self.fetchComments() + next := self.parseAssignmentExpression left := next() @@ -811,5 +1045,10 @@ func (self *_parser) parseExpression() ast.Expression { } } + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(left, comments, ast.LEADING) + self.commentMap.AddComments(left, statementComments, ast.LEADING) + } + return left } diff --git a/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/lexer.go b/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/lexer.go index bc3e74f77..a510c76d2 100644 --- a/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/lexer.go +++ b/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/lexer.go @@ -120,6 +120,7 @@ func isLineTerminator(chr rune) bool { func (self *_parser) scan() (tkn token.Token, literal string, idx file.Idx) { self.implicitSemicolon = false + self.skippedLineBreak = false for { self.skipWhiteSpace() @@ -238,9 +239,20 @@ func (self *_parser) scan() (tkn token.Token, literal string, idx file.Idx) { tkn = self.switch2(token.MULTIPLY, token.MULTIPLY_ASSIGN) case '/': if self.chr == '/' { + if self.mode&StoreComments != 0 { + runes := self.readSingleLineComment() + literal = string(runes) + tkn = token.COMMENT + return + } self.skipSingleLineComment() continue } else if self.chr == '*' { + if self.mode&StoreComments != 0 { + literal = string(self.readMultiLineComment()) + tkn = token.COMMENT + return + } self.skipMultiLineComment() continue } else { @@ -411,6 +423,39 @@ func (self *_RegExp_parser) read() { } } +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() @@ -442,6 +487,7 @@ func (self *_parser) skipWhiteSpace() { continue case '\r': if self._peek() == '\n' { + self.skippedLineBreak = true self.read() } fallthrough @@ -449,6 +495,7 @@ func (self *_parser) skipWhiteSpace() { if self.insertSemicolon { return } + self.skippedLineBreak = true self.read() continue } diff --git a/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/parser.go b/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/parser.go index 92ac5b0c7..18328edd6 100644 --- a/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/parser.go +++ b/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/parser.go @@ -49,12 +49,13 @@ 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 + str string + length int + base int chr rune // The current character chrOffset int // The offset of current character @@ -79,15 +80,22 @@ type _parser struct { mode Mode file *file.File + + comments []*ast.Comment + commentMap *ast.CommentMap + skippedLineBreak bool } func _newParser(filename, src string, base int) *_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), + 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), + comments: make([]*ast.Comment, 0), + commentMap: &ast.CommentMap{}, + skippedLineBreak: false, } } @@ -184,6 +192,9 @@ func (self *_parser) parse() (*ast.Program, error) { if false { self.errors.Sort() } + + self.addCommentStatements(program, ast.FINAL) + return program, self.errors.Err() } @@ -270,3 +281,63 @@ func (self *_parser) position(idx file.Idx) file.Position { return position } + +// findComments finds the following comments. +// Comments on the same line will be grouped together and returned. +// After the first line break, comments will be added as statement comments. +func (self *_parser) findComments(ignoreLineBreak bool) []*ast.Comment { + if self.mode&StoreComments == 0 { + return nil + } + comments := make([]*ast.Comment, 0) + + newline := false + + for self.implicitSemicolon == false || ignoreLineBreak { + if self.token != token.COMMENT { + break + } + + comment := &ast.Comment{ + Begin: self.idx, + Text: self.literal, + Position: ast.TBD, + } + + newline = self.skippedLineBreak || newline + + if newline && !ignoreLineBreak { + self.comments = append(self.comments, comment) + } else { + comments = append(comments, comment) + } + + self.next() + } + + return comments +} + +// addCommentStatements will add the previously parsed, not positioned comments to the provided node +func (self *_parser) addCommentStatements(node ast.Node, position ast.CommentPosition) { + if len(self.comments) > 0 { + self.commentMap.AddComments(node, self.comments, position) + + // Reset comments + self.comments = make([]*ast.Comment, 0) + } +} + +// fetchComments fetches the current comments, resets the slice and returns the comments +func (self *_parser) fetchComments() (comments []*ast.Comment) { + comments = self.comments + self.comments = nil + + return comments +} + +// consumeComments consumes the current comments and appends them to the provided node +func (self *_parser) consumeComments(node ast.Node, position ast.CommentPosition) { + self.commentMap.AddComments(node, self.comments, position) + self.comments = nil +} diff --git a/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/statement.go b/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/statement.go index 2059d3856..987ac02c1 100644 --- a/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/statement.go +++ b/Godeps/_workspace/src/github.com/robertkrimen/otto/parser/statement.go @@ -7,10 +7,24 @@ import ( func (self *_parser) parseBlockStatement() *ast.BlockStatement { node := &ast.BlockStatement{} + + // Find comments before the leading brace + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node, self.findComments(false), ast.LEADING) + } + node.LeftBrace = self.expect(token.LEFT_BRACE) node.List = self.parseStatementList() + + self.consumeComments(node, ast.FINAL) + node.RightBrace = self.expect(token.RIGHT_BRACE) + // Find comments after the trailing brace + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node, self.findComments(false), ast.TRAILING) + } + return node } @@ -21,7 +35,14 @@ func (self *_parser) parseEmptyStatement() ast.Statement { func (self *_parser) parseStatementList() (list []ast.Statement) { for self.token != token.RIGHT_BRACE && self.token != token.EOF { - list = append(list, self.parseStatement()) + if self.token == token.COMMENT { + self.parseCommentElement() + continue + } + statement := self.parseStatement() + list = append(list, statement) + + self.addCommentStatements(statement, ast.LEADING) } return @@ -77,6 +98,9 @@ func (self *_parser) parseStatement() ast.Statement { // LabelledStatement colon := self.idx self.next() // : + + comments := self.findComments(false) + label := identifier.Name for _, value := range self.scope.labels { if label == value { @@ -86,11 +110,17 @@ func (self *_parser) parseStatement() ast.Statement { 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 - return &ast.LabelledStatement{ + exp := &ast.LabelledStatement{ Label: identifier, Colon: colon, Statement: statement, } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(exp, comments, ast.TRAILING) + } + + return exp } self.optionalSemicolon() @@ -107,16 +137,26 @@ func (self *_parser) parseTryStatement() ast.Statement { Body: self.parseBlockStatement(), } + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node.Body, self.findComments(true), ast.TRAILING) + } + if self.token == token.CATCH { catch := self.idx self.next() self.expect(token.LEFT_PARENTHESIS) + comments := self.findComments(true) if self.token != token.IDENTIFIER { self.expect(token.IDENTIFIER) self.nextStatement() return &ast.BadStatement{From: catch, To: self.idx} } else { identifier := self.parseIdentifier() + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(identifier, comments, ast.LEADING) + } + self.expect(token.RIGHT_PARENTHESIS) node.Catch = &ast.CatchStatement{ Catch: catch, @@ -124,11 +164,22 @@ func (self *_parser) parseTryStatement() ast.Statement { Body: self.parseBlockStatement(), } } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node.Catch, self.findComments(true), ast.TRAILING) + } } if self.token == token.FINALLY { self.next() + + comments := self.findComments(true) + node.Finally = self.parseBlockStatement() + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node.Finally, comments, ast.LEADING) + } } if node.Catch == nil && node.Finally == nil { @@ -143,10 +194,15 @@ func (self *_parser) parseFunctionParameterList() *ast.ParameterList { opening := self.expect(token.LEFT_PARENTHESIS) var list []*ast.Identifier for self.token != token.RIGHT_PARENTHESIS && self.token != token.EOF { + comments := self.findComments(true) if self.token != token.IDENTIFIER { self.expect(token.IDENTIFIER) } else { - list = append(list, self.parseIdentifier()) + identifier := self.parseIdentifier() + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(identifier, comments, ast.LEADING) + } + list = append(list, identifier) } if self.token != token.RIGHT_PARENTHESIS { self.expect(token.COMMA) @@ -218,12 +274,24 @@ func (self *_parser) parseFunctionBlock(node *ast.FunctionLiteral) { func (self *_parser) parseDebuggerStatement() ast.Statement { idx := self.expect(token.DEBUGGER) + comments := self.findComments(true) + node := &ast.DebuggerStatement{ Debugger: idx, } + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node, comments, ast.TRAILING) + } + self.semicolon() + if !self.skippedLineBreak { + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node, self.findComments(false), ast.TRAILING) + } + } + return node } @@ -309,30 +377,77 @@ func (self *_parser) parseSwitchStatement() ast.Statement { func (self *_parser) parseWithStatement() ast.Statement { self.expect(token.WITH) + + // Find the comments after with + comments := self.findComments(true) + self.expect(token.LEFT_PARENTHESIS) + node := &ast.WithStatement{ Object: self.parseExpression(), } self.expect(token.RIGHT_PARENTHESIS) + // Add the key comments + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node, comments, ast.KEY) + } + + // Find the leading comments for the body + comments = self.findComments(true) + node.Body = self.parseStatement() + // Add the body comments + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node.Body, comments, ast.LEADING) + } + + // Move the trailing comments to the with statement + self.commentMap.MoveComments(node.Body, node, ast.TRAILING) + return node } func (self *_parser) parseCaseStatement() *ast.CaseStatement { + var comments []*ast.Comment + node := &ast.CaseStatement{ Case: self.idx, } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node, self.findComments(true), ast.LEADING) + } + + // Consume current comments + self.consumeComments(node, ast.LEADING) + if self.token == token.DEFAULT { self.next() } else { self.expect(token.CASE) + + comments = self.findComments(true) + node.Test = self.parseExpression() + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node.Test, comments, ast.LEADING) + } + + comments = self.findComments(true) + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node.Test, comments, ast.TRAILING) + } } + self.expect(token.COLON) + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node.Test, self.findComments(false), ast.TRAILING) + } + for { if self.token == token.EOF || self.token == token.RIGHT_BRACE || @@ -340,8 +455,12 @@ func (self *_parser) parseCaseStatement() *ast.CaseStatement { self.token == token.DEFAULT { break } - node.Consequent = append(node.Consequent, self.parseStatement()) + consequent := self.parseStatement() + node.Consequent = append(node.Consequent, consequent) + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(consequent, self.findComments(false), ast.TRAILING) + } } return node @@ -360,44 +479,84 @@ func (self *_parser) parseForIn(into ast.Expression) *ast.ForInStatement { // Already have consumed " in" + // Comments after the in, before the expression + comments := self.findComments(true) + source := self.parseExpression() + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(source, comments, ast.LEADING) + } + self.expect(token.RIGHT_PARENTHESIS) - return &ast.ForInStatement{ + comments = self.findComments(true) + body := self.parseIterationStatement() + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(body, comments, ast.LEADING) + } + + forin := &ast.ForInStatement{ Into: into, Source: source, - Body: self.parseIterationStatement(), + Body: body, } + + self.commentMap.MoveComments(body, forin, ast.TRAILING) + + return forin } func (self *_parser) parseFor(initializer ast.Expression) *ast.ForStatement { // Already have consumed " ;" + comments := self.findComments(true) + var test, update ast.Expression if self.token != token.SEMICOLON { test = self.parseExpression() + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(test, comments, ast.LEADING) + } } self.expect(token.SEMICOLON) + comments = self.findComments(true) + if self.token != token.RIGHT_PARENTHESIS { update = self.parseExpression() + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(update, comments, ast.LEADING) + } } self.expect(token.RIGHT_PARENTHESIS) - return &ast.ForStatement{ + comments = self.findComments(true) + + body := self.parseIterationStatement() + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(body, comments, ast.LEADING) + } + + forstatement := &ast.ForStatement{ Initializer: initializer, Test: test, Update: update, - Body: self.parseIterationStatement(), + Body: body, } + + self.commentMap.MoveComments(body, forstatement, ast.TRAILING) + + return forstatement } func (self *_parser) parseForOrForInStatement() ast.Statement { idx := self.expect(token.FOR) self.expect(token.LEFT_PARENTHESIS) + comments := self.findComments(true) + var left []ast.Expression forIn := false @@ -435,11 +594,19 @@ func (self *_parser) parseForOrForInStatement() ast.Statement { self.nextStatement() return &ast.BadStatement{From: idx, To: self.idx} } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(left[0], comments, ast.LEADING) + } return self.parseForIn(left[0]) } self.expect(token.SEMICOLON) - return self.parseFor(&ast.SequenceExpression{Sequence: left}) + initializer := &ast.SequenceExpression{Sequence: left} + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(initializer, comments, ast.LEADING) + } + return self.parseFor(initializer) } func (self *_parser) parseVariableStatement() *ast.VariableStatement { @@ -447,12 +614,27 @@ func (self *_parser) parseVariableStatement() *ast.VariableStatement { idx := self.expect(token.VAR) list := self.parseVariableDeclarationList(idx) - self.semicolon() - return &ast.VariableStatement{ + statement := &ast.VariableStatement{ Var: idx, List: list, } + + self.commentMap.MoveComments(statement.List[len(statement.List)-1], statement, ast.TRAILING) + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(statement, self.findComments(true), ast.TRAILING) + } + + self.semicolon() + + if self.skippedLineBreak { + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(statement, self.findComments(false), ast.TRAILING) + } + } + + return statement } func (self *_parser) parseDoWhileStatement() ast.Statement { @@ -463,7 +645,13 @@ func (self *_parser) parseDoWhileStatement() ast.Statement { }() self.expect(token.DO) + + comments := self.findComments(true) + node := &ast.DoWhileStatement{} + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node, comments, ast.KEY) + } if self.token == token.LEFT_BRACE { node.Body = self.parseBlockStatement() } else { @@ -471,49 +659,123 @@ func (self *_parser) parseDoWhileStatement() ast.Statement { } self.expect(token.WHILE) + + comments = self.findComments(true) + self.expect(token.LEFT_PARENTHESIS) node.Test = self.parseExpression() + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node.Test, comments, ast.LEADING) + } + self.expect(token.RIGHT_PARENTHESIS) + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node.Test, self.findComments(false), ast.TRAILING) + } + return node } func (self *_parser) parseWhileStatement() ast.Statement { self.expect(token.WHILE) + + // Comments after while keyword + comments := self.findComments(true) + self.expect(token.LEFT_PARENTHESIS) node := &ast.WhileStatement{ Test: self.parseExpression(), } + + // Add the while comments + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node, comments, ast.KEY) + } + self.expect(token.RIGHT_PARENTHESIS) + + // Finding comments prior to the body + comments = self.findComments(true) + node.Body = self.parseIterationStatement() + // Adding the comments prior to the body + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node.Body, comments, ast.LEADING) + } + + // Move the trailing comments to the while statement + self.commentMap.MoveComments(node.Body, node, ast.TRAILING) + return node } func (self *_parser) parseIfStatement() ast.Statement { self.expect(token.IF) + + comments := self.findComments(true) + self.expect(token.LEFT_PARENTHESIS) node := &ast.IfStatement{ Test: self.parseExpression(), } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node, comments, ast.KEY) + } + self.expect(token.RIGHT_PARENTHESIS) + comments = self.findComments(true) + if self.token == token.LEFT_BRACE { node.Consequent = self.parseBlockStatement() } else { node.Consequent = self.parseStatement() } + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node.Consequent, comments, ast.LEADING) + self.commentMap.AddComments(node.Consequent, self.findComments(true), ast.TRAILING) + } + if self.token == token.ELSE { self.next() + comments = self.findComments(true) + node.Alternate = self.parseStatement() + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(node.Alternate, comments, ast.LEADING) + self.commentMap.AddComments(node.Alternate, self.findComments(false), ast.TRAILING) + } } return node } func (self *_parser) parseSourceElement() ast.Statement { - return self.parseStatement() + + statementComment := self.fetchComments() + + statement := self.parseStatement() + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(statement, statementComment, ast.LEADING) + } + + return statement +} + +func (self *_parser) parseCommentElement() { + literal := self.literal + idx := self.expect(token.COMMENT) + self.comments = append(self.comments, &ast.Comment{ + Begin: idx, + Text: literal, + Position: ast.LEADING, + }) } func (self *_parser) parseSourceElements() []ast.Statement { @@ -524,10 +786,19 @@ func (self *_parser) parseSourceElements() []ast.Statement { break } + if self.token == token.COMMENT { + self.parseCommentElement() + continue + } + body = append(body, self.parseSourceElement()) } for self.token != token.EOF { + if self.token == token.COMMENT { + self.parseCommentElement() + continue + } body = append(body, self.parseSourceElement()) } @@ -546,6 +817,9 @@ func (self *_parser) parseProgram() *ast.Program { func (self *_parser) parseBreakStatement() ast.Statement { idx := self.expect(token.BREAK) + + breakComments := self.findComments(true) + semicolon := self.implicitSemicolon if self.token == token.SEMICOLON { semicolon = true @@ -557,10 +831,16 @@ func (self *_parser) parseBreakStatement() ast.Statement { if !self.scope.inIteration && !self.scope.inSwitch { goto illegal } - return &ast.BranchStatement{ + breakStatement := &ast.BranchStatement{ Idx: idx, Token: token.BREAK, } + + if self.mode&StoreComments != 0 { + self.commentMap.AddComments(breakStatement, breakComments, ast.TRAILING) + } + + return breakStatement } if self.token == token.IDENTIFIER { diff --git a/Godeps/_workspace/src/github.com/robertkrimen/otto/runtime.go b/Godeps/_workspace/src/github.com/robertkrimen/otto/runtime.go index 168cb1cde..a998f7acc 100644 --- a/Godeps/_workspace/src/github.com/robertkrimen/otto/runtime.go +++ b/Godeps/_workspace/src/github.com/robertkrimen/otto/runtime.go @@ -55,6 +55,7 @@ type _runtime struct { otto *Otto eval *_object // The builtin eval, for determine indirect versus direct invocation debugger func(*Otto) + random func() float64 labels []string // FIXME lck sync.Mutex diff --git a/jsre/jsre.go b/jsre/jsre.go index a4c9d970b..f4464910d 100644 --- a/jsre/jsre.go +++ b/jsre/jsre.go @@ -18,8 +18,11 @@ package jsre import ( + crand "crypto/rand" + "encoding/binary" "fmt" "io/ioutil" + "math/rand" "sync" "time" @@ -70,6 +73,18 @@ func New(assetPath string) *JSRE { return re } +// randomSource returns a pseudo random value generator. +func randomSource() *rand.Rand { + bytes := make([]byte, 8) + seed := time.Now().UnixNano() + if _, err := crand.Read(bytes); err == nil { + seed = int64(binary.LittleEndian.Uint64(bytes)) + } + + src := rand.NewSource(seed) + return rand.New(src) +} + // This function runs the main event loop from a goroutine that is started // when JSRE is created. Use Stop() before exiting to properly stop it. // The event loop processes vm access requests from the evalQueue in a @@ -81,6 +96,9 @@ func New(assetPath string) *JSRE { // called from JS through an RPC call. func (self *JSRE) runEventLoop() { vm := otto.New() + r := randomSource() + vm.SetRandomSource(r.Float64) + registry := map[*jsTimer]*jsTimer{} ready := make(chan *jsTimer)