replaced Token with Lexeme protocol on TemplateSyntaxError
This commit is contained in:
@@ -17,3 +17,29 @@ public class TemplateDoesNotExist: Error, CustomStringConvertible {
|
|||||||
return "Template named `\(templates)` does not exist. No loaders found"
|
return "Template named `\(templates)` does not exist. No loaders found"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct TemplateSyntaxError : Error, Equatable, CustomStringConvertible {
|
||||||
|
public let description:String
|
||||||
|
var lexeme: Lexeme?
|
||||||
|
|
||||||
|
public init(_ description:String) {
|
||||||
|
self.description = description
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs:TemplateSyntaxError, rhs:TemplateSyntaxError) -> Bool {
|
||||||
|
return lhs.description == rhs.description
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Range where Bound == String.Index {
|
||||||
|
internal static var unknown: Range {
|
||||||
|
return "".range
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
var range: Range<String.Index> {
|
||||||
|
return startIndex..<endIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,8 +53,29 @@ struct Lexer {
|
|||||||
|
|
||||||
return tokens
|
return tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lexemeLine(_ lexeme: Lexeme) -> (content: String, number: Int, offset: String.IndexDistance) {
|
||||||
|
var lineNumber: Int = 0
|
||||||
|
var offset = 0
|
||||||
|
var lineContent = ""
|
||||||
|
|
||||||
|
templateString.enumerateLines { (line, stop) in
|
||||||
|
lineNumber += 1
|
||||||
|
lineContent = line
|
||||||
|
if let rangeOfLine = self.templateString.range(of: line), rangeOfLine.contains(lexeme.range.lowerBound) {
|
||||||
|
offset = self.templateString.distance(from: rangeOfLine.lowerBound, to:
|
||||||
|
lexeme.range.lowerBound)
|
||||||
|
stop = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (lineContent, lineNumber, offset)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protocol Lexeme {
|
||||||
|
var range: Range<String.Index> { get }
|
||||||
|
}
|
||||||
|
|
||||||
class Scanner {
|
class Scanner {
|
||||||
let _content: String //stores original content
|
let _content: String //stores original content
|
||||||
@@ -163,20 +184,4 @@ extension String {
|
|||||||
return String(self[first..<last])
|
return String(self[first..<last])
|
||||||
}
|
}
|
||||||
|
|
||||||
func lineAndPosition(at range: Range<String.Index>) -> (content: String, number: Int, offset: String.IndexDistance) {
|
|
||||||
var lineNumber: Int = 0
|
|
||||||
var offset = 0
|
|
||||||
var lineContent = ""
|
|
||||||
|
|
||||||
enumerateLines { (line, stop) in
|
|
||||||
lineNumber += 1
|
|
||||||
lineContent = line
|
|
||||||
if let rangeOfLine = self.range(of: line), rangeOfLine.contains(range.lowerBound) {
|
|
||||||
offset = self.distance(from: rangeOfLine.lowerBound, to: range.lowerBound)
|
|
||||||
stop = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (lineContent, lineNumber, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct TemplateSyntaxError : Error, Equatable, CustomStringConvertible {
|
|
||||||
public let description:String
|
|
||||||
public var token: Token?
|
|
||||||
|
|
||||||
public init(_ description:String) {
|
|
||||||
self.description = description
|
|
||||||
}
|
|
||||||
|
|
||||||
public func contextAwareError(templateName: String?, templateContent: String) -> TemplateSyntaxError? {
|
|
||||||
guard let token = token, token.range != .unknown else { return nil }
|
|
||||||
let templateName = templateName.map({ "\($0):" }) ?? ""
|
|
||||||
let (line, lineNumber, offset) = templateContent.lineAndPosition(at: token.range)
|
|
||||||
let tokenContent = templateContent.substring(with: token.range)
|
|
||||||
let highlight = "\(String(Array(repeating: " ", count: offset)))^\(String(Array(repeating: "~", count: max(tokenContent.count - 1, 0))))"
|
|
||||||
let description = "\(templateName)\(lineNumber):\(offset): error: " + self.description + "\n\(line)\n\(highlight)\n"
|
|
||||||
return TemplateSyntaxError(description)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public func ==(lhs:TemplateSyntaxError, rhs:TemplateSyntaxError) -> Bool {
|
|
||||||
return lhs.description == rhs.description
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public protocol NodeType {
|
public protocol NodeType {
|
||||||
/// Render the node in the given context
|
/// Render the node in the given context
|
||||||
func render(_ context:Context) throws -> String
|
func render(_ context:Context) throws -> String
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ public class TokenParser {
|
|||||||
let node = try parser(self, token)
|
let node = try parser(self, token)
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
} catch {
|
} catch {
|
||||||
if var syntaxError = error as? TemplateSyntaxError, syntaxError.token == nil {
|
if var syntaxError = error as? TemplateSyntaxError, syntaxError.lexeme == nil {
|
||||||
syntaxError.token = token
|
syntaxError.lexeme = token
|
||||||
throw syntaxError
|
throw syntaxError
|
||||||
} else {
|
} else {
|
||||||
throw error
|
throw error
|
||||||
|
|||||||
@@ -68,17 +68,8 @@ open class Template: ExpressibleByStringLiteral {
|
|||||||
func render(_ context: Context) throws -> String {
|
func render(_ context: Context) throws -> String {
|
||||||
let context = context
|
let context = context
|
||||||
let parser = TokenParser(tokens: tokens, environment: context.environment)
|
let parser = TokenParser(tokens: tokens, environment: context.environment)
|
||||||
do {
|
let nodes = try parser.parse()
|
||||||
let nodes = try parser.parse()
|
return try renderNodes(nodes, context)
|
||||||
return try renderNodes(nodes, context)
|
|
||||||
} catch {
|
|
||||||
if let syntaxError = error as? TemplateSyntaxError,
|
|
||||||
let error = syntaxError.contextAwareError(templateName: name, templateContent: templateString) {
|
|
||||||
throw error
|
|
||||||
} else {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render the given template
|
/// Render the given template
|
||||||
|
|||||||
@@ -40,19 +40,7 @@ extension String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Range where Bound == String.Index {
|
public enum Token : Equatable, Lexeme {
|
||||||
internal static var unknown: Range {
|
|
||||||
return "".range
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension String {
|
|
||||||
var range: Range<String.Index> {
|
|
||||||
return startIndex..<endIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Token : Equatable {
|
|
||||||
/// A token representing a piece of text.
|
/// A token representing a piece of text.
|
||||||
case text(value: String, at: Range<String.Index>)
|
case text(value: String, at: Range<String.Index>)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user