From 2e3b8f536a4f8c304566a46b8a1e5a6fc56f5318 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Wed, 24 Jan 2024 20:15:16 +1300 Subject: [PATCH 01/36] add initial.d --- initial.d | 444 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 444 insertions(+) create mode 100644 initial.d diff --git a/initial.d b/initial.d new file mode 100644 index 0000000..7bfc03e --- /dev/null +++ b/initial.d @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2024 Jeremy Baxter. All rights reserved. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/++ + + INI is a simple, concise configuration or data interchange + + format. It consists of keys and values listed inside a section. + + An INI document might look something like this: + + --- + + [user] + + name = Kevin + + age = 26 + + emailAddress = kevin@example.net + + + + [config] + + useBundledMalloc = false + + forceDefaultInit = true + + --- + + + + $(B initial) is a library for parsing INI documents into an `INIUnit`, + + a data structure representing an INI document. The function `readINI` + + contains the parser; it parses the INI given to it and deserialises + + it into the given `INIUnit`. The `readINIFile` function can be used + + to read the INI from a file rather than a string. + + + + $(B initial) is fully @safe, and can be used in @safe code. + + + + License: [Boost License 1.0](https://www.boost.org/LICENSE_1_0.txt). + + Authors: Jeremy Baxter + +/ +module initial; + +import std.conv : to, ConvException; +import std.exception : basicExceptionCtors, enforce; +import std.format : format; +import std.traits : isSomeChar; + +private alias parserEnforce = enforce!INIParseException; + +@safe: + +/++ + + Structure that represents an INI file. + + + + Contains `INISection`s in an associative array that can be + + directly accessed if needs be, or the index operator can be + + used to get an `INISection` and automatically initialise it + + if needed. + + + + The `serialise` method can be used to serialise the entire + + structure into an INI document that can be then read again + + by the parser and modified by humans. + + + + Example: + + --- + + INIUnit ini; + + + + /* write INI data */ + + ini["section"]["key"] = "value"; + + ini["section"]["num"] = "4.8"; + + + + /* read INI data */ + + readINI(ini, ` + + [section] + + num = 5.3 + + `); + + + + /* read INI from file */ + + readINIFile(ini, "config.ini"); + + + + /* write INI to file */ + + import std.file : write; + + write("config.ini", ini.serialise()); + + --- + +/ +struct INIUnit +{ + INISection[string] sections; /++ Hashmap of INISections in this unit. +/ + string defaultSection = "main"; /++ Name of the default section. +/ + + nothrow: + + /++ + + Returns the value of *k* in the default section + + or *defaultValue* if *k* is not present. + +/ + string + key(string k, string defaultValue = null) + { + return this[defaultSection].key(k, defaultValue); + } + + /++ + + Serialises this `INIUnit` into an INI document. + + + + If *defaultSectionHeading* is true, includes the + + section heading of the default section. + + Example: + + --- + + INIUnit ini; + + + + ini["sections"] = "ages suburbs"; + + ini["ages"]["John"] = "37"; + + ini["ages"]["Mary"] = "29"; + + ini["suburbs"]["John"] = "Gordonton"; + + ini["suburbs"]["Mary"] = "Ashmore"; + + + + writeln(ini.serialise()); + + /* + + * sections = ages suburbs + + * [ages] + + * John = 37 + + * Mary = 29 + + * [suburbs] + + * John = Gordonton + + * Mary = Ashmore + + */ + + --- + +/ + char[] + serialise(bool defaultSectionHeading = false) + { + char[] buf; + + buf = this[defaultSection].serialise(defaultSectionHeading); + foreach (string sect; sections.byKey()) { + if (sect == defaultSection) + continue; + + buf ~= this[sect].serialise(); + } + + return buf; + } + + /++ + + Returns the `INISection` associated with the name *sect*. + + Initialises it if it doesn't exist. + +/ + ref INISection + opIndex(string sect) + { + if (!(sect in sections)) + sections[sect] = INISection(sect); + + return sections[sect]; + } + + /++ + + Sets the value of the key *k* in the default section to *v*. + + Example: + + --- + + ini["key"] = "value"; + + --- + +/ + void + opIndexAssign(string v, string k) + { + this[defaultSection][k] = v; + } + + /++ + + Sets the value of the `INISection` named *sect*. + +/ + void + opIndexAssign(INISection v, string sect) + { + this[sect] = v; + } +} + +/++ + + Represents an INI section. + + + + Holds keys in the form of an associative array. + + Index the `keys` property to get this data raw, or to avoid + + range errors, use the `key` or `keyAs` functions. + + + + `alias this` is applied to the `keys` associative array, + + meaning you can index the structure to get or set keys' values. + +/ +struct INISection +{ + string[string] keys; /++ Hashmap of keys belonging to this section. +/ + string name; /++ Name of this section. +/ + + /++ + + Construct a new `INISection` with the given name. + + Called by `readINI` and `INIUnit.opIndex`. + +/ + this(string name) nothrow + { + this.name = name; + keys = new string[string]; + } + + /+ + + Returns the value of `k` in this section or + + `defaultValue` if the key is not present. + +/ + string + key(string k, string defaultValue = null) nothrow + { + return k in keys ? keys[k] : defaultValue; + } + + /++ + + Using `std.conv`, converts the value of the given key + + to the given type. On conversion failure throws an + + `INITypeException` with a message containing location + + information. + +/ + T + keyAs(T)(string k) + { + try + return key(k).to!T(); + catch (ConvException) + throw new INITypeException( + format!"unable to convert [%s].%s to %s"(name, k, T.stringof)); + } + + /++ + + Serialise this section into an INI document. + + + + If `sectionHeading` is true, includes the section heading at the top. + +/ + char[] + serialise(bool sectionHeading = true) nothrow + { + char[] buf; + + if (sectionHeading) + buf ~= "[" ~ name.to!(char[]) ~ "]\n"; + + foreach (string key; keys.byKey()) { + buf ~= key ~ " = " ~ keys[key] ~ "\n"; + } + + return buf; + } + + alias keys this; +} + +/++ + + Exception thrown when parsing failure occurs. + +/ +class INIParseException : Exception +{ + mixin basicExceptionCtors; +} + +/++ + + Exception thrown when conversion failure occurs. + +/ +class INITypeException : Exception +{ + mixin basicExceptionCtors; +} + +private string +locate(size_t l, size_t c) +{ + return format!"%d:%d"(l, c); +} + +/++ + + Parse the given INI data into an `INIUnit`. + + + + To parse a file's contents, use readINIFile instead. + + + + Whitespace is first stripped from the beginning and end of + + the line before parsing it. + + + + The following rules apply when parsing: + + $(UL + + $(LI once a left square bracket character `[` is found at + + the beginning of a line, the line is considered a + + section heading) + + $(LI after the left square bracket character, any character + + can be used inside the section heading except the right + + square bracket character) + + $(LI once the right square bracket character `]` is found, + + the section heading is considered finished. Any trailing + + characters are garbage and will trigger a parse error.) + + $(LI once a character except the left square bracket is found + + at the beginning of a line, the line is considered an + + assignment in the current section) + + $(LI once an equals character `=` is found after the first + + part of an assignment, the rest of the line is considered + + the "value" while the first part is the "key") + + $(LI when whitespace is found in the first part of an assignment, + + it is ignored and the next equals sign is looked for + + immediately) + + $(LI when whitespace is found after an equals character `=` + + in an assignment, it is ignored until the first + + non-whitespace character is found) + + $(LI once a non-whitespace character is found after an equals + + character `=` in an assignment, it is counted part of the + + "value") + +/ +void +readINI(ref INIUnit ini, string data) +{ + import std.string : splitLines, strip; + + string section; /* name of current section */ + string text; /* current key, value, section, whatever */ + string key; /* name of key in section */ + string value; /* value */ + bool inAssign; /* in key assignment? */ + bool inHeading; /* in section heading? */ + bool needEqls; /* require equals sign immediately? */ + bool pastEqls; /* past equals sign in assignment? */ + bool trailing; /* trailing garbage after heading? */ + + section = ini.defaultSection; + + foreach (size_t ln, string line; data.splitLines()) { + line = line.strip(); + if (line.length == 0) + continue; + + inAssign = inHeading = needEqls = pastEqls = trailing = false; + text = key = value = ""; + foreach (size_t cn, char ch; line) { + switch (ch) { + /* beginning of a section heading? */ + case '[': + if (cn == 0) /* beginning of line? */ + inHeading = true; + else + text ~= ch; + break; + + /* end of a section heading? */ + case ']': + parserEnforce(inHeading || inAssign, + locate(ln, cn) ~ ": unexpected character ']'"); + + if (inHeading) { + section = text; + text = ""; + inHeading = false; + trailing = true; + } else if (inAssign) { + text ~= ch; + } + break; + + /* middle of an assignment? */ + case '=': + if (inAssign) { + if (pastEqls) { + /* count it as part of the value */ + text ~= ch; + } else { + key = text; + text = ""; + pastEqls = true; + needEqls = false; + } + } else { + goto default; + } + break; + + /* whitespace */ + case ' ': + case '\t': + case '\v': + case '\r': + case '\n': + case '\f': + if (inAssign) { + if (!pastEqls) + needEqls = true; + } + break; + + default: + if (cn == 0) /* beginning of line? */ + inAssign = true; + + parserEnforce((inAssign || inHeading) && !trailing, + locate(ln, cn) ~ ": unexpected character '" ~ ch ~ "'"); + + if (inAssign) + parserEnforce(!needEqls, + locate(ln, cn) ~ ": expected '=', not '" ~ ch ~ "'"); + + text ~= ch; + break; + } + } + /* once the line has finished being looped over... */ + if (inAssign) { + parserEnforce(key.length != 0, + locate(ln, line.length - 1) ~ ": key cannot be empty"); + value = text; + text = ""; + + if (!(section in ini.sections)) + ini[section] = INISection(section); + ini[section][key] = value; + } + } +} + +/++ + + Parse INI from a file located at `fileName`. + +/ +void +readINIFile(ref INIUnit ini, string fileName) +{ + import std.file : readText; + + readINI(ini, readText(fileName)); +} From 8967581b3cc6c4ace7c7c40a51e135e3648eb404 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Wed, 24 Jan 2024 21:04:26 +1300 Subject: [PATCH 02/36] license under the Boost Software License --- COPYING | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 COPYING diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..127a5bc --- /dev/null +++ b/COPYING @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file From c8f3a9e1ae102ff185bbc7d82a2debbbcc521565 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Wed, 24 Jan 2024 20:15:16 +1300 Subject: [PATCH 03/36] add initial.d --- initial.d | 444 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 444 insertions(+) create mode 100644 initial.d diff --git a/initial.d b/initial.d new file mode 100644 index 0000000..7bfc03e --- /dev/null +++ b/initial.d @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2024 Jeremy Baxter. All rights reserved. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/++ + + INI is a simple, concise configuration or data interchange + + format. It consists of keys and values listed inside a section. + + An INI document might look something like this: + + --- + + [user] + + name = Kevin + + age = 26 + + emailAddress = kevin@example.net + + + + [config] + + useBundledMalloc = false + + forceDefaultInit = true + + --- + + + + $(B initial) is a library for parsing INI documents into an `INIUnit`, + + a data structure representing an INI document. The function `readINI` + + contains the parser; it parses the INI given to it and deserialises + + it into the given `INIUnit`. The `readINIFile` function can be used + + to read the INI from a file rather than a string. + + + + $(B initial) is fully @safe, and can be used in @safe code. + + + + License: [Boost License 1.0](https://www.boost.org/LICENSE_1_0.txt). + + Authors: Jeremy Baxter + +/ +module initial; + +import std.conv : to, ConvException; +import std.exception : basicExceptionCtors, enforce; +import std.format : format; +import std.traits : isSomeChar; + +private alias parserEnforce = enforce!INIParseException; + +@safe: + +/++ + + Structure that represents an INI file. + + + + Contains `INISection`s in an associative array that can be + + directly accessed if needs be, or the index operator can be + + used to get an `INISection` and automatically initialise it + + if needed. + + + + The `serialise` method can be used to serialise the entire + + structure into an INI document that can be then read again + + by the parser and modified by humans. + + + + Example: + + --- + + INIUnit ini; + + + + /* write INI data */ + + ini["section"]["key"] = "value"; + + ini["section"]["num"] = "4.8"; + + + + /* read INI data */ + + readINI(ini, ` + + [section] + + num = 5.3 + + `); + + + + /* read INI from file */ + + readINIFile(ini, "config.ini"); + + + + /* write INI to file */ + + import std.file : write; + + write("config.ini", ini.serialise()); + + --- + +/ +struct INIUnit +{ + INISection[string] sections; /++ Hashmap of INISections in this unit. +/ + string defaultSection = "main"; /++ Name of the default section. +/ + + nothrow: + + /++ + + Returns the value of *k* in the default section + + or *defaultValue* if *k* is not present. + +/ + string + key(string k, string defaultValue = null) + { + return this[defaultSection].key(k, defaultValue); + } + + /++ + + Serialises this `INIUnit` into an INI document. + + + + If *defaultSectionHeading* is true, includes the + + section heading of the default section. + + Example: + + --- + + INIUnit ini; + + + + ini["sections"] = "ages suburbs"; + + ini["ages"]["John"] = "37"; + + ini["ages"]["Mary"] = "29"; + + ini["suburbs"]["John"] = "Gordonton"; + + ini["suburbs"]["Mary"] = "Ashmore"; + + + + writeln(ini.serialise()); + + /* + + * sections = ages suburbs + + * [ages] + + * John = 37 + + * Mary = 29 + + * [suburbs] + + * John = Gordonton + + * Mary = Ashmore + + */ + + --- + +/ + char[] + serialise(bool defaultSectionHeading = false) + { + char[] buf; + + buf = this[defaultSection].serialise(defaultSectionHeading); + foreach (string sect; sections.byKey()) { + if (sect == defaultSection) + continue; + + buf ~= this[sect].serialise(); + } + + return buf; + } + + /++ + + Returns the `INISection` associated with the name *sect*. + + Initialises it if it doesn't exist. + +/ + ref INISection + opIndex(string sect) + { + if (!(sect in sections)) + sections[sect] = INISection(sect); + + return sections[sect]; + } + + /++ + + Sets the value of the key *k* in the default section to *v*. + + Example: + + --- + + ini["key"] = "value"; + + --- + +/ + void + opIndexAssign(string v, string k) + { + this[defaultSection][k] = v; + } + + /++ + + Sets the value of the `INISection` named *sect*. + +/ + void + opIndexAssign(INISection v, string sect) + { + this[sect] = v; + } +} + +/++ + + Represents an INI section. + + + + Holds keys in the form of an associative array. + + Index the `keys` property to get this data raw, or to avoid + + range errors, use the `key` or `keyAs` functions. + + + + `alias this` is applied to the `keys` associative array, + + meaning you can index the structure to get or set keys' values. + +/ +struct INISection +{ + string[string] keys; /++ Hashmap of keys belonging to this section. +/ + string name; /++ Name of this section. +/ + + /++ + + Construct a new `INISection` with the given name. + + Called by `readINI` and `INIUnit.opIndex`. + +/ + this(string name) nothrow + { + this.name = name; + keys = new string[string]; + } + + /+ + + Returns the value of `k` in this section or + + `defaultValue` if the key is not present. + +/ + string + key(string k, string defaultValue = null) nothrow + { + return k in keys ? keys[k] : defaultValue; + } + + /++ + + Using `std.conv`, converts the value of the given key + + to the given type. On conversion failure throws an + + `INITypeException` with a message containing location + + information. + +/ + T + keyAs(T)(string k) + { + try + return key(k).to!T(); + catch (ConvException) + throw new INITypeException( + format!"unable to convert [%s].%s to %s"(name, k, T.stringof)); + } + + /++ + + Serialise this section into an INI document. + + + + If `sectionHeading` is true, includes the section heading at the top. + +/ + char[] + serialise(bool sectionHeading = true) nothrow + { + char[] buf; + + if (sectionHeading) + buf ~= "[" ~ name.to!(char[]) ~ "]\n"; + + foreach (string key; keys.byKey()) { + buf ~= key ~ " = " ~ keys[key] ~ "\n"; + } + + return buf; + } + + alias keys this; +} + +/++ + + Exception thrown when parsing failure occurs. + +/ +class INIParseException : Exception +{ + mixin basicExceptionCtors; +} + +/++ + + Exception thrown when conversion failure occurs. + +/ +class INITypeException : Exception +{ + mixin basicExceptionCtors; +} + +private string +locate(size_t l, size_t c) +{ + return format!"%d:%d"(l, c); +} + +/++ + + Parse the given INI data into an `INIUnit`. + + + + To parse a file's contents, use readINIFile instead. + + + + Whitespace is first stripped from the beginning and end of + + the line before parsing it. + + + + The following rules apply when parsing: + + $(UL + + $(LI once a left square bracket character `[` is found at + + the beginning of a line, the line is considered a + + section heading) + + $(LI after the left square bracket character, any character + + can be used inside the section heading except the right + + square bracket character) + + $(LI once the right square bracket character `]` is found, + + the section heading is considered finished. Any trailing + + characters are garbage and will trigger a parse error.) + + $(LI once a character except the left square bracket is found + + at the beginning of a line, the line is considered an + + assignment in the current section) + + $(LI once an equals character `=` is found after the first + + part of an assignment, the rest of the line is considered + + the "value" while the first part is the "key") + + $(LI when whitespace is found in the first part of an assignment, + + it is ignored and the next equals sign is looked for + + immediately) + + $(LI when whitespace is found after an equals character `=` + + in an assignment, it is ignored until the first + + non-whitespace character is found) + + $(LI once a non-whitespace character is found after an equals + + character `=` in an assignment, it is counted part of the + + "value") + +/ +void +readINI(ref INIUnit ini, string data) +{ + import std.string : splitLines, strip; + + string section; /* name of current section */ + string text; /* current key, value, section, whatever */ + string key; /* name of key in section */ + string value; /* value */ + bool inAssign; /* in key assignment? */ + bool inHeading; /* in section heading? */ + bool needEqls; /* require equals sign immediately? */ + bool pastEqls; /* past equals sign in assignment? */ + bool trailing; /* trailing garbage after heading? */ + + section = ini.defaultSection; + + foreach (size_t ln, string line; data.splitLines()) { + line = line.strip(); + if (line.length == 0) + continue; + + inAssign = inHeading = needEqls = pastEqls = trailing = false; + text = key = value = ""; + foreach (size_t cn, char ch; line) { + switch (ch) { + /* beginning of a section heading? */ + case '[': + if (cn == 0) /* beginning of line? */ + inHeading = true; + else + text ~= ch; + break; + + /* end of a section heading? */ + case ']': + parserEnforce(inHeading || inAssign, + locate(ln, cn) ~ ": unexpected character ']'"); + + if (inHeading) { + section = text; + text = ""; + inHeading = false; + trailing = true; + } else if (inAssign) { + text ~= ch; + } + break; + + /* middle of an assignment? */ + case '=': + if (inAssign) { + if (pastEqls) { + /* count it as part of the value */ + text ~= ch; + } else { + key = text; + text = ""; + pastEqls = true; + needEqls = false; + } + } else { + goto default; + } + break; + + /* whitespace */ + case ' ': + case '\t': + case '\v': + case '\r': + case '\n': + case '\f': + if (inAssign) { + if (!pastEqls) + needEqls = true; + } + break; + + default: + if (cn == 0) /* beginning of line? */ + inAssign = true; + + parserEnforce((inAssign || inHeading) && !trailing, + locate(ln, cn) ~ ": unexpected character '" ~ ch ~ "'"); + + if (inAssign) + parserEnforce(!needEqls, + locate(ln, cn) ~ ": expected '=', not '" ~ ch ~ "'"); + + text ~= ch; + break; + } + } + /* once the line has finished being looped over... */ + if (inAssign) { + parserEnforce(key.length != 0, + locate(ln, line.length - 1) ~ ": key cannot be empty"); + value = text; + text = ""; + + if (!(section in ini.sections)) + ini[section] = INISection(section); + ini[section][key] = value; + } + } +} + +/++ + + Parse INI from a file located at `fileName`. + +/ +void +readINIFile(ref INIUnit ini, string fileName) +{ + import std.file : readText; + + readINI(ini, readText(fileName)); +} From e2f5deac8284c95a97b14f3359188248e1a93a10 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Wed, 24 Jan 2024 21:04:26 +1300 Subject: [PATCH 04/36] license under the Boost Software License --- COPYING | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 COPYING diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..127a5bc --- /dev/null +++ b/COPYING @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file From 3ce21c6c79c9456b180bb2569d3e33537e0a0bc8 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Thu, 25 Jan 2024 14:34:18 +1300 Subject: [PATCH 05/36] add readme --- README.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..2b255b3 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +## initial - INI parsing library for the D programming language + +My attempt at making a sane and high-quality INI parsing library for D. + +```ini +[section] +key = value +``` + +An INI file's contents is stored in an `INIUnit` structure. This contains +multiple `INISection`s stored in an associative array that you can read +or set yourself. + +initial also provides serialisation facilities that can turn `INIUnit`s +and `INISection`s back into an INI document. Handy for if your program +has some sort of GUI configuration interface or if you're passing around +INI through a network connection. + +## Demo + +```d +INIUnit ini; + +/* write INI data to an INIUnit */ +ini["section"]["key"] = "value"; +ini["section"]["num"] = "4.8"; + +/* read INI data */ +readINI(ini, ` + [section] + num = 5.3 +`); + +assert(ini["section"]["key"] == "value"); +assert(ini["section"]["num"] == "5.3"); + +/* read INI from file */ +readINIFile(ini, "config.ini"); + +/* write INI to file */ +import std.file : write; +write("config.ini", ini.serialise()); +``` + +## Usage + +Download initial.d and put it somewhere in your import path, then compile it alongside +your program and link it in. Or you can do it all at once by using -i. + +``` +dmd -i -of=program program.d initial.d +``` From af05aba2b605afa12719fd590f6a02df34755ed8 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Thu, 25 Jan 2024 14:34:18 +1300 Subject: [PATCH 06/36] add readme --- README.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..2b255b3 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +## initial - INI parsing library for the D programming language + +My attempt at making a sane and high-quality INI parsing library for D. + +```ini +[section] +key = value +``` + +An INI file's contents is stored in an `INIUnit` structure. This contains +multiple `INISection`s stored in an associative array that you can read +or set yourself. + +initial also provides serialisation facilities that can turn `INIUnit`s +and `INISection`s back into an INI document. Handy for if your program +has some sort of GUI configuration interface or if you're passing around +INI through a network connection. + +## Demo + +```d +INIUnit ini; + +/* write INI data to an INIUnit */ +ini["section"]["key"] = "value"; +ini["section"]["num"] = "4.8"; + +/* read INI data */ +readINI(ini, ` + [section] + num = 5.3 +`); + +assert(ini["section"]["key"] == "value"); +assert(ini["section"]["num"] == "5.3"); + +/* read INI from file */ +readINIFile(ini, "config.ini"); + +/* write INI to file */ +import std.file : write; +write("config.ini", ini.serialise()); +``` + +## Usage + +Download initial.d and put it somewhere in your import path, then compile it alongside +your program and link it in. Or you can do it all at once by using -i. + +``` +dmd -i -of=program program.d initial.d +``` From cb28f5834ddda493dafc250a33f77fcf69907193 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Fri, 26 Jan 2024 13:31:22 +1300 Subject: [PATCH 07/36] add basic gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..22e64fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.a +*.o \ No newline at end of file From 13081be4f2b0608db8a10ee63fc1dad90b02f4f4 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Fri, 26 Jan 2024 13:31:22 +1300 Subject: [PATCH 08/36] add basic gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..22e64fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.a +*.o \ No newline at end of file From 5518abc00bd3b820ca5d8f6185e4fc1d0d6ee841 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Fri, 26 Jan 2024 14:22:09 +1300 Subject: [PATCH 09/36] add dub.json --- dub.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 dub.json diff --git a/dub.json b/dub.json new file mode 100644 index 0000000..fb52964 --- /dev/null +++ b/dub.json @@ -0,0 +1,12 @@ +{ + "name": "initial", + "description": "INI parsing library for the D programming language", + + "authors": ["Jeremy Baxter"], + "copyright": "Copyright © 2024, Jeremy Baxter", + "license": "BSL-1.0", + + "targetType": "library", + "sourcePaths": ["."], + "sourceFiles": ["initial.d"] +} From 753c002bb781bc7653832b932e8aa01165c3c1eb Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Fri, 26 Jan 2024 14:22:09 +1300 Subject: [PATCH 10/36] add dub.json --- dub.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 dub.json diff --git a/dub.json b/dub.json new file mode 100644 index 0000000..fb52964 --- /dev/null +++ b/dub.json @@ -0,0 +1,12 @@ +{ + "name": "initial", + "description": "INI parsing library for the D programming language", + + "authors": ["Jeremy Baxter"], + "copyright": "Copyright © 2024, Jeremy Baxter", + "license": "BSL-1.0", + + "targetType": "library", + "sourcePaths": ["."], + "sourceFiles": ["initial.d"] +} From 0a23d914f708a8790a0572f54bb616e5871afb47 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Fri, 26 Jan 2024 16:20:04 +1300 Subject: [PATCH 11/36] add defaultValue parameter to keyAs --- initial.d | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/initial.d b/initial.d index 7bfc03e..c85c999 100644 --- a/initial.d +++ b/initial.d @@ -229,16 +229,15 @@ struct INISection } /++ - + Using `std.conv`, converts the value of the given key - + to the given type. On conversion failure throws an - + `INITypeException` with a message containing location - + information. + + Using `std.conv`, converts the value of *k* to *T*. + + On conversion failure throws an `INITypeException` + + with a message containing location information. +/ T - keyAs(T)(string k) + keyAs(T)(string k, string defaultValue = null) { try - return key(k).to!T(); + return key(k, defaultValue).to!T(); catch (ConvException) throw new INITypeException( format!"unable to convert [%s].%s to %s"(name, k, T.stringof)); From be127705b32d9c4b07600e8e38857312a06422de Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Fri, 26 Jan 2024 16:20:04 +1300 Subject: [PATCH 12/36] add defaultValue parameter to keyAs --- initial.d | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/initial.d b/initial.d index 7bfc03e..c85c999 100644 --- a/initial.d +++ b/initial.d @@ -229,16 +229,15 @@ struct INISection } /++ - + Using `std.conv`, converts the value of the given key - + to the given type. On conversion failure throws an - + `INITypeException` with a message containing location - + information. + + Using `std.conv`, converts the value of *k* to *T*. + + On conversion failure throws an `INITypeException` + + with a message containing location information. +/ T - keyAs(T)(string k) + keyAs(T)(string k, string defaultValue = null) { try - return key(k).to!T(); + return key(k, defaultValue).to!T(); catch (ConvException) throw new INITypeException( format!"unable to convert [%s].%s to %s"(name, k, T.stringof)); From 9fe1a5199b69ff58177a00b315f539626e18e284 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Tue, 6 Feb 2024 12:22:36 +1300 Subject: [PATCH 13/36] fix parser docs --- initial.d | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/initial.d b/initial.d index c85c999..02e8b19 100644 --- a/initial.d +++ b/initial.d @@ -298,25 +298,25 @@ locate(size_t l, size_t c) + + The following rules apply when parsing: + $(UL - + $(LI once a left square bracket character `[` is found at + + $(LI if a left square bracket character `[` is found at + the beginning of a line, the line is considered a + section heading) - + $(LI after the left square bracket character, any character - + can be used inside the section heading except the right - + square bracket character) + + $(LI inside a section heading, any character can be used + + inside the section heading except the right square bracket + + character) + $(LI once the right square bracket character `]` is found, + the section heading is considered finished. Any trailing + characters are garbage and will trigger a parse error.) - + $(LI once a character except the left square bracket is found + + $(LI if a character except the left square bracket is found + at the beginning of a line, the line is considered an + assignment in the current section) + $(LI once an equals character `=` is found after the first + part of an assignment, the rest of the line is considered + the "value" while the first part is the "key") - + $(LI when whitespace is found in the first part of an assignment, + + $(LI if whitespace is found in the first part of an assignment, + it is ignored and the next equals sign is looked for + immediately) - + $(LI when whitespace is found after an equals character `=` + + $(LI if whitespace is found after an equals character `=` + in an assignment, it is ignored until the first + non-whitespace character is found) + $(LI once a non-whitespace character is found after an equals From e534300db3651dafa68505e62cc1570d6938ebdf Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Tue, 6 Feb 2024 12:22:36 +1300 Subject: [PATCH 14/36] fix parser docs --- initial.d | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/initial.d b/initial.d index c85c999..02e8b19 100644 --- a/initial.d +++ b/initial.d @@ -298,25 +298,25 @@ locate(size_t l, size_t c) + + The following rules apply when parsing: + $(UL - + $(LI once a left square bracket character `[` is found at + + $(LI if a left square bracket character `[` is found at + the beginning of a line, the line is considered a + section heading) - + $(LI after the left square bracket character, any character - + can be used inside the section heading except the right - + square bracket character) + + $(LI inside a section heading, any character can be used + + inside the section heading except the right square bracket + + character) + $(LI once the right square bracket character `]` is found, + the section heading is considered finished. Any trailing + characters are garbage and will trigger a parse error.) - + $(LI once a character except the left square bracket is found + + $(LI if a character except the left square bracket is found + at the beginning of a line, the line is considered an + assignment in the current section) + $(LI once an equals character `=` is found after the first + part of an assignment, the rest of the line is considered + the "value" while the first part is the "key") - + $(LI when whitespace is found in the first part of an assignment, + + $(LI if whitespace is found in the first part of an assignment, + it is ignored and the next equals sign is looked for + immediately) - + $(LI when whitespace is found after an equals character `=` + + $(LI if whitespace is found after an equals character `=` + in an assignment, it is ignored until the first + non-whitespace character is found) + $(LI once a non-whitespace character is found after an equals From 1f322e9bb99de42a3d653b306ec39776c40e569f Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Tue, 6 Feb 2024 12:31:49 +1300 Subject: [PATCH 15/36] fix readme mistake --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b255b3..ab023bb 100644 --- a/README.md +++ b/README.md @@ -48,5 +48,5 @@ Download initial.d and put it somewhere in your import path, then compile it alo your program and link it in. Or you can do it all at once by using -i. ``` -dmd -i -of=program program.d initial.d +dmd -i -of=program program.d ``` From 8fcc9f01cd021136ac0ebe8f84744f00ba205c3c Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Tue, 6 Feb 2024 12:31:49 +1300 Subject: [PATCH 16/36] fix readme mistake --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b255b3..ab023bb 100644 --- a/README.md +++ b/README.md @@ -48,5 +48,5 @@ Download initial.d and put it somewhere in your import path, then compile it alo your program and link it in. Or you can do it all at once by using -i. ``` -dmd -i -of=program program.d initial.d +dmd -i -of=program program.d ``` From 441cee090013b0a3d05a9a870f3422e3f696fc7b Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Tue, 6 Feb 2024 16:39:30 +1300 Subject: [PATCH 17/36] fix segmentation fault --- initial.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/initial.d b/initial.d index 02e8b19..a338af3 100644 --- a/initial.d +++ b/initial.d @@ -180,7 +180,7 @@ struct INIUnit void opIndexAssign(string v, string k) { - this[defaultSection][k] = v; + sections[defaultSection][k] = v; } /++ @@ -189,7 +189,7 @@ struct INIUnit void opIndexAssign(INISection v, string sect) { - this[sect] = v; + sections[sect] = v; } } From 4d863d48f58ec495c98a1acc1b30c126e049043d Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Tue, 6 Feb 2024 16:39:30 +1300 Subject: [PATCH 18/36] fix segmentation fault --- initial.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/initial.d b/initial.d index 02e8b19..a338af3 100644 --- a/initial.d +++ b/initial.d @@ -180,7 +180,7 @@ struct INIUnit void opIndexAssign(string v, string k) { - this[defaultSection][k] = v; + sections[defaultSection][k] = v; } /++ @@ -189,7 +189,7 @@ struct INIUnit void opIndexAssign(INISection v, string sect) { - this[sect] = v; + sections[sect] = v; } } From 74c9d5c15538622327bd0ac14c3b88aeca209616 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Tue, 6 Feb 2024 16:44:20 +1300 Subject: [PATCH 19/36] add second compilation example to readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ab023bb..624b9d3 100644 --- a/README.md +++ b/README.md @@ -48,5 +48,6 @@ Download initial.d and put it somewhere in your import path, then compile it alo your program and link it in. Or you can do it all at once by using -i. ``` -dmd -i -of=program program.d +dmd program.d initial.d +dmd -i program.d ``` From 6e3d642e43f00f7ead8a92f949e624a03eea8e87 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Tue, 6 Feb 2024 16:44:20 +1300 Subject: [PATCH 20/36] add second compilation example to readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ab023bb..624b9d3 100644 --- a/README.md +++ b/README.md @@ -48,5 +48,6 @@ Download initial.d and put it somewhere in your import path, then compile it alo your program and link it in. Or you can do it all at once by using -i. ``` -dmd -i -of=program program.d +dmd program.d initial.d +dmd -i program.d ``` From 179123a3b233537de5b00272eeb006a92793965b Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Tue, 6 Feb 2024 16:44:33 +1300 Subject: [PATCH 21/36] add progress to readme --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 624b9d3..52fe71e 100644 --- a/README.md +++ b/README.md @@ -51,3 +51,17 @@ your program and link it in. Or you can do it all at once by using -i. dmd program.d initial.d dmd -i program.d ``` + +## Progress + +### Finished + +- Keys +- Sections +- Default section + +### Todo + +- Comments +- Key referencing +- Testing D: From 7c2616c00474e064ea5fcc36c89b744c090cc683 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Tue, 6 Feb 2024 16:44:33 +1300 Subject: [PATCH 22/36] add progress to readme --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 624b9d3..52fe71e 100644 --- a/README.md +++ b/README.md @@ -51,3 +51,17 @@ your program and link it in. Or you can do it all at once by using -i. dmd program.d initial.d dmd -i program.d ``` + +## Progress + +### Finished + +- Keys +- Sections +- Default section + +### Todo + +- Comments +- Key referencing +- Testing D: From 588324659b36ecc54476073100c99308579caaf6 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Tue, 6 Feb 2024 16:45:12 +1300 Subject: [PATCH 23/36] implement basic comments --- README.md | 2 +- initial.d | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 52fe71e..735e392 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,9 @@ dmd -i program.d - Keys - Sections - Default section +- Comments ### Todo -- Comments - Key referencing - Testing D: diff --git a/initial.d b/initial.d index a338af3..7f4a585 100644 --- a/initial.d +++ b/initial.d @@ -298,6 +298,8 @@ locate(size_t l, size_t c) + + The following rules apply when parsing: + $(UL + + $(LI if a hash character `#` is found at the beginning of a line, + + the rest of the line is ignored) + $(LI if a left square bracket character `[` is found at + the beginning of a line, the line is considered a + section heading) @@ -340,6 +342,7 @@ readINI(ref INIUnit ini, string data) section = ini.defaultSection; + nextline: foreach (size_t ln, string line; data.splitLines()) { line = line.strip(); if (line.length == 0) @@ -349,6 +352,14 @@ readINI(ref INIUnit ini, string data) text = key = value = ""; foreach (size_t cn, char ch; line) { switch (ch) { + /* comment? */ + case '#': + if (cn == 0) + continue nextline; + + text ~= ch; + break; + /* beginning of a section heading? */ case '[': if (cn == 0) /* beginning of line? */ From 62a1cdc4910ff86f6a74a9fa2923c8b820b4cdac Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Tue, 6 Feb 2024 16:45:12 +1300 Subject: [PATCH 24/36] implement basic comments --- README.md | 2 +- initial.d | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 52fe71e..735e392 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,9 @@ dmd -i program.d - Keys - Sections - Default section +- Comments ### Todo -- Comments - Key referencing - Testing D: diff --git a/initial.d b/initial.d index a338af3..7f4a585 100644 --- a/initial.d +++ b/initial.d @@ -298,6 +298,8 @@ locate(size_t l, size_t c) + + The following rules apply when parsing: + $(UL + + $(LI if a hash character `#` is found at the beginning of a line, + + the rest of the line is ignored) + $(LI if a left square bracket character `[` is found at + the beginning of a line, the line is considered a + section heading) @@ -340,6 +342,7 @@ readINI(ref INIUnit ini, string data) section = ini.defaultSection; + nextline: foreach (size_t ln, string line; data.splitLines()) { line = line.strip(); if (line.length == 0) @@ -349,6 +352,14 @@ readINI(ref INIUnit ini, string data) text = key = value = ""; foreach (size_t cn, char ch; line) { switch (ch) { + /* comment? */ + case '#': + if (cn == 0) + continue nextline; + + text ~= ch; + break; + /* beginning of a section heading? */ case '[': if (cn == 0) /* beginning of line? */ From 756d1a42ee29658fe2d8a53c136077c130ce949f Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Wed, 5 Jun 2024 12:30:01 +1200 Subject: [PATCH 25/36] improve readme contents --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 735e392..1fbf81d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## initial - INI parsing library for the D programming language +## initial - INI parser for the D programming language My attempt at making a sane and high-quality INI parsing library for D. @@ -7,9 +7,9 @@ My attempt at making a sane and high-quality INI parsing library for D. key = value ``` -An INI file's contents is stored in an `INIUnit` structure. This contains -multiple `INISection`s stored in an associative array that you can read -or set yourself. +The contents of an INI file is stored in an `INIUnit` structure. This +contains an associative array of `INISection`s that you can read or set +yourself. initial also provides serialisation facilities that can turn `INIUnit`s and `INISection`s back into an INI document. Handy for if your program @@ -44,13 +44,13 @@ write("config.ini", ini.serialise()); ## Usage -Download initial.d and put it somewhere in your import path, then compile it alongside +Copy initial.d to somewhere in your import path, then compile it alongside your program and link it in. Or you can do it all at once by using -i. -``` -dmd program.d initial.d -dmd -i program.d -``` + dmd program.d initial.d + dmd -i program.d + +Documentation can be found by reading the documentation comments in the code. ## Progress From 89e6561297be17646a05e1f7e4df92a90c523629 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Wed, 5 Jun 2024 12:30:31 +1200 Subject: [PATCH 26/36] update readme todo list --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1fbf81d..d6f31c0 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,16 @@ Documentation can be found by reading the documentation comments in the code. - Default section - Comments -### Todo +### Unimplemented - Key referencing -- Testing D: + +Currently initial.d does all I need it to, and I consider it complete. +However if you have any questions, feel free to send an email to [my +public inbox][1] or directly to my address (jeremy@baxters.nz). Patches +are also welcome which you can generate with `git format-patch` and attach +to an email or just copy into the body of an email ([`git send-email`][2] +can do this automatically). + +[1]: https://lists.sr.ht/~jeremy/public-inbox +[2]: https://git-send-email.io From dcdbbff878d58bbdd690afd58d35774a0f750256 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Wed, 5 Jun 2024 12:33:14 +1200 Subject: [PATCH 27/36] add copying notice --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index d6f31c0..150d369 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ and `INISection`s back into an INI document. Handy for if your program has some sort of GUI configuration interface or if you're passing around INI through a network connection. +initial.d is licensed under the Boost Software License, version 1.0. See +COPYING for the full licence text. + ## Demo ```d From fb85533933de80fbec274487945bed45b2056ecc Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Wed, 5 Jun 2024 12:39:51 +1200 Subject: [PATCH 28/36] update dub.json --- dub.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dub.json b/dub.json index fb52964..9b22875 100644 --- a/dub.json +++ b/dub.json @@ -1,9 +1,9 @@ { "name": "initial", - "description": "INI parsing library for the D programming language", + "description": "INI parser", - "authors": ["Jeremy Baxter"], - "copyright": "Copyright © 2024, Jeremy Baxter", + "authors": ["Jeremy Baxter "], + "copyright": "Copyright © 2024 Jeremy Baxter", "license": "BSL-1.0", "targetType": "library", From baafe10b1723b4907128170b26c27943abf12d01 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Tue, 18 Jun 2024 17:42:26 +1200 Subject: [PATCH 29/36] fix doc comment --- initial.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/initial.d b/initial.d index 7f4a585..1181b9f 100644 --- a/initial.d +++ b/initial.d @@ -218,7 +218,7 @@ struct INISection keys = new string[string]; } - /+ + /++ + Returns the value of `k` in this section or + `defaultValue` if the key is not present. +/ From c00e0fa7e62444a04dbb7362eeba4f906e94b99a Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Tue, 18 Jun 2024 17:42:36 +1200 Subject: [PATCH 30/36] avoid converting objects twice If I have an object of type T and want to set it as the default value for a call to .keyAs(), I have to convert it to a string first. This string would then get converted again in case the key doesn't exist in the section. --- initial.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/initial.d b/initial.d index 1181b9f..289c33c 100644 --- a/initial.d +++ b/initial.d @@ -234,10 +234,10 @@ struct INISection + with a message containing location information. +/ T - keyAs(T)(string k, string defaultValue = null) + keyAs(T)(string k, T defaultValue) { try - return key(k, defaultValue).to!T(); + return k in keys ? keys[k].to!T() : defaultValue; catch (ConvException) throw new INITypeException( format!"unable to convert [%s].%s to %s"(name, k, T.stringof)); From 0b0a30673676c80dece57c380444833115f537d6 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Thu, 8 Aug 2024 17:43:32 +1200 Subject: [PATCH 31/36] .build.yml: init --- .build.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .build.yml diff --git a/.build.yml b/.build.yml new file mode 100644 index 0000000..3cd10aa --- /dev/null +++ b/.build.yml @@ -0,0 +1,15 @@ +--- +image: nixos/unstable +packages: + - nixos.ldc +environment: + CFLAGS: "-Oz -de -unittest -main -run" + NIX_CONFIG: "experimental-features = nix-command flakes" +sources: + - "https://git.sr.ht/~jeremy/initial" +tasks: + - ldc: | + ldc2 $CFLAGS initial/initial.d + - ldc-1_30_0: | + nix build -o ldc-1.30.0 --accept-flake-config github:PetarKirov/dlang.nix#ldc-1_30_0 + ldc-1.30.0/bin/ldc2 $CFLAGS initial/initial.d From c728f0f0e61ae9949618131faf4b67c44d60ef9b Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Thu, 8 Aug 2024 17:47:55 +1200 Subject: [PATCH 32/36] README.md: add build status --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 150d369..321494d 100644 --- a/README.md +++ b/README.md @@ -77,3 +77,8 @@ can do this automatically). [1]: https://lists.sr.ht/~jeremy/public-inbox [2]: https://git-send-email.io + +## Build status + +Status on building with LDC latest and 1.30.0: + [![builds.sr.ht status](https://builds.sr.ht/~jeremy/initial.svg)](https://builds.sr.ht/~jeremy/initial) From f115e015864c2b3a3658fcf0fd7c887f6966c9fe Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Thu, 8 Aug 2024 17:52:20 +1200 Subject: [PATCH 33/36] wrap lines --- initial.d | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/initial.d b/initial.d index 289c33c..8c2611d 100644 --- a/initial.d +++ b/initial.d @@ -3,27 +3,29 @@ * * Boost Software License - Version 1.0 - August 17th, 2003 * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: + * Permission is hereby granted, free of charge, to any person or + * organization obtaining a copy of the software and accompanying + * documentation covered by this license (the "Software") to use, + * reproduce, display, distribute, execute, and transmit the Software, + * and to prepare derivative works of the Software, and to permit + * third-parties to whom the Software is furnished to do so, all + * subject to the following: * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, - * must be included in all copies of the Software, in whole or in part, and - * all derivative works of the Software, unless such copies or derivative - * works are solely in the form of machine-executable object code generated by - * a source language processor. + * The copyright notices in the Software and this entire statement, + * including the above license grant, this restriction and the following + * disclaimer, must be included in all copies of the Software, in whole or + * in part, and all derivative works of the Software, unless such copies or + * derivative works are solely in the form of machine-executable object + * code generated by a source language processor. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE + * DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, + * WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ /++ From a3d17fdc6c1aea6dea288f0819bebecd3afc917b Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Thu, 8 Aug 2024 17:53:01 +1200 Subject: [PATCH 34/36] fix error with LDC 1.30.0: cannot create a `string[string]` with `new` --- initial.d | 1 - 1 file changed, 1 deletion(-) diff --git a/initial.d b/initial.d index 8c2611d..1843cec 100644 --- a/initial.d +++ b/initial.d @@ -217,7 +217,6 @@ struct INISection this(string name) nothrow { this.name = name; - keys = new string[string]; } /++ From daef20ac592366ea38beae93dfbb5d9b2436344d Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Wed, 30 Apr 2025 20:25:01 +1200 Subject: [PATCH 35/36] .build.yml: drop --- .build.yml | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 .build.yml diff --git a/.build.yml b/.build.yml deleted file mode 100644 index 3cd10aa..0000000 --- a/.build.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -image: nixos/unstable -packages: - - nixos.ldc -environment: - CFLAGS: "-Oz -de -unittest -main -run" - NIX_CONFIG: "experimental-features = nix-command flakes" -sources: - - "https://git.sr.ht/~jeremy/initial" -tasks: - - ldc: | - ldc2 $CFLAGS initial/initial.d - - ldc-1_30_0: | - nix build -o ldc-1.30.0 --accept-flake-config github:PetarKirov/dlang.nix#ldc-1_30_0 - ldc-1.30.0/bin/ldc2 $CFLAGS initial/initial.d From db7be633a22b44add8865408bb14beceb1474acc Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Wed, 30 Apr 2025 21:35:02 +1200 Subject: [PATCH 36/36] add minimal readme --- README | 4 +++ README.md | 84 ------------------------------------------------------- 2 files changed, 4 insertions(+), 84 deletions(-) create mode 100644 README delete mode 100644 README.md diff --git a/README b/README new file mode 100644 index 0000000..bf355a1 --- /dev/null +++ b/README @@ -0,0 +1,4 @@ +This is initial, an INI parsing library for the D programming language. + +Reference documentation can be found in the source code as Ddoc comments. +A quick start guide can be found at . diff --git a/README.md b/README.md deleted file mode 100644 index 321494d..0000000 --- a/README.md +++ /dev/null @@ -1,84 +0,0 @@ -## initial - INI parser for the D programming language - -My attempt at making a sane and high-quality INI parsing library for D. - -```ini -[section] -key = value -``` - -The contents of an INI file is stored in an `INIUnit` structure. This -contains an associative array of `INISection`s that you can read or set -yourself. - -initial also provides serialisation facilities that can turn `INIUnit`s -and `INISection`s back into an INI document. Handy for if your program -has some sort of GUI configuration interface or if you're passing around -INI through a network connection. - -initial.d is licensed under the Boost Software License, version 1.0. See -COPYING for the full licence text. - -## Demo - -```d -INIUnit ini; - -/* write INI data to an INIUnit */ -ini["section"]["key"] = "value"; -ini["section"]["num"] = "4.8"; - -/* read INI data */ -readINI(ini, ` - [section] - num = 5.3 -`); - -assert(ini["section"]["key"] == "value"); -assert(ini["section"]["num"] == "5.3"); - -/* read INI from file */ -readINIFile(ini, "config.ini"); - -/* write INI to file */ -import std.file : write; -write("config.ini", ini.serialise()); -``` - -## Usage - -Copy initial.d to somewhere in your import path, then compile it alongside -your program and link it in. Or you can do it all at once by using -i. - - dmd program.d initial.d - dmd -i program.d - -Documentation can be found by reading the documentation comments in the code. - -## Progress - -### Finished - -- Keys -- Sections -- Default section -- Comments - -### Unimplemented - -- Key referencing - -Currently initial.d does all I need it to, and I consider it complete. -However if you have any questions, feel free to send an email to [my -public inbox][1] or directly to my address (jeremy@baxters.nz). Patches -are also welcome which you can generate with `git format-patch` and attach -to an email or just copy into the body of an email ([`git send-email`][2] -can do this automatically). - -[1]: https://lists.sr.ht/~jeremy/public-inbox -[2]: https://git-send-email.io - -## Build status - -Status on building with LDC latest and 1.30.0: - [![builds.sr.ht status](https://builds.sr.ht/~jeremy/initial.svg)](https://builds.sr.ht/~jeremy/initial)