mirror of https://github.com/ethereum/go-ethereum
parent
91f9f355b2
commit
8324b683b4
File diff suppressed because it is too large
Load Diff
@ -1,496 +0,0 @@ |
||||
/* |
||||
Package ast declares types representing a JavaScript AST. |
||||
|
||||
Warning |
||||
|
||||
The parser and AST interfaces are still works-in-progress (particularly where |
||||
node types are concerned) and may change in the future. |
||||
|
||||
*/ |
||||
package ast |
||||
|
||||
import ( |
||||
"github.com/robertkrimen/otto/file" |
||||
"github.com/robertkrimen/otto/token" |
||||
) |
||||
|
||||
// All nodes implement the Node interface.
|
||||
type Node interface { |
||||
Idx0() file.Idx // The index of the first character belonging to the node
|
||||
Idx1() file.Idx // The index of the first character immediately after the node
|
||||
} |
||||
|
||||
// ========== //
|
||||
// Expression //
|
||||
// ========== //
|
||||
|
||||
type ( |
||||
// All expression nodes implement the Expression interface.
|
||||
Expression interface { |
||||
Node |
||||
_expressionNode() |
||||
} |
||||
|
||||
ArrayLiteral struct { |
||||
LeftBracket file.Idx |
||||
RightBracket file.Idx |
||||
Value []Expression |
||||
} |
||||
|
||||
AssignExpression struct { |
||||
Operator token.Token |
||||
Left Expression |
||||
Right Expression |
||||
} |
||||
|
||||
BadExpression struct { |
||||
From file.Idx |
||||
To file.Idx |
||||
} |
||||
|
||||
BinaryExpression struct { |
||||
Operator token.Token |
||||
Left Expression |
||||
Right Expression |
||||
Comparison bool |
||||
} |
||||
|
||||
BooleanLiteral struct { |
||||
Idx file.Idx |
||||
Literal string |
||||
Value bool |
||||
} |
||||
|
||||
BracketExpression struct { |
||||
Left Expression |
||||
Member Expression |
||||
LeftBracket file.Idx |
||||
RightBracket file.Idx |
||||
} |
||||
|
||||
CallExpression struct { |
||||
Callee Expression |
||||
LeftParenthesis file.Idx |
||||
ArgumentList []Expression |
||||
RightParenthesis file.Idx |
||||
} |
||||
|
||||
ConditionalExpression struct { |
||||
Test Expression |
||||
Consequent Expression |
||||
Alternate Expression |
||||
} |
||||
|
||||
DotExpression struct { |
||||
Left Expression |
||||
Identifier Identifier |
||||
} |
||||
|
||||
FunctionLiteral struct { |
||||
Function file.Idx |
||||
Name *Identifier |
||||
ParameterList *ParameterList |
||||
Body Statement |
||||
Source string |
||||
|
||||
DeclarationList []Declaration |
||||
} |
||||
|
||||
Identifier struct { |
||||
Name string |
||||
Idx file.Idx |
||||
} |
||||
|
||||
NewExpression struct { |
||||
New file.Idx |
||||
Callee Expression |
||||
LeftParenthesis file.Idx |
||||
ArgumentList []Expression |
||||
RightParenthesis file.Idx |
||||
} |
||||
|
||||
NullLiteral struct { |
||||
Idx file.Idx |
||||
Literal string |
||||
} |
||||
|
||||
NumberLiteral struct { |
||||
Idx file.Idx |
||||
Literal string |
||||
Value interface{} |
||||
} |
||||
|
||||
ObjectLiteral struct { |
||||
LeftBrace file.Idx |
||||
RightBrace file.Idx |
||||
Value []Property |
||||
} |
||||
|
||||
ParameterList struct { |
||||
Opening file.Idx |
||||
List []*Identifier |
||||
Closing file.Idx |
||||
} |
||||
|
||||
Property struct { |
||||
Key string |
||||
Kind string |
||||
Value Expression |
||||
} |
||||
|
||||
RegExpLiteral struct { |
||||
Idx file.Idx |
||||
Literal string |
||||
Pattern string |
||||
Flags string |
||||
Value string |
||||
} |
||||
|
||||
SequenceExpression struct { |
||||
Sequence []Expression |
||||
} |
||||
|
||||
StringLiteral struct { |
||||
Idx file.Idx |
||||
Literal string |
||||
Value string |
||||
} |
||||
|
||||
ThisExpression struct { |
||||
Idx file.Idx |
||||
} |
||||
|
||||
UnaryExpression struct { |
||||
Operator token.Token |
||||
Idx file.Idx // If a prefix operation
|
||||
Operand Expression |
||||
Postfix bool |
||||
} |
||||
|
||||
VariableExpression struct { |
||||
Name string |
||||
Idx file.Idx |
||||
Initializer Expression |
||||
} |
||||
) |
||||
|
||||
// _expressionNode
|
||||
|
||||
func (*ArrayLiteral) _expressionNode() {} |
||||
func (*AssignExpression) _expressionNode() {} |
||||
func (*BadExpression) _expressionNode() {} |
||||
func (*BinaryExpression) _expressionNode() {} |
||||
func (*BooleanLiteral) _expressionNode() {} |
||||
func (*BracketExpression) _expressionNode() {} |
||||
func (*CallExpression) _expressionNode() {} |
||||
func (*ConditionalExpression) _expressionNode() {} |
||||
func (*DotExpression) _expressionNode() {} |
||||
func (*FunctionLiteral) _expressionNode() {} |
||||
func (*Identifier) _expressionNode() {} |
||||
func (*NewExpression) _expressionNode() {} |
||||
func (*NullLiteral) _expressionNode() {} |
||||
func (*NumberLiteral) _expressionNode() {} |
||||
func (*ObjectLiteral) _expressionNode() {} |
||||
func (*RegExpLiteral) _expressionNode() {} |
||||
func (*SequenceExpression) _expressionNode() {} |
||||
func (*StringLiteral) _expressionNode() {} |
||||
func (*ThisExpression) _expressionNode() {} |
||||
func (*UnaryExpression) _expressionNode() {} |
||||
func (*VariableExpression) _expressionNode() {} |
||||
|
||||
// ========= //
|
||||
// Statement //
|
||||
// ========= //
|
||||
|
||||
type ( |
||||
// All statement nodes implement the Statement interface.
|
||||
Statement interface { |
||||
Node |
||||
_statementNode() |
||||
} |
||||
|
||||
BadStatement struct { |
||||
From file.Idx |
||||
To file.Idx |
||||
} |
||||
|
||||
BlockStatement struct { |
||||
LeftBrace file.Idx |
||||
List []Statement |
||||
RightBrace file.Idx |
||||
} |
||||
|
||||
BranchStatement struct { |
||||
Idx file.Idx |
||||
Token token.Token |
||||
Label *Identifier |
||||
} |
||||
|
||||
CaseStatement struct { |
||||
Case file.Idx |
||||
Test Expression |
||||
Consequent []Statement |
||||
} |
||||
|
||||
CatchStatement struct { |
||||
Catch file.Idx |
||||
Parameter *Identifier |
||||
Body Statement |
||||
} |
||||
|
||||
DebuggerStatement struct { |
||||
Debugger file.Idx |
||||
} |
||||
|
||||
DoWhileStatement struct { |
||||
Do file.Idx |
||||
Test Expression |
||||
Body Statement |
||||
} |
||||
|
||||
EmptyStatement struct { |
||||
Semicolon file.Idx |
||||
} |
||||
|
||||
ExpressionStatement struct { |
||||
Expression Expression |
||||
} |
||||
|
||||
ForInStatement struct { |
||||
For file.Idx |
||||
Into Expression |
||||
Source Expression |
||||
Body Statement |
||||
} |
||||
|
||||
ForStatement struct { |
||||
For file.Idx |
||||
Initializer Expression |
||||
Update Expression |
||||
Test Expression |
||||
Body Statement |
||||
} |
||||
|
||||
IfStatement struct { |
||||
If file.Idx |
||||
Test Expression |
||||
Consequent Statement |
||||
Alternate Statement |
||||
} |
||||
|
||||
LabelledStatement struct { |
||||
Label *Identifier |
||||
Colon file.Idx |
||||
Statement Statement |
||||
} |
||||
|
||||
ReturnStatement struct { |
||||
Return file.Idx |
||||
Argument Expression |
||||
} |
||||
|
||||
SwitchStatement struct { |
||||
Switch file.Idx |
||||
Discriminant Expression |
||||
Default int |
||||
Body []*CaseStatement |
||||
} |
||||
|
||||
ThrowStatement struct { |
||||
Throw file.Idx |
||||
Argument Expression |
||||
} |
||||
|
||||
TryStatement struct { |
||||
Try file.Idx |
||||
Body Statement |
||||
Catch *CatchStatement |
||||
Finally Statement |
||||
} |
||||
|
||||
VariableStatement struct { |
||||
Var file.Idx |
||||
List []Expression |
||||
} |
||||
|
||||
WhileStatement struct { |
||||
While file.Idx |
||||
Test Expression |
||||
Body Statement |
||||
} |
||||
|
||||
WithStatement struct { |
||||
With file.Idx |
||||
Object Expression |
||||
Body Statement |
||||
} |
||||
) |
||||
|
||||
// _statementNode
|
||||
|
||||
func (*BadStatement) _statementNode() {} |
||||
func (*BlockStatement) _statementNode() {} |
||||
func (*BranchStatement) _statementNode() {} |
||||
func (*CaseStatement) _statementNode() {} |
||||
func (*CatchStatement) _statementNode() {} |
||||
func (*DebuggerStatement) _statementNode() {} |
||||
func (*DoWhileStatement) _statementNode() {} |
||||
func (*EmptyStatement) _statementNode() {} |
||||
func (*ExpressionStatement) _statementNode() {} |
||||
func (*ForInStatement) _statementNode() {} |
||||
func (*ForStatement) _statementNode() {} |
||||
func (*IfStatement) _statementNode() {} |
||||
func (*LabelledStatement) _statementNode() {} |
||||
func (*ReturnStatement) _statementNode() {} |
||||
func (*SwitchStatement) _statementNode() {} |
||||
func (*ThrowStatement) _statementNode() {} |
||||
func (*TryStatement) _statementNode() {} |
||||
func (*VariableStatement) _statementNode() {} |
||||
func (*WhileStatement) _statementNode() {} |
||||
func (*WithStatement) _statementNode() {} |
||||
|
||||
// =========== //
|
||||
// Declaration //
|
||||
// =========== //
|
||||
|
||||
type ( |
||||
// All declaration nodes implement the Declaration interface.
|
||||
Declaration interface { |
||||
_declarationNode() |
||||
} |
||||
|
||||
FunctionDeclaration struct { |
||||
Function *FunctionLiteral |
||||
} |
||||
|
||||
VariableDeclaration struct { |
||||
Var file.Idx |
||||
List []*VariableExpression |
||||
} |
||||
) |
||||
|
||||
// _declarationNode
|
||||
|
||||
func (*FunctionDeclaration) _declarationNode() {} |
||||
func (*VariableDeclaration) _declarationNode() {} |
||||
|
||||
// ==== //
|
||||
// Node //
|
||||
// ==== //
|
||||
|
||||
type Program struct { |
||||
Body []Statement |
||||
|
||||
DeclarationList []Declaration |
||||
} |
||||
|
||||
// ==== //
|
||||
// Idx0 //
|
||||
// ==== //
|
||||
|
||||
func (self *ArrayLiteral) Idx0() file.Idx { return self.LeftBracket } |
||||
func (self *AssignExpression) Idx0() file.Idx { return self.Left.Idx0() } |
||||
func (self *BadExpression) Idx0() file.Idx { return self.From } |
||||
func (self *BinaryExpression) Idx0() file.Idx { return self.Left.Idx0() } |
||||
func (self *BooleanLiteral) Idx0() file.Idx { return self.Idx } |
||||
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 *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 } |
||||
func (self *NullLiteral) Idx0() file.Idx { return self.Idx } |
||||
func (self *NumberLiteral) Idx0() file.Idx { return self.Idx } |
||||
func (self *ObjectLiteral) Idx0() file.Idx { return self.LeftBrace } |
||||
func (self *RegExpLiteral) Idx0() file.Idx { return self.Idx } |
||||
func (self *SequenceExpression) Idx0() file.Idx { return self.Sequence[0].Idx0() } |
||||
func (self *StringLiteral) Idx0() file.Idx { return self.Idx } |
||||
func (self *ThisExpression) Idx0() file.Idx { return self.Idx } |
||||
func (self *UnaryExpression) Idx0() file.Idx { return self.Idx } |
||||
func (self *VariableExpression) Idx0() file.Idx { return self.Idx } |
||||
|
||||
func (self *BadStatement) Idx0() file.Idx { return self.From } |
||||
func (self *BlockStatement) Idx0() file.Idx { return self.LeftBrace } |
||||
func (self *BranchStatement) Idx0() file.Idx { return self.Idx } |
||||
func (self *CaseStatement) Idx0() file.Idx { return self.Case } |
||||
func (self *CatchStatement) Idx0() file.Idx { return self.Catch } |
||||
func (self *DebuggerStatement) Idx0() file.Idx { return self.Debugger } |
||||
func (self *DoWhileStatement) Idx0() file.Idx { return self.Do } |
||||
func (self *EmptyStatement) Idx0() file.Idx { return self.Semicolon } |
||||
func (self *ExpressionStatement) Idx0() file.Idx { return self.Expression.Idx0() } |
||||
func (self *ForInStatement) Idx0() file.Idx { return self.For } |
||||
func (self *ForStatement) Idx0() file.Idx { return self.For } |
||||
func (self *IfStatement) Idx0() file.Idx { return self.If } |
||||
func (self *LabelledStatement) Idx0() file.Idx { return self.Label.Idx0() } |
||||
func (self *Program) Idx0() file.Idx { return self.Body[0].Idx0() } |
||||
func (self *ReturnStatement) Idx0() file.Idx { return self.Return } |
||||
func (self *SwitchStatement) Idx0() file.Idx { return self.Switch } |
||||
func (self *ThrowStatement) Idx0() file.Idx { return self.Throw } |
||||
func (self *TryStatement) Idx0() file.Idx { return self.Try } |
||||
func (self *VariableStatement) Idx0() file.Idx { return self.Var } |
||||
func (self *WhileStatement) Idx0() file.Idx { return self.While } |
||||
func (self *WithStatement) Idx0() file.Idx { return self.With } |
||||
|
||||
// ==== //
|
||||
// Idx1 //
|
||||
// ==== //
|
||||
|
||||
func (self *ArrayLiteral) Idx1() file.Idx { return self.RightBracket } |
||||
func (self *AssignExpression) Idx1() file.Idx { return self.Right.Idx1() } |
||||
func (self *BadExpression) Idx1() file.Idx { return self.To } |
||||
func (self *BinaryExpression) Idx1() file.Idx { return self.Right.Idx1() } |
||||
func (self *BooleanLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } |
||||
func (self *BracketExpression) Idx1() file.Idx { return self.RightBracket + 1 } |
||||
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 *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 } |
||||
func (self *NullLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + 4) } // "null"
|
||||
func (self *NumberLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } |
||||
func (self *ObjectLiteral) Idx1() file.Idx { return self.RightBrace } |
||||
func (self *RegExpLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } |
||||
func (self *SequenceExpression) Idx1() file.Idx { return self.Sequence[0].Idx1() } |
||||
func (self *StringLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) } |
||||
func (self *ThisExpression) Idx1() file.Idx { return self.Idx } |
||||
func (self *UnaryExpression) Idx1() file.Idx { |
||||
if self.Postfix { |
||||
return self.Operand.Idx1() + 2 // ++ --
|
||||
} |
||||
return self.Operand.Idx1() |
||||
} |
||||
func (self *VariableExpression) Idx1() file.Idx { |
||||
if self.Initializer == nil { |
||||
return file.Idx(int(self.Idx) + len(self.Name) + 1) |
||||
} |
||||
return self.Initializer.Idx1() |
||||
} |
||||
|
||||
func (self *BadStatement) Idx1() file.Idx { return self.To } |
||||
func (self *BlockStatement) Idx1() file.Idx { return self.RightBrace + 1 } |
||||
func (self *BranchStatement) Idx1() file.Idx { return self.Idx } |
||||
func (self *CaseStatement) Idx1() file.Idx { return self.Consequent[len(self.Consequent)-1].Idx1() } |
||||
func (self *CatchStatement) Idx1() file.Idx { return self.Body.Idx1() } |
||||
func (self *DebuggerStatement) Idx1() file.Idx { return self.Debugger + 8 } |
||||
func (self *DoWhileStatement) Idx1() file.Idx { return self.Test.Idx1() } |
||||
func (self *EmptyStatement) Idx1() file.Idx { return self.Semicolon + 1 } |
||||
func (self *ExpressionStatement) Idx1() file.Idx { return self.Expression.Idx1() } |
||||
func (self *ForInStatement) Idx1() file.Idx { return self.Body.Idx1() } |
||||
func (self *ForStatement) Idx1() file.Idx { return self.Body.Idx1() } |
||||
func (self *IfStatement) Idx1() file.Idx { |
||||
if self.Alternate != nil { |
||||
return self.Alternate.Idx1() |
||||
} |
||||
return self.Consequent.Idx1() |
||||
} |
||||
func (self *LabelledStatement) Idx1() file.Idx { return self.Colon + 1 } |
||||
func (self *Program) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() } |
||||
func (self *ReturnStatement) Idx1() file.Idx { return self.Return } |
||||
func (self *SwitchStatement) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() } |
||||
func (self *ThrowStatement) Idx1() file.Idx { return self.Throw } |
||||
func (self *TryStatement) Idx1() file.Idx { return self.Try } |
||||
func (self *VariableStatement) Idx1() file.Idx { return self.List[len(self.List)-1].Idx1() } |
||||
func (self *WhileStatement) Idx1() file.Idx { return self.Body.Idx1() } |
||||
func (self *WithStatement) Idx1() file.Idx { return self.Body.Idx1() } |
@ -1,85 +0,0 @@ |
||||
package otto |
||||
|
||||
func (runtime *_runtime) newEvalError(message Value) *_object { |
||||
self := runtime.newErrorObject(message) |
||||
self.prototype = runtime.Global.EvalErrorPrototype |
||||
return self |
||||
} |
||||
|
||||
func builtinEvalError(call FunctionCall) Value { |
||||
return toValue_object(call.runtime.newEvalError(call.Argument(0))) |
||||
} |
||||
|
||||
func builtinNewEvalError(self *_object, _ Value, argumentList []Value) Value { |
||||
return toValue_object(self.runtime.newEvalError(valueOfArrayIndex(argumentList, 0))) |
||||
} |
||||
|
||||
func (runtime *_runtime) newTypeError(message Value) *_object { |
||||
self := runtime.newErrorObject(message) |
||||
self.prototype = runtime.Global.TypeErrorPrototype |
||||
return self |
||||
} |
||||
|
||||
func builtinTypeError(call FunctionCall) Value { |
||||
return toValue_object(call.runtime.newTypeError(call.Argument(0))) |
||||
} |
||||
|
||||
func builtinNewTypeError(self *_object, _ Value, argumentList []Value) Value { |
||||
return toValue_object(self.runtime.newTypeError(valueOfArrayIndex(argumentList, 0))) |
||||
} |
||||
|
||||
func (runtime *_runtime) newRangeError(message Value) *_object { |
||||
self := runtime.newErrorObject(message) |
||||
self.prototype = runtime.Global.RangeErrorPrototype |
||||
return self |
||||
} |
||||
|
||||
func builtinRangeError(call FunctionCall) Value { |
||||
return toValue_object(call.runtime.newRangeError(call.Argument(0))) |
||||
} |
||||
|
||||
func builtinNewRangeError(self *_object, _ Value, argumentList []Value) Value { |
||||
return toValue_object(self.runtime.newRangeError(valueOfArrayIndex(argumentList, 0))) |
||||
} |
||||
|
||||
func (runtime *_runtime) newURIError(message Value) *_object { |
||||
self := runtime.newErrorObject(message) |
||||
self.prototype = runtime.Global.URIErrorPrototype |
||||
return self |
||||
} |
||||
|
||||
func (runtime *_runtime) newReferenceError(message Value) *_object { |
||||
self := runtime.newErrorObject(message) |
||||
self.prototype = runtime.Global.ReferenceErrorPrototype |
||||
return self |
||||
} |
||||
|
||||
func builtinReferenceError(call FunctionCall) Value { |
||||
return toValue_object(call.runtime.newReferenceError(call.Argument(0))) |
||||
} |
||||
|
||||
func builtinNewReferenceError(self *_object, _ Value, argumentList []Value) Value { |
||||
return toValue_object(self.runtime.newReferenceError(valueOfArrayIndex(argumentList, 0))) |
||||
} |
||||
|
||||
func (runtime *_runtime) newSyntaxError(message Value) *_object { |
||||
self := runtime.newErrorObject(message) |
||||
self.prototype = runtime.Global.SyntaxErrorPrototype |
||||
return self |
||||
} |
||||
|
||||
func builtinSyntaxError(call FunctionCall) Value { |
||||
return toValue_object(call.runtime.newSyntaxError(call.Argument(0))) |
||||
} |
||||
|
||||
func builtinNewSyntaxError(self *_object, _ Value, argumentList []Value) Value { |
||||
return toValue_object(self.runtime.newSyntaxError(valueOfArrayIndex(argumentList, 0))) |
||||
} |
||||
|
||||
func builtinURIError(call FunctionCall) Value { |
||||
return toValue_object(call.runtime.newURIError(call.Argument(0))) |
||||
} |
||||
|
||||
func builtinNewURIError(self *_object, _ Value, argumentList []Value) Value { |
||||
return toValue_object(self.runtime.newURIError(valueOfArrayIndex(argumentList, 0))) |
||||
} |
@ -1,144 +0,0 @@ |
||||
package otto |
||||
|
||||
import ( |
||||
"fmt" |
||||
) |
||||
|
||||
type _clone struct { |
||||
runtime *_runtime |
||||
stash struct { |
||||
object map[*_object]*_object |
||||
objectEnvironment map[*_objectEnvironment]*_objectEnvironment |
||||
declarativeEnvironment map[*_declarativeEnvironment]*_declarativeEnvironment |
||||
} |
||||
} |
||||
|
||||
func (runtime *_runtime) clone() *_runtime { |
||||
|
||||
self := &_runtime{} |
||||
clone := &_clone{ |
||||
runtime: self, |
||||
} |
||||
clone.stash.object = make(map[*_object]*_object) |
||||
clone.stash.objectEnvironment = make(map[*_objectEnvironment]*_objectEnvironment) |
||||
clone.stash.declarativeEnvironment = make(map[*_declarativeEnvironment]*_declarativeEnvironment) |
||||
|
||||
globalObject := clone.object(runtime.GlobalObject) |
||||
self.GlobalEnvironment = self.newObjectEnvironment(globalObject, nil) |
||||
self.GlobalObject = globalObject |
||||
self.Global = _global{ |
||||
clone.object(runtime.Global.Object), |
||||
clone.object(runtime.Global.Function), |
||||
clone.object(runtime.Global.Array), |
||||
clone.object(runtime.Global.String), |
||||
clone.object(runtime.Global.Boolean), |
||||
clone.object(runtime.Global.Number), |
||||
clone.object(runtime.Global.Math), |
||||
clone.object(runtime.Global.Date), |
||||
clone.object(runtime.Global.RegExp), |
||||
clone.object(runtime.Global.Error), |
||||
clone.object(runtime.Global.EvalError), |
||||
clone.object(runtime.Global.TypeError), |
||||
clone.object(runtime.Global.RangeError), |
||||
clone.object(runtime.Global.ReferenceError), |
||||
clone.object(runtime.Global.SyntaxError), |
||||
clone.object(runtime.Global.URIError), |
||||
clone.object(runtime.Global.JSON), |
||||
|
||||
clone.object(runtime.Global.ObjectPrototype), |
||||
clone.object(runtime.Global.FunctionPrototype), |
||||
clone.object(runtime.Global.ArrayPrototype), |
||||
clone.object(runtime.Global.StringPrototype), |
||||
clone.object(runtime.Global.BooleanPrototype), |
||||
clone.object(runtime.Global.NumberPrototype), |
||||
clone.object(runtime.Global.DatePrototype), |
||||
clone.object(runtime.Global.RegExpPrototype), |
||||
clone.object(runtime.Global.ErrorPrototype), |
||||
clone.object(runtime.Global.EvalErrorPrototype), |
||||
clone.object(runtime.Global.TypeErrorPrototype), |
||||
clone.object(runtime.Global.RangeErrorPrototype), |
||||
clone.object(runtime.Global.ReferenceErrorPrototype), |
||||
clone.object(runtime.Global.SyntaxErrorPrototype), |
||||
clone.object(runtime.Global.URIErrorPrototype), |
||||
} |
||||
|
||||
self.EnterGlobalExecutionContext() |
||||
|
||||
self.eval = self.GlobalObject.property["eval"].value.(Value).value.(*_object) |
||||
self.GlobalObject.prototype = self.Global.ObjectPrototype |
||||
|
||||
return self |
||||
} |
||||
func (clone *_clone) object(self0 *_object) *_object { |
||||
if self1, exists := clone.stash.object[self0]; exists { |
||||
return self1 |
||||
} |
||||
self1 := &_object{} |
||||
clone.stash.object[self0] = self1 |
||||
return self0.objectClass.clone(self0, self1, clone) |
||||
} |
||||
|
||||
func (clone *_clone) declarativeEnvironment(self0 *_declarativeEnvironment) (*_declarativeEnvironment, bool) { |
||||
if self1, exists := clone.stash.declarativeEnvironment[self0]; exists { |
||||
return self1, true |
||||
} |
||||
self1 := &_declarativeEnvironment{} |
||||
clone.stash.declarativeEnvironment[self0] = self1 |
||||
return self1, false |
||||
} |
||||
|
||||
func (clone *_clone) objectEnvironment(self0 *_objectEnvironment) (*_objectEnvironment, bool) { |
||||
if self1, exists := clone.stash.objectEnvironment[self0]; exists { |
||||
return self1, true |
||||
} |
||||
self1 := &_objectEnvironment{} |
||||
clone.stash.objectEnvironment[self0] = self1 |
||||
return self1, false |
||||
} |
||||
|
||||
func (clone *_clone) value(self0 Value) Value { |
||||
self1 := self0 |
||||
switch value := self0.value.(type) { |
||||
case *_object: |
||||
self1.value = clone.object(value) |
||||
} |
||||
return self1 |
||||
} |
||||
|
||||
func (clone *_clone) valueArray(self0 []Value) []Value { |
||||
self1 := make([]Value, len(self0)) |
||||
for index, value := range self0 { |
||||
self1[index] = clone.value(value) |
||||
} |
||||
return self1 |
||||
} |
||||
|
||||
func (clone *_clone) environment(self0 _environment) _environment { |
||||
if self0 == nil { |
||||
return nil |
||||
} |
||||
return self0.clone(clone) |
||||
} |
||||
|
||||
func (clone *_clone) property(self0 _property) _property { |
||||
self1 := self0 |
||||
if value, valid := self0.value.(Value); valid { |
||||
self1.value = clone.value(value) |
||||
} else { |
||||
panic(fmt.Errorf("self0.value.(Value) != true")) |
||||
} |
||||
return self1 |
||||
} |
||||
|
||||
func (clone *_clone) declarativeProperty(self0 _declarativeProperty) _declarativeProperty { |
||||
self1 := self0 |
||||
self1.value = clone.value(self0.value) |
||||
return self1 |
||||
} |
||||
|
||||
func (clone *_clone) callFunction(self0 _callFunction) _callFunction { |
||||
if self0 == nil { |
||||
return nil |
||||
} |
||||
return self0.clone(clone) |
||||
} |
@ -1,46 +0,0 @@ |
||||
package otto |
||||
|
||||
// _cmpl_nodeCallFunction
|
||||
type _cmpl_nodeCallFunction struct { |
||||
node *_nodeFunctionLiteral |
||||
scopeEnvironment _environment // Can be either Lexical or Variable
|
||||
} |
||||
|
||||
func new_nodeCallFunction(node *_nodeFunctionLiteral, scopeEnvironment _environment) *_cmpl_nodeCallFunction { |
||||
self := &_cmpl_nodeCallFunction{ |
||||
node: node, |
||||
} |
||||
self.scopeEnvironment = scopeEnvironment |
||||
return self |
||||
} |
||||
|
||||
func (self _cmpl_nodeCallFunction) Dispatch(function *_object, environment *_functionEnvironment, runtime *_runtime, this Value, argumentList []Value, _ bool) Value { |
||||
return runtime.cmpl_call_nodeFunction(function, environment, self.node, this, argumentList) |
||||
} |
||||
|
||||
func (self _cmpl_nodeCallFunction) ScopeEnvironment() _environment { |
||||
return self.scopeEnvironment |
||||
} |
||||
|
||||
func (self _cmpl_nodeCallFunction) Source(object *_object) string { |
||||
return self.node.source |
||||
} |
||||
|
||||
func (self0 _cmpl_nodeCallFunction) clone(clone *_clone) _callFunction { |
||||
return _cmpl_nodeCallFunction{ |
||||
node: self0.node, |
||||
scopeEnvironment: clone.environment(self0.scopeEnvironment), |
||||
} |
||||
} |
||||
|
||||
// ---
|
||||
|
||||
func (runtime *_runtime) newNodeFunctionObject(node *_nodeFunctionLiteral, scopeEnvironment _environment) *_object { |
||||
self := runtime.newClassObject("Function") |
||||
self.value = _functionObject{ |
||||
call: new_nodeCallFunction(node, scopeEnvironment), |
||||
construct: defaultConstructFunction, |
||||
} |
||||
self.defineProperty("length", toValue_int(len(node.parameterList)), 0000, false) |
||||
return self |
||||
} |
@ -1,387 +0,0 @@ |
||||
// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) from github.com/robertkrimen/dbg
|
||||
|
||||
/* |
||||
Package dbg is a println/printf/log-debugging utility library. |
||||
|
||||
import ( |
||||
Dbg "github.com/robertkrimen/dbg" |
||||
) |
||||
|
||||
dbg, dbgf := Dbg.New() |
||||
|
||||
dbg("Emit some debug stuff", []byte{120, 121, 122, 122, 121}, math.Pi) |
||||
# "2013/01/28 16:50:03 Emit some debug stuff [120 121 122 122 121] 3.141592653589793" |
||||
|
||||
dbgf("With a %s formatting %.2f", "little", math.Pi) |
||||
# "2013/01/28 16:51:55 With a little formatting (3.14)" |
||||
|
||||
dbgf("%/fatal//A fatal debug statement: should not be here") |
||||
# "A fatal debug statement: should not be here" |
||||
# ...and then, os.Exit(1) |
||||
|
||||
dbgf("%/panic//Can also panic %s", "this") |
||||
# "Can also panic this" |
||||
# ...as a panic, equivalent to: panic("Can also panic this") |
||||
|
||||
dbgf("Any %s arguments without a corresponding %%", "extra", "are treated like arguments to dbg()") |
||||
# "2013/01/28 17:14:40 Any extra arguments (without a corresponding %) are treated like arguments to dbg()" |
||||
|
||||
dbgf("%d %d", 1, 2, 3, 4, 5) |
||||
# "2013/01/28 17:16:32 Another example: 1 2 3 4 5" |
||||
|
||||
dbgf("%@: Include the function name for a little context (via %s)", "%@") |
||||
# "2013... github.com/robertkrimen/dbg.TestSynopsis: Include the function name for a little context (via %@)" |
||||
|
||||
By default, dbg uses log (log.Println, log.Printf, log.Panic, etc.) for output. |
||||
However, you can also provide your own output destination by invoking dbg.New with |
||||
a customization function: |
||||
|
||||
import ( |
||||
"bytes" |
||||
Dbg "github.com/robertkrimen/dbg" |
||||
"os" |
||||
) |
||||
|
||||
# dbg to os.Stderr |
||||
dbg, dbgf := Dbg.New(func(dbgr *Dbgr) { |
||||
dbgr.SetOutput(os.Stderr) |
||||
}) |
||||
|
||||
# A slightly contrived example: |
||||
var buffer bytes.Buffer |
||||
dbg, dbgf := New(func(dbgr *Dbgr) { |
||||
dbgr.SetOutput(&buffer) |
||||
}) |
||||
|
||||
*/ |
||||
package dbg |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"io" |
||||
"log" |
||||
"os" |
||||
"regexp" |
||||
"runtime" |
||||
"strings" |
||||
"unicode" |
||||
) |
||||
|
||||
type _frmt struct { |
||||
ctl string |
||||
format string |
||||
operandCount int |
||||
panic bool |
||||
fatal bool |
||||
check bool |
||||
} |
||||
|
||||
var ( |
||||
ctlTest = regexp.MustCompile(`^\s*%/`) |
||||
ctlScan = regexp.MustCompile(`%?/(panic|fatal|check)(?:\s|$)`) |
||||
) |
||||
|
||||
func operandCount(format string) int { |
||||
count := 0 |
||||
end := len(format) |
||||
for at := 0; at < end; { |
||||
for at < end && format[at] != '%' { |
||||
at++ |
||||
} |
||||
at++ |
||||
if at < end { |
||||
if format[at] != '%' && format[at] != '@' { |
||||
count++ |
||||
} |
||||
at++ |
||||
} |
||||
} |
||||
return count |
||||
} |
||||
|
||||
func parseFormat(format string) (frmt _frmt) { |
||||
if ctlTest.MatchString(format) { |
||||
format = strings.TrimLeftFunc(format, unicode.IsSpace) |
||||
index := strings.Index(format, "//") |
||||
if index != -1 { |
||||
frmt.ctl = format[0:index] |
||||
format = format[index+2:] // Skip the second slash via +2 (instead of +1)
|
||||
} else { |
||||
frmt.ctl = format |
||||
format = "" |
||||
} |
||||
for _, tmp := range ctlScan.FindAllStringSubmatch(frmt.ctl, -1) { |
||||
for _, value := range tmp[1:] { |
||||
switch value { |
||||
case "panic": |
||||
frmt.panic = true |
||||
case "fatal": |
||||
frmt.fatal = true |
||||
case "check": |
||||
frmt.check = true |
||||
} |
||||
} |
||||
} |
||||
} |
||||
frmt.format = format |
||||
frmt.operandCount = operandCount(format) |
||||
return |
||||
} |
||||
|
||||
type Dbgr struct { |
||||
emit _emit |
||||
} |
||||
|
||||
type DbgFunction func(values ...interface{}) |
||||
|
||||
func NewDbgr() *Dbgr { |
||||
self := &Dbgr{} |
||||
return self |
||||
} |
||||
|
||||
/* |
||||
New will create and return a pair of debugging functions. You can customize where |
||||
they output to by passing in an (optional) customization function: |
||||
|
||||
import ( |
||||
Dbg "github.com/robertkrimen/dbg" |
||||
"os" |
||||
) |
||||
|
||||
# dbg to os.Stderr |
||||
dbg, dbgf := Dbg.New(func(dbgr *Dbgr) { |
||||
dbgr.SetOutput(os.Stderr) |
||||
}) |
||||
|
||||
*/ |
||||
func New(options ...interface{}) (dbg DbgFunction, dbgf DbgFunction) { |
||||
dbgr := NewDbgr() |
||||
if len(options) > 0 { |
||||
if fn, ok := options[0].(func(*Dbgr)); ok { |
||||
fn(dbgr) |
||||
} |
||||
} |
||||
return dbgr.DbgDbgf() |
||||
} |
||||
|
||||
func (self Dbgr) Dbg(values ...interface{}) { |
||||
self.getEmit().emit(_frmt{}, "", values...) |
||||
} |
||||
|
||||
func (self Dbgr) Dbgf(values ...interface{}) { |
||||
self.dbgf(values...) |
||||
} |
||||
|
||||
func (self Dbgr) DbgDbgf() (dbg DbgFunction, dbgf DbgFunction) { |
||||
dbg = func(vl ...interface{}) { |
||||
self.Dbg(vl...) |
||||
} |
||||
dbgf = func(vl ...interface{}) { |
||||
self.dbgf(vl...) |
||||
} |
||||
return dbg, dbgf // Redundant, but...
|
||||
} |
||||
|
||||
func (self Dbgr) dbgf(values ...interface{}) { |
||||
|
||||
var frmt _frmt |
||||
if len(values) > 0 { |
||||
tmp := fmt.Sprint(values[0]) |
||||
frmt = parseFormat(tmp) |
||||
values = values[1:] |
||||
} |
||||
|
||||
buffer_f := bytes.Buffer{} |
||||
format := frmt.format |
||||
end := len(format) |
||||
for at := 0; at < end; { |
||||
last := at |
||||
for at < end && format[at] != '%' { |
||||
at++ |
||||
} |
||||
if at > last { |
||||
buffer_f.WriteString(format[last:at]) |
||||
} |
||||
if at >= end { |
||||
break |
||||
} |
||||
// format[at] == '%'
|
||||
at++ |
||||
// format[at] == ?
|
||||
if format[at] == '@' { |
||||
depth := 2 |
||||
pc, _, _, _ := runtime.Caller(depth) |
||||
name := runtime.FuncForPC(pc).Name() |
||||
buffer_f.WriteString(name) |
||||
} else { |
||||
buffer_f.WriteString(format[at-1 : at+1]) |
||||
} |
||||
at++ |
||||
} |
||||
|
||||
//values_f := append([]interface{}{}, values[0:frmt.operandCount]...)
|
||||
values_f := values[0:frmt.operandCount] |
||||
values_dbg := values[frmt.operandCount:] |
||||
if len(values_dbg) > 0 { |
||||
// Adjust frmt.format:
|
||||
// (%v instead of %s because: frmt.check)
|
||||
{ |
||||
tmp := format |
||||
if len(tmp) > 0 { |
||||
if unicode.IsSpace(rune(tmp[len(tmp)-1])) { |
||||
buffer_f.WriteString("%v") |
||||
} else { |
||||
buffer_f.WriteString(" %v") |
||||
} |
||||
} else if frmt.check { |
||||
// Performing a check, so no output
|
||||
} else { |
||||
buffer_f.WriteString("%v") |
||||
} |
||||
} |
||||
|
||||
// Adjust values_f:
|
||||
if !frmt.check { |
||||
tmp := []string{} |
||||
for _, value := range values_dbg { |
||||
tmp = append(tmp, fmt.Sprintf("%v", value)) |
||||
} |
||||
// First, make a copy of values_f, so we avoid overwriting values_dbg when appending
|
||||
values_f = append([]interface{}{}, values_f...) |
||||
values_f = append(values_f, strings.Join(tmp, " ")) |
||||
} |
||||
} |
||||
|
||||
format = buffer_f.String() |
||||
if frmt.check { |
||||
// We do not actually emit to the log, but panic if
|
||||
// a non-nil value is detected (e.g. a non-nil error)
|
||||
for _, value := range values_dbg { |
||||
if value != nil { |
||||
if format == "" { |
||||
panic(value) |
||||
} else { |
||||
panic(fmt.Sprintf(format, append(values_f, value)...)) |
||||
} |
||||
} |
||||
} |
||||
} else { |
||||
self.getEmit().emit(frmt, format, values_f...) |
||||
} |
||||
} |
||||
|
||||
// Idiot-proof &Dbgr{}, etc.
|
||||
func (self *Dbgr) getEmit() _emit { |
||||
if self.emit == nil { |
||||
self.emit = standardEmit() |
||||
} |
||||
return self.emit |
||||
} |
||||
|
||||
// SetOutput will accept the following as a destination for output:
|
||||
//
|
||||
// *log.Logger Print*/Panic*/Fatal* of the logger
|
||||
// io.Writer -
|
||||
// nil Reset to the default output (os.Stderr)
|
||||
// "log" Print*/Panic*/Fatal* via the "log" package
|
||||
//
|
||||
func (self *Dbgr) SetOutput(output interface{}) { |
||||
if output == nil { |
||||
self.emit = standardEmit() |
||||
return |
||||
} |
||||
switch output := output.(type) { |
||||
case *log.Logger: |
||||
self.emit = _emitLogger{ |
||||
logger: output, |
||||
} |
||||
return |
||||
case io.Writer: |
||||
self.emit = _emitWriter{ |
||||
writer: output, |
||||
} |
||||
return |
||||
case string: |
||||
if output == "log" { |
||||
self.emit = _emitLog{} |
||||
return |
||||
} |
||||
} |
||||
panic(output) |
||||
} |
||||
|
||||
// ======== //
|
||||
// = emit = //
|
||||
// ======== //
|
||||
|
||||
func standardEmit() _emit { |
||||
return _emitWriter{ |
||||
writer: os.Stderr, |
||||
} |
||||
} |
||||
|
||||
func ln(tmp string) string { |
||||
length := len(tmp) |
||||
if length > 0 && tmp[length-1] != '\n' { |
||||
return tmp + "\n" |
||||
} |
||||
return tmp |
||||
} |
||||
|
||||
type _emit interface { |
||||
emit(_frmt, string, ...interface{}) |
||||
} |
||||
|
||||
type _emitWriter struct { |
||||
writer io.Writer |
||||
} |
||||
|
||||
func (self _emitWriter) emit(frmt _frmt, format string, values ...interface{}) { |
||||
if format == "" { |
||||
fmt.Fprintln(self.writer, values...) |
||||
} else { |
||||
if frmt.panic { |
||||
panic(fmt.Sprintf(format, values...)) |
||||
} |
||||
fmt.Fprintf(self.writer, ln(format), values...) |
||||
if frmt.fatal { |
||||
os.Exit(1) |
||||
} |
||||
} |
||||
} |
||||
|
||||
type _emitLogger struct { |
||||
logger *log.Logger |
||||
} |
||||
|
||||
func (self _emitLogger) emit(frmt _frmt, format string, values ...interface{}) { |
||||
if format == "" { |
||||
self.logger.Println(values...) |
||||
} else { |
||||
if frmt.panic { |
||||
self.logger.Panicf(format, values...) |
||||
} else if frmt.fatal { |
||||
self.logger.Fatalf(format, values...) |
||||
} else { |
||||
self.logger.Printf(format, values...) |
||||
} |
||||
} |
||||
} |
||||
|
||||
type _emitLog struct { |
||||
} |
||||
|
||||
func (self _emitLog) emit(frmt _frmt, format string, values ...interface{}) { |
||||
if format == "" { |
||||
log.Println(values...) |
||||
} else { |
||||
if frmt.panic { |
||||
log.Panicf(format, values...) |
||||
} else if frmt.fatal { |
||||
log.Fatalf(format, values...) |
||||
} else { |
||||
log.Printf(format, values...) |
||||
} |
||||
} |
||||
} |
@ -1,280 +0,0 @@ |
||||
package otto |
||||
|
||||
import ( |
||||
"fmt" |
||||
) |
||||
|
||||
// _environment
|
||||
|
||||
type _environment interface { |
||||
HasBinding(string) bool |
||||
|
||||
CreateMutableBinding(string, bool) |
||||
SetMutableBinding(string, Value, bool) |
||||
// SetMutableBinding with Lazy CreateMutableBinding(..., true)
|
||||
SetValue(string, Value, bool) |
||||
|
||||
GetBindingValue(string, bool) Value |
||||
GetValue(string, bool) Value // GetBindingValue
|
||||
DeleteBinding(string) bool |
||||
ImplicitThisValue() *_object |
||||
|
||||
Outer() _environment |
||||
|
||||
newReference(string, bool) _reference |
||||
clone(clone *_clone) _environment |
||||
runtimeOf() *_runtime |
||||
} |
||||
|
||||
// _functionEnvironment
|
||||
|
||||
type _functionEnvironment struct { |
||||
_declarativeEnvironment |
||||
arguments *_object |
||||
indexOfArgumentName map[string]string |
||||
} |
||||
|
||||
func (runtime *_runtime) newFunctionEnvironment(outer _environment) *_functionEnvironment { |
||||
return &_functionEnvironment{ |
||||
_declarativeEnvironment: _declarativeEnvironment{ |
||||
runtime: runtime, |
||||
outer: outer, |
||||
property: map[string]_declarativeProperty{}, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
func (self0 _functionEnvironment) clone(clone *_clone) _environment { |
||||
return &_functionEnvironment{ |
||||
*(self0._declarativeEnvironment.clone(clone).(*_declarativeEnvironment)), |
||||
clone.object(self0.arguments), |
||||
self0.indexOfArgumentName, |
||||
} |
||||
} |
||||
|
||||
func (self _functionEnvironment) runtimeOf() *_runtime { |
||||
return self._declarativeEnvironment.runtimeOf() |
||||
} |
||||
|
||||
// _objectEnvironment
|
||||
|
||||
type _objectEnvironment struct { |
||||
runtime *_runtime |
||||
outer _environment |
||||
Object *_object |
||||
ProvideThis bool |
||||
} |
||||
|
||||
func (self *_objectEnvironment) runtimeOf() *_runtime { |
||||
return self.runtime |
||||
} |
||||
|
||||
func (runtime *_runtime) newObjectEnvironment(object *_object, outer _environment) *_objectEnvironment { |
||||
if object == nil { |
||||
object = runtime.newBaseObject() |
||||
object.class = "environment" |
||||
} |
||||
return &_objectEnvironment{ |
||||
runtime: runtime, |
||||
outer: outer, |
||||
Object: object, |
||||
} |
||||
} |
||||
|
||||
func (self0 *_objectEnvironment) clone(clone *_clone) _environment { |
||||
self1, exists := clone.objectEnvironment(self0) |
||||
if exists { |
||||
return self1 |
||||
} |
||||
*self1 = _objectEnvironment{ |
||||
clone.runtime, |
||||
clone.environment(self0.outer), |
||||
clone.object(self0.Object), |
||||
self0.ProvideThis, |
||||
} |
||||
return self1 |
||||
} |
||||
|
||||
func (self *_objectEnvironment) HasBinding(name string) bool { |
||||
return self.Object.hasProperty(name) |
||||
} |
||||
|
||||
func (self *_objectEnvironment) CreateMutableBinding(name string, deletable bool) { |
||||
if self.Object.hasProperty(name) { |
||||
panic(hereBeDragons()) |
||||
} |
||||
mode := _propertyMode(0111) |
||||
if !deletable { |
||||
mode = _propertyMode(0110) |
||||
} |
||||
// TODO False?
|
||||
self.Object.defineProperty(name, UndefinedValue(), mode, false) |
||||
} |
||||
|
||||
func (self *_objectEnvironment) SetMutableBinding(name string, value Value, strict bool) { |
||||
self.Object.put(name, value, strict) |
||||
} |
||||
|
||||
func (self *_objectEnvironment) SetValue(name string, value Value, throw bool) { |
||||
if !self.HasBinding(name) { |
||||
self.CreateMutableBinding(name, true) // Configurable by default
|
||||
} |
||||
self.SetMutableBinding(name, value, throw) |
||||
} |
||||
|
||||
func (self *_objectEnvironment) GetBindingValue(name string, strict bool) Value { |
||||
if self.Object.hasProperty(name) { |
||||
return self.Object.get(name) |
||||
} |
||||
if strict { |
||||
panic(newReferenceError("Not Defined", name)) |
||||
} |
||||
return UndefinedValue() |
||||
} |
||||
|
||||
func (self *_objectEnvironment) GetValue(name string, throw bool) Value { |
||||
return self.GetBindingValue(name, throw) |
||||
} |
||||
|
||||
func (self *_objectEnvironment) DeleteBinding(name string) bool { |
||||
return self.Object.delete(name, false) |
||||
} |
||||
|
||||
func (self *_objectEnvironment) ImplicitThisValue() *_object { |
||||
if self.ProvideThis { |
||||
return self.Object |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func (self *_objectEnvironment) Outer() _environment { |
||||
return self.outer |
||||
} |
||||
|
||||
func (self *_objectEnvironment) newReference(name string, strict bool) _reference { |
||||
return newPropertyReference(self.Object, name, strict) |
||||
} |
||||
|
||||
// _declarativeEnvironment
|
||||
|
||||
func (runtime *_runtime) newDeclarativeEnvironment(outer _environment) *_declarativeEnvironment { |
||||
return &_declarativeEnvironment{ |
||||
runtime: runtime, |
||||
outer: outer, |
||||
property: map[string]_declarativeProperty{}, |
||||
} |
||||
} |
||||
|
||||
func (self0 *_declarativeEnvironment) clone(clone *_clone) _environment { |
||||
self1, exists := clone.declarativeEnvironment(self0) |
||||
if exists { |
||||
return self1 |
||||
} |
||||
property := make(map[string]_declarativeProperty, len(self0.property)) |
||||
for index, value := range self0.property { |
||||
property[index] = clone.declarativeProperty(value) |
||||
} |
||||
*self1 = _declarativeEnvironment{ |
||||
clone.runtime, |
||||
clone.environment(self0.outer), |
||||
property, |
||||
} |
||||
return self1 |
||||
} |
||||
|
||||
type _declarativeProperty struct { |
||||
value Value |
||||
mutable bool |
||||
deletable bool |
||||
readable bool |
||||
} |
||||
|
||||
type _declarativeEnvironment struct { |
||||
runtime *_runtime |
||||
outer _environment |
||||
property map[string]_declarativeProperty |
||||
} |
||||
|
||||
func (self *_declarativeEnvironment) HasBinding(name string) bool { |
||||
_, exists := self.property[name] |
||||
return exists |
||||
} |
||||
|
||||
func (self *_declarativeEnvironment) runtimeOf() *_runtime { |
||||
return self.runtime |
||||
} |
||||
|
||||
func (self *_declarativeEnvironment) CreateMutableBinding(name string, deletable bool) { |
||||
_, exists := self.property[name] |
||||
if exists { |
||||
panic(fmt.Errorf("CreateMutableBinding: %s: already exists", name)) |
||||
} |
||||
self.property[name] = _declarativeProperty{ |
||||
value: UndefinedValue(), |
||||
mutable: true, |
||||
deletable: deletable, |
||||
readable: false, |
||||
} |
||||
} |
||||
|
||||
func (self *_declarativeEnvironment) SetMutableBinding(name string, value Value, strict bool) { |
||||
property, exists := self.property[name] |
||||
if !exists { |
||||
panic(fmt.Errorf("SetMutableBinding: %s: missing", name)) |
||||
} |
||||
if property.mutable { |
||||
property.value = value |
||||
self.property[name] = property |
||||
} else { |
||||
typeErrorResult(strict) |
||||
} |
||||
} |
||||
|
||||
func (self *_declarativeEnvironment) SetValue(name string, value Value, throw bool) { |
||||
if !self.HasBinding(name) { |
||||
self.CreateMutableBinding(name, false) // NOT deletable by default
|
||||
} |
||||
self.SetMutableBinding(name, value, throw) |
||||
} |
||||
|
||||
func (self *_declarativeEnvironment) GetBindingValue(name string, strict bool) Value { |
||||
property, exists := self.property[name] |
||||
if !exists { |
||||
panic(fmt.Errorf("GetBindingValue: %s: missing", name)) |
||||
} |
||||
if !property.mutable && !property.readable { |
||||
if strict { |
||||
panic(newTypeError()) |
||||
} |
||||
return UndefinedValue() |
||||
} |
||||
return property.value |
||||
} |
||||
|
||||
func (self *_declarativeEnvironment) GetValue(name string, throw bool) Value { |
||||
return self.GetBindingValue(name, throw) |
||||
} |
||||
|
||||
func (self *_declarativeEnvironment) DeleteBinding(name string) bool { |
||||
property, exists := self.property[name] |
||||
if !exists { |
||||
return true |
||||
} |
||||
if !property.deletable { |
||||
return false |
||||
} |
||||
delete(self.property, name) |
||||
return true |
||||
} |
||||
|
||||
func (self *_declarativeEnvironment) ImplicitThisValue() *_object { |
||||
return nil |
||||
} |
||||
|
||||
func (self *_declarativeEnvironment) Outer() _environment { |
||||
return self.outer |
||||
} |
||||
|
||||
func (self *_declarativeEnvironment) newReference(name string, strict bool) _reference { |
||||
return newEnvironmentReference(self, name, strict, nil) |
||||
} |
@ -1,152 +0,0 @@ |
||||
package otto |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
|
||||
"github.com/robertkrimen/otto/ast" |
||||
) |
||||
|
||||
type _exception struct { |
||||
value interface{} |
||||
} |
||||
|
||||
func newException(value interface{}) *_exception { |
||||
return &_exception{ |
||||
value: value, |
||||
} |
||||
} |
||||
|
||||
func (self *_exception) eject() interface{} { |
||||
value := self.value |
||||
self.value = nil // Prevent Go from holding on to the value, whatever it is
|
||||
return value |
||||
} |
||||
|
||||
type _error struct { |
||||
Name string |
||||
Message string |
||||
|
||||
Line int // Hackish -- line where the error/exception occurred
|
||||
} |
||||
|
||||
var messageDetail map[string]string = map[string]string{ |
||||
"notDefined": "%v is not defined", |
||||
} |
||||
|
||||
func messageFromDescription(description string, argumentList ...interface{}) string { |
||||
message := messageDetail[description] |
||||
if message == "" { |
||||
message = description |
||||
} |
||||
message = fmt.Sprintf(message, argumentList...) |
||||
return message |
||||
} |
||||
|
||||
func (self _error) MessageValue() Value { |
||||
if self.Message == "" { |
||||
return UndefinedValue() |
||||
} |
||||
return toValue_string(self.Message) |
||||
} |
||||
|
||||
func (self _error) String() string { |
||||
if len(self.Name) == 0 { |
||||
return self.Message |
||||
} |
||||
if len(self.Message) == 0 { |
||||
return self.Name |
||||
} |
||||
return fmt.Sprintf("%s: %s", self.Name, self.Message) |
||||
} |
||||
|
||||
func newError(name string, argumentList ...interface{}) _error { |
||||
description := "" |
||||
var node ast.Node = nil |
||||
length := len(argumentList) |
||||
if length > 0 { |
||||
if node, _ = argumentList[length-1].(ast.Node); node != nil || argumentList[length-1] == nil { |
||||
argumentList = argumentList[0 : length-1] |
||||
length -= 1 |
||||
} |
||||
if length > 0 { |
||||
description, argumentList = argumentList[0].(string), argumentList[1:] |
||||
} |
||||
} |
||||
return _error{ |
||||
Name: name, |
||||
Message: messageFromDescription(description, argumentList...), |
||||
Line: -1, |
||||
} |
||||
//error := _error{
|
||||
// Name: name,
|
||||
// Message: messageFromDescription(description, argumentList...),
|
||||
// Line: -1,
|
||||
//}
|
||||
//if node != nil {
|
||||
// error.Line = ast.position()
|
||||
//}
|
||||
//return error
|
||||
} |
||||
|
||||
func newReferenceError(argumentList ...interface{}) _error { |
||||
return newError("ReferenceError", argumentList...) |
||||
} |
||||
|
||||
func newTypeError(argumentList ...interface{}) _error { |
||||
return newError("TypeError", argumentList...) |
||||
} |
||||
|
||||
func newRangeError(argumentList ...interface{}) _error { |
||||
return newError("RangeError", argumentList...) |
||||
} |
||||
|
||||
func newSyntaxError(argumentList ...interface{}) _error { |
||||
return newError("SyntaxError", argumentList...) |
||||
} |
||||
|
||||
func newURIError(argumentList ...interface{}) _error { |
||||
return newError("URIError", argumentList...) |
||||
} |
||||
|
||||
func typeErrorResult(throw bool) bool { |
||||
if throw { |
||||
panic(newTypeError()) |
||||
} |
||||
return false |
||||
} |
||||
|
||||
func catchPanic(function func()) (err error) { |
||||
// FIXME
|
||||
defer func() { |
||||
if caught := recover(); caught != nil { |
||||
if exception, ok := caught.(*_exception); ok { |
||||
caught = exception.eject() |
||||
} |
||||
switch caught := caught.(type) { |
||||
//case *_syntaxError:
|
||||
// err = errors.New(fmt.Sprintf("%s (line %d)", caught.String(), caught.Line+0))
|
||||
// return
|
||||
case _error: |
||||
if caught.Line == -1 { |
||||
err = errors.New(caught.String()) |
||||
} else { |
||||
// We're 0-based (for now), hence the + 1
|
||||
err = errors.New(fmt.Sprintf("%s (line %d)", caught.String(), caught.Line+1)) |
||||
} |
||||
return |
||||
case Value: |
||||
err = errors.New(toString(caught)) |
||||
return |
||||
//case string:
|
||||
// if strings.HasPrefix(caught, "SyntaxError:") {
|
||||
// err = errors.New(caught)
|
||||
// return
|
||||
// }
|
||||
} |
||||
panic(caught) |
||||
} |
||||
}() |
||||
function() |
||||
return nil |
||||
} |
@ -1,62 +0,0 @@ |
||||
package otto |
||||
|
||||
import ( |
||||
"testing" |
||||
) |
||||
|
||||
func TestError(t *testing.T) { |
||||
tt(t, func() { |
||||
test, _ := test() |
||||
|
||||
test(` |
||||
[ Error.prototype.name, Error.prototype.message, Error.prototype.hasOwnProperty("message") ]; |
||||
`, "Error,,true") |
||||
}) |
||||
} |
||||
|
||||
func TestError_instanceof(t *testing.T) { |
||||
tt(t, func() { |
||||
test, _ := test() |
||||
|
||||
test(`(new TypeError()) instanceof Error`, true) |
||||
}) |
||||
} |
||||
|
||||
func TestPanicValue(t *testing.T) { |
||||
tt(t, func() { |
||||
test, vm := test() |
||||
|
||||
vm.Set("abc", func(call FunctionCall) Value { |
||||
value, err := call.Otto.Run(`({ def: 3.14159 })`) |
||||
is(err, nil) |
||||
panic(value) |
||||
}) |
||||
|
||||
test(` |
||||
try { |
||||
abc(); |
||||
} |
||||
catch (err) { |
||||
error = err; |
||||
} |
||||
[ error instanceof Error, error.message, error.def ]; |
||||
`, "false,,3.14159") |
||||
}) |
||||
} |
||||
|
||||
func Test_catchPanic(t *testing.T) { |
||||
tt(t, func() { |
||||
vm := New() |
||||
|
||||
_, err := vm.Run(` |
||||
A syntax error that |
||||
does not define |
||||
var; |
||||
abc; |
||||
`) |
||||
is(err, "!=", nil) |
||||
|
||||
_, err = vm.Call(`abc.def`, nil) |
||||
is(err, "!=", nil) |
||||
}) |
||||
} |
@ -1,40 +0,0 @@ |
||||
package otto |
||||
|
||||
type _executionContext struct { |
||||
LexicalEnvironment _environment |
||||
VariableEnvironment _environment |
||||
this *_object |
||||
eval bool // Replace this with kind?
|
||||
} |
||||
|
||||
func newExecutionContext(lexical _environment, variable _environment, this *_object) *_executionContext { |
||||
return &_executionContext{ |
||||
LexicalEnvironment: lexical, |
||||
VariableEnvironment: variable, |
||||
this: this, |
||||
} |
||||
} |
||||
|
||||
func (self *_executionContext) getValue(name string) Value { |
||||
strict := false |
||||
return self.LexicalEnvironment.GetValue(name, strict) |
||||
} |
||||
|
||||
func (self *_executionContext) setValue(name string, value Value, throw bool) { |
||||
self.LexicalEnvironment.SetValue(name, value, throw) |
||||
} |
||||
|
||||
func (self *_executionContext) newLexicalEnvironment(object *_object) (_environment, *_objectEnvironment) { |
||||
// Get runtime from the object (for now)
|
||||
runtime := object.runtime |
||||
previousLexical := self.LexicalEnvironment |
||||
newLexical := runtime.newObjectEnvironment(object, self.LexicalEnvironment) |
||||
self.LexicalEnvironment = newLexical |
||||
return previousLexical, newLexical |
||||
} |
||||
|
||||
func (self *_executionContext) newDeclarativeEnvironment(runtime *_runtime) _environment { |
||||
previousLexical := self.LexicalEnvironment |
||||
self.LexicalEnvironment = runtime.newDeclarativeEnvironment(self.LexicalEnvironment) |
||||
return previousLexical |
||||
} |
@ -1,72 +0,0 @@ |
||||
# file |
||||
-- |
||||
import "github.com/robertkrimen/otto/file" |
||||
|
||||
Package file encapsulates the file abstractions used by the ast & parser. |
||||
|
||||
## Usage |
||||
|
||||
#### type FileSet |
||||
|
||||
```go |
||||
type FileSet struct { |
||||
} |
||||
``` |
||||
|
||||
A FileSet represents a set of source files. |
||||
|
||||
#### func (*FileSet) AddFile |
||||
|
||||
```go |
||||
func (self *FileSet) AddFile(filename, src string) int |
||||
``` |
||||
AddFile adds a new file with the given filename and src. |
||||
|
||||
This an internal method, but exported for cross-package use. |
||||
|
||||
#### func (*FileSet) Position |
||||
|
||||
```go |
||||
func (self *FileSet) Position(idx Idx) *Position |
||||
``` |
||||
Position converts an Idx in the FileSet into a Position. |
||||
|
||||
#### type Idx |
||||
|
||||
```go |
||||
type Idx int |
||||
``` |
||||
|
||||
Idx is a compact encoding of a source position within a file set. It can be |
||||
converted into a Position for a more convenient, but much larger, |
||||
representation. |
||||
|
||||
#### type Position |
||||
|
||||
```go |
||||
type Position struct { |
||||
Filename string // The filename where the error occurred, if any |
||||
Offset int // The src offset |
||||
Line int // The line number, starting at 1 |
||||
Column int // The column number, starting at 1 (The character count) |
||||
|
||||
} |
||||
``` |
||||
|
||||
Position describes an arbitrary source position including the filename, line, |
||||
and column location. |
||||
|
||||
#### func (*Position) String |
||||
|
||||
```go |
||||
func (self *Position) String() string |
||||
``` |
||||
String returns a string in one of several forms: |
||||
|
||||
file:line:column A valid position with filename |
||||
line:column A valid position without filename |
||||
file An invalid position with filename |
||||
- An invalid position without filename |
||||
|
||||
-- |
||||
**godocdown** http://github.com/robertkrimen/godocdown |
@ -1,106 +0,0 @@ |
||||
// Package file encapsulates the file abstractions used by the ast & parser.
|
||||
//
|
||||
package file |
||||
|
||||
import ( |
||||
"fmt" |
||||
"strings" |
||||
) |
||||
|
||||
// Idx is a compact encoding of a source position within a file set.
|
||||
// It can be converted into a Position for a more convenient, but much
|
||||
// larger, representation.
|
||||
type Idx int |
||||
|
||||
// Position describes an arbitrary source position
|
||||
// including the filename, line, and column location.
|
||||
type Position struct { |
||||
Filename string // The filename where the error occurred, if any
|
||||
Offset int // The src offset
|
||||
Line int // The line number, starting at 1
|
||||
Column int // The column number, starting at 1 (The character count)
|
||||
|
||||
} |
||||
|
||||
// A Position is valid if the line number is > 0.
|
||||
|
||||
func (self *Position) isValid() bool { |
||||
return self.Line > 0 |
||||
} |
||||
|
||||
// String returns a string in one of several forms:
|
||||
//
|
||||
// file:line:column A valid position with filename
|
||||
// line:column A valid position without filename
|
||||
// file An invalid position with filename
|
||||
// - An invalid position without filename
|
||||
//
|
||||
func (self *Position) String() string { |
||||
str := self.Filename |
||||
if self.isValid() { |
||||
if str != "" { |
||||
str += ":" |
||||
} |
||||
str += fmt.Sprintf("%d:%d", self.Line, self.Column) |
||||
} |
||||
if str == "" { |
||||
str = "-" |
||||
} |
||||
return str |
||||
} |
||||
|
||||
// FileSet
|
||||
|
||||
// A FileSet represents a set of source files.
|
||||
type FileSet struct { |
||||
files []*_file |
||||
last *_file |
||||
} |
||||
|
||||
// AddFile adds a new file with the given filename and src.
|
||||
//
|
||||
// This an internal method, but exported for cross-package use.
|
||||
func (self *FileSet) AddFile(filename, src string) int { |
||||
base := self.nextBase() |
||||
file := &_file{ |
||||
filename: filename, |
||||
src: src, |
||||
base: base, |
||||
} |
||||
self.files = append(self.files, file) |
||||
self.last = file |
||||
return base |
||||
} |
||||
|
||||
func (self *FileSet) nextBase() int { |
||||
if self.last == nil { |
||||
return 1 |
||||
} |
||||
return self.last.base + len(self.last.src) + 1 |
||||
} |
||||
|
||||
// Position converts an Idx in the FileSet into a Position.
|
||||
func (self *FileSet) Position(idx Idx) *Position { |
||||
position := &Position{} |
||||
for _, file := range self.files { |
||||
if idx <= Idx(file.base+len(file.src)) { |
||||
offset := int(idx) - file.base |
||||
src := file.src[:offset] |
||||
position.Filename = file.filename |
||||
position.Offset = offset |
||||
position.Line = 1 + strings.Count(src, "\n") |
||||
if index := strings.LastIndex(src, "\n"); index >= 0 { |
||||
position.Column = offset - index |
||||
} else { |
||||
position.Column = 1 + len(src) |
||||
} |
||||
} |
||||
} |
||||
return position |
||||
} |
||||
|
||||
type _file struct { |
||||
filename string |
||||
src string |
||||
base int // This will always be 1 or greater
|
||||
} |
@ -1,4 +0,0 @@ |
||||
.PHONY: test |
||||
|
||||
test: |
||||
go test
|
@ -1,190 +0,0 @@ |
||||
# 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 |
@ -1,9 +0,0 @@ |
||||
// 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() |
@ -1,175 +0,0 @@ |
||||
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 |
||||
} |
||||
|
||||
// FXIME 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 |
||||
} |
@ -1,815 +0,0 @@ |
||||
package parser |
||||
|
||||
import ( |
||||
"regexp" |
||||
|
||||
"github.com/robertkrimen/otto/ast" |
||||
"github.com/robertkrimen/otto/file" |
||||
"github.com/robertkrimen/otto/token" |
||||
) |
||||
|
||||
func (self *_parser) parseIdentifier() *ast.Identifier { |
||||
literal := self.literal |
||||
idx := self.idx |
||||
self.next() |
||||
return &ast.Identifier{ |
||||
Name: literal, |
||||
Idx: idx, |
||||
} |
||||
} |
||||
|
||||
func (self *_parser) parsePrimaryExpression() ast.Expression { |
||||
literal := self.literal |
||||
idx := self.idx |
||||
switch self.token { |
||||
case token.IDENTIFIER: |
||||
self.next() |
||||
if len(literal) > 1 { |
||||
tkn, strict := token.IsKeyword(literal) |
||||
if tkn == token.KEYWORD { |
||||
if !strict { |
||||
self.error(idx, "Unexpected reserved word") |
||||
} |
||||
} |
||||
} |
||||
return &ast.Identifier{ |
||||
Name: literal, |
||||
Idx: idx, |
||||
} |
||||
case token.NULL: |
||||
self.next() |
||||
return &ast.NullLiteral{ |
||||
Idx: idx, |
||||
Literal: literal, |
||||
} |
||||
case token.BOOLEAN: |
||||
self.next() |
||||
value := false |
||||
switch literal { |
||||
case "true": |
||||
value = true |
||||
case "false": |
||||
value = false |
||||
default: |
||||
self.error(idx, "Illegal boolean literal") |
||||
} |
||||
return &ast.BooleanLiteral{ |
||||
Idx: idx, |
||||
Literal: literal, |
||||
Value: value, |
||||
} |
||||
case token.STRING: |
||||
self.next() |
||||
value, err := parseStringLiteral(literal[1 : len(literal)-1]) |
||||
if err != nil { |
||||
self.error(idx, err.Error()) |
||||
} |
||||
return &ast.StringLiteral{ |
||||
Idx: idx, |
||||
Literal: literal, |
||||
Value: value, |
||||
} |
||||
case token.NUMBER: |
||||
self.next() |
||||
value, err := parseNumberLiteral(literal) |
||||
if err != nil { |
||||
self.error(idx, err.Error()) |
||||
value = 0 |
||||
} |
||||
return &ast.NumberLiteral{ |
||||
Idx: idx, |
||||
Literal: literal, |
||||
Value: value, |
||||
} |
||||
case token.SLASH, token.QUOTIENT_ASSIGN: |
||||
return self.parseRegExpLiteral() |
||||
case token.LEFT_BRACE: |
||||
return self.parseObjectLiteral() |
||||
case token.LEFT_BRACKET: |
||||
return self.parseArrayLiteral() |
||||
case token.LEFT_PARENTHESIS: |
||||
self.expect(token.LEFT_PARENTHESIS) |
||||
expression := self.parseExpression() |
||||
self.expect(token.RIGHT_PARENTHESIS) |
||||
return expression |
||||
case token.THIS: |
||||
self.next() |
||||
return &ast.ThisExpression{ |
||||
Idx: idx, |
||||
} |
||||
case token.FUNCTION: |
||||
return self.parseFunction(false) |
||||
} |
||||
|
||||
self.errorUnexpectedToken(self.token) |
||||
self.nextStatement() |
||||
return &ast.BadExpression{From: idx, To: self.idx} |
||||
} |
||||
|
||||
func (self *_parser) parseRegExpLiteral() *ast.RegExpLiteral { |
||||
|
||||
offset := self.chrOffset - 1 // Opening slash already gotten
|
||||
if self.token == token.QUOTIENT_ASSIGN { |
||||
offset -= 1 // =
|
||||
} |
||||
idx := self.idxOf(offset) |
||||
|
||||
pattern, err := self.scanString(offset) |
||||
endOffset := self.chrOffset |
||||
|
||||
self.next() |
||||
if err == nil { |
||||
pattern = pattern[1 : len(pattern)-1] |
||||
} |
||||
|
||||
flags := "" |
||||
if self.token == token.IDENTIFIER { // gim
|
||||
|
||||
flags = self.literal |
||||
self.next() |
||||
endOffset = self.chrOffset - 1 |
||||
} |
||||
|
||||
var value string |
||||
// TODO 15.10
|
||||
{ |
||||
// Test during parsing that this is a valid regular expression
|
||||
// Sorry, (?=) and (?!) are invalid (for now)
|
||||
pattern, err := TransformRegExp(pattern) |
||||
if err != nil { |
||||
if pattern == "" || self.mode&IgnoreRegExpErrors == 0 { |
||||
self.error(idx, "Invalid regular expression: %s", err.Error()) |
||||
} |
||||
} else { |
||||
_, err = regexp.Compile(pattern) |
||||
if err != nil { |
||||
// We should not get here, ParseRegExp should catch any errors
|
||||
self.error(idx, "Invalid regular expression: %s", err.Error()[22:]) // Skip redundant "parse regexp error"
|
||||
} else { |
||||
value = pattern |
||||
} |
||||
} |
||||
} |
||||
|
||||
literal := self.str[offset:endOffset] |
||||
|
||||
return &ast.RegExpLiteral{ |
||||
Idx: idx, |
||||
Literal: literal, |
||||
Pattern: pattern, |
||||
Flags: flags, |
||||
Value: value, |
||||
} |
||||
} |
||||
|
||||
func (self *_parser) parseVariableDeclaration(declarationList *[]*ast.VariableExpression) ast.Expression { |
||||
|
||||
if self.token != token.IDENTIFIER { |
||||
idx := self.expect(token.IDENTIFIER) |
||||
self.nextStatement() |
||||
return &ast.BadExpression{From: idx, To: self.idx} |
||||
} |
||||
|
||||
literal := self.literal |
||||
idx := self.idx |
||||
self.next() |
||||
node := &ast.VariableExpression{ |
||||
Name: literal, |
||||
Idx: idx, |
||||
} |
||||
|
||||
if declarationList != nil { |
||||
*declarationList = append(*declarationList, node) |
||||
} |
||||
|
||||
if self.token == token.ASSIGN { |
||||
self.next() |
||||
node.Initializer = self.parseAssignmentExpression() |
||||
} |
||||
|
||||
return node |
||||
} |
||||
|
||||
func (self *_parser) parseVariableDeclarationList(var_ file.Idx) []ast.Expression { |
||||
|
||||
var declarationList []*ast.VariableExpression // Avoid bad expressions
|
||||
var list []ast.Expression |
||||
|
||||
for { |
||||
list = append(list, self.parseVariableDeclaration(&declarationList)) |
||||
if self.token != token.COMMA { |
||||
break |
||||
} |
||||
self.next() |
||||
} |
||||
|
||||
self.scope.declare(&ast.VariableDeclaration{ |
||||
Var: var_, |
||||
List: declarationList, |
||||
}) |
||||
|
||||
return list |
||||
} |
||||
|
||||
func (self *_parser) parseObjectPropertyKey() (string, string) { |
||||
idx, tkn, literal := self.idx, self.token, self.literal |
||||
value := "" |
||||
self.next() |
||||
switch tkn { |
||||
case token.IDENTIFIER: |
||||
value = literal |
||||
case token.NUMBER: |
||||
var err error |
||||
_, err = parseNumberLiteral(literal) |
||||
if err != nil { |
||||
self.error(idx, err.Error()) |
||||
} else { |
||||
value = literal |
||||
} |
||||
case token.STRING: |
||||
var err error |
||||
value, err = parseStringLiteral(literal[1 : len(literal)-1]) |
||||
if err != nil { |
||||
self.error(idx, err.Error()) |
||||
} |
||||
default: |
||||
// null, false, class, etc.
|
||||
if matchIdentifier.MatchString(literal) { |
||||
value = literal |
||||
} |
||||
} |
||||
return literal, value |
||||
} |
||||
|
||||
func (self *_parser) parseObjectProperty() ast.Property { |
||||
|
||||
literal, value := self.parseObjectPropertyKey() |
||||
if literal == "get" && self.token != token.COLON { |
||||
idx := self.idx |
||||
_, value := self.parseObjectPropertyKey() |
||||
parameterList := self.parseFunctionParameterList() |
||||
|
||||
node := &ast.FunctionLiteral{ |
||||
Function: idx, |
||||
ParameterList: parameterList, |
||||
} |
||||
self.parseFunctionBlock(node) |
||||
return ast.Property{ |
||||
Key: value, |
||||
Kind: "get", |
||||
Value: node, |
||||
} |
||||
} else if literal == "set" && self.token != token.COLON { |
||||
idx := self.idx |
||||
_, value := self.parseObjectPropertyKey() |
||||
parameterList := self.parseFunctionParameterList() |
||||
|
||||
node := &ast.FunctionLiteral{ |
||||
Function: idx, |
||||
ParameterList: parameterList, |
||||
} |
||||
self.parseFunctionBlock(node) |
||||
return ast.Property{ |
||||
Key: value, |
||||
Kind: "set", |
||||
Value: node, |
||||
} |
||||
} |
||||
|
||||
self.expect(token.COLON) |
||||
|
||||
return ast.Property{ |
||||
Key: value, |
||||
Kind: "value", |
||||
Value: self.parseAssignmentExpression(), |
||||
} |
||||
} |
||||
|
||||
func (self *_parser) parseObjectLiteral() ast.Expression { |
||||
var value []ast.Property |
||||
idx0 := self.expect(token.LEFT_BRACE) |
||||
for self.token != token.RIGHT_BRACE && self.token != token.EOF { |
||||
property := self.parseObjectProperty() |
||||
value = append(value, property) |
||||
if self.token == token.COMMA { |
||||
self.next() |
||||
continue |
||||
} |
||||
} |
||||
idx1 := self.expect(token.RIGHT_BRACE) |
||||
|
||||
return &ast.ObjectLiteral{ |
||||
LeftBrace: idx0, |
||||
RightBrace: idx1, |
||||
Value: value, |
||||
} |
||||
} |
||||
|
||||
func (self *_parser) parseArrayLiteral() ast.Expression { |
||||
|
||||
idx0 := self.expect(token.LEFT_BRACKET) |
||||
var value []ast.Expression |
||||
for self.token != token.RIGHT_BRACKET && self.token != token.EOF { |
||||
if self.token == token.COMMA { |
||||
self.next() |
||||
value = append(value, nil) |
||||
continue |
||||
} |
||||
value = append(value, self.parseAssignmentExpression()) |
||||
if self.token != token.RIGHT_BRACKET { |
||||
self.expect(token.COMMA) |
||||
} |
||||
} |
||||
idx1 := self.expect(token.RIGHT_BRACKET) |
||||
|
||||
return &ast.ArrayLiteral{ |
||||
LeftBracket: idx0, |
||||
RightBracket: idx1, |
||||
Value: value, |
||||
} |
||||
} |
||||
|
||||
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()) |
||||
if self.token != token.COMMA { |
||||
break |
||||
} |
||||
self.next() |
||||
} |
||||
} |
||||
idx1 = self.expect(token.RIGHT_PARENTHESIS) |
||||
return |
||||
} |
||||
|
||||
func (self *_parser) parseCallExpression(left ast.Expression) ast.Expression { |
||||
argumentList, idx0, idx1 := self.parseArgumentList() |
||||
return &ast.CallExpression{ |
||||
Callee: left, |
||||
LeftParenthesis: idx0, |
||||
ArgumentList: argumentList, |
||||
RightParenthesis: idx1, |
||||
} |
||||
} |
||||
|
||||
func (self *_parser) parseDotMember(left ast.Expression) ast.Expression { |
||||
period := self.expect(token.PERIOD) |
||||
|
||||
literal := self.literal |
||||
idx := self.idx |
||||
|
||||
if !matchIdentifier.MatchString(literal) { |
||||
self.expect(token.IDENTIFIER) |
||||
self.nextStatement() |
||||
return &ast.BadExpression{From: period, To: self.idx} |
||||
} |
||||
|
||||
self.next() |
||||
|
||||
return &ast.DotExpression{ |
||||
Left: left, |
||||
Identifier: ast.Identifier{ |
||||
Idx: idx, |
||||
Name: literal, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
func (self *_parser) parseBracketMember(left ast.Expression) ast.Expression { |
||||
idx0 := self.expect(token.LEFT_BRACKET) |
||||
member := self.parseExpression() |
||||
idx1 := self.expect(token.RIGHT_BRACKET) |
||||
return &ast.BracketExpression{ |
||||
LeftBracket: idx0, |
||||
Left: left, |
||||
Member: member, |
||||
RightBracket: idx1, |
||||
} |
||||
} |
||||
|
||||
func (self *_parser) parseNewExpression() ast.Expression { |
||||
idx := self.expect(token.NEW) |
||||
callee := self.parseLeftHandSideExpression() |
||||
node := &ast.NewExpression{ |
||||
New: idx, |
||||
Callee: callee, |
||||
} |
||||
if self.token == token.LEFT_PARENTHESIS { |
||||
argumentList, idx0, idx1 := self.parseArgumentList() |
||||
node.ArgumentList = argumentList |
||||
node.LeftParenthesis = idx0 |
||||
node.RightParenthesis = idx1 |
||||
} |
||||
return node |
||||
} |
||||
|
||||
func (self *_parser) parseLeftHandSideExpression() ast.Expression { |
||||
|
||||
var left ast.Expression |
||||
if self.token == token.NEW { |
||||
left = self.parseNewExpression() |
||||
} else { |
||||
left = self.parsePrimaryExpression() |
||||
} |
||||
|
||||
for { |
||||
if self.token == token.PERIOD { |
||||
left = self.parseDotMember(left) |
||||
} else if self.token == token.LEFT_BRACE { |
||||
left = self.parseBracketMember(left) |
||||
} else { |
||||
break |
||||
} |
||||
} |
||||
|
||||
return left |
||||
} |
||||
|
||||
func (self *_parser) parseLeftHandSideExpressionAllowCall() ast.Expression { |
||||
|
||||
allowIn := self.scope.allowIn |
||||
self.scope.allowIn = true |
||||
defer func() { |
||||
self.scope.allowIn = allowIn |
||||
}() |
||||
|
||||
var left ast.Expression |
||||
if self.token == token.NEW { |
||||
left = self.parseNewExpression() |
||||
} else { |
||||
left = self.parsePrimaryExpression() |
||||
} |
||||
|
||||
for { |
||||
if self.token == token.PERIOD { |
||||
left = self.parseDotMember(left) |
||||
} else if self.token == token.LEFT_BRACKET { |
||||
left = self.parseBracketMember(left) |
||||
} else if self.token == token.LEFT_PARENTHESIS { |
||||
left = self.parseCallExpression(left) |
||||
} else { |
||||
break |
||||
} |
||||
} |
||||
|
||||
return left |
||||
} |
||||
|
||||
func (self *_parser) parsePostfixExpression() ast.Expression { |
||||
operand := self.parseLeftHandSideExpressionAllowCall() |
||||
|
||||
switch self.token { |
||||
case token.INCREMENT, token.DECREMENT: |
||||
// Make sure there is no line terminator here
|
||||
if self.implicitSemicolon { |
||||
break |
||||
} |
||||
tkn := self.token |
||||
idx := self.idx |
||||
self.next() |
||||
switch operand.(type) { |
||||
case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: |
||||
default: |
||||
self.error(idx, "Invalid left-hand side in assignment") |
||||
self.nextStatement() |
||||
return &ast.BadExpression{From: idx, To: self.idx} |
||||
} |
||||
return &ast.UnaryExpression{ |
||||
Operator: tkn, |
||||
Idx: idx, |
||||
Operand: operand, |
||||
Postfix: true, |
||||
} |
||||
} |
||||
|
||||
return operand |
||||
} |
||||
|
||||
func (self *_parser) parseUnaryExpression() ast.Expression { |
||||
|
||||
switch self.token { |
||||
case token.PLUS, token.MINUS, token.NOT, token.BITWISE_NOT: |
||||
fallthrough |
||||
case token.DELETE, token.VOID, token.TYPEOF: |
||||
tkn := self.token |
||||
idx := self.idx |
||||
self.next() |
||||
return &ast.UnaryExpression{ |
||||
Operator: tkn, |
||||
Idx: idx, |
||||
Operand: self.parseUnaryExpression(), |
||||
} |
||||
case token.INCREMENT, token.DECREMENT: |
||||
tkn := self.token |
||||
idx := self.idx |
||||
self.next() |
||||
operand := self.parseUnaryExpression() |
||||
switch operand.(type) { |
||||
case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: |
||||
default: |
||||
self.error(idx, "Invalid left-hand side in assignment") |
||||
self.nextStatement() |
||||
return &ast.BadExpression{From: idx, To: self.idx} |
||||
} |
||||
return &ast.UnaryExpression{ |
||||
Operator: tkn, |
||||
Idx: idx, |
||||
Operand: operand, |
||||
} |
||||
} |
||||
|
||||
return self.parsePostfixExpression() |
||||
} |
||||
|
||||
func (self *_parser) parseMultiplicativeExpression() ast.Expression { |
||||
next := self.parseUnaryExpression |
||||
left := next() |
||||
|
||||
for self.token == token.MULTIPLY || self.token == token.SLASH || |
||||
self.token == token.REMAINDER { |
||||
tkn := self.token |
||||
self.next() |
||||
left = &ast.BinaryExpression{ |
||||
Operator: tkn, |
||||
Left: left, |
||||
Right: next(), |
||||
} |
||||
} |
||||
|
||||
return left |
||||
} |
||||
|
||||
func (self *_parser) parseAdditiveExpression() ast.Expression { |
||||
next := self.parseMultiplicativeExpression |
||||
left := next() |
||||
|
||||
for self.token == token.PLUS || self.token == token.MINUS { |
||||
tkn := self.token |
||||
self.next() |
||||
left = &ast.BinaryExpression{ |
||||
Operator: tkn, |
||||
Left: left, |
||||
Right: next(), |
||||
} |
||||
} |
||||
|
||||
return left |
||||
} |
||||
|
||||
func (self *_parser) parseShiftExpression() ast.Expression { |
||||
next := self.parseAdditiveExpression |
||||
left := next() |
||||
|
||||
for self.token == token.SHIFT_LEFT || self.token == token.SHIFT_RIGHT || |
||||
self.token == token.UNSIGNED_SHIFT_RIGHT { |
||||
tkn := self.token |
||||
self.next() |
||||
left = &ast.BinaryExpression{ |
||||
Operator: tkn, |
||||
Left: left, |
||||
Right: next(), |
||||
} |
||||
} |
||||
|
||||
return left |
||||
} |
||||
|
||||
func (self *_parser) parseRelationalExpression() ast.Expression { |
||||
next := self.parseShiftExpression |
||||
left := next() |
||||
|
||||
allowIn := self.scope.allowIn |
||||
self.scope.allowIn = true |
||||
defer func() { |
||||
self.scope.allowIn = allowIn |
||||
}() |
||||
|
||||
switch self.token { |
||||
case token.LESS, token.LESS_OR_EQUAL, token.GREATER, token.GREATER_OR_EQUAL: |
||||
tkn := self.token |
||||
self.next() |
||||
return &ast.BinaryExpression{ |
||||
Operator: tkn, |
||||
Left: left, |
||||
Right: self.parseRelationalExpression(), |
||||
Comparison: true, |
||||
} |
||||
case token.INSTANCEOF: |
||||
tkn := self.token |
||||
self.next() |
||||
return &ast.BinaryExpression{ |
||||
Operator: tkn, |
||||
Left: left, |
||||
Right: self.parseRelationalExpression(), |
||||
} |
||||
case token.IN: |
||||
if !allowIn { |
||||
return left |
||||
} |
||||
tkn := self.token |
||||
self.next() |
||||
return &ast.BinaryExpression{ |
||||
Operator: tkn, |
||||
Left: left, |
||||
Right: self.parseRelationalExpression(), |
||||
} |
||||
} |
||||
|
||||
return left |
||||
} |
||||
|
||||
func (self *_parser) parseEqualityExpression() ast.Expression { |
||||
next := self.parseRelationalExpression |
||||
left := next() |
||||
|
||||
for self.token == token.EQUAL || self.token == token.NOT_EQUAL || |
||||
self.token == token.STRICT_EQUAL || self.token == token.STRICT_NOT_EQUAL { |
||||
tkn := self.token |
||||
self.next() |
||||
left = &ast.BinaryExpression{ |
||||
Operator: tkn, |
||||
Left: left, |
||||
Right: next(), |
||||
Comparison: true, |
||||
} |
||||
} |
||||
|
||||
return left |
||||
} |
||||
|
||||
func (self *_parser) parseBitwiseAndExpression() ast.Expression { |
||||
next := self.parseEqualityExpression |
||||
left := next() |
||||
|
||||
for self.token == token.AND { |
||||
tkn := self.token |
||||
self.next() |
||||
left = &ast.BinaryExpression{ |
||||
Operator: tkn, |
||||
Left: left, |
||||
Right: next(), |
||||
} |
||||
} |
||||
|
||||
return left |
||||
} |
||||
|
||||
func (self *_parser) parseBitwiseExclusiveOrExpression() ast.Expression { |
||||
next := self.parseBitwiseAndExpression |
||||
left := next() |
||||
|
||||
for self.token == token.EXCLUSIVE_OR { |
||||
tkn := self.token |
||||
self.next() |
||||
left = &ast.BinaryExpression{ |
||||
Operator: tkn, |
||||
Left: left, |
||||
Right: next(), |
||||
} |
||||
} |
||||
|
||||
return left |
||||
} |
||||
|
||||
func (self *_parser) parseBitwiseOrExpression() ast.Expression { |
||||
next := self.parseBitwiseExclusiveOrExpression |
||||
left := next() |
||||
|
||||
for self.token == token.OR { |
||||
tkn := self.token |
||||
self.next() |
||||
left = &ast.BinaryExpression{ |
||||
Operator: tkn, |
||||
Left: left, |
||||
Right: next(), |
||||
} |
||||
} |
||||
|
||||
return left |
||||
} |
||||
|
||||
func (self *_parser) parseLogicalAndExpression() ast.Expression { |
||||
next := self.parseBitwiseOrExpression |
||||
left := next() |
||||
|
||||
for self.token == token.LOGICAL_AND { |
||||
tkn := self.token |
||||
self.next() |
||||
left = &ast.BinaryExpression{ |
||||
Operator: tkn, |
||||
Left: left, |
||||
Right: next(), |
||||
} |
||||
} |
||||
|
||||
return left |
||||
} |
||||
|
||||
func (self *_parser) parseLogicalOrExpression() ast.Expression { |
||||
next := self.parseLogicalAndExpression |
||||
left := next() |
||||
|
||||
for self.token == token.LOGICAL_OR { |
||||
tkn := self.token |
||||
self.next() |
||||
left = &ast.BinaryExpression{ |
||||
Operator: tkn, |
||||
Left: left, |
||||
Right: next(), |
||||
} |
||||
} |
||||
|
||||
return left |
||||
} |
||||
|
||||
func (self *_parser) parseConditionlExpression() ast.Expression { |
||||
left := self.parseLogicalOrExpression() |
||||
|
||||
if self.token == token.QUESTION_MARK { |
||||
self.next() |
||||
consequent := self.parseAssignmentExpression() |
||||
self.expect(token.COLON) |
||||
return &ast.ConditionalExpression{ |
||||
Test: left, |
||||
Consequent: consequent, |
||||
Alternate: self.parseAssignmentExpression(), |
||||
} |
||||
} |
||||
|
||||
return left |
||||
} |
||||
|
||||
func (self *_parser) parseAssignmentExpression() ast.Expression { |
||||
left := self.parseConditionlExpression() |
||||
var operator token.Token |
||||
switch self.token { |
||||
case token.ASSIGN: |
||||
operator = self.token |
||||
case token.ADD_ASSIGN: |
||||
operator = token.PLUS |
||||
case token.SUBTRACT_ASSIGN: |
||||
operator = token.MINUS |
||||
case token.MULTIPLY_ASSIGN: |
||||
operator = token.MULTIPLY |
||||
case token.QUOTIENT_ASSIGN: |
||||
operator = token.SLASH |
||||
case token.REMAINDER_ASSIGN: |
||||
operator = token.REMAINDER |
||||
case token.AND_ASSIGN: |
||||
operator = token.AND |
||||
case token.AND_NOT_ASSIGN: |
||||
operator = token.AND_NOT |
||||
case token.OR_ASSIGN: |
||||
operator = token.OR |
||||
case token.EXCLUSIVE_OR_ASSIGN: |
||||
operator = token.EXCLUSIVE_OR |
||||
case token.SHIFT_LEFT_ASSIGN: |
||||
operator = token.SHIFT_LEFT |
||||
case token.SHIFT_RIGHT_ASSIGN: |
||||
operator = token.SHIFT_RIGHT |
||||
case token.UNSIGNED_SHIFT_RIGHT_ASSIGN: |
||||
operator = token.UNSIGNED_SHIFT_RIGHT |
||||
} |
||||
|
||||
if operator != 0 { |
||||
idx := self.idx |
||||
self.next() |
||||
switch left.(type) { |
||||
case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression: |
||||
default: |
||||
self.error(left.Idx0(), "Invalid left-hand side in assignment") |
||||
self.nextStatement() |
||||
return &ast.BadExpression{From: idx, To: self.idx} |
||||
} |
||||
return &ast.AssignExpression{ |
||||
Left: left, |
||||
Operator: operator, |
||||
Right: self.parseAssignmentExpression(), |
||||
} |
||||
} |
||||
|
||||
return left |
||||
} |
||||
|
||||
func (self *_parser) parseExpression() ast.Expression { |
||||
next := self.parseAssignmentExpression |
||||
left := next() |
||||
|
||||
if self.token == token.COMMA { |
||||
sequence := []ast.Expression{left} |
||||
for { |
||||
if self.token != token.COMMA { |
||||
break |
||||
} |
||||
self.next() |
||||
sequence = append(sequence, next()) |
||||
} |
||||
return &ast.SequenceExpression{ |
||||
Sequence: sequence, |
||||
} |
||||
} |
||||
|
||||
return left |
||||
} |
@ -1,819 +0,0 @@ |
||||
package parser |
||||
|
||||
import ( |
||||
"bytes" |
||||
"errors" |
||||
"fmt" |
||||
"regexp" |
||||
"strconv" |
||||
"strings" |
||||
"unicode" |
||||
"unicode/utf8" |
||||
|
||||
"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 |
||||
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 == '/' { |
||||
self.skipSingleLineComment() |
||||
continue |
||||
} else if self.chr == '*' { |
||||
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) 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.read() |
||||
} |
||||
fallthrough |
||||
case '\u2028', '\u2029', '\n': |
||||
if self.insertSemicolon { |
||||
return |
||||
} |
||||
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] |
||||
} |
@ -1,380 +0,0 @@ |
||||
package parser |
||||
|
||||
import ( |
||||
"../terst" |
||||
"testing" |
||||
|
||||
"github.com/robertkrimen/otto/file" |
||||
"github.com/robertkrimen/otto/token" |
||||
) |
||||
|
||||
var tt = terst.Terst |
||||
var is = terst.Is |
||||
|
||||
func TestLexer(t *testing.T) { |
||||
tt(t, func() { |
||||
setup := func(src string) *_parser { |
||||
parser := newParser("", src) |
||||
return parser |
||||
} |
||||
|
||||
test := func(src string, test ...interface{}) { |
||||
parser := setup(src) |
||||
for len(test) > 0 { |
||||
tkn, literal, idx := parser.scan() |
||||
if len(test) > 0 { |
||||
is(tkn, test[0].(token.Token)) |
||||
test = test[1:] |
||||
} |
||||
if len(test) > 0 { |
||||
is(literal, test[0].(string)) |
||||
test = test[1:] |
||||
} |
||||
if len(test) > 0 { |
||||
// FIXME terst, Fix this so that cast to file.Idx is not necessary?
|
||||
is(idx, file.Idx(test[0].(int))) |
||||
test = test[1:] |
||||
} |
||||
} |
||||
} |
||||
|
||||
test("", |
||||
token.EOF, "", 1, |
||||
) |
||||
|
||||
test("1", |
||||
token.NUMBER, "1", 1, |
||||
token.EOF, "", 2, |
||||
) |
||||
|
||||
test(".0", |
||||
token.NUMBER, ".0", 1, |
||||
token.EOF, "", 3, |
||||
) |
||||
|
||||
test("abc", |
||||
token.IDENTIFIER, "abc", 1, |
||||
token.EOF, "", 4, |
||||
) |
||||
|
||||
test("abc(1)", |
||||
token.IDENTIFIER, "abc", 1, |
||||
token.LEFT_PARENTHESIS, "", 4, |
||||
token.NUMBER, "1", 5, |
||||
token.RIGHT_PARENTHESIS, "", 6, |
||||
token.EOF, "", 7, |
||||
) |
||||
|
||||
test(".", |
||||
token.PERIOD, "", 1, |
||||
token.EOF, "", 2, |
||||
) |
||||
|
||||
test("===.", |
||||
token.STRICT_EQUAL, "", 1, |
||||
token.PERIOD, "", 4, |
||||
token.EOF, "", 5, |
||||
) |
||||
|
||||
test(">>>=.0", |
||||
token.UNSIGNED_SHIFT_RIGHT_ASSIGN, "", 1, |
||||
token.NUMBER, ".0", 5, |
||||
token.EOF, "", 7, |
||||
) |
||||
|
||||
test(">>>=0.0.", |
||||
token.UNSIGNED_SHIFT_RIGHT_ASSIGN, "", 1, |
||||
token.NUMBER, "0.0", 5, |
||||
token.PERIOD, "", 8, |
||||
token.EOF, "", 9, |
||||
) |
||||
|
||||
test("\"abc\"", |
||||
token.STRING, "\"abc\"", 1, |
||||
token.EOF, "", 6, |
||||
) |
||||
|
||||
test("abc = //", |
||||
token.IDENTIFIER, "abc", 1, |
||||
token.ASSIGN, "", 5, |
||||
token.EOF, "", 9, |
||||
) |
||||
|
||||
test("abc = 1 / 2", |
||||
token.IDENTIFIER, "abc", 1, |
||||
token.ASSIGN, "", 5, |
||||
token.NUMBER, "1", 7, |
||||
token.SLASH, "", 9, |
||||
token.NUMBER, "2", 11, |
||||
token.EOF, "", 12, |
||||
) |
||||
|
||||
test("xyzzy = 'Nothing happens.'", |
||||
token.IDENTIFIER, "xyzzy", 1, |
||||
token.ASSIGN, "", 7, |
||||
token.STRING, "'Nothing happens.'", 9, |
||||
token.EOF, "", 27, |
||||
) |
||||
|
||||
test("abc = !false", |
||||
token.IDENTIFIER, "abc", 1, |
||||
token.ASSIGN, "", 5, |
||||
token.NOT, "", 7, |
||||
token.BOOLEAN, "false", 8, |
||||
token.EOF, "", 13, |
||||
) |
||||
|
||||
test("abc = !!true", |
||||
token.IDENTIFIER, "abc", 1, |
||||
token.ASSIGN, "", 5, |
||||
token.NOT, "", 7, |
||||
token.NOT, "", 8, |
||||
token.BOOLEAN, "true", 9, |
||||
token.EOF, "", 13, |
||||
) |
||||
|
||||
test("abc *= 1", |
||||
token.IDENTIFIER, "abc", 1, |
||||
token.MULTIPLY_ASSIGN, "", 5, |
||||
token.NUMBER, "1", 8, |
||||
token.EOF, "", 9, |
||||
) |
||||
|
||||
test("if 1 else", |
||||
token.IF, "if", 1, |
||||
token.NUMBER, "1", 4, |
||||
token.ELSE, "else", 6, |
||||
token.EOF, "", 10, |
||||
) |
||||
|
||||
test("null", |
||||
token.NULL, "null", 1, |
||||
token.EOF, "", 5, |
||||
) |
||||
|
||||
test(`"\u007a\x79\u000a\x78"`, |
||||
token.STRING, "\"\\u007a\\x79\\u000a\\x78\"", 1, |
||||
token.EOF, "", 23, |
||||
) |
||||
|
||||
test(`"[First line \
|
||||
Second line \
|
||||
Third line\
|
||||
. ]" |
||||
`, |
||||
token.STRING, "\"[First line \\\nSecond line \\\n Third line\\\n. ]\"", 1, |
||||
token.EOF, "", 53, |
||||
) |
||||
|
||||
test("/", |
||||
token.SLASH, "", 1, |
||||
token.EOF, "", 2, |
||||
) |
||||
|
||||
test("var abc = \"abc\uFFFFabc\"", |
||||
token.VAR, "var", 1, |
||||
token.IDENTIFIER, "abc", 5, |
||||
token.ASSIGN, "", 9, |
||||
token.STRING, "\"abc\uFFFFabc\"", 11, |
||||
token.EOF, "", 22, |
||||
) |
||||
|
||||
test(`'\t' === '\r'`, |
||||
token.STRING, "'\\t'", 1, |
||||
token.STRICT_EQUAL, "", 6, |
||||
token.STRING, "'\\r'", 10, |
||||
token.EOF, "", 14, |
||||
) |
||||
|
||||
test(`var \u0024 = 1`, |
||||
token.VAR, "var", 1, |
||||
token.IDENTIFIER, "$", 5, |
||||
token.ASSIGN, "", 12, |
||||
token.NUMBER, "1", 14, |
||||
token.EOF, "", 15, |
||||
) |
||||
|
||||
test("10e10000", |
||||
token.NUMBER, "10e10000", 1, |
||||
token.EOF, "", 9, |
||||
) |
||||
|
||||
test(`var if var class`, |
||||
token.VAR, "var", 1, |
||||
token.IF, "if", 5, |
||||
token.VAR, "var", 8, |
||||
token.KEYWORD, "class", 12, |
||||
token.EOF, "", 17, |
||||
) |
||||
|
||||
test(`-0`, |
||||
token.MINUS, "", 1, |
||||
token.NUMBER, "0", 2, |
||||
token.EOF, "", 3, |
||||
) |
||||
|
||||
test(`.01`, |
||||
token.NUMBER, ".01", 1, |
||||
token.EOF, "", 4, |
||||
) |
||||
|
||||
test(`.01e+2`, |
||||
token.NUMBER, ".01e+2", 1, |
||||
token.EOF, "", 7, |
||||
) |
||||
|
||||
test(";", |
||||
token.SEMICOLON, "", 1, |
||||
token.EOF, "", 2, |
||||
) |
||||
|
||||
test(";;", |
||||
token.SEMICOLON, "", 1, |
||||
token.SEMICOLON, "", 2, |
||||
token.EOF, "", 3, |
||||
) |
||||
|
||||
test("//", |
||||
token.EOF, "", 3, |
||||
) |
||||
|
||||
test(";;//", |
||||
token.SEMICOLON, "", 1, |
||||
token.SEMICOLON, "", 2, |
||||
token.EOF, "", 5, |
||||
) |
||||
|
||||
test("1", |
||||
token.NUMBER, "1", 1, |
||||
) |
||||
|
||||
test("12 123", |
||||
token.NUMBER, "12", 1, |
||||
token.NUMBER, "123", 4, |
||||
) |
||||
|
||||
test("1.2 12.3", |
||||
token.NUMBER, "1.2", 1, |
||||
token.NUMBER, "12.3", 5, |
||||
) |
||||
|
||||
test("/ /=", |
||||
token.SLASH, "", 1, |
||||
token.QUOTIENT_ASSIGN, "", 3, |
||||
) |
||||
|
||||
test(`"abc"`, |
||||
token.STRING, `"abc"`, 1, |
||||
) |
||||
|
||||
test(`'abc'`, |
||||
token.STRING, `'abc'`, 1, |
||||
) |
||||
|
||||
test("++", |
||||
token.INCREMENT, "", 1, |
||||
) |
||||
|
||||
test(">", |
||||
token.GREATER, "", 1, |
||||
) |
||||
|
||||
test(">=", |
||||
token.GREATER_OR_EQUAL, "", 1, |
||||
) |
||||
|
||||
test(">>", |
||||
token.SHIFT_RIGHT, "", 1, |
||||
) |
||||
|
||||
test(">>=", |
||||
token.SHIFT_RIGHT_ASSIGN, "", 1, |
||||
) |
||||
|
||||
test(">>>", |
||||
token.UNSIGNED_SHIFT_RIGHT, "", 1, |
||||
) |
||||
|
||||
test(">>>=", |
||||
token.UNSIGNED_SHIFT_RIGHT_ASSIGN, "", 1, |
||||
) |
||||
|
||||
test("1 \"abc\"", |
||||
token.NUMBER, "1", 1, |
||||
token.STRING, "\"abc\"", 3, |
||||
) |
||||
|
||||
test(",", |
||||
token.COMMA, "", 1, |
||||
) |
||||
|
||||
test("1, \"abc\"", |
||||
token.NUMBER, "1", 1, |
||||
token.COMMA, "", 2, |
||||
token.STRING, "\"abc\"", 4, |
||||
) |
||||
|
||||
test("new abc(1, 3.14159);", |
||||
token.NEW, "new", 1, |
||||
token.IDENTIFIER, "abc", 5, |
||||
token.LEFT_PARENTHESIS, "", 8, |
||||
token.NUMBER, "1", 9, |
||||
token.COMMA, "", 10, |
||||
token.NUMBER, "3.14159", 12, |
||||
token.RIGHT_PARENTHESIS, "", 19, |
||||
token.SEMICOLON, "", 20, |
||||
) |
||||
|
||||
test("1 == \"1\"", |
||||
token.NUMBER, "1", 1, |
||||
token.EQUAL, "", 3, |
||||
token.STRING, "\"1\"", 6, |
||||
) |
||||
|
||||
test("1\n[]\n", |
||||
token.NUMBER, "1", 1, |
||||
token.LEFT_BRACKET, "", 3, |
||||
token.RIGHT_BRACKET, "", 4, |
||||
) |
||||
|
||||
test("1\ufeff[]\ufeff", |
||||
token.NUMBER, "1", 1, |
||||
token.LEFT_BRACKET, "", 5, |
||||
token.RIGHT_BRACKET, "", 6, |
||||
) |
||||
|
||||
// ILLEGAL
|
||||
|
||||
test(`3ea`, |
||||
token.ILLEGAL, "3e", 1, |
||||
token.IDENTIFIER, "a", 3, |
||||
token.EOF, "", 4, |
||||
) |
||||
|
||||
test(`3in`, |
||||
token.ILLEGAL, "3", 1, |
||||
token.IN, "in", 2, |
||||
token.EOF, "", 4, |
||||
) |
||||
|
||||
test("\"Hello\nWorld\"", |
||||
token.ILLEGAL, "", 1, |
||||
token.IDENTIFIER, "World", 8, |
||||
token.ILLEGAL, "", 13, |
||||
token.EOF, "", 14, |
||||
) |
||||
|
||||
test("\u203f = 10", |
||||
token.ILLEGAL, "", 1, |
||||
token.ASSIGN, "", 5, |
||||
token.NUMBER, "10", 7, |
||||
token.EOF, "", 9, |
||||
) |
||||
|
||||
test(`"\x0G"`, |
||||
token.STRING, "\"\\x0G\"", 1, |
||||
token.EOF, "", 7, |
||||
) |
||||
|
||||
}) |
||||
} |
@ -1,930 +0,0 @@ |
||||
package parser |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/json" |
||||
"fmt" |
||||
"os" |
||||
"reflect" |
||||
"strings" |
||||
"testing" |
||||
|
||||
"github.com/robertkrimen/otto/ast" |
||||
) |
||||
|
||||
func marshal(name string, children ...interface{}) interface{} { |
||||
if len(children) == 1 { |
||||
if name == "" { |
||||
return testMarshalNode(children[0]) |
||||
} |
||||
return map[string]interface{}{ |
||||
name: children[0], |
||||
} |
||||
} |
||||
map_ := map[string]interface{}{} |
||||
length := len(children) / 2 |
||||
for i := 0; i < length; i++ { |
||||
name := children[i*2].(string) |
||||
value := children[i*2+1] |
||||
map_[name] = value |
||||
} |
||||
if name == "" { |
||||
return map_ |
||||
} |
||||
return map[string]interface{}{ |
||||
name: map_, |
||||
} |
||||
} |
||||
|
||||
func testMarshalNode(node interface{}) interface{} { |
||||
switch node := node.(type) { |
||||
|
||||
// Expression
|
||||
|
||||
case *ast.ArrayLiteral: |
||||
return marshal("Array", testMarshalNode(node.Value)) |
||||
|
||||
case *ast.AssignExpression: |
||||
return marshal("Assign", |
||||
"Left", testMarshalNode(node.Left), |
||||
"Right", testMarshalNode(node.Right), |
||||
) |
||||
|
||||
case *ast.BinaryExpression: |
||||
return marshal("BinaryExpression", |
||||
"Operator", node.Operator.String(), |
||||
"Left", testMarshalNode(node.Left), |
||||
"Right", testMarshalNode(node.Right), |
||||
) |
||||
|
||||
case *ast.BooleanLiteral: |
||||
return marshal("Literal", node.Value) |
||||
|
||||
case *ast.CallExpression: |
||||
return marshal("Call", |
||||
"Callee", testMarshalNode(node.Callee), |
||||
"ArgumentList", testMarshalNode(node.ArgumentList), |
||||
) |
||||
|
||||
case *ast.ConditionalExpression: |
||||
return marshal("Conditional", |
||||
"Test", testMarshalNode(node.Test), |
||||
"Consequent", testMarshalNode(node.Consequent), |
||||
"Alternate", testMarshalNode(node.Alternate), |
||||
) |
||||
|
||||
case *ast.DotExpression: |
||||
return marshal("Dot", |
||||
"Left", testMarshalNode(node.Left), |
||||
"Member", node.Identifier.Name, |
||||
) |
||||
|
||||
case *ast.NewExpression: |
||||
return marshal("New", |
||||
"Callee", testMarshalNode(node.Callee), |
||||
"ArgumentList", testMarshalNode(node.ArgumentList), |
||||
) |
||||
|
||||
case *ast.NullLiteral: |
||||
return marshal("Literal", nil) |
||||
|
||||
case *ast.NumberLiteral: |
||||
return marshal("Literal", node.Value) |
||||
|
||||
case *ast.ObjectLiteral: |
||||
return marshal("Object", testMarshalNode(node.Value)) |
||||
|
||||
case *ast.RegExpLiteral: |
||||
return marshal("Literal", node.Literal) |
||||
|
||||
case *ast.StringLiteral: |
||||
return marshal("Literal", node.Literal) |
||||
|
||||
case *ast.VariableExpression: |
||||
return []interface{}{node.Name, testMarshalNode(node.Initializer)} |
||||
|
||||
// Statement
|
||||
|
||||
case *ast.Program: |
||||
return testMarshalNode(node.Body) |
||||
|
||||
case *ast.BlockStatement: |
||||
return marshal("BlockStatement", testMarshalNode(node.List)) |
||||
|
||||
case *ast.EmptyStatement: |
||||
return "EmptyStatement" |
||||
|
||||
case *ast.ExpressionStatement: |
||||
return testMarshalNode(node.Expression) |
||||
|
||||
case *ast.ForInStatement: |
||||
return marshal("ForIn", |
||||
"Into", marshal("", node.Into), |
||||
"Source", marshal("", node.Source), |
||||
"Body", marshal("", node.Body), |
||||
) |
||||
|
||||
case *ast.FunctionLiteral: |
||||
return marshal("Function", testMarshalNode(node.Body)) |
||||
|
||||
case *ast.Identifier: |
||||
return marshal("Identifier", node.Name) |
||||
|
||||
case *ast.IfStatement: |
||||
if_ := marshal("", |
||||
"Test", testMarshalNode(node.Test), |
||||
"Consequent", testMarshalNode(node.Consequent), |
||||
).(map[string]interface{}) |
||||
if node.Alternate != nil { |
||||
if_["Alternate"] = testMarshalNode(node.Alternate) |
||||
} |
||||
return marshal("If", if_) |
||||
|
||||
case *ast.LabelledStatement: |
||||
return marshal("Label", |
||||
"Name", node.Label.Name, |
||||
"Statement", testMarshalNode(node.Statement), |
||||
) |
||||
case ast.Property: |
||||
return marshal("", |
||||
"Key", node.Key, |
||||
"Value", testMarshalNode(node.Value), |
||||
) |
||||
|
||||
case *ast.ReturnStatement: |
||||
return marshal("Return", testMarshalNode(node.Argument)) |
||||
|
||||
case *ast.SequenceExpression: |
||||
return marshal("Sequence", testMarshalNode(node.Sequence)) |
||||
|
||||
case *ast.ThrowStatement: |
||||
return marshal("Throw", testMarshalNode(node.Argument)) |
||||
|
||||
case *ast.VariableStatement: |
||||
return marshal("Var", testMarshalNode(node.List)) |
||||
|
||||
} |
||||
|
||||
{ |
||||
value := reflect.ValueOf(node) |
||||
if value.Kind() == reflect.Slice { |
||||
tmp0 := []interface{}{} |
||||
for index := 0; index < value.Len(); index++ { |
||||
tmp0 = append(tmp0, testMarshalNode(value.Index(index).Interface())) |
||||
} |
||||
return tmp0 |
||||
} |
||||
} |
||||
|
||||
if node != nil { |
||||
fmt.Fprintf(os.Stderr, "testMarshalNode(%T)\n", node) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func testMarshal(node interface{}) string { |
||||
value, err := json.Marshal(testMarshalNode(node)) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
return string(value) |
||||
} |
||||
|
||||
func TestParserAST(t *testing.T) { |
||||
tt(t, func() { |
||||
|
||||
test := func(inputOutput string) { |
||||
match := matchBeforeAfterSeparator.FindStringIndex(inputOutput) |
||||
input := strings.TrimSpace(inputOutput[0:match[0]]) |
||||
wantOutput := strings.TrimSpace(inputOutput[match[1]:]) |
||||
_, program, err := testParse(input) |
||||
is(err, nil) |
||||
haveOutput := testMarshal(program) |
||||
tmp0, tmp1 := bytes.Buffer{}, bytes.Buffer{} |
||||
json.Indent(&tmp0, []byte(haveOutput), "\t\t", " ") |
||||
json.Indent(&tmp1, []byte(wantOutput), "\t\t", " ") |
||||
is("\n\t\t"+tmp0.String(), "\n\t\t"+tmp1.String()) |
||||
} |
||||
|
||||
test(` |
||||
--- |
||||
[] |
||||
`) |
||||
|
||||
test(` |
||||
; |
||||
--- |
||||
[ |
||||
"EmptyStatement" |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
;;; |
||||
--- |
||||
[ |
||||
"EmptyStatement", |
||||
"EmptyStatement", |
||||
"EmptyStatement" |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
1; true; abc; "abc"; null; |
||||
--- |
||||
[ |
||||
{ |
||||
"Literal": 1 |
||||
}, |
||||
{ |
||||
"Literal": true |
||||
}, |
||||
{ |
||||
"Identifier": "abc" |
||||
}, |
||||
{ |
||||
"Literal": "\"abc\"" |
||||
}, |
||||
{ |
||||
"Literal": null |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
{ 1; null; 3.14159; ; } |
||||
--- |
||||
[ |
||||
{ |
||||
"BlockStatement": [ |
||||
{ |
||||
"Literal": 1 |
||||
}, |
||||
{ |
||||
"Literal": null |
||||
}, |
||||
{ |
||||
"Literal": 3.14159 |
||||
}, |
||||
"EmptyStatement" |
||||
] |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
new abc(); |
||||
--- |
||||
[ |
||||
{ |
||||
"New": { |
||||
"ArgumentList": [], |
||||
"Callee": { |
||||
"Identifier": "abc" |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
new abc(1, 3.14159) |
||||
--- |
||||
[ |
||||
{ |
||||
"New": { |
||||
"ArgumentList": [ |
||||
{ |
||||
"Literal": 1 |
||||
}, |
||||
{ |
||||
"Literal": 3.14159 |
||||
} |
||||
], |
||||
"Callee": { |
||||
"Identifier": "abc" |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
true ? false : true |
||||
--- |
||||
[ |
||||
{ |
||||
"Conditional": { |
||||
"Alternate": { |
||||
"Literal": true |
||||
}, |
||||
"Consequent": { |
||||
"Literal": false |
||||
}, |
||||
"Test": { |
||||
"Literal": true |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
true || false |
||||
--- |
||||
[ |
||||
{ |
||||
"BinaryExpression": { |
||||
"Left": { |
||||
"Literal": true |
||||
}, |
||||
"Operator": "||", |
||||
"Right": { |
||||
"Literal": false |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
0 + { abc: true } |
||||
--- |
||||
[ |
||||
{ |
||||
"BinaryExpression": { |
||||
"Left": { |
||||
"Literal": 0 |
||||
}, |
||||
"Operator": "+", |
||||
"Right": { |
||||
"Object": [ |
||||
{ |
||||
"Key": "abc", |
||||
"Value": { |
||||
"Literal": true |
||||
} |
||||
} |
||||
] |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
1 == "1" |
||||
--- |
||||
[ |
||||
{ |
||||
"BinaryExpression": { |
||||
"Left": { |
||||
"Literal": 1 |
||||
}, |
||||
"Operator": "==", |
||||
"Right": { |
||||
"Literal": "\"1\"" |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
abc(1) |
||||
--- |
||||
[ |
||||
{ |
||||
"Call": { |
||||
"ArgumentList": [ |
||||
{ |
||||
"Literal": 1 |
||||
} |
||||
], |
||||
"Callee": { |
||||
"Identifier": "abc" |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
Math.pow(3, 2) |
||||
--- |
||||
[ |
||||
{ |
||||
"Call": { |
||||
"ArgumentList": [ |
||||
{ |
||||
"Literal": 3 |
||||
}, |
||||
{ |
||||
"Literal": 2 |
||||
} |
||||
], |
||||
"Callee": { |
||||
"Dot": { |
||||
"Left": { |
||||
"Identifier": "Math" |
||||
}, |
||||
"Member": "pow" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
1, 2, 3 |
||||
--- |
||||
[ |
||||
{ |
||||
"Sequence": [ |
||||
{ |
||||
"Literal": 1 |
||||
}, |
||||
{ |
||||
"Literal": 2 |
||||
}, |
||||
{ |
||||
"Literal": 3 |
||||
} |
||||
] |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
/ abc / gim; |
||||
--- |
||||
[ |
||||
{ |
||||
"Literal": "/ abc / gim" |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
if (0) |
||||
1; |
||||
--- |
||||
[ |
||||
{ |
||||
"If": { |
||||
"Consequent": { |
||||
"Literal": 1 |
||||
}, |
||||
"Test": { |
||||
"Literal": 0 |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
0+function(){ |
||||
return; |
||||
} |
||||
--- |
||||
[ |
||||
{ |
||||
"BinaryExpression": { |
||||
"Left": { |
||||
"Literal": 0 |
||||
}, |
||||
"Operator": "+", |
||||
"Right": { |
||||
"Function": { |
||||
"BlockStatement": [ |
||||
{ |
||||
"Return": null |
||||
} |
||||
] |
||||
} |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
xyzzy // Ignore it
|
||||
// Ignore this
|
||||
// And this
|
||||
/* And all.. |
||||
|
||||
|
||||
|
||||
... of this! |
||||
*/ |
||||
"Nothing happens." |
||||
// And finally this
|
||||
--- |
||||
[ |
||||
{ |
||||
"Identifier": "xyzzy" |
||||
}, |
||||
{ |
||||
"Literal": "\"Nothing happens.\"" |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
((x & (x = 1)) !== 0) |
||||
--- |
||||
[ |
||||
{ |
||||
"BinaryExpression": { |
||||
"Left": { |
||||
"BinaryExpression": { |
||||
"Left": { |
||||
"Identifier": "x" |
||||
}, |
||||
"Operator": "\u0026", |
||||
"Right": { |
||||
"Assign": { |
||||
"Left": { |
||||
"Identifier": "x" |
||||
}, |
||||
"Right": { |
||||
"Literal": 1 |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"Operator": "!==", |
||||
"Right": { |
||||
"Literal": 0 |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
{ abc: 'def' } |
||||
--- |
||||
[ |
||||
{ |
||||
"BlockStatement": [ |
||||
{ |
||||
"Label": { |
||||
"Name": "abc", |
||||
"Statement": { |
||||
"Literal": "'def'" |
||||
} |
||||
} |
||||
} |
||||
] |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
// This is not an object, this is a string literal with a label!
|
||||
({ abc: 'def' }) |
||||
--- |
||||
[ |
||||
{ |
||||
"Object": [ |
||||
{ |
||||
"Key": "abc", |
||||
"Value": { |
||||
"Literal": "'def'" |
||||
} |
||||
} |
||||
] |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
[,] |
||||
--- |
||||
[ |
||||
{ |
||||
"Array": [ |
||||
null |
||||
] |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
[,,] |
||||
--- |
||||
[ |
||||
{ |
||||
"Array": [ |
||||
null, |
||||
null |
||||
] |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
({ get abc() {} }) |
||||
--- |
||||
[ |
||||
{ |
||||
"Object": [ |
||||
{ |
||||
"Key": "abc", |
||||
"Value": { |
||||
"Function": { |
||||
"BlockStatement": [] |
||||
} |
||||
} |
||||
} |
||||
] |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
/abc/.source |
||||
--- |
||||
[ |
||||
{ |
||||
"Dot": { |
||||
"Left": { |
||||
"Literal": "/abc/" |
||||
}, |
||||
"Member": "source" |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
xyzzy |
||||
|
||||
throw new TypeError("Nothing happens.") |
||||
--- |
||||
[ |
||||
{ |
||||
"Identifier": "xyzzy" |
||||
}, |
||||
{ |
||||
"Throw": { |
||||
"New": { |
||||
"ArgumentList": [ |
||||
{ |
||||
"Literal": "\"Nothing happens.\"" |
||||
} |
||||
], |
||||
"Callee": { |
||||
"Identifier": "TypeError" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
// When run, this will call a type error to be thrown
|
||||
// This is essentially the same as:
|
||||
//
|
||||
// var abc = 1(function(){})()
|
||||
//
|
||||
test(` |
||||
var abc = 1 |
||||
(function(){ |
||||
})() |
||||
--- |
||||
[ |
||||
{ |
||||
"Var": [ |
||||
[ |
||||
"abc", |
||||
{ |
||||
"Call": { |
||||
"ArgumentList": [], |
||||
"Callee": { |
||||
"Call": { |
||||
"ArgumentList": [ |
||||
{ |
||||
"Function": { |
||||
"BlockStatement": [] |
||||
} |
||||
} |
||||
], |
||||
"Callee": { |
||||
"Literal": 1 |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
] |
||||
] |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
"use strict" |
||||
--- |
||||
[ |
||||
{ |
||||
"Literal": "\"use strict\"" |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
"use strict" |
||||
abc = 1 + 2 + 11 |
||||
--- |
||||
[ |
||||
{ |
||||
"Literal": "\"use strict\"" |
||||
}, |
||||
{ |
||||
"Assign": { |
||||
"Left": { |
||||
"Identifier": "abc" |
||||
}, |
||||
"Right": { |
||||
"BinaryExpression": { |
||||
"Left": { |
||||
"BinaryExpression": { |
||||
"Left": { |
||||
"Literal": 1 |
||||
}, |
||||
"Operator": "+", |
||||
"Right": { |
||||
"Literal": 2 |
||||
} |
||||
} |
||||
}, |
||||
"Operator": "+", |
||||
"Right": { |
||||
"Literal": 11 |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
abc = function() { 'use strict' } |
||||
--- |
||||
[ |
||||
{ |
||||
"Assign": { |
||||
"Left": { |
||||
"Identifier": "abc" |
||||
}, |
||||
"Right": { |
||||
"Function": { |
||||
"BlockStatement": [ |
||||
{ |
||||
"Literal": "'use strict'" |
||||
} |
||||
] |
||||
} |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
for (var abc in def) { |
||||
} |
||||
--- |
||||
[ |
||||
{ |
||||
"ForIn": { |
||||
"Body": { |
||||
"BlockStatement": [] |
||||
}, |
||||
"Into": [ |
||||
"abc", |
||||
null |
||||
], |
||||
"Source": { |
||||
"Identifier": "def" |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
test(` |
||||
abc = { |
||||
'"': "'", |
||||
"'": '"', |
||||
} |
||||
--- |
||||
[ |
||||
{ |
||||
"Assign": { |
||||
"Left": { |
||||
"Identifier": "abc" |
||||
}, |
||||
"Right": { |
||||
"Object": [ |
||||
{ |
||||
"Key": "\"", |
||||
"Value": { |
||||
"Literal": "\"'\"" |
||||
} |
||||
}, |
||||
{ |
||||
"Key": "'", |
||||
"Value": { |
||||
"Literal": "'\"'" |
||||
} |
||||
} |
||||
] |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
|
||||
return |
||||
|
||||
test(` |
||||
if (!abc && abc.jkl(def) && abc[0] === +abc[0] && abc.length < ghi) { |
||||
} |
||||
--- |
||||
[ |
||||
{ |
||||
"If": { |
||||
"Consequent": { |
||||
"BlockStatement": [] |
||||
}, |
||||
"Test": { |
||||
"BinaryExpression": { |
||||
"Left": { |
||||
"BinaryExpression": { |
||||
"Left": { |
||||
"BinaryExpression": { |
||||
"Left": null, |
||||
"Operator": "\u0026\u0026", |
||||
"Right": { |
||||
"Call": { |
||||
"ArgumentList": [ |
||||
{ |
||||
"Identifier": "def" |
||||
} |
||||
], |
||||
"Callee": { |
||||
"Dot": { |
||||
"Left": { |
||||
"Identifier": "abc" |
||||
}, |
||||
"Member": "jkl" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"Operator": "\u0026\u0026", |
||||
"Right": { |
||||
"BinaryExpression": { |
||||
"Left": null, |
||||
"Operator": "===", |
||||
"Right": null |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"Operator": "\u0026\u0026", |
||||
"Right": { |
||||
"BinaryExpression": { |
||||
"Left": { |
||||
"Dot": { |
||||
"Left": { |
||||
"Identifier": "abc" |
||||
}, |
||||
"Member": "length" |
||||
} |
||||
}, |
||||
"Operator": "\u003c", |
||||
"Right": { |
||||
"Identifier": "ghi" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
] |
||||
`) |
||||
}) |
||||
} |
@ -1,270 +0,0 @@ |
||||
/* |
||||
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" |
||||
"errors" |
||||
"io" |
||||
"io/ioutil" |
||||
|
||||
"github.com/robertkrimen/otto/ast" |
||||
"github.com/robertkrimen/otto/file" |
||||
"github.com/robertkrimen/otto/token" |
||||
) |
||||
|
||||
// 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)
|
||||
) |
||||
|
||||
type _parser struct { |
||||
filename string |
||||
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 |
||||
} |
||||
|
||||
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, |
||||
} |
||||
} |
||||
|
||||
func newParser(filename, src string) *_parser { |
||||
return _newParser(filename, src, 1) |
||||
} |
||||
|
||||
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) |
||||
} |
||||
|
||||
// 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) { |
||||
str, err := ReadSource(filename, src) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
{ |
||||
str := string(str) |
||||
|
||||
base := 1 |
||||
if fileSet != nil { |
||||
base = fileSet.AddFile(filename, str) |
||||
} |
||||
|
||||
parser := _newParser(filename, str, base) |
||||
parser.mode = mode |
||||
return parser.parse() |
||||
} |
||||
} |
||||
|
||||
// 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) |
||||
program, err := parser.parse() |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral), nil |
||||
} |
||||
|
||||
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() |
||||
} |
||||
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.filename |
||||
line, last := lineCount(str) |
||||
position.Line = 1 + line |
||||
if last >= 0 { |
||||
position.Column = offset - last |
||||
} else { |
||||
position.Column = 1 + len(str) |
||||
} |
||||
|
||||
return position |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -1,358 +0,0 @@ |
||||
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 |
||||
} |
@ -1,149 +0,0 @@ |
||||
package parser |
||||
|
||||
import ( |
||||
"regexp" |
||||
"testing" |
||||
) |
||||
|
||||
func TestRegExp(t *testing.T) { |
||||
tt(t, func() { |
||||
{ |
||||
// err
|
||||
test := func(input string, expect interface{}) { |
||||
_, err := TransformRegExp(input) |
||||
is(err, expect) |
||||
} |
||||
|
||||
test("[", "Unterminated character class") |
||||
|
||||
test("(", "Unterminated group") |
||||
|
||||
test("(?=)", "re2: Invalid (?=) <lookahead>") |
||||
|
||||
test("(?=)", "re2: Invalid (?=) <lookahead>") |
||||
|
||||
test("(?!)", "re2: Invalid (?!) <lookahead>") |
||||
|
||||
// An error anyway
|
||||
test("(?=", "re2: Invalid (?=) <lookahead>") |
||||
|
||||
test("\\1", "re2: Invalid \\1 <backreference>") |
||||
|
||||
test("\\90", "re2: Invalid \\90 <backreference>") |
||||
|
||||
test("\\9123456789", "re2: Invalid \\9123456789 <backreference>") |
||||
|
||||
test("\\(?=)", "Unmatched ')'") |
||||
|
||||
test(")", "Unmatched ')'") |
||||
} |
||||
|
||||
{ |
||||
// err
|
||||
test := func(input, expect string, expectErr interface{}) { |
||||
output, err := TransformRegExp(input) |
||||
is(output, expect) |
||||
is(err, expectErr) |
||||
} |
||||
|
||||
test("(?!)", "(?!)", "re2: Invalid (?!) <lookahead>") |
||||
|
||||
test(")", "", "Unmatched ')'") |
||||
|
||||
test("(?!))", "", "re2: Invalid (?!) <lookahead>") |
||||
|
||||
test("\\0", "\\0", nil) |
||||
|
||||
test("\\1", "\\1", "re2: Invalid \\1 <backreference>") |
||||
|
||||
test("\\9123456789", "\\9123456789", "re2: Invalid \\9123456789 <backreference>") |
||||
} |
||||
|
||||
{ |
||||
// err
|
||||
test := func(input string, expect string) { |
||||
result, err := TransformRegExp(input) |
||||
is(err, nil) |
||||
if is(result, expect) { |
||||
_, err := regexp.Compile(result) |
||||
if !is(err, nil) { |
||||
t.Log(result) |
||||
} |
||||
} |
||||
} |
||||
|
||||
test("", "") |
||||
|
||||
test("abc", "abc") |
||||
|
||||
test(`\abc`, `abc`) |
||||
|
||||
test(`\a\b\c`, `a\bc`) |
||||
|
||||
test(`\x`, `x`) |
||||
|
||||
test(`\c`, `c`) |
||||
|
||||
test(`\cA`, `\x01`) |
||||
|
||||
test(`\cz`, `\x1a`) |
||||
|
||||
test(`\ca`, `\x01`) |
||||
|
||||
test(`\cj`, `\x0a`) |
||||
|
||||
test(`\ck`, `\x0b`) |
||||
|
||||
test(`\+`, `\+`) |
||||
|
||||
test(`[\b]`, `[\x08]`) |
||||
|
||||
test(`\u0z01\x\undefined`, `u0z01xundefined`) |
||||
|
||||
test(`\\|'|\r|\n|\t|\u2028|\u2029`, `\\|'|\r|\n|\t|\x{2028}|\x{2029}`) |
||||
|
||||
test("]", "]") |
||||
|
||||
test("}", "}") |
||||
|
||||
test("%", "%") |
||||
|
||||
test("(%)", "(%)") |
||||
|
||||
test("(?:[%\\s])", "(?:[%\\s])") |
||||
|
||||
test("[[]", "[[]") |
||||
|
||||
test("\\101", "\\x41") |
||||
|
||||
test("\\51", "\\x29") |
||||
|
||||
test("\\051", "\\x29") |
||||
|
||||
test("\\175", "\\x7d") |
||||
|
||||
test("\\04", "\\x04") |
||||
|
||||
test(`<%([\s\S]+?)%>`, `<%([\s\S]+?)%>`) |
||||
|
||||
test(`(.)^`, "(.)^") |
||||
|
||||
test(`<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%([\s\S]+?)%>|$`, `<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%([\s\S]+?)%>|$`) |
||||
|
||||
test(`\$`, `\$`) |
||||
|
||||
test(`[G-b]`, `[G-b]`) |
||||
|
||||
test(`[G-b\0]`, `[G-b\0]`) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
func TestTransformRegExp(t *testing.T) { |
||||
tt(t, func() { |
||||
pattern, err := TransformRegExp(`\s+abc\s+`) |
||||
is(err, nil) |
||||
is(pattern, `\s+abc\s+`) |
||||
is(regexp.MustCompile(pattern).MatchString("\t abc def"), true) |
||||
}) |
||||
} |
@ -1,44 +0,0 @@ |
||||
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 |
||||
} |
@ -1,662 +0,0 @@ |
||||
package parser |
||||
|
||||
import ( |
||||
"github.com/robertkrimen/otto/ast" |
||||
"github.com/robertkrimen/otto/token" |
||||
) |
||||
|
||||
func (self *_parser) parseBlockStatement() *ast.BlockStatement { |
||||
node := &ast.BlockStatement{} |
||||
node.LeftBrace = self.expect(token.LEFT_BRACE) |
||||
node.List = self.parseStatementList() |
||||
node.RightBrace = self.expect(token.RIGHT_BRACE) |
||||
|
||||
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 { |
||||
list = append(list, self.parseStatement()) |
||||
} |
||||
|
||||
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} |
||||
} |
||||
|
||||
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: |
||||
return self.parseDoWhileStatement() |
||||
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: |
||||
self.parseFunction(true) |
||||
// FIXME
|
||||
return &ast.EmptyStatement{} |
||||
case token.SWITCH: |
||||
return self.parseSwitchStatement() |
||||
case token.RETURN: |
||||
return self.parseReturnStatement() |
||||
case token.THROW: |
||||
return self.parseThrowStatement() |
||||
case token.TRY: |
||||
return self.parseTryStatement() |
||||
} |
||||
|
||||
expression := self.parseExpression() |
||||
|
||||
if identifier, isIdentifier := expression.(*ast.Identifier); isIdentifier && self.token == token.COLON { |
||||
// LabelledStatement
|
||||
colon := self.idx |
||||
self.next() // :
|
||||
label := identifier.Name |
||||
for _, value := range self.scope.labels { |
||||
if label == value { |
||||
self.error(identifier.Idx0(), "Label '%s' already exists", label) |
||||
} |
||||
} |
||||
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{ |
||||
Label: identifier, |
||||
Colon: colon, |
||||
Statement: statement, |
||||
} |
||||
} |
||||
|
||||
self.optionalSemicolon() |
||||
|
||||
return &ast.ExpressionStatement{ |
||||
Expression: expression, |
||||
} |
||||
} |
||||
|
||||
func (self *_parser) parseTryStatement() ast.Statement { |
||||
|
||||
node := &ast.TryStatement{ |
||||
Try: self.expect(token.TRY), |
||||
Body: self.parseBlockStatement(), |
||||
} |
||||
|
||||
if self.token == token.CATCH { |
||||
catch := self.idx |
||||
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.token == token.FINALLY { |
||||
self.next() |
||||
node.Finally = self.parseBlockStatement() |
||||
} |
||||
|
||||
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) |
||||
var list []*ast.Identifier |
||||
for self.token != token.RIGHT_PARENTHESIS && self.token != token.EOF { |
||||
if self.token != token.IDENTIFIER { |
||||
self.expect(token.IDENTIFIER) |
||||
} else { |
||||
list = append(list, self.parseIdentifier()) |
||||
} |
||||
if self.token != token.RIGHT_PARENTHESIS { |
||||
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) 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) |
||||
} |
||||
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, |
||||
} |
||||
|
||||
self.semicolon() |
||||
|
||||
return node |
||||
} |
||||
|
||||
func (self *_parser) parseReturnStatement() ast.Statement { |
||||
idx := self.expect(token.RETURN) |
||||
|
||||
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() |
||||
} |
||||
|
||||
self.semicolon() |
||||
|
||||
return node |
||||
} |
||||
|
||||
func (self *_parser) parseThrowStatement() ast.Statement { |
||||
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(), |
||||
} |
||||
|
||||
self.semicolon() |
||||
|
||||
return node |
||||
} |
||||
|
||||
func (self *_parser) parseSwitchStatement() ast.Statement { |
||||
self.expect(token.SWITCH) |
||||
self.expect(token.LEFT_PARENTHESIS) |
||||
node := &ast.SwitchStatement{ |
||||
Discriminant: self.parseExpression(), |
||||
Default: -1, |
||||
} |
||||
self.expect(token.RIGHT_PARENTHESIS) |
||||
|
||||
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) |
||||
} |
||||
|
||||
return node |
||||
} |
||||
|
||||
func (self *_parser) parseWithStatement() ast.Statement { |
||||
self.expect(token.WITH) |
||||
self.expect(token.LEFT_PARENTHESIS) |
||||
node := &ast.WithStatement{ |
||||
Object: self.parseExpression(), |
||||
} |
||||
self.expect(token.RIGHT_PARENTHESIS) |
||||
|
||||
node.Body = self.parseStatement() |
||||
|
||||
return node |
||||
} |
||||
|
||||
func (self *_parser) parseCaseStatement() *ast.CaseStatement { |
||||
|
||||
node := &ast.CaseStatement{ |
||||
Case: self.idx, |
||||
} |
||||
if self.token == token.DEFAULT { |
||||
self.next() |
||||
} else { |
||||
self.expect(token.CASE) |
||||
node.Test = self.parseExpression() |
||||
} |
||||
self.expect(token.COLON) |
||||
|
||||
for { |
||||
if self.token == token.EOF || |
||||
self.token == token.RIGHT_BRACE || |
||||
self.token == token.CASE || |
||||
self.token == token.DEFAULT { |
||||
break |
||||
} |
||||
node.Consequent = append(node.Consequent, self.parseStatement()) |
||||
|
||||
} |
||||
|
||||
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) |
||||
|
||||
return &ast.ForInStatement{ |
||||
Into: into, |
||||
Source: source, |
||||
Body: self.parseIterationStatement(), |
||||
} |
||||
} |
||||
|
||||
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() |
||||
} |
||||
self.expect(token.SEMICOLON) |
||||
|
||||
if self.token != token.RIGHT_PARENTHESIS { |
||||
update = self.parseExpression() |
||||
} |
||||
self.expect(token.RIGHT_PARENTHESIS) |
||||
|
||||
return &ast.ForStatement{ |
||||
Initializer: initializer, |
||||
Test: test, |
||||
Update: update, |
||||
Body: self.parseIterationStatement(), |
||||
} |
||||
} |
||||
|
||||
func (self *_parser) parseForOrForInStatement() ast.Statement { |
||||
idx := self.expect(token.FOR) |
||||
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 |
||||
self.next() |
||||
list := self.parseVariableDeclarationList(var_) |
||||
if len(list) == 1 && self.token == token.IN { |
||||
self.next() // in
|
||||
forIn = true |
||||
left = []ast.Expression{list[0]} // There is only one declaration
|
||||
} else { |
||||
left = list |
||||
} |
||||
} 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} |
||||
} |
||||
return self.parseForIn(left[0]) |
||||
} |
||||
|
||||
self.expect(token.SEMICOLON) |
||||
return self.parseFor(&ast.SequenceExpression{Sequence: left}) |
||||
} |
||||
|
||||
func (self *_parser) parseVariableStatement() *ast.VariableStatement { |
||||
|
||||
idx := self.expect(token.VAR) |
||||
|
||||
list := self.parseVariableDeclarationList(idx) |
||||
self.semicolon() |
||||
|
||||
return &ast.VariableStatement{ |
||||
Var: idx, |
||||
List: list, |
||||
} |
||||
} |
||||
|
||||
func (self *_parser) parseDoWhileStatement() ast.Statement { |
||||
inIteration := self.scope.inIteration |
||||
self.scope.inIteration = true |
||||
defer func() { |
||||
self.scope.inIteration = inIteration |
||||
}() |
||||
|
||||
self.expect(token.DO) |
||||
node := &ast.DoWhileStatement{} |
||||
if self.token == token.LEFT_BRACE { |
||||
node.Body = self.parseBlockStatement() |
||||
} else { |
||||
node.Body = self.parseStatement() |
||||
} |
||||
|
||||
self.expect(token.WHILE) |
||||
self.expect(token.LEFT_PARENTHESIS) |
||||
node.Test = self.parseExpression() |
||||
self.expect(token.RIGHT_PARENTHESIS) |
||||
|
||||
return node |
||||
} |
||||
|
||||
func (self *_parser) parseWhileStatement() ast.Statement { |
||||
self.expect(token.WHILE) |
||||
self.expect(token.LEFT_PARENTHESIS) |
||||
node := &ast.WhileStatement{ |
||||
Test: self.parseExpression(), |
||||
} |
||||
self.expect(token.RIGHT_PARENTHESIS) |
||||
node.Body = self.parseIterationStatement() |
||||
|
||||
return node |
||||
} |
||||
|
||||
func (self *_parser) parseIfStatement() ast.Statement { |
||||
self.expect(token.IF) |
||||
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() |
||||
} |
||||
|
||||
return node |
||||
} |
||||
|
||||
func (self *_parser) parseSourceElement() ast.Statement { |
||||
return self.parseStatement() |
||||
} |
||||
|
||||
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, |
||||
} |
||||
} |
||||
|
||||
func (self *_parser) parseBreakStatement() ast.Statement { |
||||
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 |
||||
} |
||||
return &ast.BranchStatement{ |
||||
Idx: idx, |
||||
Token: token.BREAK, |
||||
} |
||||
} |
||||
|
||||
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() |
||||
return &ast.BranchStatement{ |
||||
Idx: idx, |
||||
Token: token.BREAK, |
||||
Label: identifier, |
||||
} |
||||
} |
||||
|
||||
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() |
||||
} |
||||
} |
@ -1,51 +0,0 @@ |
||||
# registry |
||||
-- |
||||
import "github.com/robertkrimen/otto/registry" |
||||
|
||||
Package registry is an expirmental package to facillitate altering the otto |
||||
runtime via import. |
||||
|
||||
This interface can change at any time. |
||||
|
||||
## Usage |
||||
|
||||
#### func Apply |
||||
|
||||
```go |
||||
func Apply(callback func(Entry)) |
||||
``` |
||||
|
||||
#### type Entry |
||||
|
||||
```go |
||||
type Entry struct { |
||||
} |
||||
``` |
||||
|
||||
|
||||
#### func Register |
||||
|
||||
```go |
||||
func Register(source func() string) *Entry |
||||
``` |
||||
|
||||
#### func (*Entry) Disable |
||||
|
||||
```go |
||||
func (self *Entry) Disable() |
||||
``` |
||||
|
||||
#### func (*Entry) Enable |
||||
|
||||
```go |
||||
func (self *Entry) Enable() |
||||
``` |
||||
|
||||
#### func (Entry) Source |
||||
|
||||
```go |
||||
func (self Entry) Source() string |
||||
``` |
||||
|
||||
-- |
||||
**godocdown** http://github.com/robertkrimen/godocdown |
@ -1,47 +0,0 @@ |
||||
/* |
||||
Package registry is an expirmental package to facillitate altering the otto runtime via import. |
||||
|
||||
This interface can change at any time. |
||||
*/ |
||||
package registry |
||||
|
||||
var registry []*Entry = make([]*Entry, 0) |
||||
|
||||
type Entry struct { |
||||
active bool |
||||
source func() string |
||||
} |
||||
|
||||
func newEntry(source func() string) *Entry { |
||||
return &Entry{ |
||||
active: true, |
||||
source: source, |
||||
} |
||||
} |
||||
|
||||
func (self *Entry) Enable() { |
||||
self.active = true |
||||
} |
||||
|
||||
func (self *Entry) Disable() { |
||||
self.active = false |
||||
} |
||||
|
||||
func (self Entry) Source() string { |
||||
return self.source() |
||||
} |
||||
|
||||
func Apply(callback func(Entry)) { |
||||
for _, entry := range registry { |
||||
if !entry.active { |
||||
continue |
||||
} |
||||
callback(*entry) |
||||
} |
||||
} |
||||
|
||||
func Register(source func() string) *Entry { |
||||
entry := newEntry(source) |
||||
registry = append(registry, entry) |
||||
return entry |
||||
} |
@ -1,2 +0,0 @@ |
||||
token_const.go: tokenfmt |
||||
./$^ | gofmt > $@
|
@ -1,171 +0,0 @@ |
||||
# token |
||||
-- |
||||
import "github.com/robertkrimen/otto/token" |
||||
|
||||
Package token defines constants representing the lexical tokens of JavaScript |
||||
(ECMA5). |
||||
|
||||
## Usage |
||||
|
||||
```go |
||||
const ( |
||||
ILLEGAL |
||||
EOF |
||||
COMMENT |
||||
KEYWORD |
||||
|
||||
STRING |
||||
BOOLEAN |
||||
NULL |
||||
NUMBER |
||||
IDENTIFIER |
||||
|
||||
PLUS // + |
||||
MINUS // - |
||||
MULTIPLY // * |
||||
SLASH // / |
||||
REMAINDER // % |
||||
|
||||
AND // & |
||||
OR // | |
||||
EXCLUSIVE_OR // ^ |
||||
SHIFT_LEFT // << |
||||
SHIFT_RIGHT // >> |
||||
UNSIGNED_SHIFT_RIGHT // >>> |
||||
AND_NOT // &^ |
||||
|
||||
ADD_ASSIGN // += |
||||
SUBTRACT_ASSIGN // -= |
||||
MULTIPLY_ASSIGN // *= |
||||
QUOTIENT_ASSIGN // /= |
||||
REMAINDER_ASSIGN // %= |
||||
|
||||
AND_ASSIGN // &= |
||||
OR_ASSIGN // |= |
||||
EXCLUSIVE_OR_ASSIGN // ^= |
||||
SHIFT_LEFT_ASSIGN // <<= |
||||
SHIFT_RIGHT_ASSIGN // >>= |
||||
UNSIGNED_SHIFT_RIGHT_ASSIGN // >>>= |
||||
AND_NOT_ASSIGN // &^= |
||||
|
||||
LOGICAL_AND // && |
||||
LOGICAL_OR // || |
||||
INCREMENT // ++ |
||||
DECREMENT // -- |
||||
|
||||
EQUAL // == |
||||
STRICT_EQUAL // === |
||||
LESS // < |
||||
GREATER // > |
||||
ASSIGN // = |
||||
NOT // ! |
||||
|
||||
BITWISE_NOT // ~ |
||||
|
||||
NOT_EQUAL // != |
||||
STRICT_NOT_EQUAL // !== |
||||
LESS_OR_EQUAL // <= |
||||
GREATER_OR_EQUAL // <= |
||||
|
||||
LEFT_PARENTHESIS // ( |
||||
LEFT_BRACKET // [ |
||||
LEFT_BRACE // { |
||||
COMMA // , |
||||
PERIOD // . |
||||
|
||||
RIGHT_PARENTHESIS // ) |
||||
RIGHT_BRACKET // ] |
||||
RIGHT_BRACE // } |
||||
SEMICOLON // ; |
||||
COLON // : |
||||
QUESTION_MARK // ? |
||||
|
||||
IF |
||||
IN |
||||
DO |
||||
|
||||
VAR |
||||
FOR |
||||
NEW |
||||
TRY |
||||
|
||||
THIS |
||||
ELSE |
||||
CASE |
||||
VOID |
||||
WITH |
||||
|
||||
WHILE |
||||
BREAK |
||||
CATCH |
||||
THROW |
||||
|
||||
RETURN |
||||
TYPEOF |
||||
DELETE |
||||
SWITCH |
||||
|
||||
DEFAULT |
||||
FINALLY |
||||
|
||||
FUNCTION |
||||
CONTINUE |
||||
DEBUGGER |
||||
|
||||
INSTANCEOF |
||||
) |
||||
``` |
||||
|
||||
#### type Token |
||||
|
||||
```go |
||||
type Token int |
||||
``` |
||||
|
||||
Token is the set of lexical tokens in JavaScript (ECMA5). |
||||
|
||||
#### func IsKeyword |
||||
|
||||
```go |
||||
func IsKeyword(literal string) (Token, bool) |
||||
``` |
||||
IsKeyword returns the keyword token if literal is a keyword, a KEYWORD token if |
||||
the literal is a future keyword (const, let, class, super, ...), or 0 if the |
||||
literal is not a keyword. |
||||
|
||||
If the literal is a keyword, IsKeyword returns a second value indicating if the |
||||
literal is considered a future keyword in strict-mode only. |
||||
|
||||
7.6.1.2 Future Reserved Words: |
||||
|
||||
const |
||||
class |
||||
enum |
||||
export |
||||
extends |
||||
import |
||||
super |
||||
|
||||
7.6.1.2 Future Reserved Words (strict): |
||||
|
||||
implements |
||||
interface |
||||
let |
||||
package |
||||
private |
||||
protected |
||||
public |
||||
static |
||||
|
||||
#### func (Token) String |
||||
|
||||
```go |
||||
func (tkn Token) String() string |
||||
``` |
||||
String returns the string corresponding to the token. For operators, delimiters, |
||||
and keywords the string is the actual token string (e.g., for the token PLUS, |
||||
the String() is "+"). For all other tokens the string corresponds to the token |
||||
name (e.g. for the token IDENTIFIER, the string is "IDENTIFIER"). |
||||
|
||||
-- |
||||
**godocdown** http://github.com/robertkrimen/godocdown |
@ -1,116 +0,0 @@ |
||||
// Package token defines constants representing the lexical tokens of JavaScript (ECMA5).
|
||||
package token |
||||
|
||||
import ( |
||||
"strconv" |
||||
) |
||||
|
||||
// Token is the set of lexical tokens in JavaScript (ECMA5).
|
||||
type Token int |
||||
|
||||
// String returns the string corresponding to the token.
|
||||
// For operators, delimiters, and keywords the string is the actual
|
||||
// token string (e.g., for the token PLUS, the String() is
|
||||
// "+"). For all other tokens the string corresponds to the token
|
||||
// name (e.g. for the token IDENTIFIER, the string is "IDENTIFIER").
|
||||
//
|
||||
func (tkn Token) String() string { |
||||
if 0 == tkn { |
||||
return "UNKNOWN" |
||||
} |
||||
if tkn < Token(len(token2string)) { |
||||
return token2string[tkn] |
||||
} |
||||
return "token(" + strconv.Itoa(int(tkn)) + ")" |
||||
} |
||||
|
||||
// This is not used for anything
|
||||
func (tkn Token) precedence(in bool) int { |
||||
|
||||
switch tkn { |
||||
case LOGICAL_OR: |
||||
return 1 |
||||
|
||||
case LOGICAL_AND: |
||||
return 2 |
||||
|
||||
case OR, OR_ASSIGN: |
||||
return 3 |
||||
|
||||
case EXCLUSIVE_OR: |
||||
return 4 |
||||
|
||||
case AND, AND_ASSIGN, AND_NOT, AND_NOT_ASSIGN: |
||||
return 5 |
||||
|
||||
case EQUAL, |
||||
NOT_EQUAL, |
||||
STRICT_EQUAL, |
||||
STRICT_NOT_EQUAL: |
||||
return 6 |
||||
|
||||
case LESS, GREATER, LESS_OR_EQUAL, GREATER_OR_EQUAL, INSTANCEOF: |
||||
return 7 |
||||
|
||||
case IN: |
||||
if in { |
||||
return 7 |
||||
} |
||||
return 0 |
||||
|
||||
case SHIFT_LEFT, SHIFT_RIGHT, UNSIGNED_SHIFT_RIGHT: |
||||
fallthrough |
||||
case SHIFT_LEFT_ASSIGN, SHIFT_RIGHT_ASSIGN, UNSIGNED_SHIFT_RIGHT_ASSIGN: |
||||
return 8 |
||||
|
||||
case PLUS, MINUS, ADD_ASSIGN, SUBTRACT_ASSIGN: |
||||
return 9 |
||||
|
||||
case MULTIPLY, SLASH, REMAINDER, MULTIPLY_ASSIGN, QUOTIENT_ASSIGN, REMAINDER_ASSIGN: |
||||
return 11 |
||||
} |
||||
return 0 |
||||
} |
||||
|
||||
type _keyword struct { |
||||
token Token |
||||
futureKeyword bool |
||||
strict bool |
||||
} |
||||
|
||||
// IsKeyword returns the keyword token if literal is a keyword, a KEYWORD token
|
||||
// if the literal is a future keyword (const, let, class, super, ...), or 0 if the literal is not a keyword.
|
||||
//
|
||||
// If the literal is a keyword, IsKeyword returns a second value indicating if the literal
|
||||
// is considered a future keyword in strict-mode only.
|
||||
//
|
||||
// 7.6.1.2 Future Reserved Words:
|
||||
//
|
||||
// const
|
||||
// class
|
||||
// enum
|
||||
// export
|
||||
// extends
|
||||
// import
|
||||
// super
|
||||
//
|
||||
// 7.6.1.2 Future Reserved Words (strict):
|
||||
//
|
||||
// implements
|
||||
// interface
|
||||
// let
|
||||
// package
|
||||
// private
|
||||
// protected
|
||||
// public
|
||||
// static
|
||||
//
|
||||
func IsKeyword(literal string) (Token, bool) { |
||||
if keyword, exists := keywordTable[literal]; exists { |
||||
if keyword.futureKeyword { |
||||
return KEYWORD, keyword.strict |
||||
} |
||||
return keyword.token, false |
||||
} |
||||
return 0, false |
||||
} |
@ -1,349 +0,0 @@ |
||||
package token |
||||
|
||||
const ( |
||||
_ Token = iota |
||||
|
||||
ILLEGAL |
||||
EOF |
||||
COMMENT |
||||
KEYWORD |
||||
|
||||
STRING |
||||
BOOLEAN |
||||
NULL |
||||
NUMBER |
||||
IDENTIFIER |
||||
|
||||
PLUS // +
|
||||
MINUS // -
|
||||
MULTIPLY // *
|
||||
SLASH // /
|
||||
REMAINDER // %
|
||||
|
||||
AND // &
|
||||
OR // |
|
||||
EXCLUSIVE_OR // ^
|
||||
SHIFT_LEFT // <<
|
||||
SHIFT_RIGHT // >>
|
||||
UNSIGNED_SHIFT_RIGHT // >>>
|
||||
AND_NOT // &^
|
||||
|
||||
ADD_ASSIGN // +=
|
||||
SUBTRACT_ASSIGN // -=
|
||||
MULTIPLY_ASSIGN // *=
|
||||
QUOTIENT_ASSIGN // /=
|
||||
REMAINDER_ASSIGN // %=
|
||||
|
||||
AND_ASSIGN // &=
|
||||
OR_ASSIGN // |=
|
||||
EXCLUSIVE_OR_ASSIGN // ^=
|
||||
SHIFT_LEFT_ASSIGN // <<=
|
||||
SHIFT_RIGHT_ASSIGN // >>=
|
||||
UNSIGNED_SHIFT_RIGHT_ASSIGN // >>>=
|
||||
AND_NOT_ASSIGN // &^=
|
||||
|
||||
LOGICAL_AND // &&
|
||||
LOGICAL_OR // ||
|
||||
INCREMENT // ++
|
||||
DECREMENT // --
|
||||
|
||||
EQUAL // ==
|
||||
STRICT_EQUAL // ===
|
||||
LESS // <
|
||||
GREATER // >
|
||||
ASSIGN // =
|
||||
NOT // !
|
||||
|
||||
BITWISE_NOT // ~
|
||||
|
||||
NOT_EQUAL // !=
|
||||
STRICT_NOT_EQUAL // !==
|
||||
LESS_OR_EQUAL // <=
|
||||
GREATER_OR_EQUAL // <=
|
||||
|
||||
LEFT_PARENTHESIS // (
|
||||
LEFT_BRACKET // [
|
||||
LEFT_BRACE // {
|
||||
COMMA // ,
|
||||
PERIOD // .
|
||||
|
||||
RIGHT_PARENTHESIS // )
|
||||
RIGHT_BRACKET // ]
|
||||
RIGHT_BRACE // }
|
||||
SEMICOLON // ;
|
||||
COLON // :
|
||||
QUESTION_MARK // ?
|
||||
|
||||
firstKeyword |
||||
IF |
||||
IN |
||||
DO |
||||
|
||||
VAR |
||||
FOR |
||||
NEW |
||||
TRY |
||||
|
||||
THIS |
||||
ELSE |
||||
CASE |
||||
VOID |
||||
WITH |
||||
|
||||
WHILE |
||||
BREAK |
||||
CATCH |
||||
THROW |
||||
|
||||
RETURN |
||||
TYPEOF |
||||
DELETE |
||||
SWITCH |
||||
|
||||
DEFAULT |
||||
FINALLY |
||||
|
||||
FUNCTION |
||||
CONTINUE |
||||
DEBUGGER |
||||
|
||||
INSTANCEOF |
||||
lastKeyword |
||||
) |
||||
|
||||
var token2string = [...]string{ |
||||
ILLEGAL: "ILLEGAL", |
||||
EOF: "EOF", |
||||
COMMENT: "COMMENT", |
||||
KEYWORD: "KEYWORD", |
||||
STRING: "STRING", |
||||
BOOLEAN: "BOOLEAN", |
||||
NULL: "NULL", |
||||
NUMBER: "NUMBER", |
||||
IDENTIFIER: "IDENTIFIER", |
||||
PLUS: "+", |
||||
MINUS: "-", |
||||
MULTIPLY: "*", |
||||
SLASH: "/", |
||||
REMAINDER: "%", |
||||
AND: "&", |
||||
OR: "|", |
||||
EXCLUSIVE_OR: "^", |
||||
SHIFT_LEFT: "<<", |
||||
SHIFT_RIGHT: ">>", |
||||
UNSIGNED_SHIFT_RIGHT: ">>>", |
||||
AND_NOT: "&^", |
||||
ADD_ASSIGN: "+=", |
||||
SUBTRACT_ASSIGN: "-=", |
||||
MULTIPLY_ASSIGN: "*=", |
||||
QUOTIENT_ASSIGN: "/=", |
||||
REMAINDER_ASSIGN: "%=", |
||||
AND_ASSIGN: "&=", |
||||
OR_ASSIGN: "|=", |
||||
EXCLUSIVE_OR_ASSIGN: "^=", |
||||
SHIFT_LEFT_ASSIGN: "<<=", |
||||
SHIFT_RIGHT_ASSIGN: ">>=", |
||||
UNSIGNED_SHIFT_RIGHT_ASSIGN: ">>>=", |
||||
AND_NOT_ASSIGN: "&^=", |
||||
LOGICAL_AND: "&&", |
||||
LOGICAL_OR: "||", |
||||
INCREMENT: "++", |
||||
DECREMENT: "--", |
||||
EQUAL: "==", |
||||
STRICT_EQUAL: "===", |
||||
LESS: "<", |
||||
GREATER: ">", |
||||
ASSIGN: "=", |
||||
NOT: "!", |
||||
BITWISE_NOT: "~", |
||||
NOT_EQUAL: "!=", |
||||
STRICT_NOT_EQUAL: "!==", |
||||
LESS_OR_EQUAL: "<=", |
||||
GREATER_OR_EQUAL: "<=", |
||||
LEFT_PARENTHESIS: "(", |
||||
LEFT_BRACKET: "[", |
||||
LEFT_BRACE: "{", |
||||
COMMA: ",", |
||||
PERIOD: ".", |
||||
RIGHT_PARENTHESIS: ")", |
||||
RIGHT_BRACKET: "]", |
||||
RIGHT_BRACE: "}", |
||||
SEMICOLON: ";", |
||||
COLON: ":", |
||||
QUESTION_MARK: "?", |
||||
IF: "if", |
||||
IN: "in", |
||||
DO: "do", |
||||
VAR: "var", |
||||
FOR: "for", |
||||
NEW: "new", |
||||
TRY: "try", |
||||
THIS: "this", |
||||
ELSE: "else", |
||||
CASE: "case", |
||||
VOID: "void", |
||||
WITH: "with", |
||||
WHILE: "while", |
||||
BREAK: "break", |
||||
CATCH: "catch", |
||||
THROW: "throw", |
||||
RETURN: "return", |
||||
TYPEOF: "typeof", |
||||
DELETE: "delete", |
||||
SWITCH: "switch", |
||||
DEFAULT: "default", |
||||
FINALLY: "finally", |
||||
FUNCTION: "function", |
||||
CONTINUE: "continue", |
||||
DEBUGGER: "debugger", |
||||
INSTANCEOF: "instanceof", |
||||
} |
||||
|
||||
var keywordTable = map[string]_keyword{ |
||||
"if": _keyword{ |
||||
token: IF, |
||||
}, |
||||
"in": _keyword{ |
||||
token: IN, |
||||
}, |
||||
"do": _keyword{ |
||||
token: DO, |
||||
}, |
||||
"var": _keyword{ |
||||
token: VAR, |
||||
}, |
||||
"for": _keyword{ |
||||
token: FOR, |
||||
}, |
||||
"new": _keyword{ |
||||
token: NEW, |
||||
}, |
||||
"try": _keyword{ |
||||
token: TRY, |
||||
}, |
||||
"this": _keyword{ |
||||
token: THIS, |
||||
}, |
||||
"else": _keyword{ |
||||
token: ELSE, |
||||
}, |
||||
"case": _keyword{ |
||||
token: CASE, |
||||
}, |
||||
"void": _keyword{ |
||||
token: VOID, |
||||
}, |
||||
"with": _keyword{ |
||||
token: WITH, |
||||
}, |
||||
"while": _keyword{ |
||||
token: WHILE, |
||||
}, |
||||
"break": _keyword{ |
||||
token: BREAK, |
||||
}, |
||||
"catch": _keyword{ |
||||
token: CATCH, |
||||
}, |
||||
"throw": _keyword{ |
||||
token: THROW, |
||||
}, |
||||
"return": _keyword{ |
||||
token: RETURN, |
||||
}, |
||||
"typeof": _keyword{ |
||||
token: TYPEOF, |
||||
}, |
||||
"delete": _keyword{ |
||||
token: DELETE, |
||||
}, |
||||
"switch": _keyword{ |
||||
token: SWITCH, |
||||
}, |
||||
"default": _keyword{ |
||||
token: DEFAULT, |
||||
}, |
||||
"finally": _keyword{ |
||||
token: FINALLY, |
||||
}, |
||||
"function": _keyword{ |
||||
token: FUNCTION, |
||||
}, |
||||
"continue": _keyword{ |
||||
token: CONTINUE, |
||||
}, |
||||
"debugger": _keyword{ |
||||
token: DEBUGGER, |
||||
}, |
||||
"instanceof": _keyword{ |
||||
token: INSTANCEOF, |
||||
}, |
||||
"const": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
}, |
||||
"class": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
}, |
||||
"enum": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
}, |
||||
"export": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
}, |
||||
"extends": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
}, |
||||
"import": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
}, |
||||
"super": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
}, |
||||
"implements": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
strict: true, |
||||
}, |
||||
"interface": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
strict: true, |
||||
}, |
||||
"let": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
strict: true, |
||||
}, |
||||
"package": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
strict: true, |
||||
}, |
||||
"private": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
strict: true, |
||||
}, |
||||
"protected": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
strict: true, |
||||
}, |
||||
"public": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
strict: true, |
||||
}, |
||||
"static": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
strict: true, |
||||
}, |
||||
} |
@ -1,222 +0,0 @@ |
||||
#!/usr/bin/env perl |
||||
|
||||
use strict; |
||||
use warnings; |
||||
|
||||
my (%token, @order, @keywords); |
||||
|
||||
{ |
||||
my $keywords; |
||||
my @const; |
||||
push @const, <<_END_; |
||||
package token |
||||
|
||||
const( |
||||
_ Token = iota |
||||
_END_ |
||||
|
||||
for (split m/\n/, <<_END_) { |
||||
ILLEGAL |
||||
EOF |
||||
COMMENT |
||||
KEYWORD |
||||
|
||||
STRING |
||||
BOOLEAN |
||||
NULL |
||||
NUMBER |
||||
IDENTIFIER |
||||
|
||||
PLUS + |
||||
MINUS - |
||||
MULTIPLY * |
||||
SLASH / |
||||
REMAINDER % |
||||
|
||||
AND & |
||||
OR | |
||||
EXCLUSIVE_OR ^ |
||||
SHIFT_LEFT << |
||||
SHIFT_RIGHT >> |
||||
UNSIGNED_SHIFT_RIGHT >>> |
||||
AND_NOT &^ |
||||
|
||||
ADD_ASSIGN += |
||||
SUBTRACT_ASSIGN -= |
||||
MULTIPLY_ASSIGN *= |
||||
QUOTIENT_ASSIGN /= |
||||
REMAINDER_ASSIGN %= |
||||
|
||||
AND_ASSIGN &= |
||||
OR_ASSIGN |= |
||||
EXCLUSIVE_OR_ASSIGN ^= |
||||
SHIFT_LEFT_ASSIGN <<= |
||||
SHIFT_RIGHT_ASSIGN >>= |
||||
UNSIGNED_SHIFT_RIGHT_ASSIGN >>>= |
||||
AND_NOT_ASSIGN &^= |
||||
|
||||
LOGICAL_AND && |
||||
LOGICAL_OR || |
||||
INCREMENT ++ |
||||
DECREMENT -- |
||||
|
||||
EQUAL == |
||||
STRICT_EQUAL === |
||||
LESS < |
||||
GREATER > |
||||
ASSIGN = |
||||
NOT ! |
||||
|
||||
BITWISE_NOT ~ |
||||
|
||||
NOT_EQUAL != |
||||
STRICT_NOT_EQUAL !== |
||||
LESS_OR_EQUAL <= |
||||
GREATER_OR_EQUAL <= |
||||
|
||||
LEFT_PARENTHESIS ( |
||||
LEFT_BRACKET [ |
||||
LEFT_BRACE { |
||||
COMMA , |
||||
PERIOD . |
||||
|
||||
RIGHT_PARENTHESIS ) |
||||
RIGHT_BRACKET ] |
||||
RIGHT_BRACE } |
||||
SEMICOLON ; |
||||
COLON : |
||||
QUESTION_MARK ? |
||||
|
||||
firstKeyword |
||||
IF |
||||
IN |
||||
DO |
||||
|
||||
VAR |
||||
FOR |
||||
NEW |
||||
TRY |
||||
|
||||
THIS |
||||
ELSE |
||||
CASE |
||||
VOID |
||||
WITH |
||||
|
||||
WHILE |
||||
BREAK |
||||
CATCH |
||||
THROW |
||||
|
||||
RETURN |
||||
TYPEOF |
||||
DELETE |
||||
SWITCH |
||||
|
||||
DEFAULT |
||||
FINALLY |
||||
|
||||
FUNCTION |
||||
CONTINUE |
||||
DEBUGGER |
||||
|
||||
INSTANCEOF |
||||
lastKeyword |
||||
_END_ |
||||
chomp; |
||||
|
||||
next if m/^\s*#/; |
||||
|
||||
my ($name, $symbol) = m/(\w+)\s*(\S+)?/; |
||||
|
||||
if (defined $symbol) { |
||||
push @order, $name; |
||||
push @const, "$name // $symbol"; |
||||
$token{$name} = $symbol; |
||||
} elsif (defined $name) { |
||||
$keywords ||= $name eq 'firstKeyword'; |
||||
|
||||
push @const, $name; |
||||
#$const[-1] .= " Token = iota" if 2 == @const; |
||||
if ($name =~ m/^([A-Z]+)/) { |
||||
push @keywords, $name if $keywords; |
||||
push @order, $name; |
||||
if ($token{SEMICOLON}) { |
||||
$token{$name} = lc $1; |
||||
} else { |
||||
$token{$name} = $name; |
||||
} |
||||
} |
||||
} else { |
||||
push @const, ""; |
||||
} |
||||
|
||||
} |
||||
push @const, ")"; |
||||
print join "\n", @const, ""; |
||||
} |
||||
|
||||
{ |
||||
print <<_END_; |
||||
|
||||
var token2string = [...]string{ |
||||
_END_ |
||||
for my $name (@order) { |
||||
print "$name: \"$token{$name}\",\n"; |
||||
} |
||||
print <<_END_; |
||||
} |
||||
_END_ |
||||
|
||||
print <<_END_; |
||||
|
||||
var keywordTable = map[string]_keyword{ |
||||
_END_ |
||||
for my $name (@keywords) { |
||||
print <<_END_ |
||||
"@{[ lc $name ]}": _keyword{ |
||||
token: $name, |
||||
}, |
||||
_END_ |
||||
} |
||||
|
||||
for my $name (qw/ |
||||
const |
||||
class |
||||
enum |
||||
export |
||||
extends |
||||
import |
||||
super |
||||
/) { |
||||
print <<_END_ |
||||
"$name": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
}, |
||||
_END_ |
||||
} |
||||
|
||||
for my $name (qw/ |
||||
implements |
||||
interface |
||||
let |
||||
package |
||||
private |
||||
protected |
||||
public |
||||
static |
||||
/) { |
||||
print <<_END_ |
||||
"$name": _keyword{ |
||||
token: KEYWORD, |
||||
futureKeyword: true, |
||||
strict: true, |
||||
}, |
||||
_END_ |
||||
} |
||||
|
||||
print <<_END_; |
||||
} |
||||
_END_ |
||||
} |
@ -1,9 +0,0 @@ |
||||
package otto |
||||
|
||||
func (runtime *_runtime) newErrorObject(message Value) *_object { |
||||
self := runtime.newClassObject("Error") |
||||
if message.IsDefined() { |
||||
self.defineProperty("message", toValue_string(toString(message)), 0111, false) |
||||
} |
||||
return self |
||||
} |
@ -1,276 +0,0 @@ |
||||
package otto |
||||
|
||||
import ( |
||||
"fmt" |
||||
) |
||||
|
||||
type _functionObject struct { |
||||
call _callFunction |
||||
construct _constructFunction |
||||
} |
||||
|
||||
func (self _functionObject) source(object *_object) string { |
||||
return self.call.Source(object) |
||||
} |
||||
|
||||
func (self0 _functionObject) clone(clone *_clone) _functionObject { |
||||
return _functionObject{ |
||||
clone.callFunction(self0.call), |
||||
self0.construct, |
||||
} |
||||
} |
||||
|
||||
func (runtime *_runtime) newNativeFunctionObject(name string, native _nativeFunction, length int) *_object { |
||||
self := runtime.newClassObject("Function") |
||||
self.value = _functionObject{ |
||||
call: newNativeCallFunction(native), |
||||
construct: defaultConstructFunction, |
||||
} |
||||
self.defineProperty("length", toValue_int(length), 0000, false) |
||||
return self |
||||
} |
||||
|
||||
func (runtime *_runtime) newBoundFunctionObject(target *_object, this Value, argumentList []Value) *_object { |
||||
self := runtime.newClassObject("Function") |
||||
self.value = _functionObject{ |
||||
call: newBoundCallFunction(target, this, argumentList), |
||||
construct: newBoundConstructFunction(target), |
||||
} |
||||
length := int(toInt32(target.get("length"))) |
||||
length -= len(argumentList) |
||||
if length < 0 { |
||||
length = 0 |
||||
} |
||||
self.defineProperty("length", toValue_int(length), 0000, false) |
||||
self.defineProperty("caller", UndefinedValue(), 0000, false) // TODO Should throw a TypeError
|
||||
self.defineProperty("arguments", UndefinedValue(), 0000, false) // TODO Should throw a TypeError
|
||||
return self |
||||
} |
||||
|
||||
func (runtime *_runtime) newBoundFunction(target *_object, this Value, argumentList []Value) *_object { |
||||
self := runtime.newBoundFunctionObject(target, this, argumentList) |
||||
self.prototype = runtime.Global.FunctionPrototype |
||||
prototype := runtime.newObject() |
||||
self.defineProperty("prototype", toValue_object(prototype), 0100, false) |
||||
prototype.defineProperty("constructor", toValue_object(self), 0100, false) |
||||
return self |
||||
} |
||||
|
||||
func (self *_object) functionValue() _functionObject { |
||||
value, _ := self.value.(_functionObject) |
||||
return value |
||||
} |
||||
|
||||
func (self *_object) Call(this Value, argumentList ...interface{}) Value { |
||||
if self.functionValue().call == nil { |
||||
panic(newTypeError("%v is not a function", toValue_object(self))) |
||||
} |
||||
return self.runtime.Call(self, this, self.runtime.toValueArray(argumentList...), false) |
||||
// ... -> runtime -> self.Function.Call.Dispatch -> ...
|
||||
} |
||||
|
||||
func (self *_object) Construct(this Value, argumentList ...interface{}) Value { |
||||
function := self.functionValue() |
||||
if function.call == nil { |
||||
panic(newTypeError("%v is not a function", toValue_object(self))) |
||||
} |
||||
if function.construct == nil { |
||||
panic(newTypeError("%v is not a constructor", toValue_object(self))) |
||||
} |
||||
return function.construct(self, this, self.runtime.toValueArray(argumentList...)) |
||||
} |
||||
|
||||
func defaultConstructFunction(self *_object, this Value, argumentList []Value) Value { |
||||
newObject := self.runtime.newObject() |
||||
newObject.class = "Object" |
||||
prototypeValue := self.get("prototype") |
||||
if !prototypeValue.IsObject() { |
||||
prototypeValue = toValue_object(self.runtime.Global.ObjectPrototype) |
||||
} |
||||
newObject.prototype = prototypeValue._object() |
||||
newObjectValue := toValue_object(newObject) |
||||
result := self.Call(newObjectValue, argumentList) |
||||
if result.IsObject() { |
||||
return result |
||||
} |
||||
return newObjectValue |
||||
} |
||||
|
||||
func (self *_object) callGet(this Value) Value { |
||||
return self.runtime.Call(self, this, []Value(nil), false) |
||||
} |
||||
|
||||
func (self *_object) callSet(this Value, value Value) { |
||||
self.runtime.Call(self, this, []Value{value}, false) |
||||
} |
||||
|
||||
// 15.3.5.3
|
||||
func (self *_object) HasInstance(of Value) bool { |
||||
if self.functionValue().call == nil { |
||||
// We should not have a HasInstance method
|
||||
panic(newTypeError()) |
||||
} |
||||
if !of.IsObject() { |
||||
return false |
||||
} |
||||
prototype := self.get("prototype") |
||||
if !prototype.IsObject() { |
||||
panic(newTypeError()) |
||||
} |
||||
prototypeObject := prototype._object() |
||||
|
||||
value := of._object().prototype |
||||
for value != nil { |
||||
if value == prototypeObject { |
||||
return true |
||||
} |
||||
value = value.prototype |
||||
} |
||||
return false |
||||
} |
||||
|
||||
type _nativeFunction func(FunctionCall) Value |
||||
|
||||
// _constructFunction
|
||||
type _constructFunction func(*_object, Value, []Value) Value |
||||
|
||||
// _callFunction
|
||||
type _callFunction interface { |
||||
Dispatch(*_object, *_functionEnvironment, *_runtime, Value, []Value, bool) Value |
||||
Source(*_object) string |
||||
ScopeEnvironment() _environment |
||||
clone(clone *_clone) _callFunction |
||||
} |
||||
|
||||
// _nativeCallFunction
|
||||
type _nativeCallFunction struct { |
||||
name string |
||||
function _nativeFunction |
||||
} |
||||
|
||||
func newNativeCallFunction(native _nativeFunction) _nativeCallFunction { |
||||
return _nativeCallFunction{"", native} |
||||
} |
||||
|
||||
func (self _nativeCallFunction) Dispatch(_ *_object, _ *_functionEnvironment, runtime *_runtime, this Value, argumentList []Value, evalHint bool) Value { |
||||
return self.function(FunctionCall{ |
||||
runtime: runtime, |
||||
evalHint: evalHint, |
||||
|
||||
This: this, |
||||
ArgumentList: argumentList, |
||||
Otto: runtime.Otto, |
||||
}) |
||||
} |
||||
|
||||
func (self _nativeCallFunction) ScopeEnvironment() _environment { |
||||
return nil |
||||
} |
||||
|
||||
func (self _nativeCallFunction) Source(*_object) string { |
||||
return fmt.Sprintf("function %s() { [native code] }", self.name) |
||||
} |
||||
|
||||
func (self0 _nativeCallFunction) clone(clone *_clone) _callFunction { |
||||
return self0 |
||||
} |
||||
|
||||
// _boundCallFunction
|
||||
type _boundCallFunction struct { |
||||
target *_object |
||||
this Value |
||||
argumentList []Value |
||||
} |
||||
|
||||
func newBoundCallFunction(target *_object, this Value, argumentList []Value) *_boundCallFunction { |
||||
self := &_boundCallFunction{ |
||||
target: target, |
||||
this: this, |
||||
argumentList: argumentList, |
||||
} |
||||
return self |
||||
} |
||||
|
||||
func (self _boundCallFunction) Dispatch(_ *_object, _ *_functionEnvironment, runtime *_runtime, this Value, argumentList []Value, _ bool) Value { |
||||
argumentList = append(self.argumentList, argumentList...) |
||||
return runtime.Call(self.target, self.this, argumentList, false) |
||||
} |
||||
|
||||
func (self _boundCallFunction) ScopeEnvironment() _environment { |
||||
return nil |
||||
} |
||||
|
||||
func (self _boundCallFunction) Source(*_object) string { |
||||
return "" |
||||
} |
||||
|
||||
func (self0 _boundCallFunction) clone(clone *_clone) _callFunction { |
||||
return _boundCallFunction{ |
||||
target: clone.object(self0.target), |
||||
this: clone.value(self0.this), |
||||
argumentList: clone.valueArray(self0.argumentList), |
||||
} |
||||
} |
||||
|
||||
func newBoundConstructFunction(target *_object) _constructFunction { |
||||
// This is not exactly as described in 15.3.4.5.2, we let [[Call]] supply the
|
||||
// bound arguments, etc.
|
||||
return func(self *_object, this Value, argumentList []Value) Value { |
||||
switch value := target.value.(type) { |
||||
case _functionObject: |
||||
return value.construct(self, this, argumentList) |
||||
} |
||||
panic(newTypeError()) |
||||
} |
||||
} |
||||
|
||||
// FunctionCall{}
|
||||
|
||||
// FunctionCall is an encapsulation of a JavaScript function call.
|
||||
type FunctionCall struct { |
||||
runtime *_runtime |
||||
_thisObject *_object |
||||
evalHint bool |
||||
|
||||
This Value |
||||
ArgumentList []Value |
||||
Otto *Otto |
||||
} |
||||
|
||||
// Argument will return the value of the argument at the given index.
|
||||
//
|
||||
// If no such argument exists, undefined is returned.
|
||||
func (self FunctionCall) Argument(index int) Value { |
||||
return valueOfArrayIndex(self.ArgumentList, index) |
||||
} |
||||
|
||||
func (self FunctionCall) getArgument(index int) (Value, bool) { |
||||
return getValueOfArrayIndex(self.ArgumentList, index) |
||||
} |
||||
|
||||
func (self FunctionCall) slice(index int) []Value { |
||||
if index < len(self.ArgumentList) { |
||||
return self.ArgumentList[index:] |
||||
} |
||||
return []Value{} |
||||
} |
||||
|
||||
func (self *FunctionCall) thisObject() *_object { |
||||
if self._thisObject == nil { |
||||
this := self.runtime.GetValue(self.This) // FIXME Is this right?
|
||||
self._thisObject = self.runtime.toObject(this) |
||||
} |
||||
return self._thisObject |
||||
} |
||||
|
||||
func (self *FunctionCall) thisClassObject(class string) *_object { |
||||
thisObject := self.thisObject() |
||||
if thisObject.class != class { |
||||
panic(newTypeError()) |
||||
} |
||||
return self._thisObject |
||||
} |
||||
|
||||
func (self FunctionCall) toObject(value Value) *_object { |
||||
return self.runtime.toObject(value) |
||||
} |
@ -1,157 +0,0 @@ |
||||
package otto |
||||
|
||||
import ( |
||||
"github.com/robertkrimen/otto/ast" |
||||
) |
||||
|
||||
type _reference interface { |
||||
GetBase() interface{} // GetBase
|
||||
GetName() string // GetReferencedName
|
||||
IsStrict() bool // IsStrictReference
|
||||
IsUnresolvable() bool // IsUnresolvableReference
|
||||
IsPropertyReference() bool // IsPropertyReference
|
||||
GetValue() Value // GetValue
|
||||
PutValue(Value) bool // PutValue
|
||||
Delete() bool |
||||
} |
||||
|
||||
// Reference
|
||||
|
||||
type _referenceDefault struct { |
||||
name string |
||||
strict bool |
||||
} |
||||
|
||||
func (self _referenceDefault) GetName() string { |
||||
return self.name |
||||
} |
||||
|
||||
func (self _referenceDefault) IsStrict() bool { |
||||
return self.strict |
||||
} |
||||
|
||||
// PropertyReference
|
||||
|
||||
type _propertyReference struct { |
||||
_referenceDefault |
||||
Base *_object |
||||
} |
||||
|
||||
func newPropertyReference(base *_object, name string, strict bool) *_propertyReference { |
||||
return &_propertyReference{ |
||||
Base: base, |
||||
_referenceDefault: _referenceDefault{ |
||||
name: name, |
||||
strict: strict, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
func (self *_propertyReference) GetBase() interface{} { |
||||
return self.Base |
||||
} |
||||
|
||||
func (self *_propertyReference) IsUnresolvable() bool { |
||||
return self.Base == nil |
||||
} |
||||
|
||||
func (self *_propertyReference) IsPropertyReference() bool { |
||||
return true |
||||
} |
||||
|
||||
func (self *_propertyReference) GetValue() Value { |
||||
if self.Base == nil { |
||||
panic(newReferenceError("notDefined", self.name)) |
||||
} |
||||
return self.Base.get(self.name) |
||||
} |
||||
|
||||
func (self *_propertyReference) PutValue(value Value) bool { |
||||
if self.Base == nil { |
||||
return false |
||||
} |
||||
self.Base.put(self.name, value, self.IsStrict()) |
||||
return true |
||||
} |
||||
|
||||
func (self *_propertyReference) Delete() bool { |
||||
if self.Base == nil { |
||||
// TODO Throw an error if strict
|
||||
return true |
||||
} |
||||
return self.Base.delete(self.name, self.IsStrict()) |
||||
} |
||||
|
||||
// ArgumentReference
|
||||
|
||||
func newArgumentReference(base *_object, name string, strict bool) *_propertyReference { |
||||
if base == nil { |
||||
panic(hereBeDragons()) |
||||
} |
||||
return newPropertyReference(base, name, strict) |
||||
} |
||||
|
||||
type _environmentReference struct { |
||||
_referenceDefault |
||||
Base _environment |
||||
node ast.Node |
||||
} |
||||
|
||||
func newEnvironmentReference(base _environment, name string, strict bool, node ast.Node) *_environmentReference { |
||||
return &_environmentReference{ |
||||
Base: base, |
||||
_referenceDefault: _referenceDefault{ |
||||
name: name, |
||||
strict: strict, |
||||
}, |
||||
node: node, |
||||
} |
||||
} |
||||
|
||||
func (self *_environmentReference) GetBase() interface{} { |
||||
return self.Base |
||||
} |
||||
|
||||
func (self *_environmentReference) IsUnresolvable() bool { |
||||
return self.Base == nil // The base (an environment) will never be nil
|
||||
} |
||||
|
||||
func (self *_environmentReference) IsPropertyReference() bool { |
||||
return false |
||||
} |
||||
|
||||
func (self *_environmentReference) GetValue() Value { |
||||
if self.Base == nil { |
||||
// This should never be reached, but just in case
|
||||
} |
||||
return self.Base.GetValue(self.name, self.IsStrict()) |
||||
} |
||||
|
||||
func (self *_environmentReference) PutValue(value Value) bool { |
||||
if self.Base == nil { |
||||
// This should never be reached, but just in case
|
||||
return false |
||||
} |
||||
self.Base.SetValue(self.name, value, self.IsStrict()) |
||||
return true |
||||
} |
||||
|
||||
func (self *_environmentReference) Delete() bool { |
||||
if self.Base == nil { |
||||
// This should never be reached, but just in case
|
||||
return false |
||||
} |
||||
return self.Base.DeleteBinding(self.name) |
||||
} |
||||
|
||||
// getIdentifierReference
|
||||
|
||||
func getIdentifierReference(environment _environment, name string, strict bool) _reference { |
||||
if environment == nil { |
||||
return newPropertyReference(nil, name, strict) |
||||
} |
||||
if environment.HasBinding(name) { |
||||
return environment.newReference(name, strict) |
||||
} |
||||
return getIdentifierReference(environment.Outer(), name, strict) |
||||
} |
@ -0,0 +1,126 @@ |
||||
package otto |
||||
|
||||
import ( |
||||
"fmt" |
||||
) |
||||
|
||||
func builtinError(call FunctionCall) Value { |
||||
return toValue_object(call.runtime.newError("", call.Argument(0))) |
||||
} |
||||
|
||||
func builtinNewError(self *_object, argumentList []Value) Value { |
||||
return toValue_object(self.runtime.newError("", valueOfArrayIndex(argumentList, 0))) |
||||
} |
||||
|
||||
func builtinError_toString(call FunctionCall) Value { |
||||
thisObject := call.thisObject() |
||||
if thisObject == nil { |
||||
panic(call.runtime.panicTypeError()) |
||||
} |
||||
|
||||
name := "Error" |
||||
nameValue := thisObject.get("name") |
||||
if nameValue.IsDefined() { |
||||
name = nameValue.string() |
||||
} |
||||
|
||||
message := "" |
||||
messageValue := thisObject.get("message") |
||||
if messageValue.IsDefined() { |
||||
message = messageValue.string() |
||||
} |
||||
|
||||
if len(name) == 0 { |
||||
return toValue_string(message) |
||||
} |
||||
|
||||
if len(message) == 0 { |
||||
return toValue_string(name) |
||||
} |
||||
|
||||
return toValue_string(fmt.Sprintf("%s: %s", name, message)) |
||||
} |
||||
|
||||
func (runtime *_runtime) newEvalError(message Value) *_object { |
||||
self := runtime.newErrorObject("EvalError", message) |
||||
self.prototype = runtime.global.EvalErrorPrototype |
||||
return self |
||||
} |
||||
|
||||
func builtinEvalError(call FunctionCall) Value { |
||||
return toValue_object(call.runtime.newEvalError(call.Argument(0))) |
||||
} |
||||
|
||||
func builtinNewEvalError(self *_object, argumentList []Value) Value { |
||||
return toValue_object(self.runtime.newEvalError(valueOfArrayIndex(argumentList, 0))) |
||||
} |
||||
|
||||
func (runtime *_runtime) newTypeError(message Value) *_object { |
||||
self := runtime.newErrorObject("TypeError", message) |
||||
self.prototype = runtime.global.TypeErrorPrototype |
||||
return self |
||||
} |
||||
|
||||
func builtinTypeError(call FunctionCall) Value { |
||||
return toValue_object(call.runtime.newTypeError(call.Argument(0))) |
||||
} |
||||
|
||||
func builtinNewTypeError(self *_object, argumentList []Value) Value { |
||||
return toValue_object(self.runtime.newTypeError(valueOfArrayIndex(argumentList, 0))) |
||||
} |
||||
|
||||
func (runtime *_runtime) newRangeError(message Value) *_object { |
||||
self := runtime.newErrorObject("RangeError", message) |
||||
self.prototype = runtime.global.RangeErrorPrototype |
||||
return self |
||||
} |
||||
|
||||
func builtinRangeError(call FunctionCall) Value { |
||||
return toValue_object(call.runtime.newRangeError(call.Argument(0))) |
||||
} |
||||
|
||||
func builtinNewRangeError(self *_object, argumentList []Value) Value { |
||||
return toValue_object(self.runtime.newRangeError(valueOfArrayIndex(argumentList, 0))) |
||||
} |
||||
|
||||
func (runtime *_runtime) newURIError(message Value) *_object { |
||||
self := runtime.newErrorObject("URIError", message) |
||||
self.prototype = runtime.global.URIErrorPrototype |
||||
return self |
||||
} |
||||
|
||||
func (runtime *_runtime) newReferenceError(message Value) *_object { |
||||
self := runtime.newErrorObject("ReferenceError", message) |
||||
self.prototype = runtime.global.ReferenceErrorPrototype |
||||
return self |
||||
} |
||||
|
||||
func builtinReferenceError(call FunctionCall) Value { |
||||
return toValue_object(call.runtime.newReferenceError(call.Argument(0))) |
||||
} |
||||
|
||||
func builtinNewReferenceError(self *_object, argumentList []Value) Value { |
||||
return toValue_object(self.runtime.newReferenceError(valueOfArrayIndex(argumentList, 0))) |
||||
} |
||||
|
||||
func (runtime *_runtime) newSyntaxError(message Value) *_object { |
||||
self := runtime.newErrorObject("SyntaxError", message) |
||||
self.prototype = runtime.global.SyntaxErrorPrototype |
||||
return self |
||||
} |
||||
|
||||
func builtinSyntaxError(call FunctionCall) Value { |
||||
return toValue_object(call.runtime.newSyntaxError(call.Argument(0))) |
||||
} |
||||
|
||||
func builtinNewSyntaxError(self *_object, argumentList []Value) Value { |
||||
return toValue_object(self.runtime.newSyntaxError(valueOfArrayIndex(argumentList, 0))) |
||||
} |
||||
|
||||
func builtinURIError(call FunctionCall) Value { |
||||
return toValue_object(call.runtime.newURIError(call.Argument(0))) |
||||
} |
||||
|
||||
func builtinNewURIError(self *_object, argumentList []Value) Value { |
||||
return toValue_object(self.runtime.newURIError(valueOfArrayIndex(argumentList, 0))) |
||||
} |
@ -0,0 +1,155 @@ |
||||
package otto |
||||
|
||||
import ( |
||||
"fmt" |
||||
) |
||||
|
||||
type _clone struct { |
||||
runtime *_runtime |
||||
_object map[*_object]*_object |
||||
_objectStash map[*_objectStash]*_objectStash |
||||
_dclStash map[*_dclStash]*_dclStash |
||||
_fnStash map[*_fnStash]*_fnStash |
||||
} |
||||
|
||||
func (in *_runtime) clone() *_runtime { |
||||
|
||||
in.lck.Lock() |
||||
defer in.lck.Unlock() |
||||
|
||||
out := &_runtime{} |
||||
clone := _clone{ |
||||
runtime: out, |
||||
_object: make(map[*_object]*_object), |
||||
_objectStash: make(map[*_objectStash]*_objectStash), |
||||
_dclStash: make(map[*_dclStash]*_dclStash), |
||||
_fnStash: make(map[*_fnStash]*_fnStash), |
||||
} |
||||
|
||||
globalObject := clone.object(in.globalObject) |
||||
out.globalStash = out.newObjectStash(globalObject, nil) |
||||
out.globalObject = globalObject |
||||
out.global = _global{ |
||||
clone.object(in.global.Object), |
||||
clone.object(in.global.Function), |
||||
clone.object(in.global.Array), |
||||
clone.object(in.global.String), |
||||
clone.object(in.global.Boolean), |
||||
clone.object(in.global.Number), |
||||
clone.object(in.global.Math), |
||||
clone.object(in.global.Date), |
||||
clone.object(in.global.RegExp), |
||||
clone.object(in.global.Error), |
||||
clone.object(in.global.EvalError), |
||||
clone.object(in.global.TypeError), |
||||
clone.object(in.global.RangeError), |
||||
clone.object(in.global.ReferenceError), |
||||
clone.object(in.global.SyntaxError), |
||||
clone.object(in.global.URIError), |
||||
clone.object(in.global.JSON), |
||||
|
||||
clone.object(in.global.ObjectPrototype), |
||||
clone.object(in.global.FunctionPrototype), |
||||
clone.object(in.global.ArrayPrototype), |
||||
clone.object(in.global.StringPrototype), |
||||
clone.object(in.global.BooleanPrototype), |
||||
clone.object(in.global.NumberPrototype), |
||||
clone.object(in.global.DatePrototype), |
||||
clone.object(in.global.RegExpPrototype), |
||||
clone.object(in.global.ErrorPrototype), |
||||
clone.object(in.global.EvalErrorPrototype), |
||||
clone.object(in.global.TypeErrorPrototype), |
||||
clone.object(in.global.RangeErrorPrototype), |
||||
clone.object(in.global.ReferenceErrorPrototype), |
||||
clone.object(in.global.SyntaxErrorPrototype), |
||||
clone.object(in.global.URIErrorPrototype), |
||||
} |
||||
|
||||
out.eval = out.globalObject.property["eval"].value.(Value).value.(*_object) |
||||
out.globalObject.prototype = out.global.ObjectPrototype |
||||
|
||||
// Not sure if this is necessary, but give some help to the GC
|
||||
clone.runtime = nil |
||||
clone._object = nil |
||||
clone._objectStash = nil |
||||
clone._dclStash = nil |
||||
clone._fnStash = nil |
||||
|
||||
return out |
||||
} |
||||
|
||||
func (clone *_clone) object(in *_object) *_object { |
||||
if out, exists := clone._object[in]; exists { |
||||
return out |
||||
} |
||||
out := &_object{} |
||||
clone._object[in] = out |
||||
return in.objectClass.clone(in, out, clone) |
||||
} |
||||
|
||||
func (clone *_clone) dclStash(in *_dclStash) (*_dclStash, bool) { |
||||
if out, exists := clone._dclStash[in]; exists { |
||||
return out, true |
||||
} |
||||
out := &_dclStash{} |
||||
clone._dclStash[in] = out |
||||
return out, false |
||||
} |
||||
|
||||
func (clone *_clone) objectStash(in *_objectStash) (*_objectStash, bool) { |
||||
if out, exists := clone._objectStash[in]; exists { |
||||
return out, true |
||||
} |
||||
out := &_objectStash{} |
||||
clone._objectStash[in] = out |
||||
return out, false |
||||
} |
||||
|
||||
func (clone *_clone) fnStash(in *_fnStash) (*_fnStash, bool) { |
||||
if out, exists := clone._fnStash[in]; exists { |
||||
return out, true |
||||
} |
||||
out := &_fnStash{} |
||||
clone._fnStash[in] = out |
||||
return out, false |
||||
} |
||||
|
||||
func (clone *_clone) value(in Value) Value { |
||||
out := in |
||||
switch value := in.value.(type) { |
||||
case *_object: |
||||
out.value = clone.object(value) |
||||
} |
||||
return out |
||||
} |
||||
|
||||
func (clone *_clone) valueArray(in []Value) []Value { |
||||
out := make([]Value, len(in)) |
||||
for index, value := range in { |
||||
out[index] = clone.value(value) |
||||
} |
||||
return out |
||||
} |
||||
|
||||
func (clone *_clone) stash(in _stash) _stash { |
||||
if in == nil { |
||||
return nil |
||||
} |
||||
return in.clone(clone) |
||||
} |
||||
|
||||
func (clone *_clone) property(in _property) _property { |
||||
out := in |
||||
if value, valid := in.value.(Value); valid { |
||||
out.value = clone.value(value) |
||||
} else { |
||||
panic(fmt.Errorf("in.value.(Value) != true")) |
||||
} |
||||
return out |
||||
} |
||||
|
||||
func (clone *_clone) dclProperty(in _dclProperty) _dclProperty { |
||||
out := in |
||||
out.value = clone.value(in.value) |
||||
return out |
||||
} |
@ -0,0 +1,24 @@ |
||||
package otto |
||||
|
||||
import ( |
||||
"github.com/robertkrimen/otto/ast" |
||||
"github.com/robertkrimen/otto/file" |
||||
) |
||||
|
||||
type _file struct { |
||||
name string |
||||
src string |
||||
base int // This will always be 1 or greater
|
||||
} |
||||
|
||||
type _compiler struct { |
||||
file *file.File |
||||
program *ast.Program |
||||
} |
||||
|
||||
func (cmpl *_compiler) parse() *_nodeProgram { |
||||
if cmpl.program != nil { |
||||
cmpl.file = cmpl.program.File |
||||
} |
||||
return cmpl._parse(cmpl.program) |
||||
} |
@ -0,0 +1,245 @@ |
||||
package otto |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
"strings" |
||||
|
||||
"github.com/robertkrimen/otto/file" |
||||
) |
||||
|
||||
type _exception struct { |
||||
value interface{} |
||||
} |
||||
|
||||
func newException(value interface{}) *_exception { |
||||
return &_exception{ |
||||
value: value, |
||||
} |
||||
} |
||||
|
||||
func (self *_exception) eject() interface{} { |
||||
value := self.value |
||||
self.value = nil // Prevent Go from holding on to the value, whatever it is
|
||||
return value |
||||
} |
||||
|
||||
type _error struct { |
||||
name string |
||||
message string |
||||
trace []_frame |
||||
|
||||
offset int |
||||
} |
||||
|
||||
type _frame struct { |
||||
file *file.File |
||||
offset int |
||||
callee string |
||||
} |
||||
|
||||
var ( |
||||
nativeFrame = _frame{} |
||||
) |
||||
|
||||
type _at int |
||||
|
||||
func (fr _frame) location() string { |
||||
if fr.file == nil { |
||||
return "<unknown>" |
||||
} |
||||
path := fr.file.Name() |
||||
line, column := _position(fr.file, fr.offset) |
||||
|
||||
if path == "" { |
||||
path = "<anonymous>" |
||||
} |
||||
|
||||
str := fmt.Sprintf("%s:%d:%d", path, line, column) |
||||
|
||||
if fr.callee != "" { |
||||
str = fmt.Sprintf("%s (%s)", fr.callee, str) |
||||
} |
||||
|
||||
return str |
||||
} |
||||
|
||||
func _position(file *file.File, offset int) (line, column int) { |
||||
{ |
||||
offset := offset - file.Base() |
||||
if offset < 0 { |
||||
return -offset, -1 |
||||
} |
||||
|
||||
src := file.Source() |
||||
if offset >= len(src) { |
||||
return -offset, -len(src) |
||||
} |
||||
src = src[:offset] |
||||
|
||||
line := 1 + strings.Count(src, "\n") |
||||
column := 0 |
||||
if index := strings.LastIndex(src, "\n"); index >= 0 { |
||||
column = offset - index |
||||
} else { |
||||
column = 1 + len(src) |
||||
} |
||||
return line, column |
||||
} |
||||
} |
||||
|
||||
// An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc.
|
||||
type Error struct { |
||||
_error |
||||
} |
||||
|
||||
// Error returns a description of the error
|
||||
//
|
||||
// TypeError: 'def' is not a function
|
||||
//
|
||||
func (err Error) Error() string { |
||||
if len(err.name) == 0 { |
||||
return err.message |
||||
} |
||||
if len(err.message) == 0 { |
||||
return err.name |
||||
} |
||||
return fmt.Sprintf("%s: %s", err.name, err.message) |
||||
} |
||||
|
||||
// String returns a description of the error and a trace of where the
|
||||
// error occurred.
|
||||
//
|
||||
// TypeError: 'def' is not a function
|
||||
// at xyz (<anonymous>:3:9)
|
||||
// at <anonymous>:7:1/
|
||||
//
|
||||
func (err Error) String() string { |
||||
str := err.Error() + "\n" |
||||
for _, frame := range err.trace { |
||||
str += " at " + frame.location() + "\n" |
||||
} |
||||
return str |
||||
} |
||||
|
||||
func (err _error) describe(format string, in ...interface{}) string { |
||||
return fmt.Sprintf(format, in...) |
||||
} |
||||
|
||||
func (self _error) messageValue() Value { |
||||
if self.message == "" { |
||||
return Value{} |
||||
} |
||||
return toValue_string(self.message) |
||||
} |
||||
|
||||
func (rt *_runtime) typeErrorResult(throw bool) bool { |
||||
if throw { |
||||
panic(rt.panicTypeError()) |
||||
} |
||||
return false |
||||
} |
||||
|
||||
func newError(rt *_runtime, name string, in ...interface{}) _error { |
||||
err := _error{ |
||||
name: name, |
||||
offset: -1, |
||||
} |
||||
description := "" |
||||
length := len(in) |
||||
|
||||
if rt != nil { |
||||
scope := rt.scope |
||||
frame := scope.frame |
||||
if length > 0 { |
||||
if at, ok := in[length-1].(_at); ok { |
||||
in = in[0 : length-1] |
||||
if scope != nil { |
||||
frame.offset = int(at) |
||||
} |
||||
length -= 1 |
||||
} |
||||
if length > 0 { |
||||
description, in = in[0].(string), in[1:] |
||||
} |
||||
} |
||||
limit := 10 |
||||
err.trace = append(err.trace, frame) |
||||
if scope != nil { |
||||
for limit > 0 { |
||||
scope = scope.outer |
||||
if scope == nil { |
||||
break |
||||
} |
||||
if scope.frame.offset >= 0 { |
||||
err.trace = append(err.trace, scope.frame) |
||||
} |
||||
limit-- |
||||
} |
||||
} |
||||
} else { |
||||
if length > 0 { |
||||
description, in = in[0].(string), in[1:] |
||||
} |
||||
} |
||||
err.message = err.describe(description, in...) |
||||
return err |
||||
} |
||||
|
||||
func (rt *_runtime) panicTypeError(argumentList ...interface{}) *_exception { |
||||
return &_exception{ |
||||
value: newError(rt, "TypeError", argumentList...), |
||||
} |
||||
} |
||||
|
||||
func (rt *_runtime) panicReferenceError(argumentList ...interface{}) *_exception { |
||||
return &_exception{ |
||||
value: newError(rt, "ReferenceError", argumentList...), |
||||
} |
||||
} |
||||
|
||||
func (rt *_runtime) panicURIError(argumentList ...interface{}) *_exception { |
||||
return &_exception{ |
||||
value: newError(rt, "URIError", argumentList...), |
||||
} |
||||
} |
||||
|
||||
func (rt *_runtime) panicSyntaxError(argumentList ...interface{}) *_exception { |
||||
return &_exception{ |
||||
value: newError(rt, "SyntaxError", argumentList...), |
||||
} |
||||
} |
||||
|
||||
func (rt *_runtime) panicRangeError(argumentList ...interface{}) *_exception { |
||||
return &_exception{ |
||||
value: newError(rt, "RangeError", argumentList...), |
||||
} |
||||
} |
||||
|
||||
func catchPanic(function func()) (err error) { |
||||
defer func() { |
||||
if caught := recover(); caught != nil { |
||||
if exception, ok := caught.(*_exception); ok { |
||||
caught = exception.eject() |
||||
} |
||||
switch caught := caught.(type) { |
||||
case _error: |
||||
err = &Error{caught} |
||||
return |
||||
case Value: |
||||
if vl := caught._object(); vl != nil { |
||||
switch vl := vl.value.(type) { |
||||
case _error: |
||||
err = &Error{vl} |
||||
return |
||||
} |
||||
} |
||||
err = errors.New(caught.string()) |
||||
return |
||||
} |
||||
panic(caught) |
||||
} |
||||
}() |
||||
function() |
||||
return nil |
||||
} |
@ -0,0 +1,192 @@ |
||||
package otto |
||||
|
||||
import ( |
||||
"testing" |
||||
) |
||||
|
||||
func TestError(t *testing.T) { |
||||
tt(t, func() { |
||||
test, _ := test() |
||||
|
||||
test(` |
||||
[ Error.prototype.name, Error.prototype.message, Error.prototype.hasOwnProperty("message") ]; |
||||
`, "Error,,true") |
||||
}) |
||||
} |
||||
|
||||
func TestError_instanceof(t *testing.T) { |
||||
tt(t, func() { |
||||
test, _ := test() |
||||
|
||||
test(`(new TypeError()) instanceof Error`, true) |
||||
}) |
||||
} |
||||
|
||||
func TestPanicValue(t *testing.T) { |
||||
tt(t, func() { |
||||
test, vm := test() |
||||
|
||||
vm.Set("abc", func(call FunctionCall) Value { |
||||
value, err := call.Otto.Run(`({ def: 3.14159 })`) |
||||
is(err, nil) |
||||
panic(value) |
||||
}) |
||||
|
||||
test(` |
||||
try { |
||||
abc(); |
||||
} |
||||
catch (err) { |
||||
error = err; |
||||
} |
||||
[ error instanceof Error, error.message, error.def ]; |
||||
`, "false,,3.14159") |
||||
}) |
||||
} |
||||
|
||||
func Test_catchPanic(t *testing.T) { |
||||
tt(t, func() { |
||||
vm := New() |
||||
|
||||
_, err := vm.Run(` |
||||
A syntax error that |
||||
does not define |
||||
var; |
||||
abc; |
||||
`) |
||||
is(err, "!=", nil) |
||||
|
||||
_, err = vm.Call(`abc.def`, nil) |
||||
is(err, "!=", nil) |
||||
}) |
||||
} |
||||
|
||||
func TestErrorContext(t *testing.T) { |
||||
tt(t, func() { |
||||
vm := New() |
||||
|
||||
_, err := vm.Run(` |
||||
undefined(); |
||||
`) |
||||
{ |
||||
err := err.(*Error) |
||||
is(err.message, "'undefined' is not a function") |
||||
is(len(err.trace), 1) |
||||
is(err.trace[0].location(), "<anonymous>:2:13") |
||||
} |
||||
|
||||
_, err = vm.Run(` |
||||
({}).abc(); |
||||
`) |
||||
{ |
||||
err := err.(*Error) |
||||
is(err.message, "'abc' is not a function") |
||||
is(len(err.trace), 1) |
||||
is(err.trace[0].location(), "<anonymous>:2:14") |
||||
} |
||||
|
||||
_, err = vm.Run(` |
||||
("abc").abc(); |
||||
`) |
||||
{ |
||||
err := err.(*Error) |
||||
is(err.message, "'abc' is not a function") |
||||
is(len(err.trace), 1) |
||||
is(err.trace[0].location(), "<anonymous>:2:14") |
||||
} |
||||
|
||||
_, err = vm.Run(` |
||||
var ghi = "ghi"; |
||||
ghi(); |
||||
`) |
||||
{ |
||||
err := err.(*Error) |
||||
is(err.message, "'ghi' is not a function") |
||||
is(len(err.trace), 1) |
||||
is(err.trace[0].location(), "<anonymous>:3:13") |
||||
} |
||||
|
||||
_, err = vm.Run(` |
||||
function def() { |
||||
undefined(); |
||||
} |
||||
function abc() { |
||||
def(); |
||||
} |
||||
abc(); |
||||
`) |
||||
{ |
||||
err := err.(*Error) |
||||
is(err.message, "'undefined' is not a function") |
||||
is(len(err.trace), 3) |
||||
is(err.trace[0].location(), "def (<anonymous>:3:17)") |
||||
is(err.trace[1].location(), "abc (<anonymous>:6:17)") |
||||
is(err.trace[2].location(), "<anonymous>:8:13") |
||||
} |
||||
|
||||
_, err = vm.Run(` |
||||
function abc() { |
||||
xyz(); |
||||
} |
||||
abc(); |
||||
`) |
||||
{ |
||||
err := err.(*Error) |
||||
is(err.message, "'xyz' is not defined") |
||||
is(len(err.trace), 2) |
||||
is(err.trace[0].location(), "abc (<anonymous>:3:17)") |
||||
is(err.trace[1].location(), "<anonymous>:5:13") |
||||
} |
||||
|
||||
_, err = vm.Run(` |
||||
mno + 1; |
||||
`) |
||||
{ |
||||
err := err.(*Error) |
||||
is(err.message, "'mno' is not defined") |
||||
is(len(err.trace), 1) |
||||
is(err.trace[0].location(), "<anonymous>:2:13") |
||||
} |
||||
|
||||
_, err = vm.Run(` |
||||
eval("xyz();"); |
||||
`) |
||||
{ |
||||
err := err.(*Error) |
||||
is(err.message, "'xyz' is not defined") |
||||
is(len(err.trace), 1) |
||||
is(err.trace[0].location(), "<anonymous>:1:1") |
||||
} |
||||
|
||||
_, err = vm.Run(` |
||||
xyzzy = "Nothing happens." |
||||
eval("xyzzy();"); |
||||
`) |
||||
{ |
||||
err := err.(*Error) |
||||
is(err.message, "'xyzzy' is not a function") |
||||
is(len(err.trace), 1) |
||||
is(err.trace[0].location(), "<anonymous>:1:1") |
||||
} |
||||
|
||||
_, err = vm.Run(` |
||||
throw Error("xyzzy"); |
||||
`) |
||||
{ |
||||
err := err.(*Error) |
||||
is(err.message, "xyzzy") |
||||
is(len(err.trace), 1) |
||||
is(err.trace[0].location(), "<anonymous>:2:19") |
||||
} |
||||
|
||||
_, err = vm.Run(` |
||||
throw new Error("xyzzy"); |
||||
`) |
||||
{ |
||||
err := err.(*Error) |
||||
is(err.message, "xyzzy") |
||||
is(len(err.trace), 1) |
||||
is(err.trace[0].location(), "<anonymous>:2:23") |
||||
} |
||||
}) |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,34 @@ |
||||
package otto |
||||
|
||||
// _scope:
|
||||
// entryFile
|
||||
// entryIdx
|
||||
// top?
|
||||
// outer => nil
|
||||
|
||||
// _stash:
|
||||
// lexical
|
||||
// variable
|
||||
//
|
||||
// _thisStash (ObjectEnvironment)
|
||||
// _fnStash
|
||||
// _dclStash
|
||||
|
||||
// An ECMA-262 ExecutionContext
|
||||
type _scope struct { |
||||
lexical _stash |
||||
variable _stash |
||||
this *_object |
||||
eval bool // Replace this with kind?
|
||||
outer *_scope |
||||
|
||||
frame _frame |
||||
} |
||||
|
||||
func newScope(lexical _stash, variable _stash, this *_object) *_scope { |
||||
return &_scope{ |
||||
lexical: lexical, |
||||
variable: variable, |
||||
this: this, |
||||
} |
||||
} |
@ -0,0 +1,275 @@ |
||||
package otto |
||||
|
||||
import ( |
||||
"fmt" |
||||
) |
||||
|
||||
// ======
|
||||
// _stash
|
||||
// ======
|
||||
|
||||
type _stash interface { |
||||
hasBinding(string) bool //
|
||||
createBinding(string, bool, Value) // CreateMutableBinding
|
||||
setBinding(string, Value, bool) // SetMutableBinding
|
||||
getBinding(string, bool) Value // GetBindingValue
|
||||
deleteBinding(string) bool //
|
||||
setValue(string, Value, bool) // createBinding + setBinding
|
||||
|
||||
outer() _stash |
||||
runtime() *_runtime |
||||
|
||||
newReference(string, bool, _at) _reference |
||||
|
||||
clone(clone *_clone) _stash |
||||
} |
||||
|
||||
// ==========
|
||||
// _objectStash
|
||||
// ==========
|
||||
|
||||
type _objectStash struct { |
||||
_runtime *_runtime |
||||
_outer _stash |
||||
object *_object |
||||
} |
||||
|
||||
func (self *_objectStash) runtime() *_runtime { |
||||
return self._runtime |
||||
} |
||||
|
||||
func (runtime *_runtime) newObjectStash(object *_object, outer _stash) *_objectStash { |
||||
if object == nil { |
||||
object = runtime.newBaseObject() |
||||
object.class = "environment" |
||||
} |
||||
return &_objectStash{ |
||||
_runtime: runtime, |
||||
_outer: outer, |
||||
object: object, |
||||
} |
||||
} |
||||
|
||||
func (in *_objectStash) clone(clone *_clone) _stash { |
||||
out, exists := clone.objectStash(in) |
||||
if exists { |
||||
return out |
||||
} |
||||
*out = _objectStash{ |
||||
clone.runtime, |
||||
clone.stash(in._outer), |
||||
clone.object(in.object), |
||||
} |
||||
return out |
||||
} |
||||
|
||||
func (self *_objectStash) hasBinding(name string) bool { |
||||
return self.object.hasProperty(name) |
||||
} |
||||
|
||||
func (self *_objectStash) createBinding(name string, deletable bool, value Value) { |
||||
if self.object.hasProperty(name) { |
||||
panic(hereBeDragons()) |
||||
} |
||||
mode := _propertyMode(0111) |
||||
if !deletable { |
||||
mode = _propertyMode(0110) |
||||
} |
||||
// TODO False?
|
||||
self.object.defineProperty(name, value, mode, false) |
||||
} |
||||
|
||||
func (self *_objectStash) setBinding(name string, value Value, strict bool) { |
||||
self.object.put(name, value, strict) |
||||
} |
||||
|
||||
func (self *_objectStash) setValue(name string, value Value, throw bool) { |
||||
if !self.hasBinding(name) { |
||||
self.createBinding(name, true, value) // Configurable by default
|
||||
} else { |
||||
self.setBinding(name, value, throw) |
||||
} |
||||
} |
||||
|
||||
func (self *_objectStash) getBinding(name string, throw bool) Value { |
||||
if self.object.hasProperty(name) { |
||||
return self.object.get(name) |
||||
} |
||||
if throw { // strict?
|
||||
panic(self._runtime.panicReferenceError("Not Defined", name)) |
||||
} |
||||
return Value{} |
||||
} |
||||
|
||||
func (self *_objectStash) deleteBinding(name string) bool { |
||||
return self.object.delete(name, false) |
||||
} |
||||
|
||||
func (self *_objectStash) outer() _stash { |
||||
return self._outer |
||||
} |
||||
|
||||
func (self *_objectStash) newReference(name string, strict bool, at _at) _reference { |
||||
return newPropertyReference(self._runtime, self.object, name, strict, at) |
||||
} |
||||
|
||||
// =========
|
||||
// _dclStash
|
||||
// =========
|
||||
|
||||
type _dclStash struct { |
||||
_runtime *_runtime |
||||
_outer _stash |
||||
property map[string]_dclProperty |
||||
} |
||||
|
||||
type _dclProperty struct { |
||||
value Value |
||||
mutable bool |
||||
deletable bool |
||||
readable bool |
||||
} |
||||
|
||||
func (runtime *_runtime) newDeclarationStash(outer _stash) *_dclStash { |
||||
return &_dclStash{ |
||||
_runtime: runtime, |
||||
_outer: outer, |
||||
property: map[string]_dclProperty{}, |
||||
} |
||||
} |
||||
|
||||
func (in *_dclStash) clone(clone *_clone) _stash { |
||||
out, exists := clone.dclStash(in) |
||||
if exists { |
||||
return out |
||||
} |
||||
property := make(map[string]_dclProperty, len(in.property)) |
||||
for index, value := range in.property { |
||||
property[index] = clone.dclProperty(value) |
||||
} |
||||
*out = _dclStash{ |
||||
clone.runtime, |
||||
clone.stash(in._outer), |
||||
property, |
||||
} |
||||
return out |
||||
} |
||||
|
||||
func (self *_dclStash) hasBinding(name string) bool { |
||||
_, exists := self.property[name] |
||||
return exists |
||||
} |
||||
|
||||
func (self *_dclStash) runtime() *_runtime { |
||||
return self._runtime |
||||
} |
||||
|
||||
func (self *_dclStash) createBinding(name string, deletable bool, value Value) { |
||||
_, exists := self.property[name] |
||||
if exists { |
||||
panic(fmt.Errorf("createBinding: %s: already exists", name)) |
||||
} |
||||
self.property[name] = _dclProperty{ |
||||
value: value, |
||||
mutable: true, |
||||
deletable: deletable, |
||||
readable: false, |
||||
} |
||||
} |
||||
|
||||
func (self *_dclStash) setBinding(name string, value Value, strict bool) { |
||||
property, exists := self.property[name] |
||||
if !exists { |
||||
panic(fmt.Errorf("setBinding: %s: missing", name)) |
||||
} |
||||
if property.mutable { |
||||
property.value = value |
||||
self.property[name] = property |
||||
} else { |
||||
self._runtime.typeErrorResult(strict) |
||||
} |
||||
} |
||||
|
||||
func (self *_dclStash) setValue(name string, value Value, throw bool) { |
||||
if !self.hasBinding(name) { |
||||
self.createBinding(name, false, value) // NOT deletable by default
|
||||
} else { |
||||
self.setBinding(name, value, throw) |
||||
} |
||||
} |
||||
|
||||
// FIXME This is called a __lot__
|
||||
func (self *_dclStash) getBinding(name string, throw bool) Value { |
||||
property, exists := self.property[name] |
||||
if !exists { |
||||
panic(fmt.Errorf("getBinding: %s: missing", name)) |
||||
} |
||||
if !property.mutable && !property.readable { |
||||
if throw { // strict?
|
||||
panic(self._runtime.panicTypeError()) |
||||
} |
||||
return Value{} |
||||
} |
||||
return property.value |
||||
} |
||||
|
||||
func (self *_dclStash) deleteBinding(name string) bool { |
||||
property, exists := self.property[name] |
||||
if !exists { |
||||
return true |
||||
} |
||||
if !property.deletable { |
||||
return false |
||||
} |
||||
delete(self.property, name) |
||||
return true |
||||
} |
||||
|
||||
func (self *_dclStash) outer() _stash { |
||||
return self._outer |
||||
} |
||||
|
||||
func (self *_dclStash) newReference(name string, strict bool, _ _at) _reference { |
||||
return &_stashReference{ |
||||
name: name, |
||||
base: self, |
||||
} |
||||
} |
||||
|
||||
// ========
|
||||
// _fnStash
|
||||
// ========
|
||||
|
||||
type _fnStash struct { |
||||
_dclStash |
||||
arguments *_object |
||||
indexOfArgumentName map[string]string |
||||
} |
||||
|
||||
func (runtime *_runtime) newFunctionStash(outer _stash) *_fnStash { |
||||
return &_fnStash{ |
||||
_dclStash: _dclStash{ |
||||
_runtime: runtime, |
||||
_outer: outer, |
||||
property: map[string]_dclProperty{}, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
func (in *_fnStash) clone(clone *_clone) _stash { |
||||
out, exists := clone.fnStash(in) |
||||
if exists { |
||||
return out |
||||
} |
||||
dclStash := in._dclStash.clone(clone).(*_dclStash) |
||||
index := make(map[string]string, len(in.indexOfArgumentName)) |
||||
for name, value := range in.indexOfArgumentName { |
||||
index[name] = value |
||||
} |
||||
*out = _fnStash{ |
||||
_dclStash: *dclStash, |
||||
arguments: clone.object(in.arguments), |
||||
indexOfArgumentName: index, |
||||
} |
||||
return out |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue