From 925c6635554fd90d410cf473a71302369de4eba2 Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Sun, 26 Oct 2014 12:11:40 +0000 Subject: [PATCH] Allow node parsing to return an error Closes #5 --- Stencil/Node.swift | 65 ++++++++++++++++++++++++++-------- Stencil/Parser.swift | 21 +++++++---- Stencil/Template.swift | 15 +++++--- StencilTests/NodeTests.swift | 14 ++++---- StencilTests/ParserTests.swift | 25 +++++++------ 5 files changed, 97 insertions(+), 43 deletions(-) diff --git a/Stencil/Node.swift b/Stencil/Node.swift index f9c3f50..54136d8 100644 --- a/Stencil/Node.swift +++ b/Stencil/Node.swift @@ -84,8 +84,8 @@ public class VariableNode : Node { } public class NowNode : Node { - public class func parse(parser:TokenParser, token:Token) -> Node { - return NowNode() + public class func parse(parser:TokenParser, token:Token) -> (node:Node?, error:Error?) { + return (NowNode(), nil) } public func render(context: Context) -> (String?, Error?) { @@ -99,29 +99,41 @@ public class ForNode : Node { let loopVariable:String let nodes:[Node] - public class func parse(parser:TokenParser, token:Token) -> Node { + public class func parse(parser:TokenParser, token:Token) -> (node:Node?, error:Error?) { let components = token.components() let count = countElements(components) if count == 4 && components[2] == "in" { let loopVariable = components[1] let variable = components[3] - let nodes = parser.parse(until(["endfor", "empty"])) + let (nodes, error) = parser.parse(until(["endfor", "empty"])) var emptyNodes = [Node]() + if let error = error { + return (nil, error) + } + if let token = parser.nextToken() { if token.contents == "empty" { - emptyNodes = parser.parse(until(["endfor"])) + let (nodes, error) = parser.parse(until(["endfor"])) parser.nextToken() + + if let error = error { + return (nil, error) + } + + if let nodes = nodes { + emptyNodes = nodes + } } } - return ForNode(variable: variable, loopVariable: loopVariable, nodes: nodes, emptyNodes:emptyNodes) + return (ForNode(variable: variable, loopVariable: loopVariable, nodes: nodes!, emptyNodes:emptyNodes), nil) } else { // TODO error } - return TextNode(text: "TODO return some error") + return (TextNode(text: "TODO return some error"), nil) } public init(variable:String, loopVariable:String, nodes:[Node], emptyNodes:[Node]) { @@ -160,36 +172,59 @@ public class IfNode : Node { public let trueNodes:[Node] public let falseNodes:[Node] - public class func parse(parser:TokenParser, token:Token) -> Node { + public class func parse(parser:TokenParser, token:Token) -> (node:Node?, error:Error?) { let variable = token.components()[1] - let trueNodes = parser.parse(until(["endif", "else"])) + let (trueNodes, error) = parser.parse(until(["endif", "else"])) + if let error = error { + return (nil, error) + } + var falseNodes = [Node]() if let token = parser.nextToken() { if token.contents == "else" { - falseNodes = parser.parse(until(["endif"])) + let (nodes, error) = parser.parse(until(["endif"])) parser.nextToken() + + if let error = error { + return (nil, error) + } + + if let nodes = nodes { + falseNodes = nodes + } } } - return IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes) + return (IfNode(variable: variable, trueNodes: trueNodes!, falseNodes: falseNodes), nil) } - public class func parse_ifnot(parser:TokenParser, token:Token) -> Node { + public class func parse_ifnot(parser:TokenParser, token:Token) -> (node:Node?, error:Error?) { let variable = token.components()[1] - let falseNodes = parser.parse(until(["endif", "else"])) + let (falseNodes, error) = parser.parse(until(["endif", "else"])) + if let error = error { + return (nil, error) + } var trueNodes = [Node]() if let token = parser.nextToken() { if token.contents == "else" { - trueNodes = parser.parse(until(["endif"])) + let (nodes, error) = parser.parse(until(["endif"])) + if let error = error { + return (nil, error) + } + + if let nodes = nodes { + trueNodes = nodes + } + parser.nextToken() } } - return IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes) + return (IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes!), nil) } public init(variable:String, trueNodes:[Node], falseNodes:[Node]) { diff --git a/Stencil/Parser.swift b/Stencil/Parser.swift index e002b18..ba88b8e 100644 --- a/Stencil/Parser.swift +++ b/Stencil/Parser.swift @@ -22,7 +22,7 @@ public func until(tags:[String])(parser:TokenParser, token:Token) -> Bool { public class TokenParser { private var tokens:[Token] - private var tags = Dictionary (Node))>() + private var tags = Dictionary ((node:Node?, error:Error?)))>() public init(tokens:[Token]) { self.tokens = tokens @@ -32,11 +32,11 @@ public class TokenParser { tags["ifnot"] = IfNode.parse_ifnot } - public func parse() -> [Node] { + public func parse() -> (nodes:[Node]?, error:Error?) { return parse(nil) } - public func parse(parse_until:((parser:TokenParser, token:Token) -> (Bool))?) -> [Node] { + public func parse(parse_until:((parser:TokenParser, token:Token) -> (Bool))?) -> (nodes:[Node]?, error:Error?) { var nodes = [Node]() while tokens.count > 0 { @@ -53,14 +53,21 @@ public class TokenParser { if let parse_until = parse_until { if parse_until(parser: self, token: token) { prependToken(token) - return nodes + return (nodes, nil) } } if let tag = tag { if let parser = self.tags[tag] { - let node = parser(self, token) - nodes.append(node) + let (node, error) = parser(self, token) + + if let error = error { + return (nil, error) + } + + if let node = node { + nodes.append(node) + } } } case .Comment(let value): @@ -68,7 +75,7 @@ public class TokenParser { } } - return nodes + return (nodes, nil) } public func nextToken() -> Token? { diff --git a/Stencil/Template.swift b/Stencil/Template.swift index a46d322..8719290 100644 --- a/Stencil/Template.swift +++ b/Stencil/Template.swift @@ -9,7 +9,7 @@ import Foundation public class Template { - let nodes:[Node] + let parser:TokenParser public convenience init(named:String) { self.init(named:named, inBundle:nil) @@ -36,11 +36,18 @@ public class Template { public init(templateString:String) { let lexer = Lexer(templateString: templateString) let tokens = lexer.tokenize() - let parser = TokenParser(tokens: tokens) - nodes = parser.parse() + parser = TokenParser(tokens: tokens) } public func render(context:Context) -> (string:String?, error:Error?) { - return renderNodes(nodes, context) + let (nodes, error) = parser.parse() + + if let error = error { + return (nil, error) + } else if let nodes = nodes { + return renderNodes(nodes, context) + } + + return (nil, nil) } } diff --git a/StencilTests/NodeTests.swift b/StencilTests/NodeTests.swift index a4f5498..fa9282f 100644 --- a/StencilTests/NodeTests.swift +++ b/StencilTests/NodeTests.swift @@ -101,12 +101,13 @@ class IfNodeTests: NodeTests { ] let parser = TokenParser(tokens: tokens) - let nodes = parser.parse() - let node = nodes.first! as IfNode + 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 - XCTAssertEqual(nodes.count, 1) + XCTAssertTrue(error == nil) + XCTAssertEqual(nodes!.count, 1) XCTAssertEqual(node.variable.variable, "value") XCTAssertEqual(node.trueNodes.count, 1) XCTAssertEqual(trueNode.text, "true") @@ -124,12 +125,13 @@ class IfNodeTests: NodeTests { ] let parser = TokenParser(tokens: tokens) - let nodes = parser.parse() - let node = nodes.first! as IfNode + 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 - XCTAssertEqual(nodes.count, 1) + XCTAssertTrue(error == nil) + XCTAssertEqual(nodes!.count, 1) XCTAssertEqual(node.variable.variable, "value") XCTAssertEqual(node.trueNodes.count, 1) XCTAssertEqual(trueNode.text, "true") diff --git a/StencilTests/ParserTests.swift b/StencilTests/ParserTests.swift index 7b027d5..9740061 100644 --- a/StencilTests/ParserTests.swift +++ b/StencilTests/ParserTests.swift @@ -16,10 +16,11 @@ class TokenParserTests: XCTestCase { Token.Text(value: "Hello World") ]) - let nodes = parser.parse() - let node = nodes.first as TextNode! + let (nodes, error) = parser.parse() + let node = nodes!.first as TextNode! - XCTAssertEqual(nodes.count, 1) + XCTAssertTrue(error == nil) + XCTAssertEqual(nodes!.count, 1) XCTAssertEqual(node.text, "Hello World") } @@ -28,11 +29,12 @@ class TokenParserTests: XCTestCase { Token.Variable(value: "name") ]) - let nodes = parser.parse() - let node = nodes.first as VariableNode! + let (nodes, error) = parser.parse() + let node = nodes!.first as VariableNode! let variable = node.variable - XCTAssertEqual(nodes.count, 1) + XCTAssertTrue(error == nil) + XCTAssertEqual(nodes!.count, 1) XCTAssertEqual(variable, Variable("name")) } @@ -41,9 +43,9 @@ class TokenParserTests: XCTestCase { Token.Comment(value: "Secret stuff!") ]) - let nodes = parser.parse() + let (nodes, error) = parser.parse() - XCTAssertEqual(nodes.count, 0) + XCTAssertEqual(nodes!.count, 0) } func testParsingTagToken() { @@ -51,8 +53,9 @@ class TokenParserTests: XCTestCase { Token.Block(value: "now"), ]) - let nodes = parser.parse() - let node = nodes.first as NowNode! - XCTAssertEqual(nodes.count, 1) + let (nodes, error) = parser.parse() + let node = nodes!.first as NowNode! + XCTAssertTrue(error == nil) + XCTAssertEqual(nodes!.count, 1) } }