{"id":41108937,"url":"https://github.com/nilium/codf","last_synced_at":"2026-01-22T15:32:54.148Z","repository":{"id":57510515,"uuid":"135540106","full_name":"nilium/codf","owner":"nilium","description":"Config file parser for Go in the style of nginx.","archived":false,"fork":false,"pushed_at":"2020-08-24T02:10:34.000Z","size":326,"stargazers_count":8,"open_issues_count":6,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-10T13:37:25.307Z","etag":null,"topics":["ast","bikeshed","config","configuration","go","golang","hacktoberfest","lexer","parser","parsing"],"latest_commit_sha":null,"homepage":"https://go.spiff.io/codf","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nilium.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-05-31T06:15:00.000Z","updated_at":"2024-08-08T01:42:19.000Z","dependencies_parsed_at":"2022-09-26T17:50:55.753Z","dependency_job_id":null,"html_url":"https://github.com/nilium/codf","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/nilium/codf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nilium%2Fcodf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nilium%2Fcodf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nilium%2Fcodf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nilium%2Fcodf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nilium","download_url":"https://codeload.github.com/nilium/codf/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nilium%2Fcodf/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28665484,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T14:01:31.714Z","status":"ssl_error","status_checked_at":"2026-01-22T13:59:23.143Z","response_time":144,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ast","bikeshed","config","configuration","go","golang","hacktoberfest","lexer","parser","parsing"],"created_at":"2026-01-22T15:32:53.267Z","updated_at":"2026-01-22T15:32:54.130Z","avatar_url":"https://github.com/nilium.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"codf\n====\n[![CircleCI](https://circleci.com/gh/nilium/codf/tree/master.svg?style=svg)](https://circleci.com/gh/nilium/codf/tree/master)\n[![codecov](https://codecov.io/gh/nilium/codf/branch/master/graph/badge.svg)](https://codecov.io/gh/nilium/codf)\n[![Go Report Card](https://goreportcard.com/badge/go.spiff.io/codf)](https://goreportcard.com/report/go.spiff.io/codf)\n[![GoDoc](https://godoc.org/go.spiff.io/codf?status.svg)](https://godoc.org/go.spiff.io/codf)\n\n\n    $ go get -u go.spiff.io/codf\n\ncodf is a personal config language for declaring structured data with\nsupport for a somewhat-wide range of built-in types.\n\nThe root codf package only covers lexing, parsing, and the AST right\nnow.\n\n\nRationale\n---------\n\n_Alternatively: Why yet another config library?_\n\nCodf exists primarily to make expressive, structured configuration easy\nto use. Expressive in this case means, more or less, relatively complex\nbut easy to read and write. There are cases of progams like this in the\nwild, such as nginx, where configuration is integral to making use of\nthem. Codf takes inspiration from nginx in particular, along with other\ncurly-braced config languages. The goal is to provide structure for\nprograms where the status quo of JSON-equivalent languages do not.\n\nWith that in mind, codf is the result of several years of building\nprograms that require configuration to define their runtime behavior.\nThis includes several programs whose configuration borders on scripting.\nThese programs need configuration not only for inputs (sockets, files,\nDBs) and outputs (metrics, logs, more DBs), but also tasks, schedules,\ndata pipelines, state transitions, and so on. Without these, the\nprograms are mostly inert and do nothing — this makes configuration\ncrucial to their operation. Thus far, all of these programs have used\nsome JSON-like language, be it JSON itself, HCL, or YAML (with TOML\nshowing up only in other programs I use).\n\nWhile all of these provide some structure, they’re ultimately poor\nexpressions of complex program configuration. They require a lot of\nwork-arounds to play nice with Go: often an AST is unavailable (I’ve\nonly seen this exposed in HCL), so all pre-processing, such as including\ndocuments or expanding variables, requires you to decode to a map to\nmanipulate the document before consuming it; the structure of the config\nbecomes tangled up in not violating a language spec (especially where\nkeys are unique); and error messages are often cryptic and useless to\nusers. All of this can be made to work, but it looks lazy and feels like\na kludge.\n\nIf you look outside of the common languages, you see configuration like\n[nginx](https://www.nginx.com/resources/wiki/start/topics/examples/full/),\n[apt.conf](https://manpages.debian.org/stretch/apt/apt.conf.5.en.html),\n[gnatsd](https://nats.io/documentation/server/gnatsd-config/)\n(a mishmash of YAML/JSON/HCL; notable more for being specific to gnatsd),\n[Caddyfiles](https://caddyserver.com/docs/caddyfile), and others (Lisps\nand Erlang terms deserve honorable mention since a program’s language\ncan work as configuration as well). So, it’s clear that there’s a way to\nexpress more than just `key = value` for configuration, but if there are\nany libraries for this, I haven’t yet found them.\n\nSo, I wrote codf. Codf allows me to write config files with directives\nand structure that expresses more than just key-value pairs. In fact, it\ncan parse a wide range of nginx configs with some adjustments for\ncomments and quoting style. Using it to handle program configuration is\nfairly easy by walking the AST, and enough metadata is available through\nit that it’s possible to provide users with helpful error feedback.\nOverall, it leads to much cleaner configuration with less reliance on\nreflection, fewer unmarshaling workarounds, and better help for users.\nThis in turn makes me happier, because I can write programs that\nfunction the way I really want them to.\n\n\nLanguage\n--------\n\nA codf document is a UTF-8 text document containing statements and\nsections. Sections, in turn, contain further statements and sections.\nDocuments may also contain comments, but are not part of a document's\nstructure or AST.\n\n\n### Statements\n\nA statement is a name followed by optional values and terminated by\na semicolon (;):\n\n    enable-gophers;\n    enable-gophers yes;\n    enable-gophers yes with-butter;\n    ; // This line doesn't have a statement -- a semicolon on its own is\n      // an empty statement and does nothing.\n\n\n### Sections\n\nA section is a name followed by optional values and braces, enclosing\nfurther sections or statements:\n\n    outer-section {\n        inner-section /some/path {\n            // ...\n        }\n\n        enable-gophers yes;\n    }\n\n\n### Parameters\n\nBoth sections and statements may have an optional set of values\nfollowing their name, as above. These are called parameters for lack of\na better term.\n\nParameters must be one of the types described below.\n\n\n### Comments\n\nA comment begins with two forward slashes (`//`) and extends to the\nnearest line ending (a linefeed / \"\\n\" / 0x0A byte):\n\n    // This is a comment\n    //This is also a comment\n    this// is not a comment;\n\nComments are not included in the parsed AST and may not be used to\ninfluence configuration.\n\n\n### Types\n\nSupported value types are integers, floats, rationals, durations,\nstrings, booleans, regular expressions, arrays, and maps.\n\n\n#### Integers\n\nIntegers can be written in base 10, 16, 8, and 2, as well as arbitrary\nbases in the range of 2-36:\n\n    base-10 12345;\n    base-16 0x70f; // 1807\n    base-8  0712;  // 458\n    base-2  0b101; // 5\n    base-3  3#210; // 21\n    base-36 36#zz; // 1295\n\nIntegers are arbitarily long and represented as a `*big.Int`.\n\n#### Floats / Decimals\n\nFloats are written either as integers with exponents or decimal numbers\n(with optional exponent):\n\n    float-decimal   1.23456;    // positive\n    float-exponent  -123456e-5; // negative\n    float-big       1.23456789e200;\n\nFloats are represented using a `*big.Float`. Precision can be adjusted\nby changing the Parser's Precision field -- if 0, it defaults to\nDefaultPrecision.\n\n#### Rationals\n\nRationals are expressed as numerator/denominator, similar to lisps. It\nis illegal to use a denominator of zero (0):\n\n    rational -5/40; // -1/8\n    rational 0/40;  // 0/1\n\nRationals are represented using a `*big.Rat`.\n\n#### Durations\n\nDurations are expressed as a sequence of integers or decimal numbers\nfollowed by an interval unit (ns, us, ms, s, m, or h). This is\ncompatible with the Go stdlib's durations, but does not allow decimals\nbeginning with a period as Go does (e.g., \".5s\" -- this has to be\nwritten as \"0.5s\" in codf). As with Go, it's valid to use \"µs\" or \"us\"\nfor microseconds.\n\n    durations 0s -1s 1h 500ms;  // 0s -1s 1h0m0s 500ms\n    decimals  0.5us 0.5s 0.5ms; // 500ns 500ms 500µs\n\nDurations are represented using `time.Duration`.\n\n#### Strings\n\nStrings take three forms: double-quoted sequences of characters, raw\nstrings, and barewords.\n\n##### Double-quoted strings\nDouble-quoted strings are surrounded by double quotes (\"...\") and permit\nall Go string escape codes (such as \\n or \\Uhhhhhhhh). In addition, in\ncontrast to Go, newlines in double-quoted strings are permitted without\nescaping them.\n\n    simple-string \"foobar\";\n    escapes       \"foo\\nbar\"; // \"foo\\nbar\"\n    newline       \"foo\n    bar\";                     // \"foo\\nbar\"\n\n##### Raw strings \nRaw strings are surrounded by backquotes (or backticks -- the \"`\"\ncharacter). Like Go raw string literals, raw strings can contain almost\nanything. Unlike Go raw string literals, a backquote can be escaped\ninside of a raw string by writing two of them: \"``\". For example:\n\n    empty           ``;           // \"\"\n    with-quotes     `\"foobar\"`;   // \"\\\"foobar\\\"\"\n    with-backquotes ```foobar```; // \"`foobar`\"\n\n##### Barewords\nBarewords are unquoted strings and usually more convenient than other\nstrings.\n\nA bareword is any text that begins with a Unicode graphical character\nminus syntactically-important characters: decimal numbers, quotes,\nsemicolons, braces, pound, and plus/minus. The rest of a bareword may\ncontain decimal numbers, pound, and plus/minus -- semicolons, braces,\nand quotes are still reserved.\n\n    leading-dot .dot;           // \".dot\"\n    symbols     $^--;           // \"$^--\"\n    slashes     /foo/bar;       // \"/foo/bar\"\n    commas      Hello, World;   // \"Hello,\" \"World\" -- two strings\n    unicode     こんにちは世界; // \"こんにちは世界\"\n\nIt is not possible to write a bareword that uses a boolean keyword\nexcept as a statement name (described below).\n\nBarewords are represented as `string`.\n\n#### Booleans\n\nBooleans can be represented using the following values:\n\n|             | True    | False   |\n| ----------- | ------- | ------- |\n| **Keyword** | `TRUE`  | `FALSE` |\n|             | `True`  | `False` |\n|             | `true`  | `false` |\n|             | `YES`   | `NO`    |\n|             | `Yes`   | `No`    |\n|             | `yes`   | `no`    |\n\nAll keywords can be written in lowercase, uppercase, or titlecase. For\nexample:\n\n    t-values YES True true; // true true true\n    f-values FALSE No no;   // false false false\n\nOther case combinations are not valid (i.e., booleans keywords\ncase-sensitive).\n\nBooleans can only occur in parameters to statements and sections. For\nexample, the bareword \"true\" as a statement name is just the string\n\"true\". The bareword \"true\" in an array or as a map key or value is the\nboolean `true` (and not permitted in map keys).\n\nBooleans are represented as `bool`.\n\n#### Regular Expressions\n\nRegular expressions are written as #/regex/, where internal /s can be\nescaped using \\/. These are treated as re2 regular expressions and\nparsed using the stdlib regexp package.\n\n    empty-regex  #//;\n    simple-regex #/foo/;\n    slash-regex  #/foo\\/bar/;\n\nRegular expressions are represented as `*regexp.Regexp`.\n\n#### Arrays\n\nArrays are ordered lists of values between square brackets (`[]`).\nValues are delimited by whitespace or other sentinel tokens (such as\nbrackets and comments):\n\n    empty-array [];\n    numbers     [1 2 3];\n    nested      [[1 2] [3 4]];\n\nAny of the above value types can be held by an array.\n\nAn array in the AST is represented as an `*Array`, which contains\na sequence of `[]ExprNode`.\n\n#### Maps\n\nMaps are unordered sets of space-delimited key-value pairs between curly\nbraces, prefixed by a pound (`#{}`). Key-value pairs in a map are\nwritten as `KEY VALUE` (minus quotes), where each KEY must be followed\nby a VALUE (separated by a space). For example:\n\n    empty-map #{};\n    normal-map #{\n        // Key    Value\n        foo      1234      // \"foo\" =\u003e 1234\n        \"bar\"    #/baz/    // \"bar\"  =\u003e #/baz/\n    };\n\nMap keys must be strings, either bare or quoted. If a key occurs more\nthan once in a map, only the last value is kept.\n\nMaps are represented as a `*Map`, which contains a map of strings to\n`*MapEntry`. Each `*MapEntry` contains the original key, value, and the\norder that it was parsed in -- as above, codf maps are unordered, so\nordering is intended only to be kept for reformatting and other tools\nright now.\n\n\n\nLicense\n-------\n\ncodf is licensed under the BSD two-clause license.\nThe license can be read in the COPYING file.\n\n\n\n% vim: set tw=72 sw=4 et :\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnilium%2Fcodf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnilium%2Fcodf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnilium%2Fcodf/lists"}