diff --git a/README.md b/README.md
index 4b9d7d5..2c040fb 100644
--- a/README.md
+++ b/README.md
@@ -82,6 +82,7 @@ All defined s-expressions are listed here, though this specification will be exp
     - `predicate` must be a `lambda` with one argument
   - Any: `(Any <source> <predicate>)`
     - `predicate` must be a `lambda` with one argument
+  - Concat: `(Concat <first> <second>)`
   - Zip: `(Zip <source>)`
   - OrderBy: `(OrderBy <source> <key_selector>)`
     - `key_selector` must be a `lambda` with one argument
diff --git a/qastle/linq_util.py b/qastle/linq_util.py
index f04fcbe..bb0e2ff 100644
--- a/qastle/linq_util.py
+++ b/qastle/linq_util.py
@@ -55,6 +55,10 @@ class Any(ast.AST):
     _fields = ['source', 'predicate']
 
 
+class Concat(ast.AST):
+    _fields = ['first', 'second']
+
+
 class Zip(ast.AST):
     _fields = ['source']
 
@@ -84,6 +88,7 @@ linq_operator_names = ('Where',
                        'Sum',
                        'All',
                        'Any',
+                       'Concat',
                        'Zip',
                        'OrderBy',
                        'OrderByDescending',
@@ -193,6 +198,10 @@ class InsertLINQNodesTransformer(ast.NodeTransformer):
                 raise SyntaxError('Any() call argument must be a lambda')
             return Any(source=self.visit(source),
                        predicate=self.visit(args[0]))
+        elif function_name == 'Concat':
+            if len(args) != 1:
+                raise SyntaxError('Concat() call must have exactly one argument')
+            return Concat(first=self.visit(source), second=self.visit(args[0]))
         elif function_name == 'Zip':
             if len(args) != 0:
                 raise SyntaxError('Zip() call must have zero arguments')
diff --git a/qastle/transform.py b/qastle/transform.py
index f109686..f1c2629 100644
--- a/qastle/transform.py
+++ b/qastle/transform.py
@@ -1,5 +1,5 @@
 from .linq_util import (Where, Select, SelectMany, First, Last, ElementAt, Aggregate, Count, Max,
-                        Min, Sum, All, Any, Zip, OrderBy, OrderByDescending, Choose)
+                        Min, Sum, All, Any, Concat, Zip, OrderBy, OrderByDescending, Choose)
 from .ast_util import wrap_ast, unwrap_ast
 
 import lark
@@ -219,6 +219,11 @@ class PythonASTToTextASTTransformer(ast.NodeVisitor):
                                                self.visit(node.source),
                                                self.visit(node.predicate))
 
+    def visit_Concat(self, node):
+        return self.make_composite_node_string('Concat',
+                                               self.visit(node.first),
+                                               self.visit(node.second))
+
     def visit_Zip(self, node):
         return self.make_composite_node_string('Zip', self.visit(node.source))
 
@@ -499,6 +504,11 @@ class TextASTToPythonASTTransformer(lark.Transformer):
                                   + str(len(fields[1].args.args)))
             return Any(source=fields[0], predicate=fields[1])
 
+        elif node_type == 'Concat':
+            if len(fields) != 2:
+                raise SyntaxError('Concat node must have two fields; found ' + str(len(fields)))
+            return Concat(first=fields[0], second=fields[1])
+
         elif node_type == 'Zip':
             if len(fields) != 1:
                 raise SyntaxError('Zip node must have one field; found ' + str(len(fields)))

diff --git a/tests/test_ast_language.py b/tests/test_ast_language.py
index 5c340e8..1597b55 100644
--- a/tests/test_ast_language.py
+++ b/tests/test_ast_language.py
@@ -240,6 +240,13 @@ def test_Any():
                                               '(Any data_source (lambda (list e) e))')
 
 
