Source Edit

The parsecfg module implements a high performance configuration file parser. The configuration file’s syntax is similar to the Windows .ini format, but much more powerful, as it is not a line based parser. String literals, raw string literals and triple quoted string literals are supported as in the Nim programming language.

Example of how a configuration file may look like:

  1. # This is a comment.
  2. ; this too.
  3. [Common]
  4. cc=gcc # '=' and ':' are the same
  5. --foo="bar" # '--cc' and 'cc' are the same, 'bar' and '"bar"' are the same (except for '#')
  6. macrosym: "#" # Note that '#' is interpreted as a comment without the quotation
  7. --verbose
  8. [Windows]
  9. isConsoleApplication=False ; another comment
  10. [Posix]
  11. isConsoleApplication=True
  12. key1: "in this string backslash escapes are interpreted\n"
  13. key2: r"in this string not"
  14. key3: """triple quotes strings
  15. are also supported. They may span
  16. multiple lines."""
  17. --"long option with spaces": r"c:\myfiles\test.txt"

Here is an example of how to use the configuration file parser:

Example: cmd: -r:off

  1. import std/parsecfg
  2. import std/[strutils, streams]
  3. let configFile = "example.ini"
  4. var f = newFileStream(configFile, fmRead)
  5. assert f != nil, "cannot open " & configFile
  6. var p: CfgParser
  7. open(p, f, configFile)
  8. while true:
  9. var e = next(p)
  10. case e.kind
  11. of cfgEof: break
  12. of cfgSectionStart: ## a `[section]` has been parsed
  13. echo "new section: " & e.section
  14. of cfgKeyValuePair:
  15. echo "key-value-pair: " & e.key & ": " & e.value
  16. of cfgOption:
  17. echo "command: " & e.key & ": " & e.value
  18. of cfgError:
  19. echo e.msg
  20. close(p)

Configuration file example

  1. charset = "utf-8"
  2. [Package]
  3. name = "hello"
  4. --threads:on
  5. [Author]
  6. name = "nim-lang"
  7. website = "nim-lang.org"

Creating a configuration file

Example:

  1. import std/parsecfg
  2. var dict = newConfig()
  3. dict.setSectionKey("","charset", "utf-8")
  4. dict.setSectionKey("Package", "name", "hello")
  5. dict.setSectionKey("Package", "--threads", "on")
  6. dict.setSectionKey("Author", "name", "nim-lang")
  7. dict.setSectionKey("Author", "website", "nim-lang.org")
  8. assert $dict == """
  9. charset=utf-8
  10. [Package]
  11. name=hello
  12. --threads:on
  13. [Author]
  14. name=nim-lang
  15. website=nim-lang.org
  16. """

Reading a configuration file

Example: cmd: -r:off

  1. import std/parsecfg
  2. let dict = loadConfig("config.ini")
  3. let charset = dict.getSectionValue("","charset")
  4. let threads = dict.getSectionValue("Package","--threads")
  5. let pname = dict.getSectionValue("Package","name")
  6. let name = dict.getSectionValue("Author","name")
  7. let website = dict.getSectionValue("Author","website")
  8. echo pname & "\n" & name & "\n" & website

Modifying a configuration file

Example: cmd: -r:off

  1. import std/parsecfg
  2. var dict = loadConfig("config.ini")
  3. dict.setSectionKey("Author", "name", "nim-lang")
  4. dict.writeConfig("config.ini")

Deleting a section key in a configuration file

Example: cmd: -r:off

  1. import std/parsecfg
  2. var dict = loadConfig("config.ini")
  3. dict.delSectionKey("Author", "website")
  4. dict.writeConfig("config.ini")

Supported INI File structure

