Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ let package = Package(
.testTarget(
name: "LexiconTests",
dependencies: [
"Lexicon"
"Lexicon",
"SwiftLexicon"
],
resources: [.copy("Resources")]
),
Expand Down
8 changes: 4 additions & 4 deletions Sources/Lexicon/CLI.Error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,24 @@ public extension CLI.Error {

case "invalidInputCharacter":
guard let string = json.string, string.count == 1 else {
throw "CLI.Error.invalidInputCharacter missing character in \(json)"
throw LexiconError("CLI.Error.invalidInputCharacter missing character in \(json)")
}
self = .invalidInputCharacter(string.first!)

case "noChildrenMatchInput":
guard let string = json.string else {
throw "CLI.Error.noChildrenMatchInput missing string in \(json)"
throw LexiconError("CLI.Error.noChildrenMatchInput missing string in \(json)")
}
self = .noChildrenMatchInput(string)

case "invalidSelection":
guard let int = json.int else {
throw "CLI.Error.invalidSelection missing int in \(json)"
throw LexiconError("CLI.Error.invalidSelection missing int in \(json)")
}
self = .invalidSelection(index: int)

default:
throw "CLI.Error cannot decode \(json)"
throw LexiconError("CLI.Error cannot decode \(json)")
}
}

Expand Down
7 changes: 5 additions & 2 deletions Sources/Lexicon/CLI.Session.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ public extension CLI {
self.events = []
}

// TODO: revisit with composable lexicons
public func event(cli: CLI, event: CustomDebugStringConvertible) async -> Event {
await self.event(cli: cli, description: event.debugDescription)
}

