Skip to content

Commit e111ce7

Browse files
authored
Merge pull request #61 from vapor/inner-commas
inner commas support in constants
2 parents f315867 + c11c2b0 commit e111ce7

File tree

2 files changed

+97
-2
lines changed

2 files changed

+97
-2
lines changed

Sources/Leaf/Buffer/Buffer+Leaf.swift

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,93 @@ extension BufferProtocol where Element == Byte {
138138

139139
extension Sequence where Iterator.Element == Byte {
140140
func extractParameters() throws -> [Parameter] {
141-
return try split(separator: .comma)
142-
.map { try Parameter($0) }
141+
let parser = ParameterParser(self.array)
142+
return try parser.process()
143+
}
144+
}
145+
146+
import Core
147+
148+
fileprivate final class ParameterParser {
149+
var buffer: Buffer
150+
151+
init(_ bytes: Bytes) {
152+
buffer = Buffer(bytes: bytes)
153+
}
154+
155+
func process() throws -> [Parameter] {
156+
var params = [Parameter]()
157+
while let next = try nextParameter() {
158+
params.append(next)
159+
}
160+
return params
161+
}
162+
163+
private func nextParameter() throws -> Parameter? {
164+
try buffer.skipWhitespace()
165+
166+
guard try !buffer.next(matchesAny: .rightParenthesis) else { return nil }
167+
guard let next = try buffer.next() else { return nil }
168+
guard next != .comma else { return try nextParameter() }
169+
buffer.returnToBuffer(next)
170+
171+
if next == .quote {
172+
return try nextConstant()
173+
} else {
174+
return try nextVariable()
175+
}
176+
}
177+
178+
private func nextVariable() throws -> Parameter {
179+
let collected = try buffer.collect(until: .comma, .rightParenthesis)
180+
// discard `,` or ')'
181+
try buffer.discardNext(1)
182+
try buffer.skipWhitespace()
183+
184+
let variable = collected
185+
.makeString()
186+
.components(separatedBy: ".")
187+
return .variable(path: variable)
188+
189+
}
190+
191+
private func nextConstant() throws -> Parameter {
192+
// discard leading `"`
193+
try buffer.discardNext(1)
194+
195+
var collected = Bytes()
196+
while let next = try buffer.next() {
197+
if next == .quote, buffer.previous != .backSlash {
198+
break
199+
}
200+
collected.append(next)
201+
}
202+
203+
return .constant(value: collected.makeString())
204+
}
205+
}
206+
207+
208+
extension ParameterParser {
209+
fileprivate final class Buffer: StaticDataBuffer {
210+
var previous: Byte? = nil
211+
var current: Byte? = nil
212+
213+
override init<B: Sequence>(bytes: B) where B.Iterator.Element == Byte {
214+
super.init(bytes: bytes)
215+
}
216+
217+
override func next() throws -> Byte? {
218+
let n = try super.next()
219+
previous = current
220+
current = n
221+
return n
222+
}
223+
224+
func skipWhitespace() throws {
225+
while try next(matches: Bytes.whitespace.contains) {
226+
try discardNext(1)
227+
}
228+
}
143229
}
144230
}

Tests/LeafTests/ContextTests.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ class ContextTests: XCTestCase {
2424
XCTAssertEqual(rendered, expectation)
2525
}
2626

27+
func testNestedComma() throws {
28+
let raw = "Hello, #(\"foo,bar\")"
29+
let template = try stem.spawnLeaf(raw: raw)
30+
let loadable = Context(["": ""])
31+
let rendered = try stem.render(template, with: loadable).makeString()
32+
let expectation = "Hello, foo,bar"
33+
XCTAssertEqual(rendered, expectation)
34+
}
35+
2736
func testBasic() throws {
2837
let template = try stem.spawnLeaf(raw: "Hello, #(name)!")
2938
let context = try Node(node: ["name": "World"])

0 commit comments

Comments
 (0)