diff --git a/CHANGELOG.md b/CHANGELOG.md
index b99690f..1163e8a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,11 @@
 
 ## Unreleased
 
+### Added
+
+* `delphin.tdl.LineComment` class ([#356])
+* `delphin.tdl.BlockComment` class ([#356])
+
 ### Fixed
 
 * `delphin.tdl.ConsList.values()` and `delphin.tdl.DiffList.values()`
@@ -9,6 +14,11 @@
   into their structures ([#357])
 * Implicit optional types are made explicit ([#360])
 
+### Changed
+
+* TDL parsing now models line and block comments ([#356])
+* TDL formatting now formats `LineComment` and `BlockComment` classes ([#356])
+
 
 ## [v1.7.0]
 
@@ -1566,5 +1576,6 @@ information about changes, except for
 [#343]: https://github.com/delph-in/pydelphin/issues/343
 [#344]: https://github.com/delph-in/pydelphin/issues/344
 [#352]: https://github.com/delph-in/pydelphin/issues/352
+[#356]: https://github.com/delph-in/pydelphin/issues/356
 [#357]: https://github.com/delph-in/pydelphin/issues/357
 [#360]: https://github.com/delph-in/pydelphin/issues/360
diff --git a/delphin/tdl.py b/delphin/tdl.py
index ec330f4..4bb0ae5 100644
--- a/delphin/tdl.py
+++ b/delphin/tdl.py
@@ -853,6 +853,14 @@ class FileInclude:
         self.path = Path(basedir, value).with_suffix('.tdl')
 
 
+class LineComment(str):
+    """Single-line comments in TDL."""
+
+
+class BlockComment(str):
+    """Multi-line comments in TDL."""
+
+
 # NOTE: be careful rearranging subpatterns in _tdl_lex_re; some must
 #       appear before others, e.g., """ before ", <! before <, etc.,
 #       to prevent short-circuiting from blocking the larger patterns
@@ -1055,9 +1063,9 @@ def _parse_tdl(tokens, path):
             except StopIteration:  # normal EOF
                 break
             if gid == 2:
-                yield ('BlockComment', token, line_no)
+                yield ('BlockComment', BlockComment(token), line_no)
             elif gid == 3:
-                yield ('LineComment', token, line_no)
+                yield ('LineComment', LineComment(token), line_no)
             elif gid == 20:
                 obj = _parse_letterset(token, line_no)
                 yield (obj.__class__.__name__, obj, line_no)
@@ -1371,6 +1379,10 @@ def format(obj, indent=0):
         return _format_environment(obj, indent)
     elif isinstance(obj, FileInclude):
         return _format_include(obj, indent)
+    elif isinstance(obj, LineComment):
+        return _format_linecomment(obj, indent)
+    elif isinstance(obj, BlockComment):
+        return _format_blockcomment(obj, indent)
     else:
         raise ValueError(f'cannot format object as TDL: {obj!r}')
 
@@ -1584,3 +1596,11 @@ def _format_environment(env, indent):
 
 def _format_include(fi, indent):
     return '{}:include "{}".'.format(' ' * indent, fi.value)
+
+
+def _format_linecomment(obj, indent):
+    return '{};{}'.format(' ' * indent, str(obj))
+
+
+def _format_blockcomment(obj, indent):
+    return '{}#|{}|#'.format(' ' * indent, str(obj))
diff --git a/docs/api/delphin.tdl.rst b/docs/api/delphin.tdl.rst
index 2d45274..f07b863 100644
--- a/docs/api/delphin.tdl.rst
+++ b/docs/api/delphin.tdl.rst
@@ -166,6 +166,16 @@ Environments and File Inclusion
    :members:
 
 
+Comments
+''''''''
+
+.. autoclass:: LineComment
+   :members:
+
+.. autoclass:: BlockComment
+   :members:
+
+
 Exceptions and Warnings
 -----------------------
 

diff --git a/tests/tdl_test.py b/tests/tdl_test.py
index 5cae05c..968691d 100644
--- a/tests/tdl_test.py
+++ b/tests/tdl_test.py
@@ -24,6 +24,8 @@ from delphin.tdl import (
     TypeEnvironment,
     InstanceEnvironment,
     FileInclude,
+    LineComment,
+    BlockComment,
     TDLError,
     TDLSyntaxError,
     TDLWarning)
@@ -721,11 +723,13 @@ def test_parse_wildcard():
 def test_parse_linecomment():
     lc = tdlparse('; this is a comment\n')
     assert lc == ' this is a comment'
+    assert isinstance(lc, LineComment)
 
 
 def test_parse_blockcomment():
     bc = tdlparse('#| this is a comment\n   on multiple lines|#')
     assert bc == ' this is a comment\n   on multiple lines'
+    assert isinstance(bc, BlockComment)
 
 
 def test_parse_environments():
@@ -962,6 +966,24 @@ def test_format_environments():
         ':end :type.')
 
 
+def test_format_fileinclude():
+    assert tdl.format(FileInclude('foo.tdl')) == ':include "foo.tdl".'
+
+
+def test_format_linecomment():
+    assert tdl.format(LineComment(' a comment')) == '; a comment'
+    assert tdl.format(LineComment('; two semicolons')) == ';; two semicolons'
+
+
+def test_format_blockcomment():
+    assert tdl.format(
+        BlockComment(' a block comment ')
+    ) == '#| a block comment |#'
+    assert tdl.format(
+        BlockComment('\n  one\n  two\n')
+    ) == '#|\n  one\n  two\n|#'
+
+
 def test_issue_357():
     # https://github.com/delph-in/pydelphin/issues/357
     t = TypeDefinition(

format function for LineComment and BlockComment
If I understand right, the `format` method is not currently implemented for all Events, in particular it is not implemented for LineComment and BlockComment? This would be useful in scenarios when someone is automatically updating part of a large TDL file and wants to copy large parts of it without modifying them (while modifying other parts).
tests/tdl_test.py::test_Term tests/tdl_test.py::test_TypeIdentifier tests/tdl_test.py::test_String tests/tdl_test.py::test_Regex tests/tdl_test.py::test_AVM tests/tdl_test.py::test_ConsList tests/tdl_test.py::test_DiffList tests/tdl_test.py::test_Coreference tests/tdl_test.py::TestConjunction::test_init tests/tdl_test.py::TestConjunction::test_and tests/tdl_test.py::TestConjunction::test_eq tests/tdl_test.py::TestConjunction::test__contains__ tests/tdl_test.py::TestConjunction::test__getitem__ tests/tdl_test.py::TestConjunction::test__setitem__ tests/tdl_test.py::TestConjunction::test__setitem__issue293 tests/tdl_test.py::TestConjunction::test__delitem__ tests/tdl_test.py::TestConjunction::test_get tests/tdl_test.py::TestConjunction::test_normalize tests/tdl_test.py::TestConjunction::test_terms tests/tdl_test.py::TestConjunction::test_add tests/tdl_test.py::TestConjunction::test_types tests/tdl_test.py::TestConjunction::test_features tests/tdl_test.py::TestConjunction::test_string tests/tdl_test.py::test_TypeDefinition tests/tdl_test.py::test_TypeAddendum tests/tdl_test.py::test_LexicalRuleDefinition tests/tdl_test.py::test_parse_identifiers tests/tdl_test.py::test_parse_supertypes tests/tdl_test.py::test_parse_no_features tests/tdl_test.py::test_parse_string_features tests/tdl_test.py::test_quoted_symbol tests/tdl_test.py::test_parse_type_features tests/tdl_test.py::test_parse_cons_list tests/tdl_test.py::test_issue_294 tests/tdl_test.py::test_parse_diff_list tests/tdl_test.py::test_parse_multiple_features tests/tdl_test.py::test_parse_multiple_avms tests/tdl_test.py::test_parse_feature_path tests/tdl_test.py::test_parse_coreferences tests/tdl_test.py::test_parse_typedef tests/tdl_test.py::test_parse_typeaddendum tests/tdl_test.py::test_parse_lexicalruledefinition tests/tdl_test.py::test_parse_docstrings tests/tdl_test.py::test_parse_letterset tests/tdl_test.py::test_parse_wildcard tests/tdl_test.py::test_parse_linecomment tests/tdl_test.py::test_parse_blockcomment tests/tdl_test.py::test_parse_environments tests/tdl_test.py::test_format_TypeTerms tests/tdl_test.py::test_format_AVM tests/tdl_test.py::test_format_lists tests/tdl_test.py::test_format_docstring_terms tests/tdl_test.py::test_format_Conjunction tests/tdl_test.py::test_format_typedefs tests/tdl_test.py::test_format_morphsets tests/tdl_test.py::test_format_environments tests/tdl_test.py::test_format_fileinclude tests/tdl_test.py::test_format_linecomment tests/tdl_test.py::test_format_blockcomment tests/tdl_test.py::test_issue_357