Skip to content

Commit b9f827d

Browse files
authored
Merge pull request #62 from vapor/cache
added cache w/ configurable max size
2 parents e111ce7 + f7df1a2 commit b9f827d

24 files changed

+239
-105
lines changed

Sources/Leaf/Argument.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,32 @@ extension Argument {
2626
}
2727
}
2828
}
29+
30+
public struct ArgumentList {
31+
public let list: [Argument]
32+
public let stem: Stem
33+
public let context: Context
34+
35+
public var isEmpty: Bool { return list.isEmpty }
36+
public var count: Int { return list.count }
37+
38+
public var first: Node? {
39+
return self[0]
40+
}
41+
42+
public var last: Node? {
43+
let last = list.count - 1
44+
return self[last]
45+
}
46+
47+
public init(list: [Argument], stem: Stem, context: Context) {
48+
self.list = list
49+
self.stem = stem
50+
self.context = context
51+
}
52+
53+
public subscript(idx: Int) -> Node? {
54+
guard idx < list.count else { return nil }
55+
return list[idx].value(with: stem, in: context)
56+
}
57+
}

Sources/Leaf/Buffer/Buffer+Leaf.swift

Lines changed: 82 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
1-
extension BufferProtocol where Element == Byte {
1+
public let permittedLeafTagCharacters: Bytes = {
2+
var permitted = Bytes(.a ... .z)
3+
permitted += Bytes(.A ... .Z)
4+
permitted += Bytes(.zero ... .nine)
5+
// -_.:
6+
permitted += [
7+
.hyphen,
8+
.underscore,
9+
.period,
10+
.colon
11+
]
12+
13+
return permitted
14+
}()
15+
16+
extension Buffer {
217
mutating func components(stem: Stem) throws -> [Leaf.Component] {
318
var comps: [Leaf.Component] = []
419
while let next = try nextComponent(stem: stem) {
520
if case let .tagTemplate(i) = next, i.isChain {
621
guard comps.count > 0 else {
7-
throw ParseError.expectedLeadingTemplate(have: nil)
22+
throw ParseError.expectedLeadingTemplate(have: nil, line: line, column: column)
823
}
924
while let last = comps.last {
1025
var loop = true
@@ -16,7 +31,7 @@ extension BufferProtocol where Element == Byte {
1631
continue
1732
default:
1833
var mutable = last
19-
try mutable.addToChain(i)
34+
try mutable.addToChain(i, line: line, column: column)
2035
comps.append(mutable)
2136
loop = false
2237
}
@@ -31,10 +46,14 @@ extension BufferProtocol where Element == Byte {
3146
}
3247

3348
mutating func nextComponent(stem: Stem) throws -> Leaf.Component? {
34-
guard let token = current else { return nil }
35-
guard token == TOKEN else { return .raw(extractUntil { $0 == TOKEN }) }
36-
let tagTemplate = try extractInstruction(stem: stem)
37-
return .tagTemplate(tagTemplate)
49+
guard let _ = current else { return nil }
50+
if foundTag() {
51+
let tagTemplate = try extractInstruction(stem: stem)
52+
return .tagTemplate(tagTemplate)
53+
} else {
54+
let raw = nextRawComponent()
55+
return .raw(raw)
56+
}
3857
}
3958

4059
mutating func extractUntil(_ until: (Element) -> Bool) -> [Element] {
@@ -49,22 +68,61 @@ extension BufferProtocol where Element == Byte {
4968

5069
return collection
5170
}
71+
72+
mutating func nextRawComponent() -> [Element] {
73+
var collection = Bytes()
74+
if let current = current {
75+
if foundTag() { return [] }
76+
77+
if !escapeCurrent() {
78+
collection.append(current)
79+
}
80+
}
81+
82+
while let value = moveForward() {
83+
if foundTag() {
84+
return collection
85+
}
86+
87+
guard !escapeCurrent() else { continue }
88+
collection.append(value)
89+
}
90+
91+
return collection
92+
}
93+
94+
private func escapeCurrent() -> Bool {
95+
return next == TOKEN && current == .backSlash
96+
}
97+
98+
private func foundTag() -> Bool {
99+
guard let current = current, let next = next else { return false }
100+
// make sure we found a token
101+
guard current == TOKEN else { return false }
102+
// make sure said token isn't escaped
103+
guard previous != .backSlash else { return false }
104+
105+
// allow left parens, special case, ie: '#(' and any valid name
106+
// also allow special case double chained
107+
let isSpecialCase = (next == .leftParenthesis || next == TOKEN)
108+
return isSpecialCase || permittedLeafTagCharacters.contains(next)
109+
}
52110
}
53111

54112
enum ParseError: LeafError {
55113
case tagTemplateNotFound(name: String)
56-
case missingBodyOpener(expected: String, have: String?)
57-
case missingBodyCloser(expected: String)
58-
case expectedOpenParenthesis
59-
case expectedLeadingTemplate(have: Leaf.Component?)
114+
case missingBodyOpener(expected: String, have: String?, line: Int, column: Int)
115+
case missingBodyCloser(expected: String, line: Int, column: Int)
116+
case expectedOpenParenthesis(line: Int, column: Int)
117+
case expectedLeadingTemplate(have: Leaf.Component?, line: Int, column: Int)
60118
}
61119

62120
/*
63121
Syntax
64122

65123
@ + '<bodyName>` + `(` + `<[argument]>` + `)` + ` { ` + <body> + ` }`
66124
*/
67-
extension BufferProtocol where Element == Byte {
125+
extension Buffer {
68126
mutating func extractInstruction(stem: Stem) throws -> TagTemplate {
69127
let name = try extractInstructionName()
70128
let parameters = try extractInstructionParameters()
@@ -93,7 +151,7 @@ extension BufferProtocol where Element == Byte {
93151
moveForward() // drop initial token from name. a secondary token implies chain
94152
let name = extractUntil { $0 == .leftParenthesis }
95153
guard current == .leftParenthesis else {
96-
throw ParseError.expectedOpenParenthesis
154+
throw ParseError.expectedOpenParenthesis(line: line, column: column)
97155
}
98156
return name.makeString()
99157
}
@@ -112,7 +170,12 @@ extension BufferProtocol where Element == Byte {
112170
mutating func extractSection(opensWith opener: Byte, closesWith closer: Byte) throws -> Bytes {
113171
guard current == opener else {
114172
let have = current.flatMap { [$0] }?.makeString()
115-
throw ParseError.missingBodyOpener(expected: [opener].makeString(), have: have)
173+
throw ParseError.missingBodyOpener(
174+
expected: [opener].makeString(),
175+
have: have,
176+
line: line,
177+
column: column
178+
)
116179
}
117180

118181
var subBodies = 0
@@ -129,7 +192,11 @@ extension BufferProtocol where Element == Byte {
129192
}
130193

131194
guard current == closer else {
132-
throw ParseError.missingBodyCloser(expected: [closer].makeString())
195+
throw ParseError.missingBodyCloser(
196+
expected: [closer].makeString(),
197+
line: line,
198+
column: column
199+
)
133200
}
134201

135202
return body

Sources/Leaf/Buffer/Buffer.swift

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,42 @@
1-
struct Buffer<T>: BufferProtocol {
2-
typealias Element = T
1+
import Bits
32

4-
private(set) var previous: T? = nil
5-
private(set) var current: T? = nil
6-
private(set) var next: T? = nil
3+
struct Buffer: BufferProtocol {
4+
typealias Element = Byte
75

8-
private var buffer: IndexingIterator<[T]>
6+
private(set) var previous: Byte? = nil
7+
private(set) var current: Byte? = nil
8+
private(set) var next: Byte? = nil
99

10-
init<S: Sequence>(_ sequence: S) where S.Iterator.Element == T {
10+
private(set) var line: Int = 1
11+
private(set) var column: Int = 0
12+
13+
private var buffer: IndexingIterator<[Byte]>
14+
15+
init<S: Sequence>(_ sequence: S) where S.Iterator.Element == Byte {
1116
buffer = sequence.array.makeIterator()
1217
// queue up first
1318
moveForward() // sets next
1419
moveForward() // sets current
1520
}
1621

1722
@discardableResult
18-
mutating func moveForward() -> T? {
23+
mutating func moveForward() -> Byte? {
1924
previous = current
2025
current = next
21-
next = buffer.next()
26+
next = self.getNext()
2227
return current
2328
}
29+
30+
private mutating func getNext() -> Byte? {
31+
let next = buffer.next()
32+
33+
if next == .newLine {
34+
line += 1
35+
column = 0
36+
} else {
37+
column += 1
38+
}
39+
40+
return next
41+
}
2442
}

Sources/Leaf/Leaf.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import Core
2+
import libc
3+
14
/**
25
🍃
36

@@ -15,9 +18,23 @@ public final class Leaf {
1518
// public let components: [Component]
1619
public let components: List<Component>
1720

21+
public let size: Int
22+
1823
internal init(raw: String, components: [Component]) {
1924
self.raw = raw
20-
self.components = List(components)
25+
let components = List(components)
26+
self.components = components
27+
28+
/// I can't find a way to dynamically infer the size of a given component, so we will use roughly
29+
/// double its raw representation
30+
let rawSize = raw.utf8.count
31+
self.size = rawSize * 2
32+
}
33+
}
34+
35+
extension Leaf: Cacheable {
36+
public func cacheSize() -> Size {
37+
return size
2138
}
2239
}
2340

Sources/Leaf/LeafComponent.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@ extension Leaf {
77
}
88

99
extension Leaf.Component {
10-
internal mutating func addToChain(_ chainedInstruction: TagTemplate) throws {
10+
internal mutating func addToChain(
11+
_ chainedInstruction: TagTemplate,
12+
line: Int,
13+
column: Int
14+
) throws {
1115
switch self {
1216
case .raw(_):
13-
throw ParseError.expectedLeadingTemplate(have: self)
17+
throw ParseError.expectedLeadingTemplate(have: self, line: line, column: column)
1418
case let .tagTemplate(current):
1519
self = .chain([current, chainedInstruction])
1620
case let .chain(chain):

Sources/Leaf/Stem+Render.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,11 @@ extension Stem {
4949
)
5050

5151
let value = try tag.run(
52-
stem: self,
53-
context: context,
5452
tagTemplate: tagTemplate,
5553
arguments: arguments
5654
)
5755

5856
let shouldRender = tag.shouldRender(
59-
stem: self,
60-
context: context,
6157
tagTemplate: tagTemplate,
6258
arguments: arguments,
6359
value: value

Sources/Leaf/Stem.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import Core
2+
import Bits
23

34
public final class Stem {
45
public let file: FileProtocol
5-
public var cache: [String: Leaf]?
6+
public var cache: SystemCache<Leaf>?
67
public fileprivate(set) var tags: [String: Tag] = defaultTags
78

8-
public init(_ file: FileProtocol, cache: [String: Leaf]? = [:]) {
9+
public init(_ file: FileProtocol, cache: SystemCache<Leaf>? = .init(maxSize: 500.megabytes)) {
910
self.file = file
1011
self.cache = cache
1112
}

Sources/Leaf/Tag/BasicTag.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
public protocol BasicTag: Tag {
2-
func run(arguments: [Argument]) throws -> Node?
2+
func run(arguments: ArgumentList) throws -> Node?
33
}
44

55
extension BasicTag {
66
public func run(
7-
stem: Stem,
8-
context: Context,
97
tagTemplate: TagTemplate,
10-
arguments: [Argument]
8+
arguments: ArgumentList
119
) throws -> Node? {
1210
return try run(arguments: arguments)
1311
}

Sources/Leaf/Tag/Models/Else.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
public final class Else: Tag {
22
public let name = "else"
33
public func run(
4-
stem: Stem,
5-
context: Context,
64
tagTemplate: TagTemplate,
7-
arguments: [Argument]) throws -> Node? {
5+
arguments: ArgumentList) throws -> Node? {
86
return nil
97
}
108
public func shouldRender(
11-
stem: Stem,
12-
context: Context,
139
tagTemplate: TagTemplate,
14-
arguments: [Argument],
10+
arguments: ArgumentList,
1511
value: Node?) -> Bool {
1612
return true
1713
}

Sources/Leaf/Tag/Models/Embed.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,14 @@ public final class Embed: Tag {
2424
}
2525

2626
public func run(
27-
stem: Stem,
28-
context: Context,
2927
tagTemplate: TagTemplate,
30-
arguments: [Argument]) throws -> Node? {
28+
arguments: ArgumentList) throws -> Node? {
3129
return nil
3230
}
3331

3432
public func shouldRender(
35-
stem: Stem,
36-
context: Context,
3733
tagTemplate: TagTemplate,
38-
arguments: [Argument],
34+
arguments: ArgumentList,
3935
value: Node?) -> Bool {
4036
// throws at precompile, should always render
4137
return true

0 commit comments

Comments
 (0)