Skip to content

Commit

Permalink
Cleanup (lots of stuff)
Browse files Browse the repository at this point in the history
  • Loading branch information
Danappelxx committed Sep 9, 2016
1 parent 47193c5 commit e312d85
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 222 deletions.
189 changes: 102 additions & 87 deletions Sources/Compiler.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// stage 2 parsing
// Stage 2 - Comile tokens into an AST

public typealias AST = [ASTNode]
public enum ASTNode: Equatable {
case text(String)
Expand All @@ -11,9 +12,12 @@ public enum ASTNode: Equatable {

public func ==(lhs: ASTNode, rhs: ASTNode) -> Bool {
switch (lhs, rhs) {
case let (.text(l), .text(r)): return l == r
case let (.variable(l), .variable(r)): return l == r
case let (.text(la), .text(ra)): return la == ra
case let (.variable(la, lb), .variable(ra, rb)): return la == ra && lb == rb
case let (.partial(la), .partial(ra)): return la == ra
case let (.section(la, lb), .section(ra, rb)): return la == ra && lb == rb
case let (.invertedSection(la, lb), .invertedSection(ra, rb)): return la == ra && lb == rb
case let (.override(la, lb), .override(ra, rb)): return la == ra && lb == rb
default: return false
}
}
Expand All @@ -23,115 +27,126 @@ public enum CompilerError: Error {
case badSectionIdentifier(got: String, expected: String)
}

public func compile(tokens: [Token], with templates: [String:AST] = [:]) throws -> AST {
var index = 0
return try compile(tokens: tokens, index: &index, templates: templates)
}
public final class Compiler {
private let tokens: [Token]
private let templates: [String:AST]

public func compile(tokens: [Token], index: inout Int, templates: [String:AST] = [:], openToken: Token? = nil) throws -> AST {
var ast = AST()
while let token = tokens.element(at: index) {
defer { index += 1 }
switch token {
case .comment:
break

case let .text(text):
ast.append(.text(text))

case let .variable(variable):
ast.append(.variable(variable, escaped: true))
case let .unescapedVariable(variable):
ast.append(.variable(variable, escaped: false))

case let .partial(partial, indentation):
guard let partial = templates[partial] else {
break
}
let indented = partial.map { node -> ASTNode in
guard case let .text(text) = node else {
return node
}
public init(tokens: [Token], templates: [String:AST] = [:]) {
self.tokens = tokens
self.templates = templates
}

return .text(text.characters.split(separator: "\n", omittingEmptySubsequences: false).map(String.init(_:)).map { indentation + $0 }.joined(separator: "\n"))
}
ast.append(.partial(ast: indented))
public func compile() throws -> AST {
var index = 0
return try compile(index: &index)
}

// nested section, recurse
case let .openSection(variable):
index += 1; defer { index -= 1 }
private func compile(index: inout Int, openToken: Token? = nil) throws -> AST {
var ast = AST()

let innerAST = try compile(tokens: tokens, index: &index, templates: templates, openToken: token)
ast.append(.section(variable: variable, ast: innerAST))
while let token = tokens.element(at: index) {
index += 1

case let .openInvertedSection(variable):
index += 1; defer { index -= 1 }
switch token {
case .comment:
continue

let innerAST = try compile(tokens: tokens, index: &index, templates: templates, openToken: token)
ast.append(.invertedSection(variable: variable, ast: innerAST))
case let .text(text):
ast.append(.text(text))

case let .openOverrideSection(identifier: idenitifer):
index += 1; defer { index -= 1 }
case let .variable(variable):
ast.append(.variable(variable, escaped: true))
case let .unescapedVariable(variable):
ast.append(.variable(variable, escaped: false))

let innerAST = try compile(tokens: tokens, index: &index, templates: templates, openToken: token)
ast.append(.override(identifier: idenitifer, ast: innerAST))
case let .partial(partial, indentation):
guard let partial = templates[partial] else {
continue
}
ast.append(.partial(ast: indent(partial: partial, with: indentation)))

case let .openParentSection(identifier: identifier):
index += 1; defer { index -= 1 }
// nested section, recurse
case let .openSection(variable):
let innerAST = try compile(index: &index, openToken: token)
ast.append(.section(variable: variable, ast: innerAST))

let innerAST = try compile(tokens: tokens, index: &index, templates: templates, openToken: token)
case let .openInvertedSection(variable):
let innerAST = try compile(index: &index, openToken: token)
ast.append(.invertedSection(variable: variable, ast: innerAST))

// if we can't find what we're trying to override, ignore it
guard let overriding = templates[identifier] else {
continue
}
case let .openOverrideSection(identifier: idenitifer):
let innerAST = try compile(index: &index, openToken: token)
ast.append(.override(identifier: idenitifer, ast: innerAST))

case let .openParentSection(identifier: identifier):
let innerAST = try compile(index: &index, openToken: token)

// for every inherited node, search for a matching node here. if found,
// replace inherited ast for that node with overriden ast
let overriden: AST = overriding.map { node in
// we don't mess with anything but override nodes
guard case let .override(identifier: overridingIdentifier, ast: _) = node else {
return node
// if we can't find what we're trying to override, ignore it
guard let overriding = templates[identifier] else {
continue
}

// search for nodes with a matching identifier
for node in innerAST {
guard case let .override(identifier: identifier, ast: _) = node, identifier == overridingIdentifier else {
continue
}
// we found a matching node! it replaces the inherited one
return node
ast.append(contentsOf: override(overriding, with: innerAST))

// close sections return early, and are used
// to end recursion started by open sections
case let .closeSection(variable):
guard let openToken = openToken else {
// if it hits this, that means it was not preceded by an opensection token
throw CompilerError.expectingToken(token: .openSection(variable: variable))
}

return node
}
switch openToken {
case let .openSection(variable: openVariable),
let .openInvertedSection(variable: openVariable),
let .openOverrideSection(identifier: openVariable),
let .openParentSection(identifier: openVariable):
guard openVariable == variable else {
throw CompilerError.badSectionIdentifier(got: variable, expected: openVariable)
}
return ast

ast.append(contentsOf: overriden)
default:
throw CompilerError.expectingToken(token: .openSection(variable: variable))
}
}
}

case let .closeSection(variable):
return ast
}
}

// TODO: also handle misnamed token
guard let openToken = openToken else {
// if it hits this, that means it was not preceded by an opensection token
throw CompilerError.expectingToken(token: .openSection(variable: variable))
fileprivate extension Compiler {
func override(_ inherited: AST, with ast: AST) -> AST {
// for every inherited node, search for a matching node in the ast.
// if found, replace inherited node with matching node
return inherited.map { inherited in
// we don't mess with anything but override nodes
guard case let .override(identifier: overridingIdentifier, ast: _) = inherited else {
return inherited
}

switch openToken {
case let .openSection(variable: openVariable),
let .openInvertedSection(variable: openVariable),
let .openOverrideSection(identifier: openVariable),
let .openParentSection(identifier: openVariable):
guard openVariable == variable else {
throw CompilerError.badSectionIdentifier(got: variable, expected: openVariable)
// search for nodes with a matching identifier
for node in ast {
guard case let .override(identifier: identifier, ast: _) = node, identifier == overridingIdentifier else {
continue
}

default:
throw CompilerError.expectingToken(token: .openSection(variable: variable))
// we found a matching node! it replaces the inherited one
return node
}

return ast
return inherited
}
}

return ast
// TODO: Fix this... it's breaking a single test case and I don't know why
func indent(partial: AST, with indentation: String) -> AST {
return partial.map { node in
guard case let .text(text) = node else {
return node
}

return .text(text.characters.split(separator: "\n", omittingEmptySubsequences: false).map(String.init(_:)).map { indentation + $0 }.joined(separator: "\n"))
}
}
}
13 changes: 0 additions & 13 deletions Sources/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,6 @@ extension Dictionary {
}
}

extension AnyIterator where Element: Equatable {
func pop(until element: Element) -> (matched: Bool, popped: [Element]) {
var popped = [Element]()
while let next = next() {
guard next != element else {
return (matched: true, popped: popped)
}
popped.append(next)
}
return (matched: false, popped: popped)
}
}

extension String {
static let newLineCharacterSet: [Character] = ["\n", "\r", "\r\n"]
static let whitespaceCharacterSet: [Character] = [" ", "\t"]
Expand Down
Loading

0 comments on commit e312d85

Please sign in to comment.