Skip to content

Commit

Permalink
Refactor format configurations.
Browse files Browse the repository at this point in the history
  • Loading branch information
objecthub committed May 10, 2023
1 parent d9bc2be commit 255b796
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 59 deletions.
8 changes: 4 additions & 4 deletions CLFormat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
CCB5F11329C1475F00587140 /* Currency.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCB5F0D029C1148900587140 /* Currency.swift */; };
CCB5F11429C1475F00587140 /* Arguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCB5F0DC29C1157C00587140 /* Arguments.swift */; };
CCB5F11C29C148A400587140 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCB5F11B29C148A400587140 /* main.swift */; };
CCB5F12329C7974B00587140 /* CLControlParserConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCB5F12229C7974B00587140 /* CLControlParserConfig.swift */; };
CCB5F12329C7974B00587140 /* CLFormatConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCB5F12229C7974B00587140 /* CLFormatConfig.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -82,7 +82,7 @@
CCB5F10029C1455B00587140 /* CLFormatTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLFormatTests.swift; sourceTree = "<group>"; };
CCB5F11929C148A400587140 /* CLFormatTool */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CLFormatTool; sourceTree = BUILT_PRODUCTS_DIR; };
CCB5F11B29C148A400587140 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
CCB5F12229C7974B00587140 /* CLControlParserConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLControlParserConfig.swift; sourceTree = "<group>"; };
CCB5F12229C7974B00587140 /* CLFormatConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLFormatConfig.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -150,7 +150,7 @@
CCB5F0DC29C1157C00587140 /* Arguments.swift */,
CCB5F0DE29C115A500587140 /* CLControl.swift */,
CCB5F0E029C115C400587140 /* CLControlParser.swift */,
CCB5F12229C7974B00587140 /* CLControlParserConfig.swift */,
CCB5F12229C7974B00587140 /* CLFormatConfig.swift */,
CCB5F0E229C115E600587140 /* StandardDirectiveSpecifier.swift */,
CCB5F0E429C1160700587140 /* CLFormat.swift */,
CCB5F0F429C1455B00587140 /* CLFormat.h */,
Expand Down Expand Up @@ -317,7 +317,7 @@
files = (
CCB5F11329C1475F00587140 /* Currency.swift in Sources */,
CCB5F11429C1475F00587140 /* Arguments.swift in Sources */,
CCB5F12329C7974B00587140 /* CLControlParserConfig.swift in Sources */,
CCB5F12329C7974B00587140 /* CLFormatConfig.swift in Sources */,
CC2FF5FB29D8C8F500AD0BC6 /* Optional.swift in Sources */,
CCB5F10C29C1475F00587140 /* Parameters.swift in Sources */,
CCB5F11029C1475F00587140 /* StandardDirectiveSpecifier.swift in Sources */,
Expand Down
17 changes: 8 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
This framework implements
[Common Lisp's `format` procedure](https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node200.html#SECTION002633000000000000000)
from scratch in Swift. `format` is a procedure that produces formatted text using a
format string similar to the `printf` format string. The formatting formalism is
significantly more expressive compared to what `printf` has to offer. It allows users
to display numbers in various formats (e.g. hex, binary, octal, roman numerals, natural
language), apply conditional formatting, output text in a tabular format, iterate over
data structures, and even apply `format` recursively to handle data that includes their
own preferred formatting strings.
format string similar to `printf`. The formatting formalism is significantly more expressive
compared to `printf`. It allows users to display numbers in various formats (e.g. hex, binary,
octal, roman numerals, natural language), apply conditional formatting, output text in a
tabular format, iterate over data structures, and even apply `format` recursively to handle
data that includes their own preferred formatting strings.

The documentation of this framework includes:

Expand Down Expand Up @@ -47,9 +46,9 @@ func clformat(_ control: String,

`control` is the formatting string. It is using the formatting language described in the
next section to define how the output will be formatted. `config` refers to the
[control parser configuration](https://github.com/objecthub/swift-clformat/blob/main/Sources/CLFormat/CLControlParserConfig.swift)
which determines how the control string gets parsed. This parameter
gets usually omitted, unless a user wants to define their own control formatting
[format configuration](https://github.com/objecthub/swift-clformat/blob/main/Sources/CLFormat/CLFormatConfig.swift)
which determines how the control string and the arguments get parsed and interpreted. This
parameter gets usually omitted, unless a user wants to define their own control formatting
language. `locale` refers to a `Locale` object which is used for executing
locale-specific directives. `tabsize` defines the maximum number of space characters that
correspond to a single tab character. `linewidth` specifies the number of characters per
Expand Down
22 changes: 11 additions & 11 deletions Sources/CLFormat/CLControl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import Foundation
/// format function is provided by this struct. The format function can be
/// called arbitrary many times for a given struct (it is reentrant as well).
///
/// A `Control` value consists of a control parser configuration as well as
/// A `Control` value consists of a format configuration as well as
/// a sequence of "components". A component is either a text fragment or a
/// formatting directive.
///
Expand All @@ -47,24 +47,24 @@ public struct CLControl: CustomStringConvertible {
}
}

/// The control parser configuration. This value needs to be preserved
/// The format configuration. This value needs to be preserved
/// because there is the need to potentially parse arguments as control
/// strings, e.g. via the ~?/indirection directive.
public let config: CLControlParserConfig
public let config: CLFormatConfig

/// The parsed control components.
public let components: [Component]

/// Constructor for manually creating control values.
public init(components: [Component], config: CLControlParserConfig? = nil) {
self.config = config ?? CLControlParserConfig.standard
public init(components: [Component], config: CLFormatConfig? = nil) {
self.config = config ?? CLFormatConfig.standard
self.components = components
}

/// Constructor for parsing a control string into a control value.
public init(string: String, config: CLControlParserConfig? = nil) throws {
public init(string: String, config: CLFormatConfig? = nil) throws {
self = try CLControlParser(control: string,
config: config ?? CLControlParserConfig.standard).parse()
config: config ?? CLFormatConfig.standard).parse()
}

/// Returns true if the control string was empty.
Expand Down Expand Up @@ -137,11 +137,11 @@ public struct CLControl: CustomStringConvertible {

///
/// A `Context` value represents a formatting context (i.e. a linked list of
/// context values. The root refers to the control parser configuration (to make
/// context values. The root refers to the format configuration (to make
/// it accessible from the formatting logic).
///
public enum Context {
case root(CLControlParserConfig)
case root(CLFormatConfig)
indirect case frame(String, Context)

public var current: String {
Expand All @@ -153,12 +153,12 @@ public enum Context {
}
}

public var parserConfig: CLControlParserConfig {
public var config: CLFormatConfig {
switch self {
case .root(let config):
return config
case .frame(_, let context):
return context.parserConfig
return context.config
}
}
}
4 changes: 2 additions & 2 deletions Sources/CLFormat/CLControlParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ import Foundation
/// conditional directives).
///
public class CLControlParser {
internal let config: CLControlParserConfig
internal let config: CLFormatConfig
internal let control: String
internal var i: String.Index

public init(control: String, config: CLControlParserConfig) {
public init(control: String, config: CLFormatConfig) {
self.config = config
self.control = control
self.i = control.startIndex
Expand Down
12 changes: 6 additions & 6 deletions Sources/CLFormat/CLFormat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ extension Optional: CLFormatConvertible, DebugCLFormatConvertible {
extension String {

public init(control: String,
config: CLControlParserConfig? = nil,
config: CLFormatConfig? = nil,
locale: Locale? = nil,
tabsize: Int = 4,
linewidth: Int = 80,
Expand All @@ -100,7 +100,7 @@ extension String {
}

public init(control: String,
config: CLControlParserConfig? = nil,
config: CLFormatConfig? = nil,
locale: Locale? = nil,
tabsize: Int = 4,
linewidth: Int = 80,
Expand Down Expand Up @@ -129,7 +129,7 @@ extension String {
}

public func clformat(_ control: String,
config: CLControlParserConfig? = CLControlParserConfig.default,
config: CLFormatConfig? = CLFormatConfig.default,
locale: Locale? = nil,
tabsize: Int = 4,
linewidth: Int = 80,
Expand All @@ -142,7 +142,7 @@ public func clformat(_ control: String,
}

public func clformat(_ control: String,
config: CLControlParserConfig? = CLControlParserConfig.default,
config: CLFormatConfig? = CLFormatConfig.default,
locale: Locale? = nil,
tabsize: Int = 4,
linewidth: Int = 80,
Expand All @@ -155,7 +155,7 @@ public func clformat(_ control: String,
}

public func clprintf(_ control: String,
config: CLControlParserConfig? = CLControlParserConfig.default,
config: CLFormatConfig? = CLFormatConfig.default,
locale: Locale? = nil,
tabsize: Int = 4,
linewidth: Int = 80,
Expand All @@ -171,7 +171,7 @@ public func clprintf(_ control: String,
}

public func clprintf(_ control: String,
config: CLControlParserConfig? = CLControlParserConfig.default,
config: CLFormatConfig? = CLFormatConfig.default,
locale: Locale? = nil,
tabsize: Int = 4,
linewidth: Int = 80,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@
import Foundation

///
/// A control parser config value contains a mapping from directive identifiers
/// (characters) to directive parsers which parse the directive. Most directive
/// are atomic (i.e. they are not composite) and parsing for those is trivial:
/// they just package up the generically parsed parameters and modifiers in a
/// new directive specifier value. Composite directives have more complex parsing
/// logic.
/// A format configuration value contains an argument factory as well as a mapping
/// from directive identifiers (characters) to directive parsers which parse the
/// directive. Most directives are atomic (i.e. they are not composite) and parsing
/// for those is trivial: they just package up the generically parsed parameters and
/// modifiers in a new directive specifier value. Composite directives have more
/// complex parsing logic.
///
public struct CLControlParserConfig {
private let makeArguments: (Locale?, Int, Int, [Any?], Int?) -> Arguments
public struct CLFormatConfig {
public var makeArguments: (Locale?, Int, Int, [Any?], Int?) -> Arguments
private var directiveParsers: [Character : DirectiveParser]

public init(makeArguments: @escaping (Locale?, Int, Int, [Any?], Int?) -> Arguments =
Expand All @@ -47,25 +47,23 @@ public struct CLControlParserConfig {
return self.makeArguments(locale, tabsize, linewidth, args, numArgumentsLeft)
}

/// Add a new directive parser to this control parser configuration for the given
/// Add a new directive parser to this format configuration for the given
/// array of characters.
public mutating func parse(_ chars: [Character], with parser: @escaping DirectiveParser) {
for char in chars {
self.directiveParsers[char] = parser
}
}

/// Add a new directive parser to this control parser configuration for the given
/// characters.
/// Add a new directive parser to this format configuration for the given characters.
public mutating func parse(_ chars: Character..., with parser: @escaping DirectiveParser) {
for char in chars {
self.directiveParsers[char] = parser
}
}

/// Add a new default directive parser which simply appends the directive to the list of
/// control components to this control parser configuration for the given
/// characters.
/// control components to this format configuration for the given characters.
public mutating func parse(_ chars: Character..., appending specifier: DirectiveSpecifier) {
self.parse(chars) { parser, parameters, modifiers in
return .append(Directive(parameters: parameters,
Expand All @@ -87,11 +85,10 @@ public struct CLControlParserConfig {
return self.directiveParsers[ch]
}

/// The standard control parser configuration. Whenever `nil` is specified as a
/// control parser config, this struct is used. The `String` initializers use this
/// configuration as a default.
public static let standard: CLControlParserConfig = {
var config = CLControlParserConfig()
/// The standard format configuration. Whenever `nil` is specified as a format configuration,
/// this struct is used. The `String` initializers use this configuration as a default.
public static let standard: CLFormatConfig = {
var config = CLFormatConfig()
config.parse("a", "A", appending: StandardDirectiveSpecifier.ascii)
config.parse("w", "W", appending: StandardDirectiveSpecifier.write)
config.parse("s", "S", appending: StandardDirectiveSpecifier.sexpr)
Expand Down Expand Up @@ -250,9 +247,8 @@ public struct CLControlParserConfig {
return config
}()

/// The default control parser configuration for the functions `clformat` and
/// `clprintf`. This parser configuation is mutable so that the behavior can be
/// changed globally. Please note that the `String` initializers do not rely on
/// this mutable config as a default.
public static var `default`: CLControlParserConfig = CLControlParserConfig.standard
/// The default format configuration for the functions `clformat` and `clprintf`. This
/// configuation is mutable so that the behavior can be changed globally. Please note
/// that the `String` initializers do not rely on this mutable config as a default.
public static var `default`: CLFormatConfig = CLFormatConfig.standard
}
8 changes: 4 additions & 4 deletions Sources/CLFormat/StandardDirectiveSpecifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ public enum StandardDirectiveSpecifier: DirectiveSpecifier {
case .iteration(let control, let force):
var res = ""
let control = control.isEmpty ? try CLControl(string: try arguments.nextAsString(),
config: context.parserConfig) : control
config: context.config) : control
var itercap = try parameters.number(0) ?? Int.max
var force = force
if modifiers.contains(.colon) {
Expand All @@ -635,7 +635,7 @@ public enum StandardDirectiveSpecifier: DirectiveSpecifier {
let subitercap = try parameters.number(1) ?? Int.max
if let obj = arg, let arr = arguments.coerceToArray(obj, capAt: subitercap) {
let formatted = try control.format(with:
context.parserConfig.makeArguments(
context.config.makeArguments(
locale: arguments.locale,
tabsize: arguments.tabsize,
args: arr,
Expand All @@ -648,7 +648,7 @@ public enum StandardDirectiveSpecifier: DirectiveSpecifier {
var newargs = [Any?]()
newargs.append(arg)
let formatted = try control.format(with:
context.parserConfig.makeArguments(
context.config.makeArguments(
locale: arguments.locale,
tabsize: arguments.tabsize,
args: newargs,
Expand Down Expand Up @@ -740,7 +740,7 @@ public enum StandardDirectiveSpecifier: DirectiveSpecifier {
}
case .indirection:
let control = try CLControl(string: try arguments.nextAsString(),
config: context.parserConfig)
config: context.config)
if modifiers.contains(.at) {
return .append(try control.format(with: arguments, in: context).string)
} else {
Expand Down

0 comments on commit 255b796

Please sign in to comment.