Refactor more components to use enum

This commit is contained in:
Kyle Fuller
2014-10-26 17:04:04 +00:00
parent 0b1ce61647
commit 5a627999d5
6 changed files with 136 additions and 123 deletions

View File

@@ -138,31 +138,33 @@ public class ForNode : Node {
if count == 4 && components[2] == "in" { if count == 4 && components[2] == "in" {
let loopVariable = components[1] let loopVariable = components[1]
let variable = components[3] let variable = components[3]
let (nodes, error) = parser.parse(until(["endfor", "empty"]))
var forNodes:[Node]!
var emptyNodes = [Node]() var emptyNodes = [Node]()
if let error = error { switch parser.parse(until(["endfor", "empty"])) {
return .Error(error: error) case .Success(let nodes):
forNodes = nodes
case .Error(let error):
return .Error(error)
} }
if let token = parser.nextToken() { if let token = parser.nextToken() {
if token.contents == "empty" { if token.contents == "empty" {
let (nodes, error) = parser.parse(until(["endfor"])) switch parser.parse(until(["endfor"])) {
parser.nextToken() case .Success(let nodes):
if let error = error {
return .Error(error: error)
}
if let nodes = nodes {
emptyNodes = nodes emptyNodes = nodes
case .Error(let error):
return .Error(error)
} }
parser.nextToken()
} }
} else { } else {
return .Error(error: NodeError(token: token, message: "`endfor` was not found.")) 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`.")) 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 { public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result {
let variable = token.components()[1] let variable = token.components()[1]
var trueNodes = [Node]()
let (trueNodes, error) = parser.parse(until(["endif", "else"]))
if let error = error {
return .Error(error:error)
}
var falseNodes = [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 let token = parser.nextToken() {
if token.contents == "else" { 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() parser.nextToken()
if let error = error {
return .Error(error:error)
}
if let nodes = nodes {
falseNodes = nodes
}
} }
} else { } 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 class func parse_ifnot(parser:TokenParser, token:Token) -> TokenParser.Result { public class func parse_ifnot(parser:TokenParser, token:Token) -> TokenParser.Result {
let variable = token.components()[1] 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 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 let token = parser.nextToken() {
if token.contents == "else" { if token.contents == "else" {
let (nodes, error) = parser.parse(until(["endif"])) switch parser.parse(until(["endif"])) {
if let error = error { case .Success(let nodes):
return .Error(error:error)
}
if let nodes = nodes {
trueNodes = nodes trueNodes = nodes
case .Error(let error):
return .Error(error)
} }
parser.nextToken() parser.nextToken()
} }
} else { } 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]) { public init(variable:String, trueNodes:[Node], falseNodes:[Node]) {

View File

@@ -13,12 +13,18 @@ public func until(tags:[String])(parser:TokenParser, token:Token) -> Bool {
} }
public class TokenParser { public class TokenParser {
public typealias TagParser = (TokenParser, Token) -> Result
public typealias NodeList = [Node]
public enum Result { public enum Result {
case Success(node: Node) case Success(node: Node)
case Error(error: Stencil.Error) 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 tokens:[Token]
private var tags = Dictionary<String, TagParser>() private var tags = Dictionary<String, TagParser>()
@@ -31,12 +37,12 @@ public class TokenParser {
tags["ifnot"] = IfNode.parse_ifnot tags["ifnot"] = IfNode.parse_ifnot
} }
public func parse() -> (nodes:[Node]?, error:Error?) { public func parse() -> Results {
return parse(nil) return parse(nil)
} }
public func parse(parse_until:((parser:TokenParser, token:Token) -> (Bool))?) -> (nodes:[Node]?, error:Error?) { public func parse(parse_until:((parser:TokenParser, token:Token) -> (Bool))?) -> TokenParser.Results {
var nodes = [Node]() var nodes = NodeList()
while tokens.count > 0 { while tokens.count > 0 {
let token = nextToken()! let token = nextToken()!
@@ -52,7 +58,7 @@ public class TokenParser {
if let parse_until = parse_until { if let parse_until = parse_until {
if parse_until(parser: self, token: token) { if parse_until(parser: self, token: token) {
prependToken(token) prependToken(token)
return (nodes, nil) return .Success(nodes:nodes)
} }
} }
@@ -62,7 +68,7 @@ public class TokenParser {
case .Success(let node): case .Success(let node):
nodes.append(node) nodes.append(node)
case .Error(let error): 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? { public func nextToken() -> Token? {

View File

@@ -37,18 +37,18 @@ public class Template {
} }
public func render(context:Context) -> Result { 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 { case .Error(let error):
return .Error(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!)
}
} }
return .Success(string: "")
} }
} }

View File

@@ -93,18 +93,18 @@ class IfNodeTests: NodeTests {
] ]
let parser = TokenParser(tokens: tokens) let parser = TokenParser(tokens: tokens)
let (nodes, error) = parser.parse() assertSuccess(parser.parse()) { nodes in
let node = nodes!.first! as IfNode let node = nodes.first! as IfNode
let trueNode = node.trueNodes.first! as TextNode let trueNode = node.trueNodes.first! as TextNode
let falseNode = node.falseNodes.first! as TextNode let falseNode = node.falseNodes.first! as TextNode
XCTAssertTrue(error == nil) XCTAssertEqual(nodes.count, 1)
XCTAssertEqual(nodes!.count, 1) XCTAssertEqual(node.variable.variable, "value")
XCTAssertEqual(node.variable.variable, "value") XCTAssertEqual(node.trueNodes.count, 1)
XCTAssertEqual(node.trueNodes.count, 1) XCTAssertEqual(trueNode.text, "true")
XCTAssertEqual(trueNode.text, "true") XCTAssertEqual(node.falseNodes.count, 1)
XCTAssertEqual(node.falseNodes.count, 1) XCTAssertEqual(falseNode.text, "false")
XCTAssertEqual(falseNode.text, "false") }
} }
func testParseIfNot() { func testParseIfNot() {
@@ -117,18 +117,18 @@ class IfNodeTests: NodeTests {
] ]
let parser = TokenParser(tokens: tokens) let parser = TokenParser(tokens: tokens)
let (nodes, error) = parser.parse() assertSuccess(parser.parse()) { nodes in
let node = nodes!.first! as IfNode let node = nodes.first! as IfNode
let trueNode = node.trueNodes.first! as TextNode let trueNode = node.trueNodes.first! as TextNode
let falseNode = node.falseNodes.first! as TextNode let falseNode = node.falseNodes.first! as TextNode
XCTAssertTrue(error == nil) XCTAssertEqual(nodes.count, 1)
XCTAssertEqual(nodes!.count, 1) XCTAssertEqual(node.variable.variable, "value")
XCTAssertEqual(node.variable.variable, "value") XCTAssertEqual(node.trueNodes.count, 1)
XCTAssertEqual(node.trueNodes.count, 1) XCTAssertEqual(trueNode.text, "true")
XCTAssertEqual(trueNode.text, "true") XCTAssertEqual(node.falseNodes.count, 1)
XCTAssertEqual(node.falseNodes.count, 1) XCTAssertEqual(falseNode.text, "false")
XCTAssertEqual(falseNode.text, "false") }
} }
func testParseIfWithoutEndIfError() { func testParseIfWithoutEndIfError() {
@@ -137,10 +137,7 @@ class IfNodeTests: NodeTests {
] ]
let parser = TokenParser(tokens: tokens) let parser = TokenParser(tokens: tokens)
let (nodes, error) = parser.parse() assertFailure(parser.parse(), "if: `endif` was not found.")
XCTAssertTrue(nodes == nil)
XCTAssertEqual(error!.description, "if: `endif` was not found.")
} }
func testParseIfNotWithoutEndIfError() { func testParseIfNotWithoutEndIfError() {
@@ -149,10 +146,7 @@ class IfNodeTests: NodeTests {
] ]
let parser = TokenParser(tokens: tokens) let parser = TokenParser(tokens: tokens)
let (nodes, error) = parser.parse() assertFailure(parser.parse(), "ifnot: `endif` was not found.")
XCTAssertTrue(nodes == nil)
XCTAssertEqual(error!.description, "ifnot: `endif` was not found.")
} }
// MARK: Rendering // MARK: Rendering
@@ -180,25 +174,23 @@ class NowNodeTests: NodeTests {
func testParseDefaultNow() { func testParseDefaultNow() {
let tokens = [ Token.Block(value: "now") ] let tokens = [ Token.Block(value: "now") ]
let parser = TokenParser(tokens: tokens) let parser = TokenParser(tokens: tokens)
let (nodes, error) = parser.parse()
let node = nodes!.first! as NowNode assertSuccess(parser.parse()) { nodes in
let node = nodes.first! as NowNode
XCTAssertTrue(error == nil) XCTAssertEqual(nodes.count, 1)
XCTAssertEqual(nodes!.count, 1) XCTAssertEqual(node.format.variable, "\"yyyy-MM-dd 'at' HH:mm\"")
XCTAssertEqual(node.format.variable, "\"yyyy-MM-dd 'at' HH:mm\"") }
} }
func testParseNowWithFormat() { func testParseNowWithFormat() {
let tokens = [ Token.Block(value: "now \"HH:mm\"") ] let tokens = [ Token.Block(value: "now \"HH:mm\"") ]
let parser = TokenParser(tokens: tokens) let parser = TokenParser(tokens: tokens)
let (nodes, error) = parser.parse()
let node = nodes!.first! as NowNode assertSuccess(parser.parse()) { nodes in
let node = nodes.first! as NowNode
XCTAssertTrue(error == nil) XCTAssertEqual(nodes.count, 1)
XCTAssertEqual(nodes!.count, 1) XCTAssertEqual(node.format.variable, "\"HH:mm\"")
XCTAssertEqual(node.format.variable, "\"HH:mm\"") }
} }
// MARK: Rendering // MARK: Rendering

View File

@@ -8,12 +8,11 @@ class TokenParserTests: XCTestCase {
Token.Text(value: "Hello World") Token.Text(value: "Hello World")
]) ])
let (nodes, error) = parser.parse() assertSuccess(parser.parse()) { nodes in
let node = nodes!.first as TextNode! let node = nodes.first as TextNode!
XCTAssertEqual(nodes.count, 1)
XCTAssertTrue(error == nil) XCTAssertEqual(node.text, "Hello World")
XCTAssertEqual(nodes!.count, 1) }
XCTAssertEqual(node.text, "Hello World")
} }
func testParsingVariableToken() { func testParsingVariableToken() {
@@ -21,13 +20,11 @@ class TokenParserTests: XCTestCase {
Token.Variable(value: "name") Token.Variable(value: "name")
]) ])
let (nodes, error) = parser.parse() assertSuccess(parser.parse()) { nodes in
let node = nodes!.first as VariableNode! let node = nodes.first as VariableNode!
let variable = node.variable XCTAssertEqual(nodes.count, 1)
XCTAssertEqual(node.variable, Variable("name"))
XCTAssertTrue(error == nil) }
XCTAssertEqual(nodes!.count, 1)
XCTAssertEqual(variable, Variable("name"))
} }
func testParsingCommentToken() { func testParsingCommentToken() {
@@ -35,9 +32,9 @@ class TokenParserTests: XCTestCase {
Token.Comment(value: "Secret stuff!") Token.Comment(value: "Secret stuff!")
]) ])
let (nodes, error) = parser.parse() assertSuccess(parser.parse()) { nodes in
XCTAssertEqual(nodes.count, 0)
XCTAssertEqual(nodes!.count, 0) }
} }
func testParsingTagToken() { func testParsingTagToken() {
@@ -45,9 +42,8 @@ class TokenParserTests: XCTestCase {
Token.Block(value: "now"), Token.Block(value: "now"),
]) ])
let (nodes, error) = parser.parse() assertSuccess(parser.parse()) { nodes in
let node = nodes!.first as NowNode! XCTAssertEqual(nodes.count, 1)
XCTAssertTrue(error == nil) }
XCTAssertEqual(nodes!.count, 1)
} }
} }

View File

@@ -2,6 +2,24 @@ import Cocoa
import XCTest import XCTest
import Stencil 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 { class StencilTests: XCTestCase {
func testReadmeExample() { func testReadmeExample() {
let templateString = "There are {{ articles.count }} articles.\n" + let templateString = "There are {{ articles.count }} articles.\n" +