diff --git a/CLFormat.xcodeproj/project.pbxproj b/CLFormat.xcodeproj/project.pbxproj index a90cca8..6183c05 100644 --- a/CLFormat.xcodeproj/project.pbxproj +++ b/CLFormat.xcodeproj/project.pbxproj @@ -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 */ @@ -82,7 +82,7 @@ CCB5F10029C1455B00587140 /* CLFormatTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLFormatTests.swift; sourceTree = ""; }; 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 = ""; }; - CCB5F12229C7974B00587140 /* CLControlParserConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLControlParserConfig.swift; sourceTree = ""; }; + CCB5F12229C7974B00587140 /* CLFormatConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLFormatConfig.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -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 */, @@ -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 */, diff --git a/README.md b/README.md index b1bc2e4..87f6dcd 100644 --- a/README.md +++ b/README.md @@ -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: @@ -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 diff --git a/Sources/CLFormat/CLControl.swift b/Sources/CLFormat/CLControl.swift index 073d595..c81d9b4 100644 --- a/Sources/CLFormat/CLControl.swift +++ b/Sources/CLFormat/CLControl.swift @@ -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. /// @@ -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. @@ -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 { @@ -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 } } } diff --git a/Sources/CLFormat/CLControlParser.swift b/Sources/CLFormat/CLControlParser.swift index c8b24cd..1044eb3 100644 --- a/Sources/CLFormat/CLControlParser.swift +++ b/Sources/CLFormat/CLControlParser.swift @@ -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 diff --git a/Sources/CLFormat/CLFormat.swift b/Sources/CLFormat/CLFormat.swift index 578a04d..173ba37 100644 --- a/Sources/CLFormat/CLFormat.swift +++ b/Sources/CLFormat/CLFormat.swift @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, diff --git a/Sources/CLFormat/CLControlParserConfig.swift b/Sources/CLFormat/CLFormatConfig.swift similarity index 87% rename from Sources/CLFormat/CLControlParserConfig.swift rename to Sources/CLFormat/CLFormatConfig.swift index 159fa53..577d2bc 100644 --- a/Sources/CLFormat/CLControlParserConfig.swift +++ b/Sources/CLFormat/CLFormatConfig.swift @@ -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 = @@ -47,7 +47,7 @@ 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 { @@ -55,8 +55,7 @@ public struct CLControlParserConfig { } } - /// 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 @@ -64,8 +63,7 @@ public struct CLControlParserConfig { } /// 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, @@ -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) @@ -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 } diff --git a/Sources/CLFormat/StandardDirectiveSpecifier.swift b/Sources/CLFormat/StandardDirectiveSpecifier.swift index 904b46f..37299a3 100644 --- a/Sources/CLFormat/StandardDirectiveSpecifier.swift +++ b/Sources/CLFormat/StandardDirectiveSpecifier.swift @@ -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) { @@ -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, @@ -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, @@ -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 {