Convert Token from enum to struct (#256)

* convert Token from enum to struct

* private setter for components

* updated CHANGELOG
This commit is contained in:
Ilya Puchka
2018-09-30 21:48:44 +01:00
committed by GitHub
parent 9a6ba94d7d
commit d9f6a82f97
11 changed files with 62 additions and 54 deletions

View File

@@ -20,7 +20,9 @@ _None_
### Internal Changes ### 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 ## 0.13.1

View File

@@ -4,7 +4,7 @@ class FilterNode : NodeType {
let token: Token? let token: Token?
class func parse(_ parser: TokenParser, token: Token) throws -> NodeType { class func parse(_ parser: TokenParser, token: Token) throws -> NodeType {
let bits = token.components() let bits = token.components
guard bits.count == 2 else { guard bits.count == 2 else {
throw TemplateSyntaxError("'filter' tag takes one argument, the filter expression") throw TemplateSyntaxError("'filter' tag takes one argument, the filter expression")

View File

@@ -9,7 +9,7 @@ class ForNode : NodeType {
let token: Token? let token: Token?
class func parse(_ parser:TokenParser, token:Token) throws -> NodeType { 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 { func hasToken(_ token: String, at index: Int) -> Bool {
return components.count > (index + 1) && components[index] == token return components.count > (index + 1) && components[index] == token

View File

@@ -240,7 +240,7 @@ class IfNode : NodeType {
let token: Token? let token: Token?
class func parse(_ parser: TokenParser, token: Token) throws -> NodeType { class func parse(_ parser: TokenParser, token: Token) throws -> NodeType {
var components = token.components() var components = token.components
components.removeFirst() components.removeFirst()
let expression = try parseExpression(components: components, tokenParser: parser, token: token) let expression = try parseExpression(components: components, tokenParser: parser, token: token)
@@ -251,7 +251,7 @@ class IfNode : NodeType {
var nextToken = parser.nextToken() var nextToken = parser.nextToken()
while let current = nextToken, current.contents.hasPrefix("elif") { while let current = nextToken, current.contents.hasPrefix("elif") {
var components = current.components() var components = current.components
components.removeFirst() components.removeFirst()
let expression = try parseExpression(components: components, tokenParser: parser, token: current) 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 { class func parse_ifnot(_ parser: TokenParser, token: Token) throws -> NodeType {
var components = token.components() var components = token.components
guard components.count == 2 else { guard components.count == 2 else {
throw TemplateSyntaxError("'ifnot' statements should use the following syntax 'ifnot condition'.") throw TemplateSyntaxError("'ifnot' statements should use the following syntax 'ifnot condition'.")
} }

View File

@@ -7,7 +7,7 @@ class IncludeNode : NodeType {
let token: Token? let token: Token?
class func parse(_ parser: TokenParser, token: Token) throws -> NodeType { 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 { guard bits.count == 2 || bits.count == 3 else {
throw TemplateSyntaxError(""" throw TemplateSyntaxError("""

View File

@@ -53,7 +53,7 @@ class ExtendsNode : NodeType {
let token: Token? let token: Token?
class func parse(_ parser: TokenParser, token: Token) throws -> NodeType { class func parse(_ parser: TokenParser, token: Token) throws -> NodeType {
let bits = token.components() let bits = token.components
guard bits.count == 2 else { guard bits.count == 2 else {
throw TemplateSyntaxError("'extends' takes one argument, the template file to be extended") throw TemplateSyntaxError("'extends' takes one argument, the template file to be extended")
@@ -124,7 +124,7 @@ class BlockNode : NodeType {
let token: Token? let token: Token?
class func parse(_ parser: TokenParser, token: Token) throws -> NodeType { class func parse(_ parser: TokenParser, token: Token) throws -> NodeType {
let bits = token.components() let bits = token.components
guard bits.count == 2 else { guard bits.count == 2 else {
throw TemplateSyntaxError("'block' tag takes one argument, the block name") throw TemplateSyntaxError("'block' tag takes one argument, the block name")
@@ -163,7 +163,7 @@ class BlockNode : NodeType {
var childContext: [String: Any] = [BlockContext.contextKey: blockContext] var childContext: [String: Any] = [BlockContext.contextKey: blockContext]
if let blockSuperNode = child.nodes.first(where: { 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} else { return false}
}) { }) {
do { do {

View File

@@ -62,7 +62,7 @@ public class VariableNode : NodeType {
let elseExpression: Resolvable? let elseExpression: Resolvable?
class func parse(_ parser:TokenParser, token:Token) throws -> NodeType { 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 { func hasToken(_ token: String, at index: Int) -> Bool {
return components.count > (index + 1) && components[index] == token return components.count > (index + 1) && components[index] == token

View File

@@ -9,7 +9,7 @@ class NowNode : NodeType {
class func parse(_ parser:TokenParser, token:Token) throws -> NodeType { class func parse(_ parser:TokenParser, token:Token) throws -> NodeType {
var format:Variable? var format:Variable?
let components = token.components() let components = token.components
guard components.count <= 2 else { guard components.count <= 2 else {
throw TemplateSyntaxError("'now' tags may only have one argument: the format string.") throw TemplateSyntaxError("'now' tags may only have one argument: the format string.")
} }

View File

@@ -1,6 +1,6 @@
public func until(_ tags: [String]) -> ((TokenParser, Token) -> Bool) { public func until(_ tags: [String]) -> ((TokenParser, Token) -> Bool) {
return { parser, token in return { parser, token in
if let name = token.components().first { if let name = token.components.first {
for tag in tags { for tag in tags {
if name == tag { if name == tag {
return true return true
@@ -36,9 +36,9 @@ public class TokenParser {
while tokens.count > 0 { while tokens.count > 0 {
let token = nextToken()! let token = nextToken()!
switch token { switch token.kind {
case .text(let text, _): case .text:
nodes.append(TextNode(text: text)) nodes.append(TextNode(text: token.contents))
case .variable: case .variable:
try nodes.append(VariableNode.parse(self, token: token)) try nodes.append(VariableNode.parse(self, token: token))
case .block: case .block:
@@ -47,7 +47,7 @@ public class TokenParser {
return nodes return nodes
} }
if let tag = token.components().first { if let tag = token.components.first {
do { do {
let parser = try findTag(name: tag) let parser = try findTag(name: tag)
let node = try parser(self, token) let node = try parser(self, token)

View File

@@ -71,47 +71,53 @@ public struct SourceMap: Equatable {
} }
} }
public enum Token : Equatable { public class Token: Equatable {
/// A token representing a piece of text. public enum Kind: Equatable {
case text(value: String, at: SourceMap) /// 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
}
/// A token representing a variable. public let contents: String
case variable(value: String, at: SourceMap) public let kind: Kind
public let sourceMap: SourceMap
/// A token representing a comment.
case comment(value: String, at: SourceMap)
/// A token representing a template block.
case block(value: String, at: SourceMap)
/// Returns the underlying value as an array seperated by spaces /// Returns the underlying value as an array seperated by spaces
public func components() -> [String] { public private(set) lazy var components: [String] = self.contents.smartSplit()
switch self {
case .block(let value, _), init(contents: String, kind: Kind, sourceMap: SourceMap) {
.variable(let value, _), self.contents = contents
.text(let value, _), self.kind = kind
.comment(let value, _): self.sourceMap = sourceMap
return value.smartSplit()
}
} }
public var contents: String { /// A token representing a piece of text.
switch self { public static func text(value: String, at sourceMap: SourceMap) -> Token {
case .block(let value, _), return Token(contents: value, kind: .text, sourceMap: sourceMap)
.variable(let value, _),
.text(let value, _),
.comment(let value, _):
return value
}
} }
public var sourceMap: SourceMap { /// A token representing a variable.
switch self { public static func variable(value: String, at sourceMap: SourceMap) -> Token {
case .block(_, let sourceMap), return Token(contents: value, kind: .variable, sourceMap: sourceMap)
.variable(_, let sourceMap),
.text(_, let sourceMap),
.comment(_, let sourceMap):
return sourceMap
}
} }
/// A token representing a comment.
public static func comment(value: String, at sourceMap: SourceMap) -> Token {
return Token(contents: value, kind: .comment, sourceMap: sourceMap)
}
/// A token representing a template block.
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
}
} }

View File

@@ -7,7 +7,7 @@ class TokenTests: XCTestCase {
describe("Token") { describe("Token") {
$0.it("can split the contents into components") { $0.it("can split the contents into components") {
let token = Token.text(value: "hello world", at: .unknown) 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.count) == 2
try expect(components[0]) == "hello" try expect(components[0]) == "hello"
@@ -16,7 +16,7 @@ class TokenTests: XCTestCase {
$0.it("can split the contents into components with single quoted strings") { $0.it("can split the contents into components with single quoted strings") {
let token = Token.text(value: "hello 'kyle fuller'", at: .unknown) 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.count) == 2
try expect(components[0]) == "hello" try expect(components[0]) == "hello"
@@ -25,7 +25,7 @@ class TokenTests: XCTestCase {
$0.it("can split the contents into components with double quoted strings") { $0.it("can split the contents into components with double quoted strings") {
let token = Token.text(value: "hello \"kyle fuller\"", at: .unknown) 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.count) == 2
try expect(components[0]) == "hello" try expect(components[0]) == "hello"