Created
July 29, 2019 20:18
-
-
Save nighthawk/060475d0ded20788e9269054e152d76d to your computer and use it in GitHub Desktop.
Eval suffix issue
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import XCTest | |
import Eval // https://github.com/tevelee/Eval | |
class MiniExpressionStandardLibraryTest: XCTestCase { | |
private func evaluate<R>(_ expression: String, inputs: [String: Any] = [:]) -> R? { | |
let context = Context(variables: inputs) | |
let interpreter = TypedInterpreter(dataTypes: MiniExpressionStandardLibrary.dataTypes, functions: MiniExpressionStandardLibrary.functions, context: context) | |
let result = interpreter.evaluate(expression) | |
return result as? R | |
} | |
func testComposition() { | |
// this doesn't work; depending on the ordering of functions either the first+third or the second fails | |
XCTAssertEqual(evaluate("(toggle == true) and (url exists)", inputs: ["toggle": true, "url": 1]), true) | |
XCTAssertEqual(evaluate("(toggle == true) and (url exists)", inputs: ["toggle": true]), false) | |
XCTAssertEqual(evaluate("(toggle == true) and (not(url exists))", inputs: ["toggle": true]), true) | |
} | |
func testComposition2() { | |
// this does work... | |
XCTAssertEqual(evaluate("(toggle == true) and (didset url)", inputs: ["toggle": true, "url": 1]), true) | |
XCTAssertEqual(evaluate("(toggle == true) and (didset url)", inputs: ["toggle": true]), false) | |
XCTAssertEqual(evaluate("(toggle == true) and (not(didset url))", inputs: ["toggle": true]), true) | |
} | |
} | |
class MiniExpressionStandardLibrary { | |
static var dataTypes: [DataTypeProtocol] { | |
return [ | |
booleanType, | |
] | |
} | |
static var functions: [FunctionProtocol] { | |
return [ | |
boolParentheses, | |
boolEqualsOperator, | |
andOperator, | |
existsOperator, | |
didsetOperator, | |
] | |
} | |
// MARK: - Types | |
static var booleanType: DataType<Bool> { | |
let trueLiteral = Literal("true", convertsTo: true) | |
let falseLiteral = Literal("false", convertsTo: false) | |
return DataType(type: Bool.self, literals: [trueLiteral, falseLiteral]) { $0.value ? "true" : "false" } | |
} | |
// MARK: - Functions | |
static var boolEqualsOperator: Function<Bool> { | |
return infixOperator("==") { (lhs: Bool, rhs: Bool) in lhs == rhs } | |
} | |
static var boolParentheses: Function<Bool> { | |
return Function([OpenKeyword("("), Variable<Bool>("body"), CloseKeyword(")")]) { $0.variables["body"] as? Bool } | |
} | |
static var andOperator: Function<Bool> { | |
return infixOperator("and") { (lhs: Bool, rhs: Bool) in lhs && rhs } | |
} | |
static var existsOperator: Function<Bool> { | |
return suffixOperator("exists") { (expression: Any?) in expression != nil } | |
} | |
static var didsetOperator: Function<Bool> { | |
return prefixOperator("didset") { (expression: Any?) in expression != nil } | |
} | |
// MARK: - Operator helpers | |
static func infixOperator<A, B, T>(_ symbol: String, body: @escaping (A, B) -> T) -> Function<T> { | |
return Function([Variable<A>("lhs"), Keyword(symbol), Variable<B>("rhs")], options: .backwardMatch) { | |
guard let lhs = $0.variables["lhs"] as? A, let rhs = $0.variables["rhs"] as? B else { return nil } | |
return body(lhs, rhs) | |
} | |
} | |
static func prefixOperator<A, T>(_ symbol: String, body: @escaping (A) -> T) -> Function<T> { | |
return Function([Keyword(symbol), Variable<A>("value")]) { | |
guard let value = $0.variables["value"] as? A else { return nil } | |
return body(value) | |
} | |
} | |
static func suffixOperator<A, T>(_ symbol: String, body: @escaping (A) -> T) -> Function<T> { | |
return Function([Variable<A>("value"), Keyword(symbol)]) { | |
guard let value = $0.variables["value"] as? A else { return nil } | |
return body(value) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment