Skip to content

Commit

Permalink
Evaluator - error handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
macfeteria committed Jan 15, 2019
1 parent 1144c95 commit be903b6
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 33 deletions.
33 changes: 20 additions & 13 deletions Sources/swiftmonkey/Evaluator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ public struct Evaluator {
case is PrefixExpression:
let pre = node as! PrefixExpression
let right = eval(node: pre.right!)
if isError(obj: right) { return right }
return evalPrefixExpression(oper: pre.operatorLiteral, right: right)
case is InfixExpression:
let infix = node as! InfixExpression
let left = eval(node: infix.left)
if isError(obj: left) { return left }
let right = eval(node: infix.right!)
if isError(obj: right) { return right }
return evalInfixExpression(oper: infix.operatorLiteral, left: left, right: right)
case is BlockStatement:
let block = node as! BlockStatement
Expand All @@ -44,6 +47,7 @@ public struct Evaluator {
case is ReturnStatement:
let returnStmt = node as! ReturnStatement
let value = eval(node:returnStmt.returnValue!)
if isError(obj: value) { return value }
return ReturnValueObj(value: value)
default:
return Evaluator.NULL
Expand All @@ -57,6 +61,9 @@ public struct Evaluator {
if let returnValue = result as? ReturnValueObj {
return returnValue.value
}
if let error = result as? ErrorObj {
return error
}
}
return result
}
Expand All @@ -66,21 +73,13 @@ public struct Evaluator {
for s in block.statements {
result = eval(node: s)
let type = result.type()
if type != ObjectType.NULL && type == ObjectType.RETURN_VALUE {
if type == ObjectType.ERROR || type == ObjectType.RETURN_VALUE {
return result
}
}
return result
}

// func eval(statements:[Statement]) -> Object {
// var result: Object = NullObj()
// for s in statements {
// result = eval(node: s)
// }
// return result
// }

func evalInfixExpression(oper: String, left:Object, right: Object) -> Object {
if left.type() == ObjectType.INTEGER && right.type() == ObjectType.INTEGER {
return evalIntegerExpression(oper: oper, left: left, right: right)
Expand All @@ -96,8 +95,11 @@ public struct Evaluator {
return leftBool.value != rightBool.value ? Evaluator.TRUE : Evaluator.FALSE
}
}
if left.type() != right.type() {
return ErrorObj(message: "type mismatch: \(left.type()) \(oper) \(right.type())")
}

return Evaluator.NULL
return ErrorObj(message: "unknow operator: \(left.type()) \(oper) \(right.type())")
}


Expand All @@ -108,7 +110,7 @@ public struct Evaluator {
case "-" :
return evalMinusPrefixOperator(right: right)
default:
return Evaluator.NULL
return ErrorObj(message: "unknow operator: \(oper) \(right.type())")
}
}

Expand All @@ -133,12 +135,12 @@ public struct Evaluator {
case "!=" :
return leftValue != rightValue ? Evaluator.TRUE : Evaluator.FALSE
default:
return Evaluator.NULL
return ErrorObj(message: "unknow operator: \(left.type()) \(oper) \(right.type())")
}
}
func evalMinusPrefixOperator(right: Object) -> Object {
if right.type() != ObjectType.INTEGER {
return Evaluator.NULL
return ErrorObj(message: "unknow operator: -\(right.type())")
}
let intObj = right as! IntegerObj
return IntegerObj(value: -intObj.value)
Expand All @@ -158,6 +160,7 @@ public struct Evaluator {

func evalIfExpression(expression: IfExpression) -> Object {
let condition = eval(node: expression.condition)
if isError(obj: condition) { return condition }
if isTruthy(obj: condition) {
return eval(node: expression.consequence)
} else if let alter = expression.alternative {
Expand All @@ -179,4 +182,8 @@ public struct Evaluator {
}
return true
}

func isError(obj: Object) -> Bool {
return obj.type() == ObjectType.ERROR
}
}
13 changes: 12 additions & 1 deletion Sources/swiftmonkey/Object.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public enum ObjectType {
case BOOLEAN
case RETURN_VALUE
case NULL
case ERROR
}

public protocol Object {
Expand All @@ -28,7 +29,6 @@ struct IntegerObj:Object {
func inspect() -> String {
return "\(value)"
}

}

struct BooleanObj:Object, Equatable {
Expand Down Expand Up @@ -65,3 +65,14 @@ struct NullObj:Object {
return "null"
}
}

struct ErrorObj:Object {
var message:String
func type() -> ObjectType {
return ObjectType.ERROR
}

func inspect() -> String {
return "\(message)"
}
}
28 changes: 25 additions & 3 deletions Tests/swiftmonkeyTests/EvaluatorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ class EvaluatorTests: XCTestCase {

func validateIntegerObject(obj:Object, expect: Int) {
let intObj = obj as! IntegerObj
XCTAssertTrue(intObj.value == expect)
XCTAssertTrue(intObj.value == expect, "Expect \(expect) Got \(intObj.value)")
}

func validateBooleanObject(obj:Object, expect: Bool) {
let intObj = obj as! BooleanObj
XCTAssertTrue(intObj.value == expect)
let boolObj = obj as! BooleanObj
XCTAssertTrue(boolObj.value == expect,"Expect \(expect) Got \(boolObj.value)")
}

func validateNullObject(obj:Object) {
Expand Down Expand Up @@ -149,5 +149,27 @@ class EvaluatorTests: XCTestCase {
}
}

func testErrorHandling () {
let tests = [
(code:"5 + true;", expectedValue:"type mismatch: INTEGER + BOOLEAN"),
(code:"5 + true; 5;", expectedValue:"type mismatch: INTEGER + BOOLEAN"),
(code:"-true;", expectedValue:"unknow operator: -BOOLEAN"),
(code:"true + false;", expectedValue:"unknow operator: BOOLEAN + BOOLEAN"),
(code:"5; true + false; 5", expectedValue:"unknow operator: BOOLEAN + BOOLEAN"),
(code:"if (10 > 1) { true + false; }", expectedValue:"unknow operator: BOOLEAN + BOOLEAN"),
(code:"""
if ( 10 > 1 ) {
if ( 10 > 1 ) {
return true + false;
}
return 1;
}
""", expectedValue:"unknow operator: BOOLEAN + BOOLEAN"),
]
for test in tests {
let resultObj = evaluate(input: test.code)
XCTAssertTrue(resultObj.inspect() == test.expectedValue, "Expect \(test.expectedValue) Got \(resultObj.inspect())" )
}
}

}
32 changes: 16 additions & 16 deletions Tests/swiftmonkeyTests/ParserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,23 @@ class ParserTests: XCTestCase {
}

func validateInteger(integerLiteral: IntegerLiteral, result: Int ) {
XCTAssertTrue(integerLiteral.value == result)
XCTAssertTrue(integerLiteral.tokenLiteral() == "\(result)")
XCTAssertTrue(integerLiteral.value == result, "Expect \(integerLiteral.value) Got \(result)")
XCTAssertTrue(integerLiteral.tokenLiteral() == "\(result)", "Expect \(integerLiteral.tokenLiteral()) Got \(result)")
}

func validateIdentifier(identifier: Identifier, result: String ) {
XCTAssertTrue(identifier.value == result)
XCTAssertTrue(identifier.tokenLiteral() == result)
XCTAssertTrue(identifier.value == result, "Expect \(identifier.value) Got \(result)")
XCTAssertTrue(identifier.tokenLiteral() == result, "Expect \(identifier.tokenLiteral()) Got \(result)")
}

func validateBoolean(boolean: Boolean, result: Bool) {
XCTAssertTrue(boolean.value == result)
XCTAssertTrue(boolean.tokenLiteral() == String(result))
XCTAssertTrue(boolean.tokenLiteral() == String(result), "Expect \(boolean.tokenLiteral()) Got \(result)")
}

func validateInfix<T>(infix: InfixExpression, left: T, op: String, right: T) {
validateLiteralExpression(expression: infix, result: left)
XCTAssertTrue(infix.operatorLiteral == op)
XCTAssertTrue(infix.operatorLiteral == op, "Expect \(infix.operatorLiteral) Got \(op)")
validateLiteralExpression(expression: infix, result: right)
}

Expand All @@ -62,7 +62,7 @@ class ParserTests: XCTestCase {
for e in parser.errors {
print(e)
}
XCTAssertTrue(parser.errors.count == 0)
XCTAssertTrue(parser.errors.count == 0, "Expect no error")
}

func testLetStatement() {
Expand All @@ -85,8 +85,8 @@ class ParserTests: XCTestCase {
validateParserError(parser: parser)

let letStatement = program.statements[0] as! LetStatement
XCTAssertTrue(letStatement.name.tokenLiteral() == test.expectedIdentifier)
XCTAssertTrue(letStatement.name.value == test.expectedIdentifier)
XCTAssertTrue(letStatement.name.tokenLiteral() == test.expectedIdentifier, "Expect \(test.expectedIdentifier) Got \(letStatement.name.tokenLiteral())")
XCTAssertTrue(letStatement.name.value == test.expectedIdentifier, "Expect \(test.expectedIdentifier) Got \(letStatement.name.value)")

validateLiteralExpression(expression: letStatement.value, result: test.expectedValue)
}
Expand All @@ -103,7 +103,7 @@ class ParserTests: XCTestCase {
let lexer = Lexer(input: code)
let parser = Parser(lexer: lexer)
let _ = parser.parseProgram()
XCTAssertTrue(parser.errors.count != 0)
XCTAssertTrue(parser.errors.count != 0, "Expect no error")
}


Expand Down Expand Up @@ -175,8 +175,8 @@ class ParserTests: XCTestCase {
XCTAssertTrue(expression.operatorLiteral == test.oper)

let integerLit = expression.right as! IntegerLiteral
XCTAssertTrue(integerLit.value == test.intValue)
XCTAssertTrue(integerLit.tokenLiteral() == "\(test.intValue)")
XCTAssertTrue(integerLit.value == test.intValue, "Expect \(test.intValue) Got \(integerLit.value)")
XCTAssertTrue(integerLit.tokenLiteral() == "\(test.intValue)", "Expect \(test.intValue) Got \(integerLit.tokenLiteral())")
}
}

Expand All @@ -197,9 +197,9 @@ class ParserTests: XCTestCase {

XCTAssertTrue(expression.operatorLiteral == test.oper)

let integerLit = expression.right as! Boolean
XCTAssertTrue(integerLit.value == test.value)
XCTAssertTrue(integerLit.tokenLiteral() == "\(test.value)")
let boolLit = expression.right as! Boolean
XCTAssertTrue(boolLit.value == test.value, "Expect \(test.value) Got \(boolLit.value)")
XCTAssertTrue(boolLit.tokenLiteral() == "\(test.value)", "Expect \(test.value) Got \(boolLit.tokenLiteral())")
}
}

Expand Down Expand Up @@ -278,7 +278,7 @@ class ParserTests: XCTestCase {
let program = parser.parseProgram()
validateParserError(parser: parser)
let result = program.string()
XCTAssertTrue(result == test.expected)
XCTAssertTrue(result == test.expected , "Expect \(test.expected) Got \(result)" )
}
}

Expand Down

0 comments on commit be903b6

Please sign in to comment.