Skip to content

Commit

Permalink
Release 13.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
robot-divkit committed Oct 31, 2022
1 parent 747051f commit 59d7338
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 61 deletions.
23 changes: 17 additions & 6 deletions DivKit/Expressions/CalcExpression/CalcExpression.swift
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ final class CalcExpression: CustomStringConvertible {
/// An array was accessed with an index outside the valid range
case arrayBounds(Symbol, Double)

case escaping

/// Empty expression
static let emptyExpression = unexpectedToken("")

Expand All @@ -188,7 +190,7 @@ final class CalcExpression: CustomStringConvertible {
case .emptyExpression:
return "Empty expression"
case let .unexpectedToken(string):
return "Unexpected token `\(string)`"
return "Error tokenizing '\(string)'."
case let .missingDelimiter(string):
return "Missing `\(string)`"
case let .undefinedSymbol(symbol):
Expand All @@ -215,6 +217,8 @@ final class CalcExpression: CustomStringConvertible {
return "\(description.prefix(1).uppercased())\(description.dropFirst()) expects \(arity)"
case let .arrayBounds(symbol, index):
return "Index \(CalcExpression.stringify(index)) out of bounds for \(symbol)"
case .escaping:
return "Incorrect string escape"
}
}
}
Expand Down Expand Up @@ -454,10 +458,11 @@ extension CalcExpression {
// comparison: -4
"&&": -5, "and": -5, // and
"||": -6, "or": -6, // or
"?": -7, ":": -7, // ternary
// assignment: -8
":": -8, // ternary
// assignment: -9
",": -100,
].mapValues { ($0, false) }
precedences["?"] = (-7, true) // ternary
let comparisonOperators = [
"<", "<=", ">=", ">",
"==", "!=", "===", "!==",
Expand All @@ -471,7 +476,7 @@ extension CalcExpression {
"<<=", ">>=", "&=", "^=", "|=", ":=",
]
for op in assignmentOperators {
precedences[op] = (-8, true)
precedences[op] = (-9, true)
}
return precedences
}()
Expand Down Expand Up @@ -1148,9 +1153,14 @@ extension UnicodeScalarView {
return .error(.unexpectedToken(hex), string)
}
string.append(Character(c))
default:
case "'", "\\":
string.append(Character(c))
case "@" where scanCharacter("{"):
string += "@{"
default:
return .error(.escaping, string)
}
part = ""
}
} while part != nil
guard scanCharacter(delimiter) else {
Expand Down Expand Up @@ -1215,7 +1225,8 @@ extension UnicodeScalarView {
} else {
stack[i...i + 2] = [.symbol(.infix(symbol.name), [lhs, rhs], nil)]
}
try collapseStack(from: 0)
let from = symbol.name == "?" ? i : 0
try collapseStack(from: from)
} else if case let .symbol(symbol2, _, _) = rhs {
if case .prefix = symbol2 {
try collapseStack(from: i + 2)
Expand Down
9 changes: 6 additions & 3 deletions DivKit/Expressions/ExpressionError.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

public enum ExpressionError: Error {
case incorrectExpression(expression: String)
case tokenizing(expression: String)
case emptyValue
case incorrectSingleItemExpression(expression: String, type: Any.Type)
case initializingValue(
Expand All @@ -19,14 +19,15 @@ public enum ExpressionError: Error {
value: String,
type: Any.Type
)
case escaping
case unknown(error: Error)
}

extension ExpressionError: CustomStringConvertible {
public var description: String {
switch self {
case let .incorrectExpression(expression):
return "Incorrect expression: '\(expression)'"
case let .tokenizing(expression):
return "Error tokenizing '\(expression)'."
case .emptyValue:
return "Empty value"
case let .incorrectSingleItemExpression(expression, type):
Expand All @@ -37,6 +38,8 @@ extension ExpressionError: CustomStringConvertible {
return "Error on initializing value '\(stringValue)' of type \(type), expression: '\(expression)'"
case let .validating(expression, value, type):
return "Value '\(value)' did not pass validation for type \(type), expression: '\(expression)'"
case .escaping:
return "Incorrect string escape"
case let .unknown(error):
return "Unknown error: \(error)"
}
Expand Down
27 changes: 16 additions & 11 deletions DivKit/Expressions/ExpressionLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public struct ExpressionLink<T> {
validator: ExpressionValueValidator<T>?,
errorTracker: ExpressionErrorTracker? = nil,
resolveNested: Bool = true
) {
self.init(
) throws {
try self.init(
rawValue: "@{\(expression)}",
validator: validator,
errorTracker: errorTracker,
Expand All @@ -35,7 +35,7 @@ public struct ExpressionLink<T> {
validator: ExpressionValueValidator<T>?,
errorTracker: ExpressionErrorTracker? = nil,
resolveNested: Bool = true
) {
) throws {
guard !rawValue.isEmpty else {
errorTracker?(.emptyValue)
return nil
Expand All @@ -50,8 +50,8 @@ public struct ExpressionLink<T> {
let currentValue = rawValue[index..<endIndex]
if rawValue.hasExpression(at: index) {
guard let (start, end) = currentValue.makeLinkIndices() else {
errorTracker?(.incorrectExpression(expression: rawValue))
return nil
errorTracker?(.tokenizing(expression: rawValue))
throw ExpressionError.tokenizing(expression: rawValue)
}
if !currentString.isEmpty {
items.append(.string(currentString))
Expand All @@ -61,7 +61,7 @@ public struct ExpressionLink<T> {
items.append(.string(""))
} else {
let value = String(currentValue[start...end])
if resolveNested, let link = ExpressionLink<String>(
if resolveNested, let link = try ExpressionLink<String>(
rawValue: value,
validator: nil,
errorTracker: errorTracker
Expand All @@ -78,6 +78,9 @@ public struct ExpressionLink<T> {
currentString = currentString + String(currentValue[index])
index = currentValue.index(after: index)
if index == endIndex {
if currentString == rawValue {
return nil
}
items.append(.string(currentString))
}
}
Expand Down Expand Up @@ -114,17 +117,19 @@ extension StringProtocol {
return i
}
}
if char == "'" {
if i == startIndex || self[index(before: i)] != "\\" {
stringStarted[nestedExpressionCounter] = !stringStarted[nestedExpressionCounter]!
}
if char == "'" && notEscaped(at: i) {
stringStarted[nestedExpressionCounter] = !stringStarted[nestedExpressionCounter]!
}
}
return nil
}

fileprivate func hasExpression(at i: String.Index) -> Bool {
self[i...].hasPrefix(expressionPrefix) && (i == startIndex || self[index(before: i)] != "\\")
self[i...].hasPrefix(expressionPrefix) && notEscaped(at: i)
}

fileprivate func notEscaped(at i: String.Index) -> Bool {
i == startIndex || self[index(before: i)] != "\\" || !notEscaped(at: index(before: i))
}
}

Expand Down
43 changes: 37 additions & 6 deletions DivKit/Expressions/ExpressionResolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,13 @@ public final class ExpressionResolver {
) ?? T(rawValue: expression)
}

@inlinable
func resolveStringBasedValue<T>(
expression: Expression<T>?,
initializer: (String) -> T?
) -> T? {
switch expression {
case let .value(val):
return val
return resolveEscaping(val)
case let .link(link):
return evaluateStringBasedValue(
link: link,
Expand All @@ -70,6 +69,39 @@ public final class ExpressionResolver {
}
}

func resolveEscaping<T>(_ value: T?) -> T? {
guard let value = value as? String else {
return value
}
var result = ""

var index = value.startIndex
let escapingValues = ["@{", "'", "\\"]

while (index < value.endIndex) {
if value[index] == "\\" {
index = value.index(index, offsetBy: 1)
let next = value[index...]
if let escaped = escapingValues.first(where: { next.starts(with: $0) }) {
result += escaped
index = value.index(index, offsetBy: escaped.count)
} else {
if next.isEmpty {
errorTracker(ExpressionError.tokenizing(expression: value))
} else {
errorTracker(ExpressionError.escaping)
}
return nil
}
} else {
result += value[index]
index = value.index(after: index)
}
}

return result as? T
}

@inlinable
func resolveNumericValue<T>(
expression: Expression<T>?
Expand Down Expand Up @@ -146,13 +178,13 @@ public final class ExpressionResolver {
return nil
}
case let .string(value):
stringValue += value.replacingOccurrences(of: "\\@{", with: "@{")
stringValue += value
case let .nestedCalcExpression(link):
if let expression = evaluateStringBasedValue(
link: link,
initializer: { $0 }
) {
let link = ExpressionLink<String>(
let link = try? ExpressionLink<String>(
expression: expression,
validator: nil,
errorTracker: link.errorTracker,
Expand Down Expand Up @@ -181,7 +213,6 @@ public final class ExpressionResolver {
@inlinable
func getVariableValue<T>(_ name: String) -> T? {
guard let value: T = variables[DivVariableName(rawValue: name)]?.typedValue() else {
DivKitLogger.error("No variable: \(name)")
return nil
}
return value
Expand All @@ -207,7 +238,7 @@ public final class ExpressionResolver {
expression: String,
initializer: (String) -> T?
) -> T? {
guard let expressionLink = ExpressionLink<T>(rawValue: expression, validator: nil) else {
guard let expressionLink = try? ExpressionLink<T>(rawValue: expression, validator: nil) else {
return nil
}
return resolveStringBasedValue(
Expand Down
14 changes: 11 additions & 3 deletions DivKit/Expressions/Serialization/Expression+Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,17 @@ func expressionTransform<T, U>(
transform: (U) -> T?,
validator: ExpressionValueValidator<T>? = nil
) -> Expression<T>? {
if let rawValue = value as? String,
let resolver = ExpressionLink<T>(rawValue: rawValue, validator: validator) {
return .link(resolver)
do {
if let rawValue = value as? String,
let resolver = try ExpressionLink<T>(
rawValue: rawValue,
validator: validator,
errorTracker: { DivKitLogger.error($0.description) }
) {
return .link(resolver)
}
} catch {
return nil
}

guard let value = value else {
Expand Down
3 changes: 3 additions & 0 deletions DivKitExtensions/DivExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ extension Div {
public func makeImageURLs(with expressionResolver: ExpressionResolver) -> [URL] {
var urls: [URL] = value.background?.compactMap { $0.makeImageURL(with: expressionResolver) }
?? []
if let url = LottieExtensionHandler.getPreloadURL(div: value) {
urls.append(url)
}
switch self {
case let .divImage(divImage):
if let url = divImage.resolveImageUrl(expressionResolver) {
Expand Down
6 changes: 3 additions & 3 deletions DivKitExtensions/Lottie/AnimationBlock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@ import LayoutKit

final class AnimationBlock: SizeForwardingBlock {
let animatableView: Lazy<AnimatableView>
let animationHolder: AnimationHolder
let sizeProvider: Block

var sizeProvider: Block
var debugDescription: String {
return "Animation Block playing animation with view: \(animatableView)"
}
let holder: AnimationHolder

init(
animatableView: Lazy<AnimatableView>,
animationHolder: AnimationHolder,
sizeProvider: Block
) {
self.animatableView = animatableView
self.holder = animationHolder
self.animationHolder = animationHolder
self.sizeProvider = sizeProvider
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ extension AnimationBlock {
renderingDelegate _: RenderingDelegate?
) {
let lottieView = view as! AnimationBlockView
if lottieView.animationHolder !== holder {
if lottieView.animationHolder !== animationHolder {
lottieView.animatableView = animatableView.value
lottieView.animationHolder = holder
lottieView.animationHolder = animationHolder
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions DivKitExtensions/Lottie/LottieExtensionHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ public final class LottieExtensionHandler: DivExtensionHandler {
sizeProvider: block
)
}

static func getPreloadURL(div: DivBase) -> URL? {
let extensionData = div.extensions?.first { $0.id == "lottie" }
guard let paramsDict = extensionData?.params,
let params = LottieExtensionParams(params: paramsDict) else { return nil }
return params.source.url
}
}

private class JSONAnimationHolder: AnimationHolder {
Expand Down Expand Up @@ -133,3 +140,12 @@ private struct LottieExtensionParams {
}
}
}

extension LottieExtensionParams.Source {
fileprivate var url: URL? {
switch self {
case let .url(url): return url
case .json: return nil
}
}
}
2 changes: 1 addition & 1 deletion LayoutKit/LayoutKit/Blocks/GalleryBlock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ extension GalleryBlock {
direction: direction,
crossAlignment: crossAlignment,
scrollMode: scrollMode,
state: GalleryViewState(contentPosition: contentPosition),
state: GalleryViewState(contentPosition: contentPosition, isScrolling: false),
widthTrait: widthTrait,
heightTrait: heightTrait,
areEmptySpaceTouchesEnabled: areEmptySpaceTouchesEnabled,
Expand Down
Loading

0 comments on commit 59d7338

Please sign in to comment.