Example:

  1. import std/parsecfg
  2. import std/streams
  3. var dict = loadConfig(newStringStream("""[Simple Values]
  4. key=value
  5. spaces in keys=allowed
  6. spaces in values=allowed as well
  7. spaces around the delimiter = obviously
  8. you can also use : to delimit keys from values
  9. [All Values Are Strings]
  10. values like this: 19990429
  11. or this: 3.14159265359
  12. are they treated as numbers : no
  13. integers floats and booleans are held as: strings
  14. can use the API to get converted values directly: true
  15. [No Values]
  16. key_without_value
  17. # empty string value is not allowed =
  18. [ Seletion A ]
  19. space around section name will be ignored
  20. [You can use comments]
  21. # like this
  22. ; or this
  23. # By default only in an empty line.
  24. # Inline comments can be harmful because they prevent users
  25. # from using the delimiting characters as parts of values.
  26. # That being said, this can be customized.
  27. [Sections Can Be Indented]
  28. can_values_be_as_well = True
  29. does_that_mean_anything_special = False
  30. purpose = formatting for readability
  31. # Did I mention we can indent comments, too?
  32. """)
  33. )
  34. let section1 = "Simple Values"
  35. assert dict.getSectionValue(section1, "key") == "value"
  36. assert dict.getSectionValue(section1, "spaces in keys") == "allowed"
  37. assert dict.getSectionValue(section1, "spaces in values") == "allowed as well"
  38. assert dict.getSectionValue(section1, "spaces around the delimiter") == "obviously"
  39. assert dict.getSectionValue(section1, "you can also use") == "to delimit keys from values"
  40. let section2 = "All Values Are Strings"
  41. assert dict.getSectionValue(section2, "values like this") == "19990429"
  42. assert dict.getSectionValue(section2, "or this") == "3.14159265359"
  43. assert dict.getSectionValue(section2, "are they treated as numbers") == "no"
  44. assert dict.getSectionValue(section2, "integers floats and booleans are held as") == "strings"
  45. assert dict.getSectionValue(section2, "can use the API to get converted values directly") == "true"
  46. let section3 = "Seletion A"
  47. assert dict.getSectionValue(section3,
  48. "space around section name will be ignored", "not an empty value") == ""
  49. let section4 = "Sections Can Be Indented"
  50. assert dict.getSectionValue(section4, "can_values_be_as_well") == "True"
  51. assert dict.getSectionValue(section4, "does_that_mean_anything_special") == "False"
  52. assert dict.getSectionValue(section4, "purpose") == "formatting for readability"

Imports

strutils, lexbase, streams, tables, decode_helpers, since

Types

  1. CfgEvent = object of RootObj
  2. case kind*: CfgEventKind ## the kind of the event
  3. of cfgEof:
  4. nil
  5. of cfgSectionStart:
  6. section*: string ## `section` contains the name of the
  7. ## parsed section start (syntax: `[section]`)
  8. of cfgKeyValuePair, cfgOption:
  9. key*, value*: string ## contains the (key, value) pair if an option
  10. ## of the form `--key: value` or an ordinary
  11. ## `key= value` pair has been parsed.
  12. ## `value==""` if it was not specified in the
  13. ## configuration file.
  14. of cfgError: ## the parser encountered an error: `msg`
  15. msg*: string ## contains the error message. No exceptions
  16. ## are thrown if a parse error occurs.

describes a parsing event Source Edit

  1. CfgEventKind = enum
  2. cfgEof, ## end of file reached
  3. cfgSectionStart, ## a `[section]` has been parsed
  4. cfgKeyValuePair, ## a `key=value` pair has been detected
  5. cfgOption, ## a `--key=value` command line option
  6. cfgError ## an error occurred during parsing

enumeration of all events that may occur when parsing Source Edit

  1. CfgParser = object of BaseLexer

the parser object. Source Edit

  1. Config = OrderedTableRef[string, OrderedTableRef[string, string]]

Source Edit

Procs

  1. proc `$`(dict: Config): string {....raises: [IOError, OSError],
  2. tags: [WriteIOEffect], forbids: [].}

Writes the contents of the table to string.

Note: Comment statement will be ignored.

Source Edit

  1. proc close(c: var CfgParser) {....gcsafe, extern: "npc$1",
  2. raises: [IOError, OSError],
  3. tags: [WriteIOEffect], forbids: [].}

Closes the parser c and its associated input stream. Source Edit

  1. proc delSection(dict: var Config; section: string) {....raises: [], tags: [],
  2. forbids: [].}

