From 6bc38900d9d9f65f663d50505e14cb1d7e005b37 Mon Sep 17 00:00:00 2001 From: brettlangdon Date: Wed, 17 Jun 2015 19:40:22 -0400 Subject: [PATCH] some more documentation is good --- README.md | 195 +++++++++++++----------------------------------- example_test.go | 5 ++ forge.go | 80 ++++++++++++++++++++ 3 files changed, 136 insertions(+), 144 deletions(-) diff --git a/README.md b/README.md index d80bd26..719c077 100644 --- a/README.md +++ b/README.md @@ -5,128 +5,49 @@ forge Forge is a configuration syntax and parser. -**Note:** This is still under active development, please report any issues or feature requests. - ## Installation `git get github.com/brettlangdon/forge` -## File format - -The format was influenced a lot by nginx configuration file format. - -```config -# Global settings -global_key = "string value"; - -# Sub section -sub_settings { - sub_int = 500; - sub_float = 80.80; - # Sub-Sub Section - sub_sub_settings { - sub_sub_sub_settings { - key = "value"; - } - } -} - -# Second section -second { - key = "value"; - global_reference = sub_settings.sub_float; - local_reference = .key; # References second.key - - include "/path/to/other/settings/*.cfg"; -} -``` - -For normal settings the format is the key followed by an equal sign followed by the value and lastly ending with a semicolon. -` = ;` - -Sections (basically a map) is formatted as the section name with the section's settings wrapped in brackets. -`
{ = ; }` - -Comments start with a pound sign `#` and end with a newline. A comment can exist on the same line as settings/sections, but the comment must end the line. - -Includes are allowed simply by using the directive `include` followed by a string pointing to the location of the file(s) you want to include. -`include` uses go's [filepath.Match](http://golang.org/pkg/path/filepath/#Glob) functionality to find all files matching the provided pattern. -Each file is included directly where it's `include` statement is called from. -`include "/etc/app/*.cfg";` - -## Data types - -### Boolean -A boolean value is either `true` or `false` of any case. - -`TRUE`, `true`, `True`, `FALSE`, `False`, `false`. - -### Null - -A null value is allowed as `null` of any case. - -`NULL`, `Null`, `null`. - -### String -A string value is wrapped by double quotes (single quotes will not work). - -`"string value"`, `"single ' quotes ' allowed"`. - -As of right now there is no way to escape double quotes within a string's value; - -### Number - -There are two supported numbers, Integer and Float, both of which are simply numbers with the later having one period. - -`500`, `50.56`. - -### Section - -Sections are essentially maps, that is a setting whose purpose is to hold other settings. -Sections can be used to namespace settings. +## Documentation -`section { setting = "value"; }`. +Documentation can be viewed on godoc: https://godoc.org/github.com/brettlangdon/forge +## Example -### References - -References are used to refer to previously defined settings. There are two kinds of references, a global reference and a local reference; - -The general format for a reference is a mix of identifiers and periods, for example `production.db.name`. - -A global reference is a reference which starts looking for its value from the top most section (global section). - -A local reference is a reference whose value starts with a period, this reference will start looking for it's value from the current section it is within (local section). +You can see example usage in the `example` folder. -```config -production { - db { - name = "forge"; +``cfg +# example.cfg + +# Global directives +global = "global value"; +# Primary section +primary { + string = "primary string value"; + integer = 500; + float = 80.80; + boolean = true; + negative = FALSE; + nothing = NULL; + # Include external files + include "./include*.cfg"; + # Primary-sub section + sub { + key = "primary sub key value"; } } -development { - db { - name = production.db.name; - } - db_name = .db.name; +# Secondary section +secondary { + another = "secondary another value"; + global_reference = global; + primary_sub_key = primary.sub.key; + another_again = .another; # References secondary.another + _under = 50; } ``` -## API - -`github.com/brettlangdon/forge` - -* `forge.ParseString(data string) (map[string]interface{}, error)` -* `forge.ParseBytes(data []byte) (map[string]interface{}, error)` -* `forge.ParseFile(filename string) (map[string]interface{}, error)` -* `forge.ParseReader(reader io.Reader) (map[string]interface{}, error)` - - -## Example - -You can see example usage in the `example` folder. - ```go package main @@ -138,52 +59,38 @@ import ( ) func main() { - // Parse the file `example.cfg` as a map[string]interface{} + // Parse a `SectionValue` from `example.cfg` settings, err := forge.ParseFile("example.cfg") if err != nil { panic(err) } - // Convert the settings to JSON for printing - jsonBytes, err := json.Marshal(settings) - if err != nil { - panic(err) + // Get a single value + if settings.Contains("global") { + // Get `global` casted as `StringValue` + value := settings.GetString("global") + fmt.Printf("global = \"%s\"\r\n", value.GetValue()) } - // Print the parsed settings - fmt.Println(string(jsonBytes)) -} -``` - -## Future Plans - -The following features are currently on my bucket list for the future: + // Get a nested value + value, err := settings.Resolve("primary.included_setting") + fmt.Printf("primary.included_setting = \"%s\"\r\n", value.GetValue()) -### Operations/Expressions + // You can also traverse down the sections + primary, err := settings.GetSection("primary") + value, err := primary.GetString("included_setting") + fmt.Printf("primary.included_setting = \"%s\"\r\n", value.GetValue()) -Would be nice to have Addition/Subtraction/Multiplication/Division: + // Convert settings to a map + settingsMap, err := settings.ToMap() + fmt.Printf("global = \"%s\"\r\n", settingsMap["global"]) -```config -whole = 100 -half = whole / 2; -double = whole * 2; -one_more = whole + 1; -one_less = whole - 1; -``` - -Also Concatenation for strings: - -```config -domain = "github.com"; -username = "brettlangdon"; -name = "forge"; -repo_url = domain + "/" + username + "/" + name; + // Convert settings to JSON + jsonBytes, err := settings.ToJSON() + fmt.Printf("\r\n\r\n%s\r\n", string(jsonBytes)) +} ``` -### API - -I'll probably revisit the API, I just threw it together quick, want to make sure it right. - -### Documentation +## Issues/Requests? -Documentation is a good thing. +Please feel free to open a github issue for any issues you have or any feature requests. diff --git a/example_test.go b/example_test.go index 24acd2e..3f631fb 100644 --- a/example_test.go +++ b/example_test.go @@ -25,6 +25,11 @@ func Example() { value, err := settings.Resolve("primary.included_setting") fmt.Printf("primary.included_setting = \"%s\"\r\n", value.GetValue()) + // You can also traverse down the sections manually + primary, err := settings.GetSection("primary") + value, err := primary.GetString("included_setting") + fmt.Printf("primary.included_setting = \"%s\"\r\n", value.GetValue()) + // Convert settings to a map settingsMap, err := settings.ToMap() fmt.Printf("global = \"%s\"\r\n", settingsMap["global"]) diff --git a/forge.go b/forge.go index ea17033..60dd03c 100644 --- a/forge.go +++ b/forge.go @@ -1,3 +1,83 @@ +// Forge configuration file format and parser. +// +// Config file example: +// +// # example.cfg +// top_level = "a string"; +// primary { +// primary_int = 500; +// sub_section { +// sub_float = 50.5; # End of line comment +// } +// } +// secondary { +// secondary_bool = true; +// secondary_null = null; +// +// # Reference other config value +// local_ref = .secondary_null; +// global_ref = primary.sub_section.sub_float; +// +// # Include all files matching the provided pattern +// include "/etc/app/*.cfg"; +// } +// +// +// Config file format: +// +// IDENTIFIER: [_a-zA-Z]+ +// +// BOOL: 'true' | 'false' +// NULL: 'null' +// INTEGER: [0-9]+ +// FLOAT: INTEGER '.' INTEGER +// STRING: '"' .* '"' +// REFERENCE: [IDENTIFIER] ('.' IDENTIFIER)+ +// VALUE: BOOL | NULL | INTEGER | FLOAT | STRING | REFERENCE +// +// INCLUDE: 'include ' STRING ';' +// DIRECTIVE: (IDENTIFIER '=' VALUE | INCLUDE) ';' +// SECTION: IDENTIFIER '{' (DIRECTIVE | SECTION)* '}' +// COMMENT: '#' .* NEWLINE '\n' +// +// CONFIG_FILE: (COMMENT | DIRECTIVE | SECTION)* +// +// +// Values +// * String: +// Any value enclosed in double quotes (single quotes not allowed) (e.g. "string") +// * Integer: +// Any number without decimal places (e.g. 500) +// * Float: +// Any number with decimal places (e.g. 500.55) +// * Boolean: +// The identifiers 'true' or 'false' of any case (e.g. TRUE, True, true, FALSE, False, false) +// * Null: +// The identifier 'null' of any case (e.g. NULL, Null, null) +// * Global reference: +// An identifier which may contain periods, the references are resolved from the global +// section (e.g. global_value, section.sub_section.value) +// * Local reference: +// An identifier which main contain periods which starts with a period, the references +// are resolved from the settings current section (e.g. .value, .sub_section.value) +// +// +// Directives +// * Comment: +// A comment is a pound symbol ('#') followed by any text any which ends with a newline (e.g. '# I am a comment\n') +// A comment can either be on a line of it's own or at the end of any line. Nothing can come after the comment +// until after the newline. +// * Directive: +// A directive is a setting, a identifier and a value. They are in the format ' = ;' +// All directives must end in a semicolon. The value can be any of the types defined above. +// * Section: +// A section is a grouping of directives under a common name. They are in the format ' { }'. +// All sections must be wrapped in brackets ('{', '}') and must all have a name. They do not end in a semicolon. +// Sections may be left empty, they do not have to contain any directives. +// * Include: +// An include statement tells the config parser to include the contents of another config file where the include +// statement is defined. Includes are in the format 'include "";'. The can be any glob +// like pattern which is compatible with `path.filepath.Match` http://golang.org/pkg/path/filepath/#Match package forge import (