hron - Human Readable Object Notation
XML and JSON (and others) aim to be human-readable, language independent, data interchange formats. However they are not without flaws:
- The XML author has to take care avoiding letters such as &<> and make sure to encode them properly. This hurts readability and writeability.
- JSON struggles with multi-line texts.
- JSON number values are, depending on platform, unserialized as double, long or decimal. This makes JSON number values less useful as, depending on how the value is unserialized, information may be lost. We have personally encountered this in a financial application with disastrous end results.
- JSON lacks strong deserializers on the .NET platform. There are many attempts but none feel as strong as the XMLDOM did already in 1998.
hron stands for human readable objection notation:
- we put human readable in the acronym to remind us why we started thinking about hron in the first place.
- hron supports multi-line texts
- hron doesn't require you to escape "special" characters
- In hron, we made indention significant (as in python) in order to eliminate end tags and to improve readability
- hron has objects and values. Objects consist of other objects or values. Values are always text values.
- There is no special array type. Arrays are created by first adding a named object or value and then just adding another one witout a name. This creates a two element array.
# This is an ini file using hron
# object values are started with '@'
@Greeting
=Title
Hello World from hron!
=WelcomeMessage
Hello there!
String values in hron are started with '='
Just as in Python, indentation is significant in hron
Idention promotes readability but also allows hron string values
to be multi-line and relieves them from the need for escaping.
Let us say that again, there exists _no_ character escaping in hron.
Letters like this are fine in an hron string: &<>\"'@=
This helps readability!
@DataBaseConnection
=Name
CustomerDB
=ConnectionString
Data Source=.\SQLEXPRESS;Initial Catalog=Customers
=TimeOut
10
@User
=UserName
ATestUser
=Password
123
# As we don't 'name' the below object, this will implicitly create an array out of the
# above DataBaseConnection and the below object. Like in real life, adding an apple
# next to an existing apple does not create some new concept of "array of apples",
# two apples next to each other implicitly constitute a "collection of apples"
# without the need for any special sauce (pun intended).
@
=Name
PartnerDB
=ConnectionString
Data Source=.\SQLEXPRESS;Initial Catalog=Partners
The above would (in groovy/java lingo, replace Map with dictionary for .Net) parse to a map with two keys: 'Greeting' and 'DataBaseConnection' where the value for key Greeting is a map with two string values and the value for key DataBaseConnection is a list which contains two map objects. For a good examlpe of what is an is not valid, take a look at the parser unit tests , some which are very descriptive.
Possibly! Check in the languages sub directory.
We are busy implementing parsers in various languages, some as reference implementations with little concern for parser performance (i.e. the groovy parser), others with specific performance metrics in mind (like the java one). Shortly upcoming parsers include scala and c++.
If you can not find a parser in your language, fork the repo and write one! We welcome all contributions. We would especially welcome an implementation in javascript and any insane implementations in long forgotten or obscure languages, because...well because we like computer languages.
We are also working on a standardized set of test files, both positive and negative, which will constitute a smoke test for new parsers.
Rules for the grammar notation:
- The symbol "::=" serves the same purpose as colon in Bison.
- Unquoted parentheses group.
- A trailing unquoted asterisk (*) indicates 0 or more repetitions.
- A trailing unquoted plus indicates 1 or more repetitions.
- Unquoted square braces indicate an optional phrase.
- "p EXCEPT q" means that parser succeeds if p succeeds and q fails
anychar ::= <parses any character>
whitespace ::= <parses any whitespace character>
eos ::= <parses END OF STREAM>
eol ::= <parses END OF LINE (END OF STREAM counts as END OF LINE)>
any_indention ::= <parses any indention>
indention ::= <parses the current indention>
indent ::= <increases indention by one>
dedent ::= <decreases indention by one>
empty_string ::= (whitespace EXCEPT eol)*
string ::= (anychar EXCEPT eol)*
comment_string ::= any_indention "#" string
preprocessor ::= "!" string eol
preprocessors ::= preprocessor*
empty_line ::= empty_string eol
comment_line ::= comment_string eol
nonempty_line ::= indention string eol
value_line ::= nonempty_line | comment_line | empty_line
value_lines ::= (value_line EXCEPT eos)*
value ::= indention "=" string eol indent values_lines dedent
empty ::= empty_string eol
comment ::= comment_string eol
object ::= indention "@" string eol indent members dedent
member ::= value | object | comment | empty
members ::= (member EXCEPT eos)*
hron ::= preprocessors members
Just a few guys who got tired of name spaces and entity escaping and figured the world would be a better place with a simple, human readable data interchange format.
Current contributors:
hron language specification, BNF, and implementations in CSharp and FSharp.
Contributions to hron language specification, implementations in groovy and java.
Together with Mårten responsible for spawning the idea for hron. Contributions to language specification, invaluable feedback.