process template lines when Lexer is created not when parsing each token. (#230)

This commit is contained in:
Ilya Puchka
2018-09-10 11:39:19 +01:00
committed by GitHub
parent 00e71c1b4d
commit acda1b0caf
7 changed files with 52 additions and 51 deletions

View File

@@ -66,11 +66,11 @@ open class SimpleErrorReporter: ErrorReporter {
func describe(token: Token) -> String {
let templateName = token.sourceMap.filename ?? ""
let line = token.sourceMap.line
let highlight = "\(String(Array(repeating: " ", count: line.offset)))^\(String(Array(repeating: "~", count: max(token.contents.characters.count - 1, 0))))"
let location = token.sourceMap.location
let highlight = "\(String(Array(repeating: " ", count: location.lineOffset)))^\(String(Array(repeating: "~", count: max(token.contents.characters.count - 1, 0))))"
return "\(templateName)\(line.number):\(line.offset): error: \(templateError.reason)\n"
+ "\(line.content)\n"
return "\(templateName)\(location.lineNumber):\(location.lineOffset): error: \(templateError.reason)\n"
+ "\(location.content)\n"
+ "\(highlight)\n"
}

View File

@@ -1,12 +1,20 @@
import Foundation
typealias Line = (content: String, number: UInt, range: Range<String.Index>)
struct Lexer {
let templateName: String?
let templateString: String
let lines: [Line]
init(templateName: String? = nil, templateString: String) {
self.templateName = templateName
self.templateString = templateString
self.lines = templateString.components(separatedBy: .newlines).enumerated().flatMap {
guard !$0.element.isEmpty else { return nil }
return (content: $0.element, number: UInt($0.offset + 1), templateString.range(of: $0.element)!)
}
}
func createToken(string: String, at range: Range<String.Index>) -> Token {
@@ -25,8 +33,8 @@ struct Lexer {
if string.hasPrefix("{{") || string.hasPrefix("{%") || string.hasPrefix("{#") {
let value = strip()
let range = templateString.range(of: value, range: range) ?? range
let line = templateString.rangeLine(range)
let sourceMap = SourceMap(filename: templateName, line: line)
let location = rangeLocation(range)
let sourceMap = SourceMap(filename: templateName, location: location)
if string.hasPrefix("{{") {
return .variable(value: value, at: sourceMap)
@@ -37,8 +45,8 @@ struct Lexer {
}
}
let line = templateString.rangeLine(range)
let sourceMap = SourceMap(filename: templateName, line: line)
let location = rangeLocation(range)
let sourceMap = SourceMap(filename: templateName, location: location)
return .text(value: string, at: sourceMap)
}
@@ -72,6 +80,14 @@ struct Lexer {
return tokens
}
func rangeLocation(_ range: Range<String.Index>) -> ContentLocation {
guard let line = self.lines.first(where: { $0.range.contains(range.lowerBound) }) else {
return ("", 0, 0)
}
let offset = templateString.distance(from: line.range.lowerBound, to: range.lowerBound)
return (line.content, line.number, offset)
}
}
class Scanner {
@@ -179,23 +195,6 @@ extension String {
let last = findLastNot(character: character) ?? endIndex
return String(self[first..<last])
}
public func rangeLine(_ range: Range<String.Index>) -> RangeLine {
var lineNumber: UInt = 0
var offset: Int = 0
var lineContent = ""
for line in components(separatedBy: CharacterSet.newlines) {
lineNumber += 1
lineContent = line
if let rangeOfLine = self.range(of: line), rangeOfLine.contains(range.lowerBound) {
offset = distance(from: rangeOfLine.lowerBound, to: range.lowerBound)
break
}
}
return (lineContent, lineNumber, offset)
}
}
public typealias RangeLine = (content: String, number: UInt, offset: Int)
public typealias ContentLocation = (content: String, lineNumber: UInt, lineOffset: Int)

View File

@@ -125,9 +125,9 @@ public class TokenParser {
}
// find offset of filter in the containing token so that only filter is highligted, not the whole token
if let filterTokenRange = containingToken.contents.range(of: filterToken) {
var rangeLine = containingToken.sourceMap.line
rangeLine.offset += containingToken.contents.distance(from: containingToken.contents.startIndex, to: filterTokenRange.lowerBound)
syntaxError.token = .variable(value: filterToken, at: SourceMap(filename: containingToken.sourceMap.filename, line: rangeLine))
var location = containingToken.sourceMap.location
location.lineOffset += containingToken.contents.distance(from: containingToken.contents.startIndex, to: filterTokenRange.lowerBound)
syntaxError.token = .variable(value: filterToken, at: SourceMap(filename: containingToken.sourceMap.filename, location: location))
} else {
syntaxError.token = containingToken
}

View File

@@ -57,17 +57,17 @@ extension String {
public struct SourceMap: Equatable {
public let filename: String?
public let line: RangeLine
public let location: ContentLocation
init(filename: String? = nil, line: RangeLine = ("", 0, 0)) {
init(filename: String? = nil, location: ContentLocation = ("", 0, 0)) {
self.filename = filename
self.line = line
self.location = location
}
static let unknown = SourceMap()
public static func ==(lhs: SourceMap, rhs: SourceMap) -> Bool {
return lhs.filename == rhs.filename && lhs.line == rhs.line
return lhs.filename == rhs.filename && lhs.location == rhs.location
}
}