From e295a1621918fabbf4fd82418ea11f3aae5ab38d Mon Sep 17 00:00:00 2001 From: TehBrian <32250137+TehBrian@users.noreply.github.com> Date: Mon, 19 Aug 2024 18:06:05 -0400 Subject: [PATCH] [hocon/en] clean up document (#5035) --- hocon.html.markdown | 209 ++++++++++++++++++++++---------------------- 1 file changed, 106 insertions(+), 103 deletions(-) diff --git a/hocon.html.markdown b/hocon.html.markdown index b09e20f0..bd101a9d 100644 --- a/hocon.html.markdown +++ b/hocon.html.markdown @@ -9,17 +9,15 @@ Human-Optimized Configuration Object Notation, or HOCON, is a configuration and data serialization format designed to be easily editable by humans. It's a superset of JSON, meaning that any valid JSON is valid HOCON, but it -differs in being much less pedantic and opinionated. With its flexible yet -easily determinable syntax, resulting configuration files are often much less -noisy than some other formats. +differs in being less opinionated. With its flexible yet determinable syntax, +resulting configuration files are often less noisy than with other formats. -Additionally, its support for comments makes it much better suited for -user-facing configurations than JSON. +Additionally, its support for comments makes it better-suited for user-facing +configuration than JSON. ``` -// Comments can either look like this, -# or they can look like this. -// Anything after // or # is a comment. +// Anything after // or # is a comment. This is a comment. +# This is also a comment. ################## ### THE BASICS ### @@ -37,24 +35,29 @@ colon3 : value equals1=value equals2= value equals3 = value -# As you'll see, HOCON is very nonrestrictive regarding its syntax style. +# As you'll see, HOCON has a very nonrestrictive syntax. -# HOCON also isn't opinionated on how keys look. +# HOCON isn't opinionated on how keys look. THIS_IS_A_VALID_KEY: value this-is-also-a-valid-key: value -keys can have spaces too: value +keys can have spaces: value or even numbers like 12345: value "you can even quote keys if you'd like!": value -# A key, followed by any separator, and then finally a value, is called a field. +# Keys are case sensitive. +unique: value 1 +UnIqUe: value 3 +UNIQUE: value 2 + +# A key, followed by any separator, followed by a value, is called a field. this_entire_line_is: a field ################### ### VALUE TYPES ### ################### -# The types that a value can be are string, number, object, array, boolean, and -# null. Every value type except for array and object are called simple values. +# A value can be of type: string, number, object, array, boolean, null. +# Simple values are values of any type except array and object. ## SIMPLE VALUES ## @@ -62,18 +65,16 @@ quoted_string: "I like quoting my strings." unquoted_string: I don't like quoting my strings. # Special characters that cannot be used in unquoted strings are: # $ " { } [ ] : = , + # ` ^ ? ! @ * & -# Unquoted strings do not support any kind of escaping. If using one of those -# special characters is desired, use a quoted string. -multi-line_string: """ - This entire thing is a string! - One giant, multi-line string. - You can put 'single' and "double" quotes without it being invalid. -""" +# Unquoted strings do not support any kind of escaping. +# To use one of those special characters in a string, use a quoted string. +multiline_string: """This entire thing is a string! +One giant, multiline string. +You can put 'single' and "double" quotes without it being invalid.""" number: 123 negative: -123 fraction: 3.1415926536 -scientific_notation: 1.2e6 // same as 1.2 * (10^6) +scientific_notation: 1.2e6 // 1.2 * 10^6 boolean: true # or false empty: null @@ -81,10 +82,11 @@ empty: null ## ARRAYS ## # Arrays hold lists of values. + # Values in arrays can be separated with commas.. array: [ 1, 2, 3, 4, 5 ] fibonacci: [1,1,2,3,5,8,13] -multiples_of_5: [5, 10, 15, 20,] # Notice the trailing comma. That's okay here. +multiples_of_5: [5, 10, 15, 20,] # Notice the trailing comma. That's allowed. # or newlines.. friends: [ "Brian" @@ -97,25 +99,27 @@ ingredients: [ "Egg", "Sugar", "Oil", - "Flour", # Notice the trailing comma. That's okay here too. + "Flour", # Trailing comma. That's allowed here too. ] -# Once again, HOCON has a very loose syntax. Use whichever style you prefer. +# Once again, HOCON has a very liberal syntax. Use whichever style you prefer. + no newline before or after bracket: ["This" "is" "an" "array!"] -# Just like any other value, arrays can hold other arrays. +# Arrays can hold other arrays. array in array: [ [1, 2, 3], ["a", "b", "c"] ] array in array in array: [ [ [1, 2], [8, 9] ], [ ["a", "b" ], ["y", "z"] ] ] ## OBJECTS ## # Objects hold fields. + # Just like arrays, fields in objects can be separated with commas.. object: { key: value, another_key: another_value } server_connection: {ip: "127.0.0.1", port: 80} -first: {letter: a, number: 1,} # Notice the trailing comma. +first: {letter: a, number: 1,} # Trailing comma. # or newlines.. power_grid: { max_capacity: 15000 @@ -127,10 +131,10 @@ food_colors: { pear: green, apple: red, plum: purple, - banana: yellow, # Trailing commas are okay here too! + banana: yellow, # Trailing comma. These pesky things show up everywhere! } -# Arrays can hold objects just like any other value type. +# Arrays can hold objects. coworkers: [ { name: Jeff @@ -152,7 +156,7 @@ no_separator { speed_of_light: very fast ten: 10 - # Objects can go inside other objects just like any other value. + # Objects can hold other objects. another_object { twenty: 20 speed_of_sound: also pretty fast @@ -224,10 +228,10 @@ my_car: { # and then back to an object value, the new object will completely override any # previous value. -// Null, a non-object value, completely overrides the object value. +// Null, a non-object value, overrides the object. my_car: null -// Then, this object completely overrides null. +// Then, this object overrides null. my_car: { nickname: "My New Car" type: 4-door minivan @@ -242,49 +246,49 @@ my_car: { ## SIMPLE VALUE CONCATENATION ## -# Simple values (all value types except objects and arrays) separated by +# Simple values (all value types except array and object) separated by # whitespace are concatenated into a single string. The whitespace between # values is preserved. -number_concatenation: 1 2 3 12.5 -3 2e5 // same as: "1 2 3 12.5 -3 2e5" +number_concat: 1 2 3 12.5 -3 2e5 // "1 2 3 12.5 -3 2e5" boolean_concat: true false true // "true false true" null_concat: null null null // "null null null" mixed_concat: 1 true null // "1 true null" # String value concatenation can appear anywhere that a quoted string can. -number_concat_in_array: [1 2, 3 4, 5 6] // same as: ["1 2", "3 4", "5 6"] +number_concat_in_array: [1 2, 3 4, 5 6] // ["1 2", "3 4", "5 6"] # In fact, unquoted strings are actually just string value concatenations. -unquoted_string_concat: his name is jeff // same as: "his name is jeff" +unquoted_string_concat: his name is jeff // "his name is jeff" # Going further, even keys that are unquoted strings are actually just string # value concatenations. -this is a key: value // the KEY is the same as: "this is a key" +this is a key: value // the KEY is: "this is a key" # The following field is identical to the field above. "this is a key": value -# Quoted strings can also be concatenated. This will be useful later, -# when we cover substitutions. -quoted_string_concat: "her"" name" "is ""jenna" // same as: "her name is jenna" +# Quoted strings can also be concatenated. +# This will be useful later, when we cover substitutions. +quoted_string_concat: "her"" name" "is ""jenna" // "her name is jenna" # Notice that the whitespace (or lack thereof) between values is preserved. ## ARRAY CONCATENATION ## # Arrays separated by whitespace are merged into a single array. -array_concat: [1, 2, 3] [4, 5, 6] // same as: [1, 2, 3, 4, 5, 6] +array_concat: [1, 2, 3] [4, 5, 6] // [1, 2, 3, 4, 5, 6] # Arrays cannot be concatenated with a non-array value. -//array_concat: true [false] results in an error -//array_concat: 1 [2] results in an error +//array_concat: true [false] // error! +//array_concat: 1 [2] // error! ## OBJECT CONCATENATION ## # Objects separated by whitespace are merged into a single object. # The merge functionality is identical to that of duplicate key object merging. -lamp: {on: true} {color: tan} // same as: {on: true, color: tan} +lamp: {on: true} {color: tan} // {on: true, color: tan} # Similarly to arrays, objects cannot be concatenated with a non-object value. -//object_concat: true {on: false} results in an error -//object_concat: 1 {number: 2} results in an error +//object_concat: true {on: false} // error! +//object_concat: 1 {number: 2} // error! ######################## ### PATH EXPRESSIONS ### @@ -305,13 +309,12 @@ country: { } } } -# For example, the path to the address of the house could be written as: +# The path to the address could be written as: # country.city.neighborhood.house.address # Country, city, neighborhood, house, and address are all elements. -# Path expressions are used in two places: substitutions (which will be -# covered in a moment), and as keys. -# That's right: keys themselves can also be path expressions. +# Path expressions are used in two places: substitutions (which we'll get to +# in just a moment), and as keys. That's right: keys can be path expressions. foo: { bar: { baz: { @@ -319,8 +322,8 @@ foo: { } } } -# Rather than tediously specifying each object, a path expression can be used. -# The following field represents the same object found above. +# Rather than tediously specifying each object, a path expression could be used. +# The following field represents the same object. foo.bar.baz.number: 12 # Fields and objects specified with path expressions are merged in the same way @@ -333,41 +336,42 @@ foo.bar.baz.bool: true ##################### # Substitutions refer to a specific value from some path expression. -# They're only allowed in values, not keys or nested inside other substitutions. +# They're only allowed in values, not in keys or nested in other substitutions. me: { favorite_animal: parrots favorite_food: cookies } -# The syntax for a substitution is either ${path_expression} or -# ${?path_expression}. The latter syntax will be discussed in a moment. +# There are two syntaxes for substitutions: +# ${path_expression} and ${?path_expression}. +# The latter syntax will be covered in a moment. my_fav_animal: ${me.favorite_animal} my_fav_food: ${me.favorite_food} # Substitutions are not parsed inside quoted strings. To get around this, # either use an unquoted string or value concatenation. animal_announcement: My favorite animal is ${my_fav_animal} -// the value is: My favorite animal is parrots +// "My favorite animal is parrots" food_announcement: "My favorite food is "${my_fav_food}"!" -// the value is: "My favorite food is cookies!" +// "My favorite food is cookies!" # Substitutions are parsed last in the document. Because of this, you can # reference a key that hasn't been defined yet. color_announcement: "My favorite color is" ${my_fav_color}"!" -// the value is: "My favorite color is blue!" +// "My favorite color is blue!" my_fav_color: blue # Another effect of substitutions being parsed last is that substitutions will -# always use the latest, as in last, value assigned in the entire document, -# which includes merged objects. +# always use the latest, as in last, value assigned in the entire document. color: green -their_favorite_color: ${color} // the value is: orange +their_favorite_color: ${color} // orange color: orange +# This includes merged objects. random_object: { number: 12 } -the_number: ${random_object.number} // the value is: 15 +the_number: ${random_object.number} // 15 random_object: { number: 15 } @@ -379,13 +383,13 @@ random_object: { # A substitution using the ${path_expression} syntax with an undefined path # expression, meaning a path expression that does not point to a defined value, # is invalid and will therefore generate an error. -//${does.not.exist} will throw an error +//${does.not.exist} // error! # However, an undefined substitution using the ${?path_expression} syntax # has different behavior depending on what it is the value of. request: { - # If it is the value of a field, then the field will not be created. - response: ${?does.not.exist} // this field won't be created and does not exist + # If it is the value of a field, then the field won't be created. + response: ${?does.not.exist} // this field does not exist type: HTTP } @@ -397,19 +401,19 @@ request: { # If it is a value in an array, then it is simply not added. values: [ 172, "Brian", ${?does.not.exist}, null, true, ] -// the value is: [ 172, "Brian", null, true ] +// [ 172, "Brian", null, true ] # If it is part of simple value concatenation, it acts as an empty string. final_string: "String One"${?does.not.exist}"String Two" -// the value is: "String OneString Two" +// "String OneString Two" # If it is part of array concatenation, it acts as an empty array. final_array: [ 1, 2, 3 ] ${?does.not.exist} [ 7, 8, 9 ] -// the value is: [ 1, 2, 3, 7, 8, 9 ] +// [ 1, 2, 3, 7, 8, 9 ] # If it is part of object concatenation, it acts as an empty object. -final_array: { a: 1 } ${?does.not.exist} { c: 3 } -// the value is: { a: 1, c: 3 } +final_object: { a: 1 } ${?does.not.exist} { c: 3 } +// { a: 1, c: 3 } ###################################### ### SELF-REFERENTIAL SUBSTITUTIONS ### @@ -419,18 +423,18 @@ final_array: { a: 1 } ${?does.not.exist} { c: 3 } # document. However, in cases when this would create a cycle, the substitution # looks only backwards. -# A field which contains a substitution that points to itself or points to +# A field that contains a substitution that points to itself or points to # other fields that eventually point back to itself is called a # self-referential field. -letters: "a b c" // the value is: "a b c" +letters: "a b c" // "a b c" letters: ${letters}" d" // "a b c d" letters: ${letters}" e" // "a b c d e" -PATH: [/bin] // the value is: [/bin] +PATH: [/bin] // [/bin] PATH: ${PATH} [/usr/bin] // [/bin, /usr/bin] PATH: ${PATH} [/usr/local/bin] // [/bin, /usr/bin, /usr/local/bin] -x: "x" // the value is: "x" +x: "x" // "x" y: ${x}"y" // "xy" x: ${y}"z" // "xyz" @@ -439,34 +443,33 @@ x: ${y}"z" // "xyz" ########################## # In addition to : and =, there actually exists another separator: += -# A field separated with += acts as a self-referential array concatenation. -# In short, it appends an element to a previously defined array. +# A field separated with += implies self-referential array concatenation. +# Essentially, it appends an element to a previously defined array. a: [1] b: [1] -# This field: -a += 2 // the value is: [1, 2] -# functions the same as: -b: ${?b} [2] // the value is: [1, 2] +# These two fields are equivalent. +a += 2 // [1, 2] +b: ${?b} [2] // [1, 2] -USERS: [/usr/luke] // the value is: [/usr/luke] +USERS: [/usr/luke] // [/usr/luke] USERS += /usr/devon // [/usr/luke, /usr/devon] USERS += /usr/michael // [/usr/luke, /usr/devon, /usr/michael] # Since += only appends elements to a previously existing array, if the previous # value was not an array, an error will be generated. OTHER_USERS: /usr/luke -//OTHER_USERS += /usr/devon results in an error +//OTHER_USERS += /usr/devon // error! -# Notice that the underlying substitution syntax used is ${?path}, not ${path}. +# The underlying substitution syntax used is ${?path}, not ${path}. # Recall that, using the ${?} syntax, an undefined substitution in array # concatenation acts as an empty array. Because of this, it is perfectly # acceptable if the field that is being set is initially undefined. -//z: [] not necessary -z += 3 // the value is: [3] -z += 4 // the value is: [3, 4] +//z: [] // not necessary +z += 3 // [3] +z += 4 // [3, 4] -NEW_USERS += /usr/sandra // the value is: [/usr/sandra] +NEW_USERS += /usr/sandra // [/usr/sandra] NEW_USERS += /usr/kennedy // [/usr/sandra, /usr/kennedy] NEW_USERS += /usr/robin // [/usr/sandra, /usr/kennedy, /usr/robin] @@ -494,13 +497,13 @@ include classpath("config.conf") # If the included file does not exist, it will be silently ignored and act as if # it were an empty object. However, if it is wrapped around required(), then # parsing will explicitly error if the file cannot be resolved. -//include required("doesnt_exist.conf") will error -//include required(url("https://example.com/doesnt_exist.conf")) will error -//include required(file("doesnt_exist.conf")) will error -//include required(classpath("doesnt_exist.conf")) will error +//include required("doesnt_exist.conf") // error! +//include required(url("https://example.com/doesnt_exist.conf")) // error! +//include required(file("doesnt_exist.conf")) // error! +//include required(classpath("doesnt_exist.conf")) // error! -# The file specified by the include statement is called the included file, and -# the file which contains the include statement is called the including file. +# The file specified by the include statement is called the included file. +# The file containing the include statement is called the including file. # Including a file functions as if you directly replaced the include statement, # wherever it may be, with the contents of the included file's root object. @@ -518,19 +521,19 @@ screensaver: { turn_on_after: 1m } -# And then we include that file. +# Then, we include that file. include file("user_config.conf") # We can now reference values from that file! -path_to_user_screensaver: ${screensaver.image} // -greeting: "Welcome, "${username}"!" // the value is: "Welcome, RandomUser1337!" +path_to_user_screensaver: ${screensaver.image} // "usr/images/screensaver.jpg" +greeting: "Welcome, "${username}"!" // "Welcome, RandomUser1337!" # Duplicate keys override as they normally do. -status: "Auto Login: "${auto_login} // the value is: "Auto Login: true" +status: "Auto Login: "${auto_login} // "Auto Login: true" auto_login: false -status: "Auto Login: "${auto_login} // the value is: "Auto Login: false" +status: "Auto Login: "${auto_login} // "Auto Login: false" -# Object merging is also the same as usual. +# Object merging is the same as usual. screensaver: { // This gets added to the screensaver object. enable_during_day: false @@ -550,7 +553,7 @@ admin_page: { password: pass12345 } -# And then we include that file nested inside another object. +# Then, we include that file nested inside an object. websites: { my_epic_website: { include file("server_settings.conf") @@ -562,13 +565,13 @@ websites: { server_port: ${websites.my_epic_website.port} the_password: "The password is: "${websites.my_epic_website.admin_page.password} -// the value is: The password is: pass12345 +// "The password is: pass12345" max_conn: "Max Connections: "${websites.my_epic_website.max_connections} -// the value is: Max Connections: 10 +// "Max Connections: 10" ``` ### More Resources + [Official HOCON Specification](https://github.com/lightbend/config/blob/master/HOCON.md) -+ [HOCON Playground](https://hocon-playground.herokuapp.com) ++ [HOCON Playground](https://hocon-playground.tehbrian.dev)