diff --git a/Stencil/Node.swift b/Stencil/Node.swift index 379212b..ecacf1f 100644 --- a/Stencil/Node.swift +++ b/Stencil/Node.swift @@ -138,31 +138,33 @@ public class ForNode : Node { if count == 4 && components[2] == "in" { let loopVariable = components[1] let variable = components[3] - let (nodes, error) = parser.parse(until(["endfor", "empty"])) + + var forNodes:[Node]! var emptyNodes = [Node]() - if let error = error { - return .Error(error: error) + switch parser.parse(until(["endfor", "empty"])) { + case .Success(let nodes): + forNodes = nodes + case .Error(let error): + return .Error(error) } if let token = parser.nextToken() { if token.contents == "empty" { - let (nodes, error) = parser.parse(until(["endfor"])) - parser.nextToken() - - if let error = error { - return .Error(error: error) - } - - if let nodes = nodes { + switch parser.parse(until(["endfor"])) { + case .Success(let nodes): emptyNodes = nodes + case .Error(let error): + return .Error(error) } + + parser.nextToken() } } else { return .Error(error: NodeError(token: token, message: "`endfor` was not found.")) } - return .Success(node:ForNode(variable: variable, loopVariable: loopVariable, nodes: nodes!, emptyNodes:emptyNodes)) + return .Success(node:ForNode(variable: variable, loopVariable: loopVariable, nodes: forNodes, emptyNodes:emptyNodes)) } return .Error(error: NodeError(token: token, message: "Invalid syntax. Expected `for x in y`.")) @@ -206,61 +208,60 @@ public class IfNode : Node { public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result { let variable = token.components()[1] - - let (trueNodes, error) = parser.parse(until(["endif", "else"])) - if let error = error { - return .Error(error:error) - } - + var trueNodes = [Node]() var falseNodes = [Node]() + switch parser.parse(until(["endif", "else"])) { + case .Success(let nodes): + trueNodes = nodes + case .Error(let error): + return .Error(error) + } + if let token = parser.nextToken() { if token.contents == "else" { - let (nodes, error) = parser.parse(until(["endif"])) + switch parser.parse(until(["endif"])) { + case .Success(let nodes): + falseNodes = nodes + case .Error(let error): + return .Error(error) + } parser.nextToken() - - if let error = error { - return .Error(error:error) - } - - if let nodes = nodes { - falseNodes = nodes - } } } else { return .Error(error:NodeError(token: token, message: "`endif` was not found.")) } - return .Success(node:IfNode(variable: variable, trueNodes: trueNodes!, falseNodes: falseNodes)) + return .Success(node:IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes)) } public class func parse_ifnot(parser:TokenParser, token:Token) -> TokenParser.Result { let variable = token.components()[1] - - let (falseNodes, error) = parser.parse(until(["endif", "else"])) - if let error = error { - return .Error(error:error) - } var trueNodes = [Node]() + var falseNodes = [Node]() + + switch parser.parse(until(["endif", "else"])) { + case .Success(let nodes): + falseNodes = nodes + case .Error(let error): + return .Error(error) + } if let token = parser.nextToken() { if token.contents == "else" { - let (nodes, error) = parser.parse(until(["endif"])) - if let error = error { - return .Error(error:error) - } - - if let nodes = nodes { + switch parser.parse(until(["endif"])) { + case .Success(let nodes): trueNodes = nodes + case .Error(let error): + return .Error(error) } - parser.nextToken() } } else { - return .Error(error: NodeError(token: token, message: "`endif` was not found.")) + return .Error(error:NodeError(token: token, message: "`endif` was not found.")) } - return .Success(node: IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes!)) + return .Success(node:IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes)) } public init(variable:String, trueNodes:[Node], falseNodes:[Node]) { diff --git a/Stencil/Parser.swift b/Stencil/Parser.swift index d7a113f..833a82f 100644 --- a/Stencil/Parser.swift +++ b/Stencil/Parser.swift @@ -13,12 +13,18 @@ public func until(tags:[String])(parser:TokenParser, token:Token) -> Bool { } public class TokenParser { + public typealias TagParser = (TokenParser, Token) -> Result + public typealias NodeList = [Node] + public enum Result { case Success(node: Node) case Error(error: Stencil.Error) } - - typealias TagParser = (TokenParser, Token) -> Result + + public enum Results { + case Success(nodes: NodeList) + case Error(error: Stencil.Error) + } private var tokens:[Token] private var tags = Dictionary() @@ -31,12 +37,12 @@ public class TokenParser { tags["ifnot"] = IfNode.parse_ifnot } - public func parse() -> (nodes:[Node]?, error:Error?) { + public func parse() -> Results { return parse(nil) } - public func parse(parse_until:((parser:TokenParser, token:Token) -> (Bool))?) -> (nodes:[Node]?, error:Error?) { - var nodes = [Node]() + public func parse(parse_until:((parser:TokenParser, token:Token) -> (Bool))?) -> TokenParser.Results { + var nodes = NodeList() while tokens.count > 0 { let token = nextToken()! @@ -52,7 +58,7 @@ public class TokenParser { if let parse_until = parse_until { if parse_until(parser: self, token: token) { prependToken(token) - return (nodes, nil) + return .Success(nodes:nodes) } } @@ -62,7 +68,7 @@ public class TokenParser { case .Success(let node): nodes.append(node) case .Error(let error): - return (nil, error) + return .Error(error:error) } } } @@ -71,7 +77,7 @@ public class TokenParser { } } - return (nodes, nil) + return .Success(nodes:nodes) } public func nextToken() -> Token? { diff --git a/Stencil/Template.swift b/Stencil/Template.swift index 3f0a3d6..dfac346 100644 --- a/Stencil/Template.swift +++ b/Stencil/Template.swift @@ -37,18 +37,18 @@ public class Template { } public func render(context:Context) -> Result { - let (nodes, error) = parser.parse() + switch parser.parse() { + case .Success(let nodes): + let (result, error) = renderNodes(nodes, context) + if let result = result { + return .Success(string:result) + } else if let error = error { + return .Error(error:error) + } + return .Success(string:"") - if let error = error { - return .Error(error: error) - } else if let nodes = nodes { - let result = renderNodes(nodes, context) - if let string = result.0 { - return .Success(string: string) - } else { - return .Error(error: result.1!) - } + case .Error(let error): + return .Error(error:error) } - return .Success(string: "") } } diff --git a/StencilTests/NodeTests.swift b/StencilTests/NodeTests.swift index 452d3ed..4f2b3e7 100644 --- a/StencilTests/NodeTests.swift +++ b/StencilTests/NodeTests.swift @@ -93,18 +93,18 @@ class IfNodeTests: NodeTests { ] let parser = TokenParser(tokens: tokens) - let (nodes, error) = parser.parse() - let node = nodes!.first! as IfNode - let trueNode = node.trueNodes.first! as TextNode - let falseNode = node.falseNodes.first! as TextNode + assertSuccess(parser.parse()) { nodes in + let node = nodes.first! as IfNode + let trueNode = node.trueNodes.first! as TextNode + let falseNode = node.falseNodes.first! as TextNode - XCTAssertTrue(error == nil) - XCTAssertEqual(nodes!.count, 1) - XCTAssertEqual(node.variable.variable, "value") - XCTAssertEqual(node.trueNodes.count, 1) - XCTAssertEqual(trueNode.text, "true") - XCTAssertEqual(node.falseNodes.count, 1) - XCTAssertEqual(falseNode.text, "false") + XCTAssertEqual(nodes.count, 1) + XCTAssertEqual(node.variable.variable, "value") + XCTAssertEqual(node.trueNodes.count, 1) + XCTAssertEqual(trueNode.text, "true") + XCTAssertEqual(node.falseNodes.count, 1) + XCTAssertEqual(falseNode.text, "false") + } } func testParseIfNot() { @@ -117,18 +117,18 @@ class IfNodeTests: NodeTests { ] let parser = TokenParser(tokens: tokens) - let (nodes, error) = parser.parse() - let node = nodes!.first! as IfNode - let trueNode = node.trueNodes.first! as TextNode - let falseNode = node.falseNodes.first! as TextNode + assertSuccess(parser.parse()) { nodes in + let node = nodes.first! as IfNode + let trueNode = node.trueNodes.first! as TextNode + let falseNode = node.falseNodes.first! as TextNode - XCTAssertTrue(error == nil) - XCTAssertEqual(nodes!.count, 1) - XCTAssertEqual(node.variable.variable, "value") - XCTAssertEqual(node.trueNodes.count, 1) - XCTAssertEqual(trueNode.text, "true") - XCTAssertEqual(node.falseNodes.count, 1) - XCTAssertEqual(falseNode.text, "false") + XCTAssertEqual(nodes.count, 1) + XCTAssertEqual(node.variable.variable, "value") + XCTAssertEqual(node.trueNodes.count, 1) + XCTAssertEqual(trueNode.text, "true") + XCTAssertEqual(node.falseNodes.count, 1) + XCTAssertEqual(falseNode.text, "false") + } } func testParseIfWithoutEndIfError() { @@ -137,10 +137,7 @@ class IfNodeTests: NodeTests { ] let parser = TokenParser(tokens: tokens) - let (nodes, error) = parser.parse() - - XCTAssertTrue(nodes == nil) - XCTAssertEqual(error!.description, "if: `endif` was not found.") + assertFailure(parser.parse(), "if: `endif` was not found.") } func testParseIfNotWithoutEndIfError() { @@ -149,10 +146,7 @@ class IfNodeTests: NodeTests { ] let parser = TokenParser(tokens: tokens) - let (nodes, error) = parser.parse() - - XCTAssertTrue(nodes == nil) - XCTAssertEqual(error!.description, "ifnot: `endif` was not found.") + assertFailure(parser.parse(), "ifnot: `endif` was not found.") } // MARK: Rendering @@ -180,25 +174,23 @@ class NowNodeTests: NodeTests { func testParseDefaultNow() { let tokens = [ Token.Block(value: "now") ] let parser = TokenParser(tokens: tokens) - let (nodes, error) = parser.parse() - let node = nodes!.first! as NowNode - - XCTAssertTrue(error == nil) - XCTAssertEqual(nodes!.count, 1) - XCTAssertEqual(node.format.variable, "\"yyyy-MM-dd 'at' HH:mm\"") + assertSuccess(parser.parse()) { nodes in + let node = nodes.first! as NowNode + XCTAssertEqual(nodes.count, 1) + XCTAssertEqual(node.format.variable, "\"yyyy-MM-dd 'at' HH:mm\"") + } } func testParseNowWithFormat() { let tokens = [ Token.Block(value: "now \"HH:mm\"") ] let parser = TokenParser(tokens: tokens) - let (nodes, error) = parser.parse() - let node = nodes!.first! as NowNode - - XCTAssertTrue(error == nil) - XCTAssertEqual(nodes!.count, 1) - XCTAssertEqual(node.format.variable, "\"HH:mm\"") + assertSuccess(parser.parse()) { nodes in + let node = nodes.first! as NowNode + XCTAssertEqual(nodes.count, 1) + XCTAssertEqual(node.format.variable, "\"HH:mm\"") + } } // MARK: Rendering diff --git a/StencilTests/ParserTests.swift b/StencilTests/ParserTests.swift index a326ca3..5f1966d 100644 --- a/StencilTests/ParserTests.swift +++ b/StencilTests/ParserTests.swift @@ -8,12 +8,11 @@ class TokenParserTests: XCTestCase { Token.Text(value: "Hello World") ]) - let (nodes, error) = parser.parse() - let node = nodes!.first as TextNode! - - XCTAssertTrue(error == nil) - XCTAssertEqual(nodes!.count, 1) - XCTAssertEqual(node.text, "Hello World") + assertSuccess(parser.parse()) { nodes in + let node = nodes.first as TextNode! + XCTAssertEqual(nodes.count, 1) + XCTAssertEqual(node.text, "Hello World") + } } func testParsingVariableToken() { @@ -21,13 +20,11 @@ class TokenParserTests: XCTestCase { Token.Variable(value: "name") ]) - let (nodes, error) = parser.parse() - let node = nodes!.first as VariableNode! - let variable = node.variable - - XCTAssertTrue(error == nil) - XCTAssertEqual(nodes!.count, 1) - XCTAssertEqual(variable, Variable("name")) + assertSuccess(parser.parse()) { nodes in + let node = nodes.first as VariableNode! + XCTAssertEqual(nodes.count, 1) + XCTAssertEqual(node.variable, Variable("name")) + } } func testParsingCommentToken() { @@ -35,9 +32,9 @@ class TokenParserTests: XCTestCase { Token.Comment(value: "Secret stuff!") ]) - let (nodes, error) = parser.parse() - - XCTAssertEqual(nodes!.count, 0) + assertSuccess(parser.parse()) { nodes in + XCTAssertEqual(nodes.count, 0) + } } func testParsingTagToken() { @@ -45,9 +42,8 @@ class TokenParserTests: XCTestCase { Token.Block(value: "now"), ]) - let (nodes, error) = parser.parse() - let node = nodes!.first as NowNode! - XCTAssertTrue(error == nil) - XCTAssertEqual(nodes!.count, 1) + assertSuccess(parser.parse()) { nodes in + XCTAssertEqual(nodes.count, 1) + } } } diff --git a/StencilTests/StencilTests.swift b/StencilTests/StencilTests.swift index 9f85f2d..fbeca80 100644 --- a/StencilTests/StencilTests.swift +++ b/StencilTests/StencilTests.swift @@ -2,6 +2,24 @@ import Cocoa import XCTest import Stencil +func assertSuccess(result:TokenParser.Results, block:(([Node]) -> ())) { + switch result { + case .Success(let nodes): + block(nodes) + case .Error(let error): + XCTAssert(false, "Unexpected error") + } +} + +func assertFailure(result:TokenParser.Results, description:String) { + switch result { + case .Success(let nodes): + XCTAssert(false, "Unexpected error") + case .Error(let error): + XCTAssertEqual("\(error)", description) + } +} + class StencilTests: XCTestCase { func testReadmeExample() { let templateString = "There are {{ articles.count }} articles.\n" +