public func event(cli: CLI, description: String) async -> Event {
Event(
time: Date.timeIntervalSinceReferenceDate - startTime,
record: await cli.record(),
description: event.debugDescription
description: description
)
}
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/Lexicon/Lexicon.Document.Merge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ private extension Lexicon.Document {
}
let components = anchorID.components(separatedBy: ".").filter { !$0.isEmpty }
guard components.first == rootName, let anchorName = components.last else {
throw "Cannot graft imported lexicon at '\(anchorID)' inside root '\(rootName)'"
throw LexiconError("Cannot graft imported lexicon at '\(anchorID)' inside root '\(rootName)'")
}
let parentPath = components.dropLast().unlessEmpty?.joined(separator: ".")
let leaf = sourceRoot.rebased(
Expand Down Expand Up @@ -314,7 +314,7 @@ private extension Lexicon.Graph.Node {
return
}
guard var child = children[name] else {
throw "Could not find lemma path component: \(name)"
throw LexiconError("Could not find lemma path component: \(name)")
}
try child.mutate(path: path.dropFirst(), body: body)
children[name] = child
Expand All @@ -325,7 +325,7 @@ private extension SortedDictionary where Key == String, Value == Lexicon.Graph.N

mutating func mutate(_ key: Key, body: (inout Value) throws -> Void) throws {
guard var value = self[key] else {
throw "Could not find lemma: \(key)"
throw LexiconError("Could not find lemma: \(key)")
}
try body(&value)
self[key] = value
Expand Down
2 changes: 1 addition & 1 deletion Sources/Lexicon/Lexicon.Document.Search.swift
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ public extension Lexicon.Search {
) async throws -> [[Double]] {
let embeddings = try await provider.embed(texts)
guard embeddings.count == texts.count else {
throw "Embedding provider returned \(embeddings.count) vectors for \(texts.count) texts."
throw LexiconError("Embedding provider returned \(embeddings.count) vectors for \(texts.count) texts.")
}
return embeddings
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Lexicon/Lexicon.Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public extension Lexicon {
node = root
}
guard let root = node else {
throw "The document does not declare a root lemma"
throw LexiconError("The document does not declare a root lemma")
}
return Graph(root: root, date: date)
}
Expand Down
92 changes: 56 additions & 36 deletions Sources/Lexicon/TaskPaper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,48 @@ public extension UTType {
}

public class TaskPaper {

public typealias Node = Lexicon.Graph.Node


public struct Options: Sendable, Hashable {
public var allowsPlainTextOutlines: Bool

public init(allowsPlainTextOutlines: Bool = false) {
self.allowsPlainTextOutlines = allowsPlainTextOutlines
}

public static let lexicon = Self()
public static let plainTextOutline = Self(allowsPlainTextOutlines: true)
}

public static let pattern = try! (
line: NSRegularExpression(pattern: "^(?<tabs>\\t*)(?<content>.+)"),
lemma: NSRegularExpression(pattern: "^(?<lemma>[\\w]+):?\\s*$"), // TODO: Optional colon `:?` allows plain text (tabbed) outlines, but this should be opted into
lemma: NSRegularExpression(pattern: "^(?<lemma>[\\w]+):\\s*$"),
plainTextLemma: NSRegularExpression(pattern: "^(?<lemma>[\\w]+):?\\s*$"),
operator: NSRegularExpression(pattern: "^(?<operator>[+=?])\\s*(?<content>.*\\S)?\\s*$")
)

public let string: String

public let options: Options

private var result: Result<Lexicon.Graph, Error>?
private var documentResult: Result<Lexicon.Document, Error>?
public init(_ string: String) {

public init(_ string: String, options: Options = .lexicon) {
self.string = string
self.options = options
}
public init(_ UTF8: Data) throws {

public init(_ UTF8: Data, options: Options = .lexicon) throws {
guard let string = String(data: UTF8, encoding: .utf8) else {
throw "Data is not UTF-8"
throw LexiconError("Data is not UTF-8")
}
self.string = string
self.options = options
}

public func decode() throws -> Lexicon.Graph {

if let result = result {
return try result.get()
}
Expand All @@ -63,7 +78,7 @@ public class TaskPaper {
var path: [Node] = []
var document = Lexicon.Document()
var error: Error?

string.enumerateLines{ line, stop in
do {
guard let match = TaskPaper.pattern.line.firstMatch(in: line, options: [], range: line.nsRange) else {
Expand All @@ -78,7 +93,7 @@ public class TaskPaper {
error = o
}
}

while path.count > 1 {
reduce(&path)
}
Expand All @@ -90,7 +105,7 @@ public class TaskPaper {
documentResult = .failure(error)
throw error
}

documentResult = .success(document)
return document
}
Expand All @@ -115,7 +130,7 @@ public class TaskPaper {
continue
}
try decode(line: sourceLine.content, depth: sourceLine.depth, path: &path, document: &document)
if let line = SourceMap.Line(sourceLine, path: path) {
if let line = SourceMap.Line(sourceLine, path: path, taskPaper: self) {
lines.append(line)
}
}
Expand All @@ -127,7 +142,7 @@ public class TaskPaper {
let child = path.removeLast()
path[path.endIndex - 1].children[child.name] = child
}

func decode(line: String, depth: Int, path: inout [Node], document: inout Lexicon.Document) throws {

if line.hasPrefix("@ ") {
Expand Down Expand Up @@ -162,29 +177,29 @@ public class TaskPaper {
}
return
}
let name = TaskPaper.pattern.lemma.first(in: line)?["lemma"]

let name = lemmaName(in: line)

guard path.isNotEmpty else {
guard let name = name, depth == 0 else {
return // ignore everything before the first root node
}
path = [Node(name: name)]
return
}

if let name = name {

if depth == 0 {
finishCurrentRoot(path: &path, document: &document)
path = [Node(name: name)]
return
}

let indent = depth - (path.count - 1)

switch indent {

case 1: // child
break

Expand All @@ -193,19 +208,19 @@ public class TaskPaper {

case ..<0: // ancestor
guard path.count + indent > 0 else {
throw "Line with wrong indent (\(indent)): '\(line)'"
throw LexiconError("Line with wrong indent (\(indent)): '\(line)'")
}
for _ in 0...abs(indent) {
reduce(&path)
}

default:
throw "Line with wrong indent (\(indent)): '\(line)'"
throw LexiconError("Line with wrong indent (\(indent)): '\(line)'")
}

path.append(Node(name: name))
}

else if
let match = TaskPaper.pattern.operator.first(in: line),
let symbol = match["operator"],
Expand Down Expand Up @@ -243,6 +258,11 @@ public class TaskPaper {
reduce(&path)
}
}

func lemmaName(in line: String) -> String? {
let pattern = options.allowsPlainTextOutlines ? Self.pattern.plainTextLemma : Self.pattern.lemma
return pattern.first(in: line)?["lemma"]
}
}

public extension TaskPaper {
Expand Down Expand Up @@ -300,15 +320,15 @@ public extension TaskPaper {
static func encode(_ node: Lexicon.Graph.Node, date: Date = .init()) -> String {
encode(Lexicon.Graph(root: node, date: date))
}

static func encode(_ graph: Lexicon.Graph) -> String {
encode(Lexicon.Document(graph))
}

static func encode(_ document: Lexicon.Document) -> String {

var lines: [String] = []

for comment in document.comments {
lines.append("# \(comment)")
}
Expand Down Expand Up @@ -339,13 +359,13 @@ public extension TaskPaper {
if let protonym = node.protonym {
lines.append("\(tabs)= \(protonym)")
} else {
for type in node.type.sorted(by: <) { // TODO: consider whether to sort it lexicographically
for type in node.type.sorted(by: <) {
lines.append("\(tabs)+ \(type)")
}
}
}
}

return lines.joined(separator: "\n")
}
}
Expand Down Expand Up @@ -433,9 +453,9 @@ private extension TaskPaper {

private extension TaskPaper.SourceMap.Line {

init?(_ sourceLine: TaskPaper.ParsedSourceLine, path: [TaskPaper.Node]) {
init?(_ sourceLine: TaskPaper.ParsedSourceLine, path: [TaskPaper.Node], taskPaper: TaskPaper) {
let nodePath = TaskPaper.nodePath(path)
if let name = TaskPaper.pattern.lemma.first(in: sourceLine.content)?["lemma"] {
if let name = taskPaper.lemmaName(in: sourceLine.content) {
self.init(
line: sourceLine.line,
depth: sourceLine.depth,
Expand Down
2 changes: 1 addition & 1 deletion Sources/Lexicon/util/Optional.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public extension Optional {
_ file: String = #file,
_ line: Int = #line
) throws -> Wrapped {
try or(throw: "⚠️ \(function):\(file):\(line)")
try or(throw: LexiconError("Missing value in \(function):\(file):\(line)"))
}

@inlinable func or(throw error: @autoclosure () -> Error) throws -> Wrapped {
Expand Down
Loading
Loading