Add dump and dumps.
diff --git a/README.md b/README.md
index e1a794d..b0d6a8c 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,7 @@
 # pytoml
 
-This project aims at being a specs-conforming and strict parser for [TOML][1] files.
-The parser currently supports [version 0.4.0][2] of the specs.
-
-The project supports Python 2.7 and 3.4+.
+This project aims at being a specs-conforming and strict parser and writer for [TOML][1] files.
+The library currently supports [version 0.4.0][2] of the specs and runs with Python 2.7 and 3.4+.
 
 Install:
 
@@ -15,12 +13,18 @@
     >>> toml.loads('a = 1')
     {'a': 1}
     >>> with open('file.toml', 'rb') as fin:
-    ...     toml.load(fin)
+    ...     obj = toml.load(fin)
+    >>> obj
     {'a': 1}
 
 The `loads` function accepts either a bytes object
 (that gets decoded as UTF-8 with no BOM allowed),
 or a unicode object.
 
+Use `dump` or `dumps` to serialize a dict into TOML.
+
+    >>> print toml.dumps(obj)
+    a = 1
+
   [1]: https://github.com/toml-lang/toml
   [2]: https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md
diff --git a/pytoml/__init__.py b/pytoml/__init__.py
index e20c2dd..8dc7315 100644
--- a/pytoml/__init__.py
+++ b/pytoml/__init__.py
@@ -1 +1,3 @@
-from .parser import TomlError, load, loads
+from .core import TomlError
+from .parser import load, loads
+from .writer import dump, dumps
diff --git a/pytoml/writer.py b/pytoml/writer.py
new file mode 100644
index 0000000..8fcbd61
--- /dev/null
+++ b/pytoml/writer.py
@@ -0,0 +1,99 @@
+from __future__ import unicode_literals
+import io, datetime, sys
+
+if sys.version_info[0] == 3:
+    long = int
+    unicode = str
+
+def dumps(obj):
+    fout = io.StringIO()
+    dump(fout, obj)
+    return fout.getvalue()
+
+_escapes = { '\n': 'n', '\r': 'r', '\\': '\\', '\t': 't', '\b': 'b', '\f': 'f', '"': '"' }
+
+def _escape_string(s):
+    res = []
+    start = 0
+    def flush():
+        if start != i:
+            res.append(s[start:i])
+        return i + 1
+
+    i = 0
+    while i < len(s):
+        c = s[i]
+        if c in '"\\\n\r\t\b\f':
+            start = flush()
+            res.append('\\' + _escapes[c])
+        elif ord(c) < 0x20:
+            start = flush()
+            res.append('\\u%04x' % ord(c))
+        i += 1
+
+    flush()
+    return '"' + ''.join(res) + '"'
+
+def _escape_id(s):
+    if any(not c.isalnum() and c not in '-_' for c in s):
+        return _escape_string(s)
+    return s
+
+def _format_list(v):
+    return '[{}]'.format(', '.join(_format_value(obj) for obj in v))
+
+def _format_value(v):
+    if isinstance(v, bool):
+        return 'true' if v else 'false'
+    if isinstance(v, int) or isinstance(v, long):
+        return unicode(v)
+    if isinstance(v, float):
+        return '{:.17f}'.format(v)
+    elif isinstance(v, unicode) or isinstance(v, bytes):
+        return _escape_string(v)
+    elif isinstance(v, datetime.datetime):
+        offs = v.utcoffset()
+        offs = offs.total_seconds() // 60 if offs is not None else 0
+
+        if offs == 0:
+            suffix = 'Z'
+        else:
+            if offs > 0:
+                suffix = '+'
+            else:
+                suffix = '-'
+                offs = -offs
+            suffix = '{}{:.02}{:.02}'.format(suffix, offs // 60, offs % 60)
+
+        if v.microsecond:
+            return v.strftime('%Y-%m-%dT%H:%M:%S.%f') + suffix
+        else:
+            return v.strftime('%Y-%m-%dT%H:%M:%S') + suffix
+    elif isinstance(v, list):
+        return _format_list(v)
+    else:
+        raise RuntimeError('XXX')
+
+def dump(fout, obj):
+    tables = [((), obj, False)]
+
+    while tables:
+        name, table, is_array = tables.pop()
+        if name:
+            section_name = '.'.join(_escape_id(c) for c in name)
+            if is_array:
+                fout.write('[[{}]]\n'.format(section_name))
+            else:
+                fout.write('[{}]\n'.format(section_name))
+
+        for k in table:
+            v = table[k]
+            if isinstance(v, dict):
+                tables.append((name + (k,), v, False))
+            elif isinstance(v, list) and v and all(isinstance(o, dict) for o in v):
+                tables.extend((name + (k,), d, True) for d in reversed(v))
+            else:
+                fout.write('{} = {}\n'.format(_escape_id(k), _format_value(v)))
+
+        if tables:
+            fout.write('\n')
diff --git a/setup.py b/setup.py
index fe23056..f755ef9 100644
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@
 
 setup(
     name='pytoml',
-    version='0.1.1',
+    version='0.1.2',
 
     description='A parser for TOML-0.4.0',
     author='Martin Vejnár',
diff --git a/test/test.py b/test/test.py
index 79f4756..fd3af75 100644
--- a/test/test.py
+++ b/test/test.py
@@ -19,9 +19,18 @@
 
             try:
                 with open(os.path.join(top, fname), 'rb') as fin:
-                    parsed = toml.load(fin, _testbench_literal, _testbench_array)
+                    parsed = toml.load(fin)
             except toml.TomlError:
                 parsed = None
+            else:
+                dumped = toml.dumps(parsed)
+                parsed2 = toml.loads(dumped)
+                if parsed != parsed2:
+                    failed.append(fname)
+                    continue
+
+                with open(os.path.join(top, fname), 'rb') as fin:
+                    parsed = toml.load(fin, _testbench_literal, _testbench_array)
 
             try:
                 with io.open(os.path.join(top, fname[:-5] + '.json'), 'rt', encoding='utf-8') as fin: