1
0
mirror of https://github.com/robertkrimen/otto synced 2025-09-28 18:45:22 +08:00

Line tracking for error reporting

This commit is contained in:
Robert Krimen 2012-10-09 16:41:45 -07:00
parent f81f540a01
commit 326264ae3e
11 changed files with 182 additions and 36 deletions

View File

@ -18,6 +18,7 @@ TEST := -v --run _eval
TEST := -v --run Broken
TEST := -v --run ParseSuccess
TEST := .
TEST := -v --run OttoError
test:
go test $(TEST)

View File

@ -8,6 +8,8 @@ import (
type _error struct {
Name string
Message string
Line int // Hackish -- line where the error/exception occurred
}
var messageDetail map[string]string = map[string]string{
@ -48,6 +50,7 @@ func newError(name string, argumentList... interface{}) _error {
return _error{
Name: name,
Message: messageFromDescription(description, argumentList...),
Line: -1,
}
}
@ -86,7 +89,11 @@ func catchPanic(function func()) (err error) {
err = errors.New(caught.String())
return
case _error:
err = errors.New(caught.String())
if caught.Line == -1 {
err = errors.New(caught.String())
} else {
err = errors.New(fmt.Sprintf("%s (line %d)", caught.String(), caught.Line))
}
return
case _result:
if caught.Kind == resultThrow {

View File

@ -20,6 +20,21 @@ func (self *_runtime) evaluateBody(list []_node) Value {
}
func (self *_runtime) evaluate(node _node) Value {
defer func() {
// This defer is lame (unecessary overhead)
// It would be better to mark the errors at the source
if caught := recover(); caught != nil {
switch caught := caught.(type) {
case _error:
if caught.Line == -1 {
caught.Line = node.position()
}
panic(caught) // Panic the modified _error
}
panic(caught)
}
}()
switch node := node.(type) {
case *_variableDeclarationListNode:

15
node.go
View File

@ -10,6 +10,8 @@ import (
type _node interface {
Type() _nodeType
String() string
setPosition(int)
position() int
}
type _nodeType int
@ -18,6 +20,19 @@ func (self _nodeType) Type() _nodeType {
return self
}
type _node_ struct {
_nodeType
Line int // Line in the source
}
func (self *_node_) setPosition(Line int) {
self.Line = Line
}
func (self *_node_) position() int {
return self.Line
}
const (
nodeEmpty _nodeType = iota
nodeCall

View File

@ -7,6 +7,7 @@ import (
type _arrayNode struct {
_nodeType
_node_
nodeList []_node
}
@ -26,6 +27,7 @@ func (self *_arrayNode) String() string {
type _assignmentNode struct {
_nodeType
_node_
Assignment string
Operator string
Left _node
@ -48,6 +50,7 @@ func (self _assignmentNode) String() string {
type _binaryOperationNode struct {
_nodeType
_node_
Operator string
Left _node
Right _node
@ -68,6 +71,7 @@ func (self _binaryOperationNode) String() string {
type _callNode struct {
_nodeType
_node_
Callee _node
ArgumentList []_node
}
@ -86,6 +90,7 @@ func (self _callNode) String() string {
type _commaNode struct {
_nodeType
_node_
Sequence []_node
}
@ -102,6 +107,7 @@ func (self _commaNode) String() string {
type _comparisonNode struct {
_nodeType
_node_
Comparator string
Left _node
Right _node
@ -122,6 +128,7 @@ func (self _comparisonNode) String() string {
type _conditionalNode struct {
_nodeType
_node_
Test _node
Consequent _node
Alternate _node
@ -141,6 +148,7 @@ func (self _conditionalNode) String() string {
type _functionNode struct {
_nodeType
_node_
_declaration bool
ParameterList []string
Body []_node
@ -165,6 +173,7 @@ func (self *_functionNode) AddParameter(identifier string) {
type _identifierNode struct {
_nodeType
_node_
Value string
}
@ -181,6 +190,7 @@ func (self *_identifierNode) String() string {
type _dotMemberNode struct {
_nodeType
_node_
Target _node
Member string
}
@ -199,6 +209,7 @@ func (self *_dotMemberNode) String() string {
type _bracketMemberNode struct {
_nodeType
_node_
Target _node
Member _node
}
@ -217,6 +228,7 @@ func (self *_bracketMemberNode) String() string {
type _newNode struct {
_nodeType
_node_
Callee _node
ArgumentList []_node
}
@ -235,6 +247,7 @@ func (self _newNode) String() string {
type _objectNode struct {
_nodeType
_node_
propertyList []*_objectPropertyNode
}
@ -257,6 +270,7 @@ func (self *_objectNode) String() string {
type _objectPropertyNode struct {
_nodeType
_node_
Key string
Value _node
}
@ -275,6 +289,7 @@ func (self *_objectPropertyNode) String() string {
type _regExpNode struct {
_nodeType
_node_
Pattern string
Flags string
}
@ -293,6 +308,7 @@ func (self *_regExpNode) String() string {
type _thisNode struct {
_nodeType
_node_
}
func newThisNode() *_thisNode {
@ -307,6 +323,7 @@ func (self *_thisNode) String() string {
type _unaryOperationNode struct {
_nodeType
_node_
Operator string
Target _node
}
@ -334,6 +351,7 @@ const (
type _valueNode struct {
_nodeType
_node_
Value Value
Text string
Kind _valueNodeType

View File

@ -6,6 +6,7 @@ import (
type _blockNode struct {
_nodeType
_node_
Body []_node
}
@ -24,6 +25,7 @@ func (self _blockNode) String() string {
type _breakNode struct {
_nodeType
_node_
Target string
}
@ -40,6 +42,7 @@ func (self _breakNode) String() string {
type _continueNode struct {
_nodeType
_node_
Target string
}
@ -56,6 +59,7 @@ func (self _continueNode) String() string {
type _doWhileNode struct {
_nodeType
_node_
Test _node
Body _node
_labelSet _labelSet
@ -77,6 +81,7 @@ func (self _doWhileNode) String() string {
type _emptyNode struct {
_nodeType
_node_
}
func newEmptyNode() *_emptyNode {
@ -91,6 +96,7 @@ func (self _emptyNode) String() string {
type _forNode struct {
_nodeType
_node_
Initial _node
Test _node
Update _node
@ -122,6 +128,7 @@ func (self _forNode) String() string {
type _forInNode struct {
_nodeType
_node_
Into _node
Source _node
Body _node
@ -150,6 +157,7 @@ func (self _forInNode) String() string {
type _ifNode struct {
_nodeType
_node_
Test _node
Consequent _node
Alternate _node
@ -180,6 +188,7 @@ func (self _ifNode) String() string {
type _programNode struct {
_nodeType
_node_
Body []_node
VariableList []_declaration
FunctionList []_declaration
@ -206,6 +215,7 @@ func (self _programNode) toFunction(parameterList []string) *_functionNode {
type _returnNode struct {
_nodeType
_node_
Argument _node
}
@ -224,6 +234,7 @@ func (self _returnNode) String() string {
type _switchNode struct {
_nodeType
_node_
Discriminant _node
Default int
CaseList [](*_caseNode)
@ -247,6 +258,7 @@ func (self _switchNode) String() string {
type _caseNode struct {
_nodeType
_node_
Test _node
Body []_node
}
@ -272,6 +284,7 @@ func (self _caseNode) String() string {
type _throwNode struct {
_nodeType
_node_
Argument _node
}
@ -288,6 +301,7 @@ func (self _throwNode) String() string {
type _tryCatchNode struct {
_nodeType
_node_
Try _node
Catch *_catchNode
Finally *_blockNode
@ -320,6 +334,7 @@ func (self *_tryCatchNode) AddCatch(identifier string, body *_blockNode) {
type _catchNode struct {
_nodeType
_node_
Identifier string
Body *_blockNode
}
@ -338,6 +353,7 @@ func (self _catchNode) String() string {
type _variableDeclarationListNode struct {
_nodeType
_node_
VariableList []*_variableDeclarationNode
}
@ -353,6 +369,7 @@ func (self _variableDeclarationListNode) String() string {
type _variableDeclarationNode struct {
_nodeType
_node_
Identifier string
Operator string
Initializer _node
@ -374,6 +391,7 @@ func (self _variableDeclarationNode) String() string {
type _whileNode struct {
_nodeType
_node_
Test _node
Body _node
_labelSet _labelSet
@ -395,6 +413,7 @@ func (self _whileNode) String() string {
type _withNode struct {
_nodeType
_node_
Object _node
Body _node
}

View File

@ -21,4 +21,12 @@ func TestOttoError(t *testing.T) {
_, err = ToValue([]byte{})
Is(err, "TypeError: Unable to convert value: [] ([]uint8)")
_, err = Otto.Run(`
(function(){
return abcdef.length
})()
`)
Is(err, "ReferenceError: abcdef is not defined (line 2)")
}

View File

@ -21,7 +21,9 @@ func (self *_parser) ParsePrimaryExpression() _node {
return self.ParseFunction(false)
case "this":
self.Next()
return newThisNode()
node := newThisNode()
self.markNode(node)
return node
case "{":
return self.ParseObjectLiteral()
case "[":
@ -56,7 +58,9 @@ func (self *_parser) ParseObjectProperty() *_objectPropertyNode {
self.Expect(":")
value := self.ParseAssignmentExpression()
return newObjectPropertyNode(key, value)
node := newObjectPropertyNode(key, value)
self.markNode(node)
return node
}
func (self *_parser) ParseRegExpLiteral(token _token) *_regExpNode {
@ -74,12 +78,15 @@ func (self *_parser) ParseRegExpLiteral(token _token) *_regExpNode {
panic(token.newSyntaxError("Invalid regular expression"))
}
return newRegExpNode(pattern_, flags)
node := newRegExpNode(pattern_, flags)
self.markNode(node)
return node
}
func (self *_parser) ParseObjectLiteral() *_objectNode {
node := newObjectNode()
self.markNode(node)
self.Expect("{")
for !self.Match("}") {
@ -111,7 +118,9 @@ func (self *_parser) ParseArrayLiteral() *_arrayNode {
}
self.Expect("]")
return newArrayNode(nodeList)
node := newArrayNode(nodeList)
self.markNode(node)
return node
}
func (self *_parser) ParseArgumentList() (argumentList []_node) {
@ -131,6 +140,7 @@ func (self *_parser) ParseArgumentList() (argumentList []_node) {
func (self *_parser) ParseCallExpression(left _node) _node {
left = newCallNode(left)
self.markNode(left)
left.(*_callNode).ArgumentList = self.ParseArgumentList()
return left
}
@ -138,19 +148,24 @@ func (self *_parser) ParseCallExpression(left _node) _node {
func (self *_parser) ParseDotMember(left _node) _node {
self.Expect(".")
member := self.ConsumeIdentifier().Value
return newDotMemberNode(left, member)
node := newDotMemberNode(left, member)
self.markNode(node)
return node
}
func (self *_parser) ParseBracketMember(left _node) _node {
self.Expect("[")
member := self.ParseExpression()
self.Expect("]")
return newBracketMemberNode(left, member)
node := newBracketMemberNode(left, member)
self.markNode(node)
return node
}
func (self *_parser) ParseNewExpression() _node {
self.Expect("new")
node := newnewNode(self.ParseLeftHandSideExpression())
self.markNode(node)
if self.Match("(") {
node.ArgumentList = self.ParseArgumentList()
}
@ -219,7 +234,9 @@ func (self *_parser) ParsePostfixExpression() _node {
default:
panic(self.History(-1).newSyntaxError("Invalid left-hand side in assignment"))
}
return newUnaryOperationNode("=" + self.Consume(), left)
node := newUnaryOperationNode("=" + self.Consume(), left)
self.markNode(node)
return node
}
return left
@ -232,7 +249,9 @@ func (self *_parser) ParseUnaryExpression() _node {
switch token := self.Peek(); token.Kind {
case "+", "-", "!", "~":
return newUnaryOperationNode(self.Consume(), self.ParseUnaryExpression())
node := newUnaryOperationNode(self.Consume(), self.ParseUnaryExpression())
self.markNode(node)
return node
case "++", "--": // Prefix, either ++= or --=
operation := self.Consume()
left := self.ParseUnaryExpression()
@ -241,9 +260,13 @@ func (self *_parser) ParseUnaryExpression() _node {
default:
panic(self.History(-1).newSyntaxError("Invalid left-hand side in assignment"))
}
return newUnaryOperationNode(operation + "=", left)
node := newUnaryOperationNode(operation + "=", left)
self.markNode(node)
return node
case "delete", "void", "typeof":
return newUnaryOperationNode(self.Consume(), self.ParseUnaryExpression())
node := newUnaryOperationNode(self.Consume(), self.ParseUnaryExpression())
self.markNode(node)
return node
}
return self.ParsePostfixExpression()
@ -256,6 +279,7 @@ REPEAT:
switch self.Peek().Kind {
case "*", "/", "%":
left = newBinaryOperationNode(self.Consume(), left, self.ParseUnaryExpression())
self.markNode(left)
goto REPEAT
}
@ -269,6 +293,7 @@ REPEAT:
switch self.Peek().Kind {
case "+", "-":
left = newBinaryOperationNode(self.Consume(), left, self.ParseMultiplicativeExpression())
self.markNode(left)
goto REPEAT
}
@ -282,6 +307,7 @@ REPEAT:
switch self.Peek().Kind {
case "<<", ">>", ">>>":
left = newBinaryOperationNode(self.Consume(), left, self.ParseAdditiveExpression())
self.markNode(left)
goto REPEAT
}
return left
@ -295,14 +321,20 @@ func (self *_parser) ParseRelationalExpression() _node {
switch self.Peek().Kind {
case "<", ">", "<=", ">=":
return newComparisonNode(self.Consume(), left, self.ParseRelationalExpression())
node := newComparisonNode(self.Consume(), left, self.ParseRelationalExpression())
self.markNode(node)
return node
case "instanceof":
return newBinaryOperationNode(self.Consume(), left, self.ParseRelationalExpression())
node := newBinaryOperationNode(self.Consume(), left, self.ParseRelationalExpression())
self.markNode(node)
return node
case "in":
if !self.Scope().AllowIn {
return left
}
return newBinaryOperationNode(self.Consume(), left, self.ParseRelationalExpression())
node := newBinaryOperationNode(self.Consume(), left, self.ParseRelationalExpression())
self.markNode(node)
return node
}
@ -316,6 +348,7 @@ REPEAT:
switch self.Peek().Kind {
case "==", "!=", "===", "!==":
left = newComparisonNode(self.Consume(), left, self.ParseRelationalExpression())
self.markNode(left)
goto REPEAT
}
@ -327,6 +360,7 @@ func (self *_parser) ParseBitwiseANDExpression() _node {
for self.Match("&") {
left = newBinaryOperationNode(self.Consume(), left, self.ParseEqualityExpression())
self.markNode(left)
}
return left
@ -337,6 +371,7 @@ func (self *_parser) ParseBitwiseXORExpression() _node {
for self.Match("^") {
left = newBinaryOperationNode(self.Consume(), left, self.ParseBitwiseANDExpression())
self.markNode(left)
}
return left
@ -347,6 +382,7 @@ func (self *_parser) ParseBitwiseORExpression() _node {
for self.Match("|") {
left = newBinaryOperationNode(self.Consume(), left, self.ParseBitwiseXORExpression())
self.markNode(left)
}
return left
@ -357,6 +393,7 @@ func (self *_parser) ParseLogicalANDExpression() _node {
for self.Match("&&") {
left = newBinaryOperationNode(self.Consume(), left, self.ParseBitwiseORExpression())
self.markNode(left)
}
return left
@ -367,6 +404,7 @@ func (self *_parser) ParseLogicalORExpression() _node {
for self.Match("||") {
left = newBinaryOperationNode(self.Consume(), left, self.ParseLogicalANDExpression())
self.markNode(left)
}
return left
@ -378,7 +416,9 @@ func (self *_parser) ParseConditionlExpression() _node {
if self.Accept("?") {
consequent := self.ParseAssignmentExpression()
self.Expect(":");
return newConditionalNode(left, consequent, self.ParseAssignmentExpression())
node := newConditionalNode(left, consequent, self.ParseAssignmentExpression())
self.markNode(node)
return node
}
return left
@ -393,6 +433,7 @@ func (self *_parser) ParseAssignmentExpression() _node {
panic(self.History(-1).newSyntaxError("Invalid left-hand side in assignment"))
}
left = newAssignmentNode(self.Consume(), left, self.ParseAssignmentExpression())
self.markNode(left)
}
return left
}
@ -408,7 +449,9 @@ func (self *_parser) ParseExpression() _node {
}
nodeList = append(nodeList, self.ParseAssignmentExpression())
}
return newCommaNode(nodeList)
node := newCommaNode(nodeList)
self.markNode(node)
return node
}
return left

View File

@ -8,7 +8,9 @@ func (self *_parser) ParseStatement() _node {
switch self.Peek().Kind {
case ";":
self.Next()
return newEmptyNode()
node := newEmptyNode()
self.markNode(node)
return node
case "if":
return self.ParseIf()
case "do":
@ -30,7 +32,9 @@ func (self *_parser) ParseStatement() _node {
case "function":
self.ParseFunctionDeclaration()
// TODO Should be FunctionDeclarationStatement
return newEmptyNode()
node := newEmptyNode()
self.markNode(node)
return node
case "switch":
return self.ParseSwitch()
case "return":
@ -234,6 +238,7 @@ func (self *_parser) parseStatementUntil(stop func() bool) []_node {
func (self *_parser) ParseBlock() *_blockNode {
node := newBlockNode()
self.markNode(node)
self.Expect("{")
node.Body = self.parseStatementUntil(func() bool {
@ -250,11 +255,12 @@ func (self *_parser) ParseReturnStatement() _node {
panic(self.History(-1).newSyntaxError("Illegal return statement"))
}
if self.Match("\n") {
return newReturnNode()
}
node := newReturnNode()
self.markNode(node)
if self.Match("\n") {
return node
}
if !self.Match(";") {
if !self.Match("}") && !self.Match("EOF") {
@ -276,6 +282,7 @@ func (self *_parser) ParseThrow() _node {
}
node := newThrowNode(self.ParseExpression())
self.markNode(node)
self.ConsumeSemicolon()
@ -289,7 +296,8 @@ func (self *_parser) ParseSwitch() _node {
discriminant := self.ParseExpression()
self.Expect(")")
_switchNode := newSwitchNode(discriminant)
switchNode := newSwitchNode(discriminant)
self.markNode(switchNode)
self.Expect("{")
@ -301,17 +309,17 @@ func (self *_parser) ParseSwitch() _node {
result := self.ParseCase()
if result.Test == nil {
if _switchNode.Default != -1 {
if switchNode.Default != -1 {
panic(hereBeDragons("Already saw a default:"))
}
_switchNode.Default = i
switchNode.Default = i
}
_switchNode.AddCase(result)
switchNode.AddCase(result)
}
return nil
})
return _switchNode
return switchNode
}
func (self *_parser) ParseCase() *_caseNode {
@ -319,9 +327,11 @@ func (self *_parser) ParseCase() *_caseNode {
var node *_caseNode
if self.Accept("default") {
node = newDefaultCaseNode()
self.markNode(node)
} else {
self.Expect("case")
node = newCaseNode(self.ParseExpression())
self.markNode(node)
}
self.Expect(":")
@ -338,6 +348,7 @@ func (self *_parser) ParseCase() *_caseNode {
func (self *_parser) ParseVariable() *_variableDeclarationNode {
node := newVariableDeclarationNode(self.ConsumeIdentifier().Value)
self.markNode(node)
for _, value := range []string{"=", ":="} {
if self.Accept(value) {
@ -354,6 +365,7 @@ func (self *_parser) ParseVariableDeclaration() *_variableDeclarationListNode {
self.Expect("var")
node := newVariableDeclarationListNode()
self.markNode(node)
for {
variable := self.ParseVariable()
@ -381,12 +393,13 @@ func (self *_parser) ParseFunction(declare bool) _node {
self.Expect("function")
_functionNode := newFunctionNode()
functionNode := newFunctionNode()
self.markNode(functionNode)
if self.Match("identifier") {
identifier := self.ConsumeIdentifier()
if declare {
self.Scope().AddFunction(identifier.Value, _functionNode)
self.Scope().AddFunction(identifier.Value, functionNode)
}
}
@ -398,9 +411,9 @@ func (self *_parser) ParseFunction(declare bool) _node {
self.Expect("(")
for !self.Accept(")") {
identifier := self.ConsumeIdentifier().Value
_functionNode.AddParameter(identifier)
functionNode.AddParameter(identifier)
if identifier == "arguments" {
_functionNode.ArgumentsIsParameter = true
functionNode.ArgumentsIsParameter = true
}
if !self.Match(")") {
self.Expect(",")
@ -411,14 +424,14 @@ func (self *_parser) ParseFunction(declare bool) _node {
self.EnterScope()
defer self.LeaveScope()
self.parseInFunction(func() _node {
_functionNode.Body = self.ParseBlock().Body
functionNode.Body = self.ParseBlock().Body
return nil
})
_functionNode.VariableList = self.Scope().VariableList
_functionNode.FunctionList = self.Scope().FunctionList
functionNode.VariableList = self.Scope().VariableList
functionNode.FunctionList = self.Scope().FunctionList
}
return _functionNode;
return functionNode;
}
/*func (self *_parser) ParseFunctionParameterList() []string {*/
@ -448,6 +461,7 @@ func (self *_parser) parseForIn(into _node) *_forInNode {
})
node := newForInNode(into, source, body)
self.markNode(node)
node._labelSet[""] = true
return node
}
@ -473,6 +487,7 @@ func (self *_parser) parseFor(initial _node) *_forNode {
})
node := newForNode(initial, test, update, body)
self.markNode(node)
node._labelSet[""] = true
return node
}

View File

@ -280,3 +280,8 @@ func (self *_parser) Unexpected(token _token) *_syntaxError {
}
return token.newSyntaxError("Unexpected token %s", token.Text)
}
func (self *_parser) markNode(node _node) {
node.setPosition(self.lexer.Line)
}

View File

@ -43,7 +43,7 @@ func TestSynopsis(t *testing.T) {
{
value, err := Otto.Run("abcdefghijlmnopqrstuvwxyz.length")
Is(err, "ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined")
Is(err, "ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined (line 0)")
if err != nil {
IsTrue(value.IsUndefined())
}