Skip to content
This repository has been archived by the owner on Aug 4, 2022. It is now read-only.

Commit

Permalink
Merge pull request #1 from g-Off/initial-commit
Browse files Browse the repository at this point in the history
initial commit
  • Loading branch information
g-Off authored Sep 26, 2019
2 parents b091fa2 + 11d8f41 commit 88d47cc
Show file tree
Hide file tree
Showing 56 changed files with 5,769 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

92 changes: 92 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/Liquid.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Liquid"
BuildableName = "Liquid"
BlueprintName = "Liquid"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "LiquidTests"
BuildableName = "LiquidTests"
BlueprintName = "LiquidTests"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "LiquidTests"
BuildableName = "LiquidTests"
BlueprintName = "LiquidTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Liquid"
BuildableName = "Liquid"
BlueprintName = "Liquid"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
23 changes: 23 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// swift-tools-version:5.0

import PackageDescription

let package = Package(
name: "Liquid",
products: [
.library(
name: "Liquid",
targets: ["Liquid"]),
],
dependencies: [
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
.target(
name: "Liquid",
dependencies: []),
.testTarget(
name: "LiquidTests",
dependencies: ["Liquid"]),
]
)
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Liquid


37 changes: 37 additions & 0 deletions Sources/Liquid/Block.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// Block.swift
//
//
// Created by Geoffrey Foster on 2019-09-02.
//

import Foundation

open class Block {
let name: String

init(name: String) {
self.name = name
}

func parse(body: BlockBody, tokenizer: Tokenizer, context: ParseContext) throws -> Bool {
let endTagName = "end\(name)"

var continueParsing: Bool = true
try body.parse(tokenizer, context: context) { (tagName, markup) in
guard let tagName = tagName else {
throw SyntaxError.unclosedTag(name)
}
if tagName == endTagName {
continueParsing = false
} else {
try handleUnknown(tag: tagName, markup: markup)
}
}
return continueParsing
}

func handleUnknown(tag: String, markup: String?) throws {
throw SyntaxError.unknownTag(tag)
}
}
42 changes: 42 additions & 0 deletions Sources/Liquid/BlockBody.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// BlockBody.swift
//
//
// Created by Geoffrey Foster on 2019-09-02.
//

import Foundation

class BlockBody {
private var nodes: [Node] = []

func parse(_ tokenizer: Tokenizer, context: ParseContext, step: (_ tagName: String?, _ markup: String?) throws -> Void) throws -> Void {
while let token = tokenizer.next() {
switch token {
case .text(let value):
nodes.append(StringNode(value))
case .variable(let value):
nodes.append(VariableNode(try Variable(string: value)))
case .tag(let value):
let tagName = value.name
guard let tagType = context.tags[tagName] else {
return try step(tagName, value.markup)
}
let tag = try tagType(tagName, value.markup, context)
try tag.parse(tokenizer, context: context)
nodes.append(tag)
}
}
try step(nil, nil)
}

func render(context: Context) throws -> [String] {
var result: [String] = []
for node in nodes {
result.append(contentsOf: try node.render(context: context))
if context.hasInterrupt { break }
}

return result
}
}
162 changes: 162 additions & 0 deletions Sources/Liquid/Context.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
//
// Context.swift
//
//
// Created by Geoffrey Foster on 2019-09-02.
//

import Foundation

class Scope {
private var values: [String: Value]
let mutable: Bool

init(mutable: Bool = true, values: [String: Value] = [:]) {
self.mutable = mutable
self.values = values
}

subscript(key: String) -> Value? {
get {
return values[key]
}
set {
guard mutable else { return }
values[key] = newValue
}
}
}

public struct EnvironmentKey: Hashable {
public let rawValue: String

public init(_ rawValue: String) {
self.rawValue = rawValue
}
}

public struct RegisterKey: Hashable {
public let rawValue: String

public init(_ rawValue: String) {
self.rawValue = rawValue
}
}

public final class Context {
enum Interrupt {
case `break`
case `continue`
}

private var scopes: [Scope]
private var filters: [String: FilterFunc]

// Registers are for internal data structure storage, like forloop's and cycles to store data
private var registers: [String: Value] = [:]
//Environments are for mutliples runs of the same template with data that is persisted across
private(set) var environment: [String: Value]

public let encoder: Encoder

init(values: [String: Value] = [:], environment: [String: Value] = [:], filters: [String: FilterFunc] = [:], encoder: Encoder) {
self.scopes = [Scope(values: values)]
self.environment = environment
self.filters = filters
self.encoder = encoder
}

func withScope<T>(_ scope: Scope? = nil, block: () throws -> T) rethrows -> T {
if let scope = scope {
pushScope(scope)
}
defer {
if scope != nil {
popScope()
}
}
return try block()
}

private func pushScope(_ scope: Scope = Scope()) {
scopes.append(scope)
}

func popScope() {
_ = scopes.popLast()
}

func value(named name: String) -> Value? {
var value: Value?
for scope in scopes.reversed() {
if let scopedValue = scope[name] {
value = scopedValue
break
}
}

return value
}

func setValue(_ value: Value, named name: String) {
let scope = scopes.reversed().first { $0.mutable }!
scope[name] = value
}

subscript(key: EnvironmentKey) -> Value? {
get {
return environment[key.rawValue]
}
set {
environment[key.rawValue] = newValue
}
}

subscript(key: EnvironmentKey, default defaultValue: @autoclosure () -> Value) -> Value {
get {
return environment[key.rawValue] ?? defaultValue()
}
set {
environment[key.rawValue] = newValue
}
}

subscript(key: RegisterKey) -> Value? {
get {
return registers[key.rawValue]
}
set {
registers[key.rawValue] = newValue
}
}

subscript(key: RegisterKey, default defaultValue: @autoclosure () -> Value) -> Value {
get {
return registers[key.rawValue] ?? defaultValue()
}
set {
registers[key.rawValue] = newValue
}
}

private var interrupts: [Interrupt] = []

func push(interrupt: Interrupt) {
interrupts.append(interrupt)
}

func popInterrupt() -> Interrupt {
guard let interrupt = interrupts.popLast() else {
fatalError() // TODO: exception ?
}
return interrupt
}

var hasInterrupt: Bool {
return !interrupts.isEmpty
}

func filter(named: String) -> FilterFunc? {
return filters[named]
}
}
Loading

0 comments on commit 88d47cc

Please sign in to comment.