Show HN: SAN – a Safe and Nice TOML/YAML Alternative
astrocorp.net
astrocorp.net
We've created a new file format designed specifically for configuration: SAN (pronounce /seɪn/ like sane).
You can find a Go parser here: https://github.com/phasersec/san-go
Vim syntax here: https://github.com/z0mbie42/vim-san
SAN was created because of a need to have a Simple And Neat configuration format (with comments, unlike JSON, easy to parse unlike YAML....).
The main killer features compared to YAML/TOML are the following:
* Comments as first class citizens which means programs can manipulate and modify files with comments without destroying them.
* Safe
* Human and parser friendly
* Easy to use, even without syntax coloration
It's an open format and any feedback is welcome.
>Comments as first class citizens which means programs can manipulate and modify files with comments without destroying them.
>Safe
>Human and parser friendly
>Easy to use
Those were also my goals with StrictYAML: http://hitchdev.com/strictyaml
So the main differences are the ecosystem: a CLI to auto format - think go fmt) and validate SAN files, parsers for languages others than Go
Bazel have too it's own (turing complete ?) Programming configuration language
I'm a little bummed I was beat to it. I was going back and forth on allowing defining functions. Definitely no classes.
+1 for quoting all strings
+1 for comments
-1 for the pronunciation hehe :P
I don't like that map values don't need commas while arrays do, and also don't know how I feel about the equal sign instead of colon.
Great attempt overall. Thank you.
I've opened an issue https://github.com/astrocorp42/san/issues/12
That was one issue I had with TOML, because the allowed data schema is not JSON compatible. Switching from YAML or JSON to TOML or SAN is therefore often difficult and as such easier to just stay with the one that is currently used.
For new projects, it might be nice. But having to work around this limitation when you need it is also a bit annoying.
In my side projects, generally I'll hand-write a parser with regular expressions because I haven't yet found one I actually like both looking at and editing. I can fix every single other problem except that one. With SAN, the issue is the bracket delimiters. I hate futzing with them, just let me use tabs. I want this:
Monthly Recurring
Patreon: 8 | 9/17/2018 | 2mo - Acc: Capital One - Tags: arts
Workflowy: 4.99 | 9/17/2018 | 6mo - Acc: Amex - Tags: tools
RubyTapas: 15 | 9/17/2018 | 6mo - Acc: Amex - Tags: education
Verizon: 70.17 | 9/17/2018 | 6mo - Acc: Amex - Tags: utilities,tools
This is easy to look at, easy to edit with a text editor, and, once the parser's written, dead simple with Ruby POROs, easy to parse into queryable data structures.I have yet to find any format that's intended to be human-editable that doesn't actually feel 'nice to the touch'. So I just make my own.
When hand-editing the format above, I generally don't type in a whole line at a time. I'll just add in the basics, and fill it out later, perhaps with machine processing. Tags in particular are easy to copy-paste in by hand and are generally the last part of the line.
I hate, hate, hate the ambiguity and the flavor of the week tokenization/operator precedence/does it trim whitespace questions.
This is JSON's biggest strength: it's trivially unambiguous.
The reason for brackets are auto formatting by the san CLI (think go/rust fmt)
If my configuration needs auto-formatting, something's gone very very wrong in the design of my application.
As for heterogenous list types, great reason to have them is when your list is logically the same type but physically isn't.
In some formats like Cargo.toml, the following are equivelant:
dep="1.8"
dep={version="1.8"}
This allows a short-hand for the common case. Being able to do this in lists would improve the file formats I've been making. includes:
- relative-include-file.yml
- url: git://stuff.git
file: git-included-file.yml
Not possible here.(Of course I would need to change the string to a map as well, but that is just clunky to write all the time.)
They can all be replaced by maps which may seems more verbose but carry in fact far more information and clear
So for me you get a shoe if you reverse your argument. Type limitations aren't improving write or readability IMO and just make it harder, because you have to think about using the right type. Application specific schema checkers should take care of the checking the type.
I would definitely change the supposed pronunciation though, no one is going to pronunce san as sane.
No, absolutely not.
I hope this gains traction, so that I could use it in my projects without people being upset about "some obscure configuration language".
Can I ask you why?
strs = [
"a",
"b",
"c"
]
If you don't have a trailing comma (looking at you, JSON), when you add a new item, you have to make changes on two lines: strs = [
"a",
"b",
"c", // Change 1.
"d" // Change 2.
]
On the other hand, when you have obligatory trailing comma on multiline lists, there is only one change: strs = [
"a",
"b",
"c", // No change, since the comma was already there.
"d", // Change 1.
]I will explore this.
The second point is acknowledged
To add my 2 cents. I really like what I saw in Skylark(Starlark?). It's fairly close to my dream setup: a simple Python interpreter with no libraries.
Pythons object notation looks 95% like JSON, it supports comments, solid multiline support, well known escaping rules, basic format strings when you need them.
A KVIN file follows one simple pattern: it's a list of key-values or comments. Comments are any line that starts with any amount of whitespace and then a '#'. A key-value starts with a key, ends with a value, and has a '=' in the middle:
key = value
A key is made up of any valid C-identifier or C-numeric expression. You can 'chain' keys together with '.'s: key ::= key [. axis] | axis
axis ::= c-identifier | c-number
A value is any valid c-identifier, c-number, or "byte string". A byte string starts with a '"', ends with a '"'. If you want to include a '"' then you have to escape with a '\'. If you want to include a '\', then you '\' the '\'. All whitespace is included in a byte-string: even a '\n'!Strangely, KVIN understands relative paths. If a key starts with '.' then that means it's relative to the path just above it. Each '.' means 'go up one level from the last value':
foo.bar = 0
.baz = 2
Would be a JSON like this: { "foo" : { "bar" : 0, "baz" : 2 } }
KVIN has one final trick, besides the busted old turbo he's been trying to get in his iRoc: he knows how to autonumber a list: foo.10 = a
.# = b
.# = c
.# = d
Which would be the JSON: { "foo" : [ ..., a, b, c, d ] }
With 'a' starting at index '10'. (The base index of arrays is 0.)I've got code laying around, somewhere, that implements KVIN.
I'm not sure I buy that it's a large improvement over TOML -- In practice I haven't found the homogeneous list requirement to be an issue, and I really appreciate toml's writing styles and the use of sections to clearly delineate.
I think TOML is still my favorite (especially in terms of readability) but SAN looks awesome
- a clean syntax and data model
- easy to parse and use
- friendly to human
- improving on JSON
SAM vs. Mark Notation - SAM is primarily for configuration data
- Mark can be used for both configuration and mixed content (like HTML, XML)
- SAM is pure data (no templating)
- Mark is also designed for templating, e.g. Mark TemplateTabs would be more sane for indentation: one tab, one indentation level. Also, it would match gofmt, if that matters to you.
I would never unleash a curly braces syntax on a non-developer. And if it's a format for developers then there are already plenty of options.
Meanwhile, with significant whitespace, non-developers have to learn to be aware that a syntactic element intended to mean nothing now suddenly means something, and that one type of whitespace means something different than another and that they can't be used together, which is counter-intuitive.
I think there's a touch of Stockholm syndrome in your belief otherwise. You have been corrupted and are projecting "intuitive" on to the pure. ;-)
Furthermore, how would you explain to a non developer that ``` a: b: nested ```
is different than: ``` a: b: non nestee ```
title: SAN Example
instead of title = "SAN Example"
and use dictionaries by default, that is creator
name: Sylvain Kerkour
website: https://kerkour.com
instead of creator = {
name = "Sylvain Kerkour"
website = "https://kerkour.com"
}
or something like that more or less...2. No multi-line support?
What about JSON5?
2) Awesome!
[0] https://github.com/hashicorp/hcl [1] https://xkcd.com/927/