Evaluation is swift library which can evaluate content of String and return result or postfix notation. This library is runnable on Linux too :)
Operands must be same type
Operation | Precedence | Type | Operands type |
---|---|---|---|
! |
200 | Unary | Bool |
* |
150 | Binary | Int , UInt , Double , Float |
/ |
150 | Binary | Int , UInt , Double , Float |
% |
150 | Binary | Int , UInt |
+ |
140 | Binary | Int , UInt , Double , Float , String |
- |
140 | Binary | Int , UInt , Double , Float |
?? |
131 | Binary | Any? |
== |
130 | Binary | Int , UInt , Double , Float , String , Bool , nil |
!= |
130 | Binary | Int , UInt , Double , Float , String , Bool , nil |
< |
130 | Binary | Int , UInt , Double , Float , String |
<= |
130 | Binary | Int , UInt , Double , Float , String |
> |
130 | Binary | Int , UInt , Double , Float , String |
>= |
130 | Binary | Int , UInt , Double , Float , String |
&& |
120 | Binary | Bool |
|| |
110 | Binary | Bool |
You can cast with theese functions:
String(_) -> String
Double(_) -> Double?
Float(_) -> Float?
Int(_) -> Int?
UInt(_) -> UInt?
Bool(_) -> Bool?
You can use evaluations with String extension or with Evaluation
class just don't forget to import library
import Evaluation
This class gives you power to evaluate from postfix tokens.
public class PostfixEvaluation {
/// Serialized postfix in JSON format
public func serializedPostfix() throws -> Data
public func serializedPostfix() throws -> String
/// Construct from JSON
public init(postfix: String) throws
public init(postfixData: Data) throws
/// Evaluate
public func evaluate(with data: [String: Any] = [:]) throws -> Any?
}
InfixEvaluation class divide proccess to two parts, first token parsing and postfix transformation and second evaluates expression.
public class InfixEvaluation: PostfixEvaluation {
/// Serialized postfix in JSON format
public func serializedPostfix() throws -> Data
public func serializedPostfix() throws -> String
/// Perform lexical analysis and makes postfix notation of given expression
///
/// - Parameter string: expression
/// - Throws: lexical and syntax error as `EvaluationError`
public init(expression string: String) throws
/// Evaluate expression and return result
///
/// - Parameter data: Dictionary with variables used in expression
/// - Returns: result of expression
/// - Throws: EvaluationError
public func evaluate(with data: [String : Any] = default) throws -> Any?
}
We add string extenstion so all you need to write is:
try "1+5".evaluate() as! Int // 6
"1+5".evaluateAsInt()! // 6
public extension String {
public func evaluate(with data: [String : Any] = default) throws -> Any?
public func evaluateAsString(with data: [String : Any] = default) -> String?
public func evaluateAsBool(with data: [String : Any] = default) -> Bool?
public func evaluateAsInt(with data: [String : Any] = default) -> Int?
public func evaluateAsUInt(with data: [String : Any] = default) -> UInt?
public func evaluateAsDouble(with data: [String : Any] = default) -> Double?
public func evaluateAsFloat(with data: [String : Any] = default) -> Float?
}
You can use variables in evaluation if you pass [String: Any]
to evaluate(with:)
then you can access them with dot notation
let data: [String: Any] = [
"name": "John",
"sleeps": 8
]
// John is awake 16 hours per day
"name + \" is awake \" + String(24 - sleeps) + \" hours per day\"".evaluateAsString(with: data)!
If you are sure about variable type ([Any] or [String: Any]) you can use .count
to get number of elements
let data: [String: Any] = [
"numbers": [1, 2 ,3 , 4]
]
"numbers.count".evaluateAsInt(with: data)! // 4
import Evaluation
// If you wan to know if something goes wrong use Evaluation class
do {
let eval = try InfixEvaluation(expression: "1 + 5 < seven")
if let result = (try eval.evaluate(with: ["seven": 7])) as? Bool {
print(result) // true
}
} catch let error {
print(error)
}
// when you are interested only in result you can use extension
print("1 + 5".evaluateAsInt()!) // 6
let data = [
"person1": [
"name": "Matt",
"age": 21,
"male": true,
"children": [
"Ann",
"Matt Jr."
]
],
"person2": [
"name": "Miriam",
"age": 19,
"male": false,
"children": [String]()
]
]
print("person1.name != person2.name".evaluateAsBool(with: data)!) // true
print("person1.children.count == person2.children.count".evaluateAsBool(with: data)!) // false
// You can change type like this
print("Int( (Double(person1.age) + Double(person2.age)) / 2.0 )".evaluateAsInt(with: data)!) // 20
// JSON usage
do {
let infixEval = try InfixEvaluation(expression: "3 + 1")
let postfix: String = try infixEval.serializedPostfix()
print(postfix) // [{"type":{"int":"Int"},"value":"3"},{"type":{"int":"Int"},"value":"1"},{"type":{"operation":"+"},"value":"+"}]
let postfixEval = try PostfixEvaluation(postfix: postfix)
let result = try postfixEval.evaluate() as! Int
print(result) // 4
} catch let error {
print(error)
}
If something goes wrong Evaluation throw EvaluationError
struct with attributes kind
and description
public struct EvaluationError: Error, CustomStringConvertible {
public enum ErrorKind {
case canNotApplyBinOperand(oper: String, type1: String, type2: String)
case canNotApplyUnaryOperand(oper: String, type: String)
case unexpectedEnd(reading: String)
case unexpectedCharacter(reading: String, expected: String, got: String)
case syntaxError
case missingValue(forOperand: String)
case nilFound(expr: String)
case decodingError
case encodingError
case unknownOperation(operation: String)
}
/// Error kind
public let kind: ErrorKind
/// A textual representation of this instance.
public var description: String { get }
}
You can add this to Package.swift
dependencies: [
.package(url: "https://github.com/LeoNavel/Evaluation.git", from: "0.3.0")
]