Create unit tests for our new Nix pretty-printer.

Let's start with the unit tests and a small dummy implementation. These
tests are pretty basic and don't contain a lot of nested expressions, so
the tricky tests are coming later.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
nix_expr: Implement basic pretty printer.

This pretty printer now can handle everything but attrsets and it's
implemented in two phases: The first phase is producing all the syntax
we need and the second one only does indentation.

One major reason to do it like that is that we want to use multiline
strings whenever they're suitable (such as when the maximum line width
is exceeded) and avoid them if for example we just have a few \ns and
the list would fit inside the corresponding line.

Atom and Container are solely used for indentation and the reason
they're classes is because Python doesn't have ADT (or static typing at
all) and we want to build a nice tree of the indentation structure.

Basically, the structure of the tree is comprised of Container (which
acts more or less as a node, but with prefix/suffix and alternative
inline representation) and Atom, which is a single leaf of our tree.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
nix_expr: Implement encoding of attribute sets.

This implementation currently is very basic and doesn't handle nested
dictionaries/attrsets very well.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
nix_expr: Don't indent inline suffix.

We don't want to indent the suffix when the whole expression is to be
inlined completely.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
test_nix_pprint: Document and set maxwidth's to 0.

This will ensure that the pretty printer will break on every possible
occasion. It is not secessarily needed, but it makes the tests more
consistent.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
nix_expr: Properly propagate maxwidth keyword.

Nested inlines didn't get the specified maxwidth keyword and therefore
have been inline even when it exceeded the maxwidth value.

This and a dependant test is now fixed.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
nix_expr: Indent the prefix of Container properly.

Whoops, we didn't do any indentation of the container prefix. This only
showed up in the tests for nested lists/attributes, because that's where
we actually have an indentation of more than zero levels.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
nix_expr: Add indentation when hecking maxwidth.

This also only shows up when using nested containers, so we need to
check the lines content _and_ indentation for every container.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
test_nix_pprint: Add test for nested lists.

Of course, this doesn't test something like [[ ... ]] yet, as we haven't
implemented this feature yet. But it's coming soon :-)

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
nix_expr: Sort attributes based on their keys.

It's critical not only for the tests that we always get the same output
for the same input. For generating the physical spec this is even more
important, because we want to avoid rebuilding the system configurations
every time a dict want's to be ordered differently than the time before.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
nix_expr: Use basestring for checking string type.

We already used it in _enc_attrset() but forgot to do so in the _enc()
method as well.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
nix_expr: Rename and expose Atom as RawValue.

This makes it possible to use arbitrary values for the pretty printer,
which is especially useful for defining functions or importing Nix
expressions from another file.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
nix_expr: Merge attrsets with only one key.

If you have something like:

{'a': {'b': {'c': 666}}}

This will become "a.b.c = 666".

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
test_nix_pprint: Add test for nested attrsets.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
nix_expr: Quote attr if it starts with a digit.

We need to use { "1a" = ...; } instead of { 1a = ...; } because
otherwise evaluation would fail here.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
nix_expr: Merge nested singleton lists.

Use [ [ [ 1 ] ] ] instead of adding two extra newlines (one for the
starting bracket, one for the ending bracket) for every list.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
test_nix_pprint: Rename to test_nix_expr.py.

Initially this was only about pretty printing and before I did the first
commit, I actually called the main implementation nix_pprint.py. But
in the end I decided to go for nix_expr.py but didn't rename the test
case.

This now renames the test module and class name appropriately.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
test_nix_pprint: Add and imlement assert_nix().

This method first of all shortens the line length and the main purpose
is to print a multi-line error if the assertion doesn't hold. That way
it's easier to spot what went wrong.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
nix_expr: Add a function to merge node constants.

This adds the enclose_node() function, which is used to merge a prefix
and/or suffix into another node while being agnostic on whether it is a
RawValue or a Container. For the latter we need to also take care of
properly merging the value into the inline_variant of the correspending
Container.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
nix_expr: Support pretty-printing functions.

This adds a new container instance called Function, which takes the
function head as a string value (we don't allow any native Python types
here, just a raw string value) and the body as an arbitrarily nested
Python data type. That should make it possible to pretty-print full
machine configurations.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>