diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a2f9d8..9eb5a89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,9 @@ _None_ ### Internal Changes -_None_ +- `Token` type converted to struct to allow computing token components only once. + [Ilya Puchka](https://github.com/ilyapuchka) + [#256](https://github.com/stencilproject/Stencil/pull/256) ## 0.13.1 diff --git a/Sources/FilterTag.swift b/Sources/FilterTag.swift index 4cf9746..9371b3c 100644 --- a/Sources/FilterTag.swift +++ b/Sources/FilterTag.swift @@ -4,7 +4,7 @@ class FilterNode : NodeType { let token: Token? class func parse(_ parser: TokenParser, token: Token) throws -> NodeType { - let bits = token.components() + let bits = token.components guard bits.count == 2 else { throw TemplateSyntaxError("'filter' tag takes one argument, the filter expression") diff --git a/Sources/ForTag.swift b/Sources/ForTag.swift index 0bc8775..92d24be 100644 --- a/Sources/ForTag.swift +++ b/Sources/ForTag.swift @@ -9,7 +9,7 @@ class ForNode : NodeType { let token: Token? class func parse(_ parser:TokenParser, token:Token) throws -> NodeType { - let components = token.components() + let components = token.components func hasToken(_ token: String, at index: Int) -> Bool { return components.count > (index + 1) && components[index] == token diff --git a/Sources/IfTag.swift b/Sources/IfTag.swift index 7376054..125f55f 100644 --- a/Sources/IfTag.swift +++ b/Sources/IfTag.swift @@ -240,7 +240,7 @@ class IfNode : NodeType { let token: Token? class func parse(_ parser: TokenParser, token: Token) throws -> NodeType { - var components = token.components() + var components = token.components components.removeFirst() let expression = try parseExpression(components: components, tokenParser: parser, token: token) @@ -251,7 +251,7 @@ class IfNode : NodeType { var nextToken = parser.nextToken() while let current = nextToken, current.contents.hasPrefix("elif") { - var components = current.components() + var components = current.components components.removeFirst() let expression = try parseExpression(components: components, tokenParser: parser, token: current) @@ -273,7 +273,7 @@ class IfNode : NodeType { } class func parse_ifnot(_ parser: TokenParser, token: Token) throws -> NodeType { - var components = token.components() + var components = token.components guard components.count == 2 else { throw TemplateSyntaxError("'ifnot' statements should use the following syntax 'ifnot condition'.") } diff --git a/Sources/Include.swift b/Sources/Include.swift index d6287cc..9c82b68 100644 --- a/Sources/Include.swift +++ b/Sources/Include.swift @@ -7,7 +7,7 @@ class IncludeNode : NodeType { let token: Token? class func parse(_ parser: TokenParser, token: Token) throws -> NodeType { - let bits = token.components() + let bits = token.components guard bits.count == 2 || bits.count == 3 else { throw TemplateSyntaxError(""" diff --git a/Sources/Inheritence.swift b/Sources/Inheritence.swift index c276d10..e512bfb 100644 --- a/Sources/Inheritence.swift +++ b/Sources/Inheritence.swift @@ -53,7 +53,7 @@ class ExtendsNode : NodeType { let token: Token? class func parse(_ parser: TokenParser, token: Token) throws -> NodeType { - let bits = token.components() + let bits = token.components guard bits.count == 2 else { throw TemplateSyntaxError("'extends' takes one argument, the template file to be extended") @@ -124,7 +124,7 @@ class BlockNode : NodeType { let token: Token? class func parse(_ parser: TokenParser, token: Token) throws -> NodeType { - let bits = token.components() + let bits = token.components guard bits.count == 2 else { throw TemplateSyntaxError("'block' tag takes one argument, the block name") @@ -163,7 +163,7 @@ class BlockNode : NodeType { var childContext: [String: Any] = [BlockContext.contextKey: blockContext] if let blockSuperNode = child.nodes.first(where: { - if case .variable(let variable, _)? = $0.token, variable == "block.super" { return true } + if let token = $0.token, case .variable = token.kind, token.contents == "block.super" { return true } else { return false} }) { do { diff --git a/Sources/Node.swift b/Sources/Node.swift index dc7a72c..7e0c420 100644 --- a/Sources/Node.swift +++ b/Sources/Node.swift @@ -62,7 +62,7 @@ public class VariableNode : NodeType { let elseExpression: Resolvable? class func parse(_ parser:TokenParser, token:Token) throws -> NodeType { - var components = token.components() + var components = token.components func hasToken(_ token: String, at index: Int) -> Bool { return components.count > (index + 1) && components[index] == token diff --git a/Sources/NowTag.swift b/Sources/NowTag.swift index 6d354ed..ac0ccfb 100644 --- a/Sources/NowTag.swift +++ b/Sources/NowTag.swift @@ -9,7 +9,7 @@ class NowNode : NodeType { class func parse(_ parser:TokenParser, token:Token) throws -> NodeType { var format:Variable? - let components = token.components() + let components = token.components guard components.count <= 2 else { throw TemplateSyntaxError("'now' tags may only have one argument: the format string.") } diff --git a/Sources/Parser.swift b/Sources/Parser.swift index 1f5d50d..d95f546 100644 --- a/Sources/Parser.swift +++ b/Sources/Parser.swift @@ -1,6 +1,6 @@ public func until(_ tags: [String]) -> ((TokenParser, Token) -> Bool) { return { parser, token in - if let name = token.components().first { + if let name = token.components.first { for tag in tags { if name == tag { return true @@ -36,9 +36,9 @@ public class TokenParser { while tokens.count > 0 { let token = nextToken()! - switch token { - case .text(let text, _): - nodes.append(TextNode(text: text)) + switch token.kind { + case .text: + nodes.append(TextNode(text: token.contents)) case .variable: try nodes.append(VariableNode.parse(self, token: token)) case .block: @@ -47,7 +47,7 @@ public class TokenParser { return nodes } - if let tag = token.components().first { + if let tag = token.components.first { do { let parser = try findTag(name: tag) let node = try parser(self, token) diff --git a/Sources/Tokenizer.swift b/Sources/Tokenizer.swift index 54e6891..cb5d483 100644 --- a/Sources/Tokenizer.swift +++ b/Sources/Tokenizer.swift @@ -71,47 +71,53 @@ public struct SourceMap: Equatable { } } -public enum Token : Equatable { +public class Token: Equatable { + public enum Kind: Equatable { + /// A token representing a piece of text. + case text + /// A token representing a variable. + case variable + /// A token representing a comment. + case comment + /// A token representing a template block. + case block + } + + public let contents: String + public let kind: Kind + public let sourceMap: SourceMap + + /// Returns the underlying value as an array seperated by spaces + public private(set) lazy var components: [String] = self.contents.smartSplit() + + init(contents: String, kind: Kind, sourceMap: SourceMap) { + self.contents = contents + self.kind = kind + self.sourceMap = sourceMap + } + /// A token representing a piece of text. - case text(value: String, at: SourceMap) + public static func text(value: String, at sourceMap: SourceMap) -> Token { + return Token(contents: value, kind: .text, sourceMap: sourceMap) + } /// A token representing a variable. - case variable(value: String, at: SourceMap) + public static func variable(value: String, at sourceMap: SourceMap) -> Token { + return Token(contents: value, kind: .variable, sourceMap: sourceMap) + } /// A token representing a comment. - case comment(value: String, at: SourceMap) + public static func comment(value: String, at sourceMap: SourceMap) -> Token { + return Token(contents: value, kind: .comment, sourceMap: sourceMap) + } /// A token representing a template block. - case block(value: String, at: SourceMap) - - /// Returns the underlying value as an array seperated by spaces - public func components() -> [String] { - switch self { - case .block(let value, _), - .variable(let value, _), - .text(let value, _), - .comment(let value, _): - return value.smartSplit() - } + public static func block(value: String, at sourceMap: SourceMap) -> Token { + return Token(contents: value, kind: .block, sourceMap: sourceMap) + } + + public static func == (lhs: Token, rhs: Token) -> Bool { + return lhs.contents == rhs.contents && lhs.kind == rhs.kind && lhs.sourceMap == rhs.sourceMap } - public var contents: String { - switch self { - case .block(let value, _), - .variable(let value, _), - .text(let value, _), - .comment(let value, _): - return value - } - } - - public var sourceMap: SourceMap { - switch self { - case .block(_, let sourceMap), - .variable(_, let sourceMap), - .text(_, let sourceMap), - .comment(_, let sourceMap): - return sourceMap - } - } } diff --git a/Tests/StencilTests/TokenSpec.swift b/Tests/StencilTests/TokenSpec.swift index 0722b6d..ee4be2b 100644 --- a/Tests/StencilTests/TokenSpec.swift +++ b/Tests/StencilTests/TokenSpec.swift @@ -7,7 +7,7 @@ class TokenTests: XCTestCase { describe("Token") { $0.it("can split the contents into components") { let token = Token.text(value: "hello world", at: .unknown) - let components = token.components() + let components = token.components try expect(components.count) == 2 try expect(components[0]) == "hello" @@ -16,7 +16,7 @@ class TokenTests: XCTestCase { $0.it("can split the contents into components with single quoted strings") { let token = Token.text(value: "hello 'kyle fuller'", at: .unknown) - let components = token.components() + let components = token.components try expect(components.count) == 2 try expect(components[0]) == "hello" @@ -25,7 +25,7 @@ class TokenTests: XCTestCase { $0.it("can split the contents into components with double quoted strings") { let token = Token.text(value: "hello \"kyle fuller\"", at: .unknown) - let components = token.components() + let components = token.components try expect(components.count) == 2 try expect(components[0]) == "hello"