Repository: delph-in/pydelphin

Issue:
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).

Comments:

Patch:
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
 -----------------------
 


Test patch:
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(