Deletes the specified section and all of its sub keys. Source Edit

  1. proc delSectionKey(dict: var Config; section, key: string) {....raises: [KeyError],
  2. tags: [], forbids: [].}

Deletes the key of the specified section. Source Edit

  1. proc errorStr(c: CfgParser; msg: string): string {....gcsafe, extern: "npc$1",
  2. raises: [ValueError], tags: [], forbids: [].}

Returns a properly formatted error message containing current line and column information. Source Edit

  1. proc getColumn(c: CfgParser): int {....gcsafe, extern: "npc$1", raises: [],
  2. tags: [], forbids: [].}

Gets the current column the parser has arrived at. Source Edit

  1. proc getFilename(c: CfgParser): string {....gcsafe, extern: "npc$1", raises: [],
  2. tags: [], forbids: [].}

Gets the filename of the file that the parser processes. Source Edit

  1. proc getLine(c: CfgParser): int {....gcsafe, extern: "npc$1", raises: [], tags: [],
  2. forbids: [].}

Gets the current line the parser has arrived at. Source Edit

  1. proc getSectionValue(dict: Config; section, key: string; defaultVal = ""): string {.
  2. ...raises: [KeyError], tags: [], forbids: [].}

Gets the key value of the specified Section. Returns the specified default value if the specified key does not exist. Source Edit

  1. proc ignoreMsg(c: CfgParser; e: CfgEvent): string {....gcsafe, extern: "npc$1",
  2. raises: [ValueError], tags: [], forbids: [].}

Returns a properly formatted warning message containing that an entry is ignored. Source Edit

  1. proc loadConfig(filename: string): Config {.
  2. ...raises: [IOError, OSError, Exception, ValueError, KeyError],
  3. tags: [WriteIOEffect, ReadIOEffect, RootEffect], forbids: [].}

Loads the specified configuration file into a new Config instance. Source Edit

  1. proc loadConfig(stream: Stream; filename: string = "[stream]"): Config {.
  2. ...raises: [IOError, OSError, Exception, ValueError, KeyError],
  3. tags: [ReadIOEffect, RootEffect, WriteIOEffect], forbids: [].}

Loads the specified configuration from stream into a new Config instance. filename parameter is only used for nicer error messages. Source Edit

  1. proc newConfig(): Config {....raises: [], tags: [], forbids: [].}

Creates a new configuration table. Useful when wanting to create a configuration file. Source Edit

  1. proc next(c: var CfgParser): CfgEvent {....gcsafe, extern: "npc$1",
  2. raises: [IOError, OSError, ValueError],
  3. tags: [ReadIOEffect], forbids: [].}

Retrieves the first/next event. This controls the parser. Source Edit

  1. proc open(c: var CfgParser; input: Stream; filename: string; lineOffset = 0) {.
  2. ...gcsafe, extern: "npc$1", raises: [IOError, OSError, Exception],
  3. tags: [ReadIOEffect, RootEffect], forbids: [].}

Initializes the parser with an input stream. Filename is only used for nice error messages. lineOffset can be used to influence the line number information in the generated error messages. Source Edit

  1. proc setSectionKey(dict: var Config; section, key, value: string) {.
  2. ...raises: [KeyError], tags: [], forbids: [].}

Sets the Key value of the specified Section. Source Edit

  1. proc warningStr(c: CfgParser; msg: string): string {....gcsafe, extern: "npc$1",
  2. raises: [ValueError], tags: [], forbids: [].}

Returns a properly formatted warning message containing current line and column information. Source Edit

  1. proc writeConfig(dict: Config; filename: string) {....raises: [IOError, OSError],
  2. tags: [WriteIOEffect], forbids: [].}

Writes the contents of the table to the specified configuration file.

Note: Comment statement will be ignored.

Source Edit

  1. proc writeConfig(dict: Config; stream: Stream) {....raises: [IOError, OSError],
  2. tags: [WriteIOEffect], forbids: [].}

Writes the contents of the table to the specified stream.

Note: Comment statement will be ignored.

Source Edit

Iterators

  1. iterator sections(dict: Config): lent string {....raises: [], tags: [], forbids: [].}

Iterates through the sections in the dict. Source Edit