Skip to content

Instantly share code, notes, and snippets.

@JadenGeller
Last active September 12, 2024 04:50
Show Gist options
  • Save JadenGeller/e4b337991e028ba97b68e837fb201102 to your computer and use it in GitHub Desktop.
Save JadenGeller/e4b337991e028ba97b68e837fb201102 to your computer and use it in GitHub Desktop.
parsing custom attributed text format
import SwiftUI
struct AttributedStringBuilder {
@Stack var attributes: RichTextAttributes = .init()
var string: AttributedString = ""
mutating func text(_ span: String) {
string += AttributedString(span, attributes: .init(attributes))
}
mutating func attribute(_ span: String) {
attributes.parse(span.trimmingCharacters(in: .whitespaces)) // todo: only do at end prob?
}
mutating func push() {
$attributes.push()
}
mutating func pop() {
try? $attributes.pop() // todo: error handling
}
}
struct DirectiveProcessor {
var upstream: AttributedStringBuilder
var span = ""
var isDirective = false
mutating func delimit() {
if isDirective {
upstream.attribute(span)
} else if !span.isEmpty {
upstream.text(span)
}
span = ""
}
mutating func directive() {
delimit()
if !isDirective {
upstream.push()
isDirective = true
}
}
mutating func open() {
delimit()
isDirective = false
}
mutating func close() {
delimit()
if isDirective {
// oops! a dangling directive. we pushed but was never opened!
upstream.pop()
}
upstream.pop()
}
mutating func char(_ character: Character) {
span.append(character)
}
}
struct EscapingTokenizer {
var upstream: DirectiveProcessor
var isEscaped = false
mutating func token(_ character: Character) {
switch (isEscaped, character) {
case (true, #"\"#):
isEscaped = false
upstream.char(character)
case (true, "{"):
isEscaped = false
upstream.char(character)
case (true, "}"):
isEscaped = false
upstream.char(character)
case (true, "n"): // fixme: allow other escapes?
isEscaped = false
upstream.char("\n")
case (true, _):
isEscaped = false
upstream.directive()
upstream.char(character)
case (false, #"\"#):
isEscaped = true
case (false, "{"):
upstream.open()
case (false, "}"):
upstream.close()
case (false, _):
upstream.char(character)
}
}
mutating func terminate() {
upstream.delimit()
}
}
\textStyle body {The \color blue {sky appears blue} due to a phenomenon called \italic \color indigo {Rayleigh scattering}. This occurs when \weight bold {sunlight} passes through the Earth's atmosphere and \highlight yellow {interacts with molecules and small particles}.}
\textStyle body {Sunlight is made up of different colors, each with its own wavelength. \highlight cyan \italic {Blue light} has a shorter wavelength and is scattered in all directions by the gases and particles in the atmosphere. This scattered blue light is what we see when we look up at the sky.}
\textStyle body {In contrast, \color red {red light} has a longer wavelength and is scattered less, which is why the sky can appear \highlight orange {reddish during sunrise and sunset} when the light path through the atmosphere is longer.}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment