Merge branch 'master' into dynamic-filter
This commit is contained in:
@@ -1,11 +1,16 @@
|
||||
matrix:
|
||||
include:
|
||||
- os: osx
|
||||
osx_image: xcode9.2
|
||||
env: SWIFT_VERSION=4.0.3
|
||||
- os: osx
|
||||
osx_image: xcode9.4
|
||||
env: SWIFT_VERSION=4.1
|
||||
- os: osx
|
||||
osx_image: xcode10
|
||||
env: SWIFT_VERSION=4.2
|
||||
- os: linux
|
||||
env: SWIFT_VERSION=4.0.3
|
||||
- os: linux
|
||||
env: SWIFT_VERSION=4.1
|
||||
- os: linux
|
||||
|
||||
@@ -19,11 +19,15 @@ _None_
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
_None_
|
||||
- Fixed using parenthesis in boolean expressions, they now can be used without spaces around them.
|
||||
[Ilya Puchka](https://github.com/ilyapuchka)
|
||||
[#254](https://github.com/stencilproject/Stencil/pull/254)
|
||||
|
||||
### 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// swift-tools-version:4.1
|
||||
// swift-tools-version:4.0
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -236,7 +236,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 parser.compileExpression(components: components, token: token)
|
||||
@@ -247,7 +247,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 parser.compileExpression(components: components, token: current)
|
||||
|
||||
@@ -269,7 +269,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'.")
|
||||
}
|
||||
|
||||
@@ -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("""
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.")
|
||||
}
|
||||
|
||||
@@ -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 environment.findTag(name: tag)
|
||||
let node = try parser(self, token)
|
||||
|
||||
@@ -17,6 +17,12 @@ extension String {
|
||||
components[components.count-1] += word
|
||||
} else if specialCharacters.contains(word) {
|
||||
components[components.count-1] += word
|
||||
} else if word != "(" && word.first == "(" || word != ")" && word.first == ")" {
|
||||
components.append(String(word.prefix(1)))
|
||||
appendWord(String(word.dropFirst()))
|
||||
} else if word != "(" && word.last == "(" || word != ")" && word.last == ")" {
|
||||
appendWord(String(word.dropLast()))
|
||||
components.append(String(word.suffix(1)))
|
||||
} else {
|
||||
components.append(word)
|
||||
}
|
||||
@@ -71,47 +77,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(value: String, at: SourceMap)
|
||||
|
||||
case text
|
||||
/// A token representing a variable.
|
||||
case variable(value: String, at: SourceMap)
|
||||
|
||||
case variable
|
||||
/// A token representing a comment.
|
||||
case comment(value: String, at: SourceMap)
|
||||
|
||||
case comment
|
||||
/// A token representing a template block.
|
||||
case block(value: String, at: SourceMap)
|
||||
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 func components() -> [String] {
|
||||
switch self {
|
||||
case .block(let value, _),
|
||||
.variable(let value, _),
|
||||
.text(let value, _),
|
||||
.comment(let value, _):
|
||||
return value.smartSplit()
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
public var contents: String {
|
||||
switch self {
|
||||
case .block(let value, _),
|
||||
.variable(let value, _),
|
||||
.text(let value, _),
|
||||
.comment(let value, _):
|
||||
return value
|
||||
}
|
||||
/// A token representing a piece of text.
|
||||
public static func text(value: String, at sourceMap: SourceMap) -> Token {
|
||||
return Token(contents: value, kind: .text, sourceMap: sourceMap)
|
||||
}
|
||||
|
||||
public var sourceMap: SourceMap {
|
||||
switch self {
|
||||
case .block(_, let sourceMap),
|
||||
.variable(_, let sourceMap),
|
||||
.text(_, let sourceMap),
|
||||
.comment(_, let sourceMap):
|
||||
return sourceMap
|
||||
/// A token representing a variable.
|
||||
public static func variable(value: String, at sourceMap: SourceMap) -> Token {
|
||||
return Token(contents: value, kind: .variable, sourceMap: 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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
38
Sources/_SwiftSupport.swift
Normal file
38
Sources/_SwiftSupport.swift
Normal file
@@ -0,0 +1,38 @@
|
||||
import Foundation
|
||||
|
||||
#if !swift(>=4.1)
|
||||
public extension Sequence {
|
||||
func compactMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult] {
|
||||
return try flatMap(transform)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !swift(>=4.1)
|
||||
public extension Collection {
|
||||
func index(_ i: Self.Index, offsetBy n: Int) -> Self.Index {
|
||||
let indexDistance = Self.IndexDistance(n)
|
||||
return index(i, offsetBy: indexDistance)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !swift(>=4.1)
|
||||
public extension TemplateSyntaxError {
|
||||
public static func ==(lhs: TemplateSyntaxError, rhs: TemplateSyntaxError) -> Bool {
|
||||
return lhs.reason == rhs.reason &&
|
||||
lhs.description == rhs.description &&
|
||||
lhs.token == rhs.token &&
|
||||
lhs.stackTrace == rhs.stackTrace &&
|
||||
lhs.templateName == rhs.templateName
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !swift(>=4.1)
|
||||
public extension Variable {
|
||||
public static func ==(lhs: Variable, rhs: Variable) -> Bool {
|
||||
return lhs.variable == rhs.variable
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -145,7 +145,7 @@ class IfNodeTests: XCTestCase {
|
||||
|
||||
$0.it("can parse an if with complex expression") {
|
||||
let tokens: [Token] = [
|
||||
.block(value: "if value == \"test\" and not name", at: .unknown),
|
||||
.block(value: "if value == \"test\" and (not name or not (name and surname) or( some )and other )", at: .unknown),
|
||||
.text(value: "true", at: .unknown),
|
||||
.block(value: "endif", at: .unknown)
|
||||
]
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user