+def test_Concat():
+    first_node = Concat(first=unwrap_ast(ast.parse('sequence1')),
+                        second=unwrap_ast(ast.parse('sequence2')))
+    assert_equivalent_python_ast_and_text_ast(wrap_ast(first_node),
+                                              '(Concat sequence1 sequence2)')
+
+
 def test_Zip():
     first_node = Zip(source=unwrap_ast(ast.parse('data_source')))
     assert_equivalent_python_ast_and_text_ast(wrap_ast(first_node),
diff --git a/tests/test_linq_util.py b/tests/test_linq_util.py
index 65b3893..cef83c9 100644
--- a/tests/test_linq_util.py
+++ b/tests/test_linq_util.py
@@ -341,6 +341,27 @@ def test_any_bad():
         insert_linq_nodes(ast.parse('the_source.Any(None)'))
 
 
+def test_concat():
+    initial_ast = ast.parse("sequence1.Concat(sequence2)")
+    final_ast = insert_linq_nodes(initial_ast)
+    expected_ast = wrap_ast(Concat(first=unwrap_ast(ast.parse('sequence1')),
+                                   second=unwrap_ast(ast.parse('sequence2'))))
+    assert_ast_nodes_are_equal(final_ast, expected_ast)
+
+
+def test_concat_composite():
+    initial_ast = ast.parse("the_source.First().Concat(sequence)")
+    final_ast = insert_linq_nodes(initial_ast)
+    expected_ast = wrap_ast(Concat(first=First(source=unwrap_ast(ast.parse('the_source'))),
+                                   second=unwrap_ast(ast.parse('sequence'))))
+    assert_ast_nodes_are_equal(final_ast, expected_ast)
+
+
+def test_concat_bad():
+    with pytest.raises(SyntaxError):
+        insert_linq_nodes(ast.parse('the_source.Concat()'))
+
+
 def test_zip():
     initial_ast = ast.parse("the_source.Zip()")
     final_ast = insert_linq_nodes(initial_ast)

`Concat` node type
As in <https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.concat?view=net-5.0>.
tests/test_ast_language.py::test_Concat tests/test_linq_util.py::test_concat tests/test_linq_util.py::test_concat_composite tests/test_linq_util.py::test_concat_bad
tests/test_ast_language.py::test_empty tests/test_ast_language.py::test_whitespace tests/test_ast_language.py::test_identifiers tests/test_ast_language.py::test_string_literals tests/test_ast_language.py::test_numeric_literals tests/test_ast_language.py::test_list tests/test_ast_language.py::test_tuple tests/test_ast_language.py::test_dict tests/test_ast_language.py::test_attr tests/test_ast_language.py::test_subscript tests/test_ast_language.py::test_call tests/test_ast_language.py::test_if tests/test_ast_language.py::test_unary_operators tests/test_ast_language.py::test_binary_operators tests/test_ast_language.py::test_boolean_operators tests/test_ast_language.py::test_comparison_operators tests/test_ast_language.py::test_lambda tests/test_ast_language.py::test_Where tests/test_ast_language.py::test_Select tests/test_ast_language.py::test_SelectMany tests/test_ast_language.py::test_First tests/test_ast_language.py::test_Last tests/test_ast_language.py::test_ElementAt tests/test_ast_language.py::test_Aggregate tests/test_ast_language.py::test_Count tests/test_ast_language.py::test_Max tests/test_ast_language.py::test_Min tests/test_ast_language.py::test_Sum tests/test_ast_language.py::test_All tests/test_ast_language.py::test_Any tests/test_ast_language.py::test_Zip tests/test_ast_language.py::test_OrderBy tests/test_ast_language.py::test_OrderByDescending tests/test_ast_language.py::test_Choose tests/test_linq_util.py::test_visit_call_attribute_generic tests/test_linq_util.py::test_visit_call_attribute_linq_operator tests/test_linq_util.py::test_visit_call_name_generic tests/test_linq_util.py::test_visit_call_name_linq_operator tests/test_linq_util.py::test_visit_call_name_linq_operator_no_source tests/test_linq_util.py::test_visit_call_generic tests/test_linq_util.py::test_where tests/test_linq_util.py::test_where_composite tests/test_linq_util.py::test_where_bad tests/test_linq_util.py::test_select tests/test_linq_util.py::test_select_composite tests/test_linq_util.py::test_select_bad tests/test_linq_util.py::test_select_copy tests/test_linq_util.py::test_select_deepcopy tests/test_linq_util.py::test_selectmany tests/test_linq_util.py::test_selectmany_composite tests/test_linq_util.py::test_selectmany_bad tests/test_linq_util.py::test_first tests/test_linq_util.py::test_first_composite tests/test_linq_util.py::test_first_bad tests/test_linq_util.py::test_last tests/test_linq_util.py::test_last_composite tests/test_linq_util.py::test_last_bad tests/test_linq_util.py::test_elementat tests/test_linq_util.py::test_elementat_composite tests/test_linq_util.py::test_elementat_bad tests/test_linq_util.py::test_aggregate tests/test_linq_util.py::test_aggregate_composite tests/test_linq_util.py::test_aggregate_bad tests/test_linq_util.py::test_count tests/test_linq_util.py::test_count_composite tests/test_linq_util.py::test_count_bad tests/test_linq_util.py::test_max tests/test_linq_util.py::test_max_composite tests/test_linq_util.py::test_max_bad tests/test_linq_util.py::test_min tests/test_linq_util.py::test_min_composite tests/test_linq_util.py::test_min_bad tests/test_linq_util.py::test_sum tests/test_linq_util.py::test_sum_composite tests/test_linq_util.py::test_sum_bad tests/test_linq_util.py::test_all tests/test_linq_util.py::test_all_composite tests/test_linq_util.py::test_all_bad tests/test_linq_util.py::test_any tests/test_linq_util.py::test_any_composite tests/test_linq_util.py::test_any_bad tests/test_linq_util.py::test_zip tests/test_linq_util.py::test_zip_composite tests/test_linq_util.py::test_zip_bad tests/test_linq_util.py::test_orderby tests/test_linq_util.py::test_orderby_composite tests/test_linq_util.py::test_orderby_bad tests/test_linq_util.py::test_orderbydescending tests/test_linq_util.py::test_orderbydescending_composite tests/test_linq_util.py::test_orderbydescending_bad tests/test_linq_util.py::test_choose tests/test_linq_util.py::test_choose_composite tests/test_linq_util.py::test_choose_bad