Swiftpack.co - Package - Swift-Squirrel/Evaluation

Evaluation

Evaluation is swift library which can evaluate content of String and return result or postfix notation. This library is runnable on Linux too :)

Operations

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 |

Casting functions

You can cast with theese functions:

String(_) -> String
Double(_) -> Double?
Float(_) -> Float?
Int(_) -> Int?
UInt(_) -> UInt?
Bool(_) -> Bool?

Usage

You can use evaluations with String extension or with Evaluation class just don't forget to import library

import Evaluation

PostfixEvaluation class

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
    public var postfix: [PostfixEvaluation.Token] { get }
    
    /// Construct from JSON
    public init(postfix: [PostfixEvaluation.Token])
    public init(postfix: String) throws 
    public init(postfixData: Data) throws
    
    /// Evaluate
    public func evaluate(with data: [String: Any] = [:]) throws -> Any?
}

InfixEvaluation class

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?
}

String extension

We add string extenstion so all you need to write is:

try "1+5".evaluate() as! Int // 6

"1+5".evaluateAsInt()! // 6

extension

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?
}

Using variables

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)!

Array function

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

Examples

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)
}

Errors

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 }
}

Installation

Package Manager

You can add this to Package.swift

dependencies: [
	.package(url: "https://github.com/Swift-Squirrel/Evaluation.git", from: "0.3.1")
]

Github

link
Stars: 0
Help us keep the lights on

Dependencies

Used By

Total: 1

Releases

0.3.0 - Sep 26, 2017

From this time you can use postfix evaluation. That means you can store postfix and use it later to complete evaluation

0.2.0 - Aug 23, 2017

Swift 4 compatibility

0.1.0 - Aug 23, 2017

Releasing string evalutaion class and extension so you can evaluate from string.