mirror of https://github.com/ethereum/go-ethereum
Merge pull request #536 from zsfelfoldi/develop
using robertkrimen/otto, godeps updatedpull/551/head
commit
3133372a6a
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