From e29f6e5997be448adfd077a02469c21a1ebe5c23 Mon Sep 17 00:00:00 2001
From: Copybara <copybara@example.com>
Date: Mon, 6 May 2024 15:22:05 -0400
Subject: [PATCH] Project import generated by Copybara.

GitOrigin-RevId: b8bebf065c6b2212c17d7d4dbfb8e5427b9e96d7
---
 .github/CODEOWNERS                            |    3 +
 .github/workflows/test.yaml                   |   24 +
 README.md                                     |   99 +
 fhirpath/.gitignore                           |    4 +
 fhirpath/compopts/compopts.go                 |   55 +
 fhirpath/doc.go                               |    9 +
 fhirpath/evalopts/evalopts.go                 |   74 +
 fhirpath/fhirpath.go                          |  118 +
 fhirpath/fhirpath_expression_test.go          |  292 ++
 fhirpath/fhirpath_test.go                     | 1413 ++++++
 fhirpath/fhirpathtest/fhirpathtest.go         |   51 +
 .../fhirpathtest/fhirpathtest_example_test.go |   47 +
 fhirpath/fhirpathtest/fhirpathtest_test.go    |   61 +
 fhirpath/internal/compile/compile.go          |   46 +
 fhirpath/internal/compile/doc.go              |    5 +
 fhirpath/internal/expr/arithmetic.go          |  197 +
 fhirpath/internal/expr/booleans.go            |   47 +
 fhirpath/internal/expr/context.go             |   47 +
 fhirpath/internal/expr/doc.go                 |    5 +
 fhirpath/internal/expr/expressions.go         |  779 ++++
 fhirpath/internal/expr/expressions_test.go    | 1465 ++++++
 fhirpath/internal/expr/exprtest/doc.go        |    5 +
 fhirpath/internal/expr/exprtest/doubles.go    |   37 +
 fhirpath/internal/expr/operators.go           |   29 +
 fhirpath/internal/funcs/doc.go                |    6 +
 fhirpath/internal/funcs/function.go           |   94 +
 fhirpath/internal/funcs/function_table.go     |   23 +
 .../internal/funcs/function_table_test.go     |   49 +
 fhirpath/internal/funcs/function_test.go      |  187 +
 fhirpath/internal/funcs/impl/conversion.go    |  646 +++
 .../internal/funcs/impl/conversion_test.go    | 2112 +++++++++
 fhirpath/internal/funcs/impl/errors.go        |    9 +
 fhirpath/internal/funcs/impl/existence.go     |  106 +
 .../internal/funcs/impl/existence_test.go     |  494 ++
 fhirpath/internal/funcs/impl/filtering.go     |   35 +
 .../internal/funcs/impl/filtering_test.go     |  114 +
 fhirpath/internal/funcs/impl/math.go          |  367 ++
 fhirpath/internal/funcs/impl/math_test.go     | 1059 +++++
 fhirpath/internal/funcs/impl/not.go           |   25 +
 fhirpath/internal/funcs/impl/not_test.go      |   54 +
 fhirpath/internal/funcs/impl/r4.go            |   42 +
 fhirpath/internal/funcs/impl/r4_test.go       |  120 +
 fhirpath/internal/funcs/impl/strings.go       |  435 ++
 fhirpath/internal/funcs/impl/strings_test.go  | 1148 +++++
 fhirpath/internal/funcs/impl/subsetting.go    |  238 +
 .../internal/funcs/impl/subsetting_test.go    |  677 +++
 fhirpath/internal/funcs/impl/utility.go       |   24 +
 fhirpath/internal/funcs/impl/utility_test.go  |   50 +
 fhirpath/internal/funcs/table.go              |  392 ++
 fhirpath/internal/grammar/fhirpath.g4         |  179 +
 fhirpath/internal/grammar/fhirpath_lexer.go   |  410 ++
 fhirpath/internal/grammar/fhirpath_parser.go  | 4109 +++++++++++++++++
 fhirpath/internal/grammar/fhirpath_visitor.go |  135 +
 fhirpath/internal/grammar/generate.go         |    3 +
 fhirpath/internal/grammar/generate.sh         |   12 +
 fhirpath/internal/opts/opts.go                |   66 +
 fhirpath/internal/parser/doc.go               |    6 +
 fhirpath/internal/parser/error_handling.go    |   22 +
 fhirpath/internal/parser/transforms.go        |   12 +
 fhirpath/internal/parser/visitor.go           |  515 +++
 fhirpath/internal/reflection/consts.go        |    7 +
 fhirpath/internal/reflection/doc.go           |    5 +
 fhirpath/internal/reflection/elements.go      |   38 +
 .../internal/reflection/type_specifier.go     |  138 +
 .../reflection/type_specifier_test.go         |  308 ++
 fhirpath/options.go                           |   32 +
 fhirpath/patch/doc.go                         |    7 +
 fhirpath/patch/patch.go                       |  597 +++
 fhirpath/patch/patch_test.go                  |  824 ++++
 fhirpath/system/cmp.go                        |   81 +
 fhirpath/system/collection.go                 |  239 +
 fhirpath/system/collection_test.go            |  320 ++
 fhirpath/system/consts.go                     |   26 +
 fhirpath/system/date.go                       |  269 ++
 fhirpath/system/date_test.go                  |  437 ++
 fhirpath/system/date_time.go                  |  289 ++
 fhirpath/system/date_time_test.go             |  474 ++
 fhirpath/system/doc.go                        |    8 +
 fhirpath/system/layouts.go                    |   86 +
 fhirpath/system/primitives.go                 |  303 ++
 fhirpath/system/primitives_test.go            |  402 ++
 fhirpath/system/quantity.go                   |  211 +
 fhirpath/system/quantity_test.go              |  117 +
 fhirpath/system/time.go                       |  192 +
 fhirpath/system/time_test.go                  |  383 ++
 fhirpath/system/types.go                      |  166 +
 fhirpath/system/types_test.go                 |  281 ++
 go.mod                                        |   28 +
 go.sum                                        |  715 +++
 gotchas.md                                    |   25 +
 internal/bundle/bundle.go                     |   79 +
 internal/bundle/bundle_entry.go               |  285 ++
 internal/bundle/bundle_entry_example_test.go  |   94 +
 internal/bundle/bundle_entry_test.go          |  477 ++
 internal/bundle/bundle_option.go              |   51 +
 internal/bundle/bundle_test.go                |  216 +
 internal/bundle/identity.go                   |   27 +
 internal/bundle/identity_test.go              |   48 +
 internal/bundleopt/bundleopt.go               |   25 +
 .../containedresource/contained_resource.go   |  186 +
 .../contained_resource_example_test.go        |   88 +
 .../contained_resource_test.go                |  291 ++
 internal/element/canonical/canonical.go       |  151 +
 internal/element/canonical/canonical_test.go  |  221 +
 .../codeableconcept/codeableconcept.go        |   17 +
 internal/element/coding/coding.go             |   20 +
 internal/element/element.go                   |    6 +
 internal/element/etag/etag.go                 |   25 +
 internal/element/etag/etag_test.go            |   58 +
 internal/element/extension/extension.go       |  232 +
 .../extension/extension_example_test.go       |   42 +
 internal/element/extension/extension_test.go  |  446 ++
 internal/element/extract.go                   |  208 +
 internal/element/extract_test.go              |  315 ++
 internal/element/identifier/docs.go           |    7 +
 internal/element/identifier/identifier.go     |  174 +
 .../identifier/identifier_example_test.go     |   52 +
 .../element/identifier/identifier_test.go     |  329 ++
 internal/element/identifier/opts.go           |  103 +
 internal/element/meta/meta.go                 |   95 +
 internal/element/meta/meta_example_test.go    |   25 +
 internal/element/meta/meta_test.go            |  233 +
 internal/element/reference/identity.go        |  131 +
 internal/element/reference/identity_test.go   |  306 ++
 internal/element/reference/literal.go         |  346 ++
 internal/element/reference/literal_test.go    |  361 ++
 internal/element/reference/reference.go       |  184 +
 internal/element/reference/reference_test.go  |  455 ++
 internal/fhir/constraints.go                  |  129 +
 internal/fhir/doc.go                          |   13 +
 internal/fhir/duration.go                     |  110 +
 internal/fhir/duration_test.go                |  161 +
 internal/fhir/elements_general.go             |  307 ++
 internal/fhir/elements_general_test.go        |   45 +
 internal/fhir/elements_metadata.go            |   19 +
 internal/fhir/elements_primitive.go           |  343 ++
 internal/fhir/elements_primitive_test.go      |  390 ++
 internal/fhir/elements_special.go             |   26 +
 internal/fhir/elements_special_test.go        |   28 +
 internal/fhir/encoding.go                     |   26 +
 internal/fhir/encoding_test.go                |   33 +
 internal/fhir/iface.go                        |  146 +
 internal/fhir/iface_test.go                   |  483 ++
 internal/fhir/protofields.go                  |   13 +
 internal/fhir/time.go                         |  358 ++
 internal/fhir/time_test.go                    |  352 ++
 internal/fhirconv/doc.go                      |    5 +
 internal/fhirconv/integer.go                  |  112 +
 internal/fhirconv/integer_test.go             |  182 +
 internal/fhirconv/string.go                   |  158 +
 internal/fhirconv/string_test.go              |  358 ++
 internal/fhirconv/time.go                     |  138 +
 internal/fhirconv/time_test.go                |  290 ++
 internal/fhirtest/doc.go                      |   12 +
 internal/fhirtest/elements.go                 |   46 +
 internal/fhirtest/meta.go                     |   65 +
 internal/fhirtest/random.go                   |   69 +
 internal/fhirtest/resources.go                |  298 ++
 internal/fhirtest/resources_example_test.go   |   46 +
 internal/fhirtest/resources_test.go           |  326 ++
 internal/narrow/narrow.go                     |  176 +
 internal/narrow/narrow_example_test.go        |  268 ++
 internal/narrow/narrow_test.go                |  703 +++
 internal/protofields/descriptor.go            |   12 +
 internal/protofields/dummies.go               |  364 ++
 internal/protofields/fields.go                |  212 +
 internal/protofields/fields_test.go           |   61 +
 internal/protofields/strcase.go               |   22 +
 internal/protofields/update.go                |   61 +
 internal/protofields/update_test.go           |  138 +
 internal/resource/canonical_identity.go       |   58 +
 internal/resource/canonical_identity_test.go  |   88 +
 internal/resource/consts.go                   |  441 ++
 internal/resource/identity.go                 |  187 +
 internal/resource/identity_test.go            |  137 +
 internal/resource/options.go                  |   31 +
 internal/resource/options_test.go             |   82 +
 internal/resource/patient/patient.go          |  172 +
 internal/resource/patient/patient_test.go     |  172 +
 internal/resource/resource.go                 |  244 +
 internal/resource/resource_example_test.go    |   32 +
 internal/resource/resource_test.go            |  390 ++
 internal/resource/type.go                     |   86 +
 internal/resource/type_test.go                |  127 +
 internal/resourceopt/resourceopt.go           |   83 +
 internal/slices/slices.go                     |  219 +
 internal/slices/slices_example_test.go        |  220 +
 internal/slices/slices_test.go                |  707 +++
 internal/stablerand/doc.go                    |   18 +
 internal/stablerand/rand.go                   |  120 +
 internal/units/doc.go                         |    5 +
 internal/units/time.go                        |   88 +
 192 files changed, 43115 insertions(+)
 create mode 100644 .github/CODEOWNERS
 create mode 100644 .github/workflows/test.yaml
 create mode 100644 README.md
 create mode 100644 fhirpath/.gitignore
 create mode 100644 fhirpath/compopts/compopts.go
 create mode 100644 fhirpath/doc.go
 create mode 100644 fhirpath/evalopts/evalopts.go
 create mode 100644 fhirpath/fhirpath.go
 create mode 100644 fhirpath/fhirpath_expression_test.go
 create mode 100644 fhirpath/fhirpath_test.go
 create mode 100644 fhirpath/fhirpathtest/fhirpathtest.go
 create mode 100644 fhirpath/fhirpathtest/fhirpathtest_example_test.go
 create mode 100644 fhirpath/fhirpathtest/fhirpathtest_test.go
 create mode 100644 fhirpath/internal/compile/compile.go
 create mode 100644 fhirpath/internal/compile/doc.go
 create mode 100644 fhirpath/internal/expr/arithmetic.go
 create mode 100644 fhirpath/internal/expr/booleans.go
 create mode 100644 fhirpath/internal/expr/context.go
 create mode 100644 fhirpath/internal/expr/doc.go
 create mode 100644 fhirpath/internal/expr/expressions.go
 create mode 100644 fhirpath/internal/expr/expressions_test.go
 create mode 100644 fhirpath/internal/expr/exprtest/doc.go
 create mode 100644 fhirpath/internal/expr/exprtest/doubles.go
 create mode 100644 fhirpath/internal/expr/operators.go
 create mode 100644 fhirpath/internal/funcs/doc.go
 create mode 100644 fhirpath/internal/funcs/function.go
 create mode 100644 fhirpath/internal/funcs/function_table.go
 create mode 100644 fhirpath/internal/funcs/function_table_test.go
 create mode 100644 fhirpath/internal/funcs/function_test.go
 create mode 100644 fhirpath/internal/funcs/impl/conversion.go
 create mode 100644 fhirpath/internal/funcs/impl/conversion_test.go
 create mode 100644 fhirpath/internal/funcs/impl/errors.go
 create mode 100644 fhirpath/internal/funcs/impl/existence.go
 create mode 100644 fhirpath/internal/funcs/impl/existence_test.go
 create mode 100644 fhirpath/internal/funcs/impl/filtering.go
 create mode 100644 fhirpath/internal/funcs/impl/filtering_test.go
 create mode 100644 fhirpath/internal/funcs/impl/math.go
 create mode 100644 fhirpath/internal/funcs/impl/math_test.go
 create mode 100644 fhirpath/internal/funcs/impl/not.go
 create mode 100644 fhirpath/internal/funcs/impl/not_test.go
 create mode 100644 fhirpath/internal/funcs/impl/r4.go
 create mode 100644 fhirpath/internal/funcs/impl/r4_test.go
 create mode 100644 fhirpath/internal/funcs/impl/strings.go
 create mode 100644 fhirpath/internal/funcs/impl/strings_test.go
 create mode 100644 fhirpath/internal/funcs/impl/subsetting.go
 create mode 100644 fhirpath/internal/funcs/impl/subsetting_test.go
 create mode 100644 fhirpath/internal/funcs/impl/utility.go
 create mode 100644 fhirpath/internal/funcs/impl/utility_test.go
 create mode 100644 fhirpath/internal/funcs/table.go
 create mode 100644 fhirpath/internal/grammar/fhirpath.g4
 create mode 100644 fhirpath/internal/grammar/fhirpath_lexer.go
 create mode 100644 fhirpath/internal/grammar/fhirpath_parser.go
 create mode 100644 fhirpath/internal/grammar/fhirpath_visitor.go
 create mode 100644 fhirpath/internal/grammar/generate.go
 create mode 100755 fhirpath/internal/grammar/generate.sh
 create mode 100644 fhirpath/internal/opts/opts.go
 create mode 100644 fhirpath/internal/parser/doc.go
 create mode 100644 fhirpath/internal/parser/error_handling.go
 create mode 100644 fhirpath/internal/parser/transforms.go
 create mode 100644 fhirpath/internal/parser/visitor.go
 create mode 100644 fhirpath/internal/reflection/consts.go
 create mode 100644 fhirpath/internal/reflection/doc.go
 create mode 100644 fhirpath/internal/reflection/elements.go
 create mode 100644 fhirpath/internal/reflection/type_specifier.go
 create mode 100644 fhirpath/internal/reflection/type_specifier_test.go
 create mode 100644 fhirpath/options.go
 create mode 100644 fhirpath/patch/doc.go
 create mode 100644 fhirpath/patch/patch.go
 create mode 100644 fhirpath/patch/patch_test.go
 create mode 100644 fhirpath/system/cmp.go
 create mode 100644 fhirpath/system/collection.go
 create mode 100644 fhirpath/system/collection_test.go
 create mode 100644 fhirpath/system/consts.go
 create mode 100644 fhirpath/system/date.go
 create mode 100644 fhirpath/system/date_test.go
 create mode 100644 fhirpath/system/date_time.go
 create mode 100644 fhirpath/system/date_time_test.go
 create mode 100644 fhirpath/system/doc.go
 create mode 100644 fhirpath/system/layouts.go
 create mode 100644 fhirpath/system/primitives.go
 create mode 100644 fhirpath/system/primitives_test.go
 create mode 100644 fhirpath/system/quantity.go
 create mode 100644 fhirpath/system/quantity_test.go
 create mode 100644 fhirpath/system/time.go
 create mode 100644 fhirpath/system/time_test.go
 create mode 100644 fhirpath/system/types.go
 create mode 100644 fhirpath/system/types_test.go
 create mode 100644 go.mod
 create mode 100644 go.sum
 create mode 100644 gotchas.md
 create mode 100644 internal/bundle/bundle.go
 create mode 100644 internal/bundle/bundle_entry.go
 create mode 100644 internal/bundle/bundle_entry_example_test.go
 create mode 100644 internal/bundle/bundle_entry_test.go
 create mode 100644 internal/bundle/bundle_option.go
 create mode 100644 internal/bundle/bundle_test.go
 create mode 100644 internal/bundle/identity.go
 create mode 100644 internal/bundle/identity_test.go
 create mode 100644 internal/bundleopt/bundleopt.go
 create mode 100644 internal/containedresource/contained_resource.go
 create mode 100644 internal/containedresource/contained_resource_example_test.go
 create mode 100644 internal/containedresource/contained_resource_test.go
 create mode 100644 internal/element/canonical/canonical.go
 create mode 100644 internal/element/canonical/canonical_test.go
 create mode 100644 internal/element/codeableconcept/codeableconcept.go
 create mode 100644 internal/element/coding/coding.go
 create mode 100644 internal/element/element.go
 create mode 100644 internal/element/etag/etag.go
 create mode 100644 internal/element/etag/etag_test.go
 create mode 100644 internal/element/extension/extension.go
 create mode 100644 internal/element/extension/extension_example_test.go
 create mode 100644 internal/element/extension/extension_test.go
 create mode 100644 internal/element/extract.go
 create mode 100644 internal/element/extract_test.go
 create mode 100644 internal/element/identifier/docs.go
 create mode 100644 internal/element/identifier/identifier.go
 create mode 100644 internal/element/identifier/identifier_example_test.go
 create mode 100644 internal/element/identifier/identifier_test.go
 create mode 100644 internal/element/identifier/opts.go
 create mode 100644 internal/element/meta/meta.go
 create mode 100644 internal/element/meta/meta_example_test.go
 create mode 100644 internal/element/meta/meta_test.go
 create mode 100644 internal/element/reference/identity.go
 create mode 100644 internal/element/reference/identity_test.go
 create mode 100644 internal/element/reference/literal.go
 create mode 100644 internal/element/reference/literal_test.go
 create mode 100644 internal/element/reference/reference.go
 create mode 100644 internal/element/reference/reference_test.go
 create mode 100644 internal/fhir/constraints.go
 create mode 100644 internal/fhir/doc.go
 create mode 100644 internal/fhir/duration.go
 create mode 100644 internal/fhir/duration_test.go
 create mode 100644 internal/fhir/elements_general.go
 create mode 100644 internal/fhir/elements_general_test.go
 create mode 100644 internal/fhir/elements_metadata.go
 create mode 100644 internal/fhir/elements_primitive.go
 create mode 100644 internal/fhir/elements_primitive_test.go
 create mode 100644 internal/fhir/elements_special.go
 create mode 100644 internal/fhir/elements_special_test.go
 create mode 100644 internal/fhir/encoding.go
 create mode 100644 internal/fhir/encoding_test.go
 create mode 100644 internal/fhir/iface.go
 create mode 100644 internal/fhir/iface_test.go
 create mode 100644 internal/fhir/protofields.go
 create mode 100644 internal/fhir/time.go
 create mode 100644 internal/fhir/time_test.go
 create mode 100644 internal/fhirconv/doc.go
 create mode 100644 internal/fhirconv/integer.go
 create mode 100644 internal/fhirconv/integer_test.go
 create mode 100644 internal/fhirconv/string.go
 create mode 100644 internal/fhirconv/string_test.go
 create mode 100644 internal/fhirconv/time.go
 create mode 100644 internal/fhirconv/time_test.go
 create mode 100644 internal/fhirtest/doc.go
 create mode 100644 internal/fhirtest/elements.go
 create mode 100644 internal/fhirtest/meta.go
 create mode 100644 internal/fhirtest/random.go
 create mode 100644 internal/fhirtest/resources.go
 create mode 100644 internal/fhirtest/resources_example_test.go
 create mode 100644 internal/fhirtest/resources_test.go
 create mode 100644 internal/narrow/narrow.go
 create mode 100644 internal/narrow/narrow_example_test.go
 create mode 100644 internal/narrow/narrow_test.go
 create mode 100644 internal/protofields/descriptor.go
 create mode 100644 internal/protofields/dummies.go
 create mode 100644 internal/protofields/fields.go
 create mode 100644 internal/protofields/fields_test.go
 create mode 100644 internal/protofields/strcase.go
 create mode 100644 internal/protofields/update.go
 create mode 100644 internal/protofields/update_test.go
 create mode 100644 internal/resource/canonical_identity.go
 create mode 100644 internal/resource/canonical_identity_test.go
 create mode 100644 internal/resource/consts.go
 create mode 100644 internal/resource/identity.go
 create mode 100644 internal/resource/identity_test.go
 create mode 100644 internal/resource/options.go
 create mode 100644 internal/resource/options_test.go
 create mode 100644 internal/resource/patient/patient.go
 create mode 100644 internal/resource/patient/patient_test.go
 create mode 100644 internal/resource/resource.go
 create mode 100644 internal/resource/resource_example_test.go
 create mode 100644 internal/resource/resource_test.go
 create mode 100644 internal/resource/type.go
 create mode 100644 internal/resource/type_test.go
 create mode 100644 internal/resourceopt/resourceopt.go
 create mode 100644 internal/slices/slices.go
 create mode 100644 internal/slices/slices_example_test.go
 create mode 100644 internal/slices/slices_test.go
 create mode 100644 internal/stablerand/doc.go
 create mode 100644 internal/stablerand/rand.go
 create mode 100644 internal/units/doc.go
 create mode 100644 internal/units/time.go

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..7a3abc2
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,3 @@
+# Note: For syntax, see <https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-syntax>
+
+* @verily-src/fhirpathgo-eng-reviewers
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
new file mode 100644
index 0000000..1a1a2b4
--- /dev/null
+++ b/.github/workflows/test.yaml
@@ -0,0 +1,24 @@
+name: Build and Test
+on:
+  pull_request:
+    branches:
+      - main
+
+jobs:
+  build-test:
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v4
+      - name: Setup Go
+        uses: actions/setup-go@v5
+        with:
+          # Use the central Go version defined in go.mod to make it easier
+          # to perform upgrades.
+          go-version-file: go.mod
+      - name: Vet
+        run: go vet -v -unreachable=false ./...
+      - name: Build
+        run: go build -v ./...
+      - name: Test
+        run: go test ./...
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1db2431
--- /dev/null
+++ b/README.md
@@ -0,0 +1,99 @@
+# FHIRPath
+
+This package contains a Go implementation of the [FHIRPath][fhirpath] specification, implemented directly with
+the [google/fhir][google-fhir] proto definitions.
+
+This package aims to be compliant with both:
+
+- the [N1 Normative Release](http://hl7.org/fhirpath/N1/) specification, and
+- the [R4 specifications](http://hl7.org/fhir/R4/fhirpath.html).
+
+## Import
+
+```go
+import "github.com/verily-src/fhirpath-go/fhirpath"
+```
+
+## Usage
+
+A FHIRPath must be compiled before running it against a resource using the `Compile` method like so:
+
+```go
+expression, err := fhirpath.Compile("Patient.name.given")
+if err != nil {
+    panic("error while compiling FHIRPath")
+}
+```
+
+The compilation result can then be run against a resource:
+
+```go
+inputResources := []fhir.Resource{somePatient, someMedication}
+
+result, err := expression.Evaluate(inputResources)
+if err != nil {
+    panic("error while running FHIRPath against resource")
+}
+```
+
+As defined in the FHIRPath specification, the output of evaluation is a **Collection**. So, the
+result of Evaluate is of type `[]any`. As such, the result must be unpacked and cast to the desired
+type for further processing.
+
+### CompileOptions and EvaluateOptions
+
+Options are provided for optional modification of compilation and evaluation. There is currently
+support for:
+
+- adding custom functions during Compile time
+- adding custom external constant variables
+
+#### To add a custom function
+
+The constraints on the custom function are as follows:
+
+- First argument must be `system.Collection`
+- Arguments that follow must be either a fhir proto type or primitive system type
+
+```go
+customFn := func (input system.Collection, args ...any) (system.Collection error) {
+    fmt.Print("called custom fn")
+    return input, nil
+}
+expression, err := fhirpath.Compile("print()", WithFunction("print", customFn))
+```
+
+#### To add external constants
+
+The constraints on external constants are as follows:
+
+- Must be a fhir proto type, primitive system type, or `system.Collection`
+- If you pass in a collection, contained elements must be fhir proto or system type.
+
+```go
+customVar := system.String("custom variable")
+result, err := expression.Evaluate([]fhir.Resource{someResource}, WithConstant("var", customVar))
+```
+
+### System Types
+
+The FHIRPath [spec](http://hl7.org/fhirpath/N1/#literals) defines the following custom System types:
+
+- Boolean
+- String
+- Integer
+- Decimal
+- Quantity
+- Date
+- Time
+- DateTime
+
+FHIR Protos get implicitly converted to the above types according to this
+[chart](http://hl7.org/fhir/R4/fhirpath.html#types), when used in some FHIRPath expressions.
+
+### Things to be aware of
+
+FHIRPath is not the most intuitive language, and there are some quirks. See [gotchas](gotchas.md).
+
+[fhirpath]: http://hl7.org/fhirpath/
+[google-fhir]: https://github.com/google/fhir
diff --git a/fhirpath/.gitignore b/fhirpath/.gitignore
new file mode 100644
index 0000000..0396a49
--- /dev/null
+++ b/fhirpath/.gitignore
@@ -0,0 +1,4 @@
+*antlr*.jar
+*.interp
+*.tokens
+*fhirpath_base_visitor.go
diff --git a/fhirpath/compopts/compopts.go b/fhirpath/compopts/compopts.go
new file mode 100644
index 0000000..9b04db1
--- /dev/null
+++ b/fhirpath/compopts/compopts.go
@@ -0,0 +1,55 @@
+/*
+Package compopts provides CompileOption values for FHIRPath.
+
+This package exists to isolate the options away from the core FHIRPath logic,
+since this will simplify discovery of compile-specific options.
+*/
+package compopts
+
+import (
+	"errors"
+
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/opts"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/parser"
+)
+
+var ErrMultipleTransforms = errors.New("multiple transforms provided")
+
+// AddFunction creates a CompileOption that will register a custom FHIRPath
+// function that can be called during evaluation with the given name.
+//
+// If the function already exists, then compilation will return an error.
+func AddFunction(name string, fn any) opts.CompileOption {
+	return opts.Transform(func(cfg *opts.CompileConfig) error {
+		return cfg.Table.Register(name, fn)
+	})
+}
+
+// Transform creates a CompileOption that will set a transform
+// to be called on each expression returned by the Visitor.
+//
+// If there is already a Transform set, then compilation will return an error.
+func Transform(v parser.VisitorTransform) opts.CompileOption {
+	return opts.Transform(func(cfg *opts.CompileConfig) error {
+		if cfg.Transform != nil {
+			return ErrMultipleTransforms
+		}
+		cfg.Transform = v
+		return nil
+	})
+}
+
+// Permissive is an option that enables deprecated behavior in FHIRPath field
+// navigation. This can be used as a temporary fix for FHIRpaths that have never
+// been valid FHIRPaths, but have worked up until this point.
+//
+// This option is marked Deprecated so that it nags users until the paths can
+// be resolved.
+//
+// Deprecated: Please update FHIRPaths whenever possible.
+func Permissive() opts.CompileOption {
+	return opts.Transform(func(cfg *opts.CompileConfig) error {
+		cfg.Permissive = true
+		return nil
+	})
+}
diff --git a/fhirpath/doc.go b/fhirpath/doc.go
new file mode 100644
index 0000000..4e1405d
--- /dev/null
+++ b/fhirpath/doc.go
@@ -0,0 +1,9 @@
+/*
+Package fhirpath implements the FHIRPath specification.
+
+More documentation about the FHIRPath specification can be found on HL7
+websites:
+* https://www.hl7.org/fhir/fhirpath.html
+* http://hl7.org/fhirpath/N1/
+*/
+package fhirpath
diff --git a/fhirpath/evalopts/evalopts.go b/fhirpath/evalopts/evalopts.go
new file mode 100644
index 0000000..eff566a
--- /dev/null
+++ b/fhirpath/evalopts/evalopts.go
@@ -0,0 +1,74 @@
+/*
+Package evalopts provides EvaluateOption values for FHIRPath.
+
+This package exists to isolate the options away from the core FHIRPath logic,
+since this will simplify discovery of evaluation-specific options.
+*/
+package evalopts
+
+import (
+	"errors"
+	"fmt"
+	"time"
+
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/opts"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+var (
+	ErrUnsupportedType  = errors.New("external constant type not supported")
+	ErrExistingConstant = errors.New("constant already exists")
+)
+
+// OverrideTime returns an EvaluateOption that can be used to override the time
+// that will be used in FHIRPath expressions.
+func OverrideTime(t time.Time) opts.EvaluateOption {
+	return opts.Transform(func(cfg *opts.EvaluateConfig) error {
+		cfg.Context.Now = t
+		return nil
+	})
+}
+
+// EnvVariable returns an EvaluateOption that sets FHIRPath environment variables
+// (e.g. %action).
+//
+// The input must be one of:
+//   - A FHIRPath System type,
+//   - A FHIR Element or Resource type, or
+//   - A FHIRPath Collection, containing the above types.
+//
+// If an EnvVariable is specified that already exists in the expression, then
+// evaluation will yield an ErrExistingConstant error. If an EnvVariable is
+// contains a type that is not one of the above valid types, then evaluation
+// will yield an ErrUnsupportedType error.
+func EnvVariable(name string, value any) opts.EvaluateOption {
+	return opts.Transform(func(cfg *opts.EvaluateConfig) error {
+		if err := validateType(value); err != nil {
+			return err
+		}
+		if _, ok := cfg.Context.ExternalConstants[name]; !ok {
+			cfg.Context.ExternalConstants[name] = value
+			return nil
+		}
+		return fmt.Errorf("%w: %s", ErrExistingConstant, name)
+	})
+}
+
+// validateType validates that the input type is a supported
+// fhir proto or System type. If a system.Collection is passed in,
+// recursively checks each element.
+func validateType(input any) error {
+	var err error
+	switch v := input.(type) {
+	case fhir.Base, system.Any:
+		break
+	case system.Collection:
+		for _, elem := range v {
+			err = errors.Join(err, validateType(elem))
+		}
+	default:
+		err = fmt.Errorf("%w: %T", ErrUnsupportedType, input)
+	}
+	return err
+}
diff --git a/fhirpath/fhirpath.go b/fhirpath/fhirpath.go
new file mode 100644
index 0000000..395de89
--- /dev/null
+++ b/fhirpath/fhirpath.go
@@ -0,0 +1,118 @@
+package fhirpath
+
+import (
+	"errors"
+
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/evalopts"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/compile"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/opts"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/parser"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+var (
+	ErrInvalidField     = expr.ErrInvalidField
+	ErrUnsupportedType  = evalopts.ErrUnsupportedType
+	ErrExistingConstant = evalopts.ErrExistingConstant
+)
+
+// Expression is the FHIRPath expression that will be compiled from a FHIRPath string
+type Expression struct {
+	expression expr.Expression
+	path       string
+}
+
+// Compile parses and compiles the FHIRPath expression down to a single
+// Expression object.
+//
+// If there are any syntax or semantic errors, this will return an
+// error indicating the compilation failure reason.
+func Compile(expr string, options ...CompileOption) (*Expression, error) {
+	config, err := compile.PopulateConfig(options...)
+	if err != nil {
+		return nil, err
+	}
+
+	tree, err := compile.Tree(expr)
+	if err != nil {
+		return nil, err
+	}
+
+	visitor := &parser.FHIRPathVisitor{
+		Functions:  config.Table,
+		Permissive: config.Permissive,
+	}
+	vr, ok := visitor.Visit(tree).(*parser.VisitResult)
+	if !ok {
+		return nil, errors.New("input expression currently unsupported")
+	}
+
+	if vr.Error != nil {
+		return nil, vr.Error
+	}
+	return &Expression{
+		expression: vr.Result,
+		path:       expr,
+	}, nil
+}
+
+// String returns the string representation of this FHIRPath expression.
+// This is just the input that initially produced the FHIRPath value.
+func (e *Expression) String() string {
+	return e.path
+}
+
+// MustCompile compiles the FHIRpath expression input, and returns the
+// compiled expression. If any compilation error occurs, this function
+// will panic.
+func MustCompile(expr string, opts ...CompileOption) *Expression {
+	result, err := Compile(expr, opts...)
+	if err != nil {
+		panic(err)
+	}
+	return result
+}
+
+// Evaluate the expression, returning either a collection of elements, or error
+func (e *Expression) Evaluate(input []fhir.Resource, options ...EvaluateOption) (system.Collection, error) {
+	config := &opts.EvaluateConfig{
+		Context: expr.InitializeContext(slices.MustConvert[any](input)),
+	}
+	config, err := opts.ApplyOptions(config, options...)
+	if err != nil {
+		return nil, err
+	}
+
+	collection := slices.MustConvert[any](input)
+	return e.expression.Evaluate(config.Context, collection)
+}
+
+// EvaluateAsString evaluates the expression, returning a string or error
+func (e *Expression) EvaluateAsString(input []fhir.Resource, options ...EvaluateOption) (string, error) {
+	got, err := e.Evaluate(input, options...)
+	if err != nil {
+		return "", err
+	}
+	return got.ToString()
+}
+
+// EvaluateAsBool evaluates the expression, returning either a boolean or error
+func (e *Expression) EvaluateAsBool(input []fhir.Resource, options ...EvaluateOption) (bool, error) {
+	got, err := e.Evaluate(input, options...)
+	if err != nil {
+		return false, err
+	}
+	return got.ToBool()
+}
+
+// EvaluateAsInt32 evaluates the expression, returning either an int32 or error
+func (e *Expression) EvaluateAsInt32(input []fhir.Resource, options ...EvaluateOption) (int32, error) {
+	got, err := e.Evaluate(input, options...)
+	if err != nil {
+		return 0, err
+	}
+	return got.ToInt32()
+}
diff --git a/fhirpath/fhirpath_expression_test.go b/fhirpath/fhirpath_expression_test.go
new file mode 100644
index 0000000..daed640
--- /dev/null
+++ b/fhirpath/fhirpath_expression_test.go
@@ -0,0 +1,292 @@
+package fhirpath_test
+
+import (
+	"errors"
+	"math"
+	"testing"
+
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath"
+	"github.com/verily-src/fhirpath-go/fhirpath/fhirpathtest"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+func TestExpressionString(t *testing.T) {
+	const want = "Patient.name"
+	expr, err := fhirpath.Compile(want)
+	if err != nil {
+		t.Fatalf("Expression.String(): got unexpected err: %v", err)
+	}
+
+	got := expr.String()
+
+	if got != want {
+		t.Errorf("Expression.String(): got %v, want %v", got, want)
+	}
+}
+
+func TestExpressionEvaluateAsBool_EvaluationError_ReturnsError(t *testing.T) {
+	want := errors.New("some error")
+	path := fhirpathtest.Error(want)
+
+	_, err := path.EvaluateAsBool(nil)
+
+	if got, want := err, want; !errors.Is(got, want) {
+		t.Errorf("EvaluateAsBool: want err %v, got %v", want, got)
+	}
+}
+
+func TestExpressionEvaluateAsBool_NonConvertibleResult_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input system.Collection
+	}{
+		{
+			name:  "Collection of more than 1",
+			input: system.Collection{system.String("1"), system.String("2")},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			path := fhirpathtest.ReturnCollection(tc.input)
+
+			_, err := path.EvaluateAsBool(nil)
+
+			if err == nil {
+				t.Fatalf("EvaluateAsBool: Expected error")
+			}
+		})
+	}
+}
+
+func TestExpressionEvaluateAsBoo_EmptyCollection_ReturnsFalse(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input system.Collection
+	}{
+		{
+			name:  "Empty Collection",
+			input: system.Collection{},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			path := fhirpathtest.ReturnCollection(tc.input)
+
+			result, err := path.EvaluateAsBool(nil)
+
+			if err != nil {
+				t.Fatalf("EvaluateAsBool: Expected no error")
+			}
+
+			if result {
+				t.Fatalf("EvaluateAsBool: Expected false")
+			}
+		})
+	}
+}
+
+func TestExpressionEvaluateAsBool_ConvertibleResult_ReturnsBool(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input any
+		want  bool
+	}{
+		{
+			name:  "system boolean",
+			input: system.Boolean(true),
+			want:  true,
+		}, {
+			name:  "FHIR boolean",
+			input: fhir.Boolean(true),
+			want:  true,
+		}, {
+			name:  "Random singleton type",
+			input: system.String("Hello world"),
+			want:  true,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			path := fhirpathtest.Return(tc.input)
+
+			got, err := path.EvaluateAsBool(nil)
+			if err != nil {
+				t.Fatalf("EvaluateAsBool: Unexpected error %v", err)
+			}
+
+			if got != tc.want {
+				t.Errorf("EvaluateAsBool: want %v, got %v", tc.want, got)
+			}
+		})
+	}
+}
+
+func TestExpressionEvaluateAsString_EvaluationError_ReturnsError(t *testing.T) {
+	want := errors.New("some error")
+	path := fhirpathtest.Error(want)
+
+	_, err := path.EvaluateAsString(nil)
+
+	if got, want := err, want; !errors.Is(got, want) {
+		t.Errorf("EvaluateAsString: want err %v, got %v", want, got)
+	}
+}
+
+func TestExpressionEvaluateAsString_NonConvertibleResult_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input system.Collection
+	}{
+		{
+			name:  "Empty Collection",
+			input: system.Collection{},
+		}, {
+			name:  "Collection of more than 1",
+			input: system.Collection{system.String("1"), system.String("2")},
+		}, {
+			name:  "Invalid type",
+			input: system.Collection{fhir.Integer(42)},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			path := fhirpathtest.ReturnCollection(tc.input)
+
+			_, err := path.EvaluateAsString(nil)
+
+			if err == nil {
+				t.Fatalf("EvaluateAsString: Expected error")
+			}
+		})
+	}
+}
+
+func TestExpressionEvaluateAsString_ConvertibleResult_ReturnsString(t *testing.T) {
+	const str = "hello world"
+	testCases := []struct {
+		name  string
+		input any
+		want  string
+	}{
+		{
+			name:  "system String",
+			input: system.String(str),
+			want:  str,
+		}, {
+			name:  "FHIR String",
+			input: fhir.String(str),
+			want:  str,
+		}, {
+			name:  "FHIR Code",
+			input: fhir.Code(str),
+			want:  str,
+		}, {
+			name:  "FHIR ID",
+			input: fhir.ID(str),
+			want:  str,
+		}, {
+			name:  "FHIR Markdown",
+			input: fhir.Markdown(str),
+			want:  str,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			path := fhirpathtest.Return(tc.input)
+
+			got, err := path.EvaluateAsString(nil)
+			if err != nil {
+				t.Fatalf("EvaluateAsString: Unexpected error %v", err)
+			}
+
+			if got != tc.want {
+				t.Errorf("EvaluateAsString: want %v, got %v", tc.want, got)
+			}
+		})
+	}
+}
+
+func TestExpressionEvaluateAsInt32_EvaluationError_ReturnsError(t *testing.T) {
+	want := errors.New("some error")
+	path := fhirpathtest.Error(want)
+
+	_, err := path.EvaluateAsInt32(nil)
+
+	if got, want := err, want; !errors.Is(got, want) {
+		t.Errorf("EvaluateAsInt32: want err %v, got %v", want, got)
+	}
+}
+
+func TestExpressionEvaluateAsInt32_NonConvertibleResult_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input system.Collection
+	}{
+		{
+			name:  "Empty Collection",
+			input: system.Collection{},
+		}, {
+			name:  "Collection of more than 1",
+			input: system.Collection{system.Integer(1), system.Integer(2)},
+		}, {
+			name:  "Integer not representable",
+			input: system.Collection{fhir.UnsignedInt(math.MaxUint32)},
+		}, {
+			name:  "Invalid Type",
+			input: system.Collection{fhir.String("hello world")},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			path := fhirpathtest.ReturnCollection(tc.input)
+
+			_, err := path.EvaluateAsInt32(nil)
+
+			if err == nil {
+				t.Fatalf("EvaluateAsInt32: Expected error")
+			}
+		})
+	}
+}
+
+func TestExpressionEvaluateAsInt32_ConvertibleResult_ReturnsInt32(t *testing.T) {
+	const val = 42
+	testCases := []struct {
+		name  string
+		input any
+		want  int32
+	}{
+		{
+			name:  "system Integer",
+			input: system.Integer(val),
+			want:  val,
+		}, {
+			name:  "FHIR Integer",
+			input: fhir.Integer(val),
+			want:  val,
+		}, {
+			name:  "FHIR PositiveInt",
+			input: fhir.PositiveInt(val),
+			want:  val,
+		}, {
+			name:  "FHIR ID",
+			input: fhir.UnsignedInt(val),
+			want:  val,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			path := fhirpathtest.Return(tc.input)
+
+			got, err := path.EvaluateAsInt32(nil)
+			if err != nil {
+				t.Fatalf("EvaluateAsInt32: Unexpected error %v", err)
+			}
+
+			if got != tc.want {
+				t.Errorf("EvaluateAsInt32: want %v, got %v", tc.want, got)
+			}
+		})
+	}
+}
diff --git a/fhirpath/fhirpath_test.go b/fhirpath/fhirpath_test.go
new file mode 100644
index 0000000..9734b3e
--- /dev/null
+++ b/fhirpath/fhirpath_test.go
@@ -0,0 +1,1413 @@
+package fhirpath_test
+
+import (
+	"errors"
+	"testing"
+	"time"
+
+	cpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/codes_go_proto"
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	drpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/document_reference_go_proto"
+	epb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/encounter_go_proto"
+	lpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/list_go_proto"
+	mrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_request_go_proto"
+	opb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/observation_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	prpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/practitioner_go_proto"
+	tpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/task_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/shopspring/decimal"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/element/extension"
+	"github.com/verily-src/fhirpath-go/internal/element/reference"
+	"github.com/verily-src/fhirpath-go/internal/fhirconv"
+	"github.com/verily-src/fhirpath-go/fhirpath"
+	"github.com/verily-src/fhirpath-go/fhirpath/compopts"
+	"github.com/verily-src/fhirpath-go/fhirpath/evalopts"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+type evaluateTestCase struct {
+	name            string
+	inputPath       string
+	inputCollection []fhir.Resource
+	wantCollection  system.Collection
+	compileOptions  []fhirpath.CompileOption
+	evaluateOptions []fhirpath.EvaluateOption
+}
+
+var patientChu = &ppb.Patient{
+	Id:     fhir.ID("123"),
+	Active: fhir.Boolean(true),
+	Gender: &ppb.Patient_GenderCode{
+		Value: cpb.AdministrativeGenderCode_FEMALE,
+	},
+	BirthDate: fhir.MustParseDate("2000-03-22"),
+	Telecom: []*dtpb.ContactPoint{
+		{
+			System: &dtpb.ContactPoint_SystemCode{Value: cpb.ContactPointSystemCode_PHONE},
+		},
+	},
+	Name: []*dtpb.HumanName{
+		{
+			Use: &dtpb.HumanName_UseCode{
+				Value: cpb.NameUseCode_NICKNAME,
+			},
+			Given:  []*dtpb.String{fhir.String("Senpai")},
+			Family: fhir.String("Chu"),
+		},
+		{
+			Use: &dtpb.HumanName_UseCode{
+				Value: cpb.NameUseCode_OFFICIAL,
+			},
+			Given:  []*dtpb.String{fhir.String("Kang")},
+			Family: fhir.String("Chu"),
+		},
+	},
+	Contact: []*ppb.Patient_Contact{
+		{
+			Name: &dtpb.HumanName{
+				Given:  []*dtpb.String{fhir.String("Senpai")},
+				Family: fhir.String("Rodusek"),
+			},
+		},
+	},
+}
+var fooExtension, _ = extension.FromElement("foourl", fhir.String("foovalue"))
+var barExtension, _ = extension.FromElement("barurl", fhir.String("barvalue"))
+var nameVoldemort = &dtpb.HumanName{
+	Given: []*dtpb.String{
+		fhir.String("Lord"),
+	},
+	Family: fhir.String("Voldemort"),
+}
+var patientVoldemort = &ppb.Patient{
+	Id:     fhir.ID("123"),
+	Active: fhir.Boolean(true),
+	Gender: &ppb.Patient_GenderCode{
+		Value: cpb.AdministrativeGenderCode_FEMALE,
+	},
+	Deceased: &ppb.Patient_DeceasedX{
+		Choice: &ppb.Patient_DeceasedX_Boolean{
+			Boolean: fhir.Boolean(true),
+		},
+	},
+	MultipleBirth: &ppb.Patient_MultipleBirthX{
+		Choice: &ppb.Patient_MultipleBirthX_Integer{
+			Integer: fhir.Integer(int32(2)),
+		},
+	},
+	Meta: &dtpb.Meta{
+		Tag: []*dtpb.Coding{
+			{
+				Code: fhir.Code("#blessed"),
+			},
+		},
+	},
+	Name: []*dtpb.HumanName{nameVoldemort},
+	Extension: []*dtpb.Extension{
+		fooExtension,
+		barExtension,
+	},
+}
+var docRef = &drpb.DocumentReference{
+	Status: &drpb.DocumentReference_StatusCode{
+		Value: cpb.DocumentReferenceStatusCode_CURRENT,
+	},
+	Content: []*drpb.DocumentReference_Content{
+		{
+			Attachment: &dtpb.Attachment{
+				ContentType: &dtpb.Attachment_ContentTypeCode{
+					Value: "image",
+				},
+				Url:   fhir.URL("http://image"),
+				Title: fhir.String("title"),
+			},
+		},
+	},
+}
+var questionnaireRef, _ = reference.Typed("Questionnaire", "1234")
+var obsWithRef = &opb.Observation{
+	Meta: &dtpb.Meta{
+		Extension: []*dtpb.Extension{
+			{
+				Url: fhir.URI("https://extension"),
+				Value: &dtpb.Extension_ValueX{
+					Choice: &dtpb.Extension_ValueX_Reference{
+						Reference: questionnaireRef,
+					},
+				},
+			},
+		},
+	},
+	DerivedFrom: []*dtpb.Reference{
+		questionnaireRef,
+	},
+}
+var listWithNilRef = &lpb.List{
+	Entry: []*lpb.List_Entry{
+		{Item: &dtpb.Reference{Type: fhir.URI("Location")}},
+	},
+}
+
+func testEvaluate(t *testing.T, testCases []evaluateTestCase) {
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			compiledExpression, err := fhirpath.Compile(tc.inputPath, tc.compileOptions...)
+			if err != nil {
+				t.Fatalf("Compiling \"%s\" returned unexpected error: %v", tc.inputPath, err)
+			}
+
+			got, err := compiledExpression.Evaluate(tc.inputCollection, tc.evaluateOptions...)
+
+			if err != nil {
+				t.Fatalf("Evaluating \"%s\" returned unexpected error: %v", tc.inputPath, err)
+			}
+			if diff := cmp.Diff(tc.wantCollection, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Evaluating \"%s\" returned unexpected diff (-want, +got)\n%s", tc.inputPath, diff)
+			}
+		})
+	}
+}
+
+func TestEvaluate_PathSelection_ReturnsError(t *testing.T) {
+	end := system.MustParseDateTime("@2016-01-01T12:22:33Z")
+	task := makeTaskWithEndTime(end)
+
+	testCases := []struct {
+		name    string
+		path    string
+		input   fhir.Resource
+		wantErr error
+	}{
+		{
+			name:    "Invalid value_us field on DateTime",
+			path:    "(Task.input.value as DataRequirement).dateFilter[0].value.end.value_us",
+			input:   task,
+			wantErr: fhirpath.ErrInvalidField,
+		}, {
+			name:    "Invalid timezone field on DateTime",
+			path:    "(Task.input.value as DataRequirement).dateFilter[0].value.end.timezone",
+			input:   task,
+			wantErr: fhirpath.ErrInvalidField,
+		}, {
+			name:    "Field is not in correct casing, but exists",
+			path:    "Patient.multiple_birth",
+			input:   patientVoldemort,
+			wantErr: fhirpath.ErrInvalidField,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			sut, err := fhirpath.Compile(tc.path)
+			if err != nil {
+				t.Fatalf("fhirpath.Compile(%v): unexpected err: %v", tc.name, err)
+			}
+
+			_, err = sut.Evaluate([]fhir.Resource{tc.input})
+
+			if got, want := err, tc.wantErr; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+				t.Errorf("fhirpath.Compile(%v): want err '%v', got '%v'", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestEvaluate_PathSelection_ReturnsResult(t *testing.T) {
+	practitioner := &prpb.Practitioner{
+		Name: []*dtpb.HumanName{nameVoldemort},
+	}
+	end := system.MustParseDateTime("@2016-01-01T12:22:33Z")
+	task := makeTaskWithEndTime(end)
+
+	testCases := []evaluateTestCase{
+		{
+			name:            "Patient.name.given returns given name",
+			inputPath:       "Patient.name.given",
+			inputCollection: []fhir.Resource{patientVoldemort},
+			wantCollection:  system.Collection{fhir.String("Lord")},
+		},
+		{
+			name:            "Patient.name returns name",
+			inputPath:       "Patient.name",
+			inputCollection: []fhir.Resource{patientVoldemort},
+			wantCollection:  system.Collection{nameVoldemort},
+		},
+		{
+			name:            "Extension with resource type returns extensions",
+			inputPath:       "Patient.extension",
+			inputCollection: []fhir.Resource{patientVoldemort},
+			wantCollection:  system.Collection{fooExtension, barExtension},
+		},
+		{
+			name:            "Extension without resource type returns extensions",
+			inputPath:       "extension",
+			inputCollection: []fhir.Resource{patientVoldemort},
+			wantCollection:  system.Collection{fooExtension, barExtension},
+		},
+		{
+			name:            "Patient.name returns empty on non-patient resource",
+			inputPath:       "Patient.name",
+			inputCollection: []fhir.Resource{practitioner},
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "Accessing code field returns code",
+			inputPath:       "Patient.gender",
+			inputCollection: []fhir.Resource{patientVoldemort},
+			wantCollection:  system.Collection{patientVoldemort.Gender},
+		},
+		{
+			name:            "converts value field of primitive to System primitive",
+			inputPath:       "Patient.name.given.value",
+			inputCollection: []fhir.Resource{patientVoldemort},
+			wantCollection:  system.Collection{system.String("Lord")},
+		},
+		{
+			name:            "returns empty on non-existent field",
+			inputPath:       "Patient.language",
+			inputCollection: []fhir.Resource{patientVoldemort},
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "returns value from a field with the _value suffix",
+			inputPath:       "Encounter.class",
+			inputCollection: []fhir.Resource{&epb.Encounter{ClassValue: fhir.Coding("class-system", "class-code")}},
+			wantCollection:  system.Collection{fhir.Coding("class-system", "class-code")},
+		},
+		{
+			name:      "value as Quantity returns fhir Quantity datatype",
+			inputPath: "Observation.value as Quantity",
+			inputCollection: []fhir.Resource{
+				&opb.Observation{
+					Value: &opb.Observation_ValueX{
+						Choice: &opb.Observation_ValueX_Quantity{
+							Quantity: &dtpb.Quantity{
+								Value: fhir.Decimal(float64(22.2)),
+							},
+						},
+					},
+				},
+			},
+			wantCollection: []any{
+				&dtpb.Quantity{
+					Value: fhir.Decimal(float64(22.2)),
+				},
+			},
+		},
+		{
+			name:      "Quantity with addition returns system.Quantity",
+			inputPath: "Observation.value as Quantity + 2",
+			inputCollection: []fhir.Resource{
+				&opb.Observation{
+					Value: &opb.Observation_ValueX{
+						Choice: &opb.Observation_ValueX_Quantity{
+							Quantity: &dtpb.Quantity{
+								Value: fhir.Decimal(float64(22.2)),
+							},
+						},
+					},
+				},
+			},
+			wantCollection: []any{system.MustParseQuantity("24.2", "")},
+		},
+		{
+			name:            "reference field returns Type/ID",
+			inputPath:       "Observation.derivedFrom[0].reference",
+			inputCollection: []fhir.Resource{obsWithRef},
+			wantCollection:  system.Collection{fhir.String("Questionnaire/1234")},
+		},
+		{
+			name:            "reference extension field returns Type/ID",
+			inputPath:       "Observation.meta.extension('https://extension').value.reference",
+			inputCollection: []fhir.Resource{obsWithRef},
+			wantCollection:  system.Collection{fhir.String("Questionnaire/1234")},
+		},
+		{
+			name:            "nil reference does not panic",
+			inputPath:       "List.entry.item.where(type = 'Location').reference",
+			inputCollection: []fhir.Resource{listWithNilRef},
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "Valid access of time field",
+			inputPath:       "(Task.input.value as DataRequirement).dateFilter[0].value.end.value",
+			inputCollection: []fhir.Resource{task},
+			wantCollection:  system.Collection{system.String(fhirconv.DateTimeToString(end.ToProtoDateTime()))},
+		},
+	}
+	testEvaluate(t, testCases)
+}
+
+func makeTaskWithEndTime(end system.DateTime) *tpb.Task {
+	start := system.MustParseDateTime("@2016-01-01T12:00:00Z")
+	task := &tpb.Task{
+		Input: []*tpb.Task_Parameter{
+			{
+				Value: &tpb.Task_Parameter_ValueX{
+					Choice: &tpb.Task_Parameter_ValueX_DataRequirement{
+						DataRequirement: &dtpb.DataRequirement{
+							DateFilter: []*dtpb.DataRequirement_DateFilter{
+								{
+									Value: &dtpb.DataRequirement_DateFilter_ValueX{
+										Choice: &dtpb.DataRequirement_DateFilter_ValueX_Period{
+											Period: &dtpb.Period{
+												Start: start.ToProtoDateTime(),
+												End:   end.ToProtoDateTime(),
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+	return task
+}
+
+func TestEvaluate_LegacyPathSelection_ReturnsResult(t *testing.T) {
+	compileOptions := []fhirpath.CompileOption{compopts.Permissive()}
+	end := system.MustParseDateTime("@2016-01-01T12:22:33Z")
+	task := makeTaskWithEndTime(end)
+
+	testCases := []evaluateTestCase{
+		{
+			name:            "Legacy: Evaluates ValueX fields and value_us fields",
+			inputPath:       "(Task.input.value as DataRequirement).dateFilter[0].value.period.end.value_us",
+			inputCollection: []fhir.Resource{task},
+			wantCollection:  system.Collection{end},
+			compileOptions:  compileOptions,
+		},
+	}
+	testEvaluate(t, testCases)
+}
+
+func TestEvaluate_Literal_ReturnsLiteral(t *testing.T) {
+	decimal := system.Decimal(decimal.NewFromFloat(1.450))
+	date, _ := system.ParseDate("2023-05-30")
+	time, _ := system.ParseTime("08:30:55.999")
+	dateTime, _ := system.ParseDateTime("2023-06-14T13:48:55.555Z")
+	quantity, _ := system.ParseQuantity("20", "years")
+
+	testCases := []evaluateTestCase{
+		{
+			name:            "null literal returns empty collection",
+			inputPath:       "{}",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "boolean literal returns Boolean",
+			inputPath:       "true",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "string literal returns escaped string",
+			inputPath:       "'string test\\ 1\\''",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.String("string test 1'")},
+		},
+		{
+			name:            "integer literal returns Integer",
+			inputPath:       "23",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Integer(23)},
+		},
+		{
+			name:            "decimal literal returns Decimal",
+			inputPath:       "1.450",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{decimal},
+		},
+		{
+			name:            "date literal returns Date",
+			inputPath:       "@2023-05-30",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{date},
+		},
+		{
+			name:            "time literal returns Time",
+			inputPath:       "@T08:30:55.999",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{time},
+		},
+		{
+			name:            "dateTime literal returns DateTime",
+			inputPath:       "@2023-06-14T13:48:55.555Z",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{dateTime},
+		},
+		{
+			name:            "quantity literal returns Quantity",
+			inputPath:       "20 years",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{quantity},
+		},
+	}
+
+	testEvaluate(t, testCases)
+}
+
+func TestEvaluate_ThisInvocation_Evaluates(t *testing.T) {
+	testCases := []evaluateTestCase{
+		{
+			name:            "returns nickname with where()",
+			inputPath:       "Patient.name.given.where($this = 'Senpai')",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{dtpb.String{Value: "Senpai"}},
+		},
+	}
+
+	testEvaluate(t, testCases)
+}
+
+func TestEvaluate_Index_ReturnsIndex(t *testing.T) {
+	nameOne := &dtpb.HumanName{
+		Given: []*dtpb.String{
+			fhir.String("Kobe"),
+			fhir.String("Bean"),
+		},
+		Family: fhir.String("Bryant"),
+	}
+	nameTwo := &dtpb.HumanName{
+		Given: []*dtpb.String{
+			fhir.String("The"),
+		},
+		Family: fhir.String("Goat"),
+	}
+	patient := &ppb.Patient{
+		Name: []*dtpb.HumanName{
+			nameOne,
+			nameTwo,
+		},
+	}
+
+	testCases := []evaluateTestCase{
+		{
+			name:            "first index returns result",
+			inputPath:       "Patient.name[0]",
+			inputCollection: []fhir.Resource{patient},
+			wantCollection:  system.Collection{nameOne},
+		},
+		{
+			name:            "second index returns result",
+			inputPath:       "Patient.name[1]",
+			inputCollection: []fhir.Resource{patient},
+			wantCollection:  system.Collection{nameTwo},
+		},
+		{
+			name:            "indexing name.given",
+			inputPath:       "Patient.name.given[2]",
+			inputCollection: []fhir.Resource{patient},
+			wantCollection:  system.Collection{fhir.String("The")},
+		},
+		{
+			name:            "indexing multiple times",
+			inputPath:       "Patient.name[0].given[1]",
+			inputCollection: []fhir.Resource{patient},
+			wantCollection:  system.Collection{fhir.String("Bean")},
+		},
+		{
+			name:            "out of bounds index returns empty",
+			inputPath:       "Patient.name.given[5]",
+			inputCollection: []fhir.Resource{patient},
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "empty collection index returns empty",
+			inputPath:       "Patient.name.given[{}]",
+			inputCollection: []fhir.Resource{patient},
+			wantCollection:  system.Collection{},
+		},
+	}
+
+	testEvaluate(t, testCases)
+}
+
+func TestEvaluateEquality_ReturnsBoolean(t *testing.T) {
+	request := &mrpb.MedicationRequest{
+		Intent: &mrpb.MedicationRequest_IntentCode{Value: cpb.MedicationRequestIntentCode_FILLER_ORDER},
+	}
+
+	testCases := []evaluateTestCase{
+		{
+			name:            "querying active field",
+			inputPath:       "Patient.active = true",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "two contrary conditions on 2 resources with an OR, first one true",
+			inputPath:       "Patient.active = true or Observation.status = 'final'",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "two contrary conditions on 2 resources with an OR, second one true",
+			inputPath:       "Observation.status = 'final' or Patient.active = true",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "inverse of active field",
+			inputPath:       "Patient.active != true",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+		{
+			name:            "querying given name",
+			inputPath:       "Patient.name[0].given = 'Senpai'",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "equality of complex types",
+			inputPath:       "Patient.name[0].given = Patient.contact.name.given",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "complex types not equal",
+			inputPath:       "Patient.name.family != Patient.contact.name.family",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "comparing non-equal fields",
+			inputPath:       "Patient.name.family = Patient.contact.name.family",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+		{
+			name:            "comparing non-existent field",
+			inputPath:       "Patient.maritalStatus = false",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "comparing dates",
+			inputPath:       "Patient.birthDate = @2000-03-22",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "compare date with dateTime",
+			inputPath:       "@2012-12-31 = @2012-12-31T",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "compare dateTime with date",
+			inputPath:       "@2012-12-31T = @2012-12-31",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "comparing non-equal dates",
+			inputPath:       "@2000-01-02 != @2000-01-01",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "comparing mismatched date precision",
+			inputPath:       "@2000-01 = @2000-01-03",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "comparing mismatched date precision that isn't equal",
+			inputPath:       "@2000-02 = @2000-01-03",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+		{
+			name:            "respects timezones for DateTime comparison",
+			inputPath:       "@2000-02-01T12:30:00Z = @2000-02-01T13:30:00+01:00",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "comparing gender code",
+			inputPath:       "Patient.gender = 'female'",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "comparing code with non-enum value",
+			inputPath:       "DocumentReference.content[0].attachment.contentType = 'image'",
+			inputCollection: []fhir.Resource{docRef},
+			wantCollection:  []any{system.Boolean(true)},
+		},
+		{
+			name:            "comparing name use code",
+			inputPath:       "Patient.name[0].use = 'nickname'",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "comparing telecom system code",
+			inputPath:       "telecom.system = 'phone'",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "comparing incorrect telecom code",
+			inputPath:       "telecom.system = 'carrier pigeon'",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+		{
+			name:            "mismatched case on code",
+			inputPath:       "Patient.name.use = 'NICKNAME'",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+		{
+			name:            "comparing multi-word code",
+			inputPath:       "MedicationRequest.intent = 'filler-order'",
+			inputCollection: []fhir.Resource{request},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "mismatched case for multi-word code",
+			inputPath:       "MedicationRequest.intent = 'fillerOrder'",
+			inputCollection: []fhir.Resource{request},
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+		{
+			name:            "comparing decimal to integer",
+			inputPath:       "1 = 1.000",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "comparing decimal to quantity",
+			inputPath:       "24.3 = 24.3 'kg'",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "comparing integer to quantity",
+			inputPath:       "2 = 2.0 'lbs'",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+	}
+
+	testEvaluate(t, testCases)
+}
+
+func TestParenthesizedExpression_MaintainsPrecedence(t *testing.T) {
+	patient := &ppb.Patient{
+		Name: []*dtpb.HumanName{
+			{
+				Given: []*dtpb.String{
+					fhir.String("Alex"),
+					fhir.String("Jon"),
+					fhir.String("Matt"),
+					fhir.String("Heming"),
+				},
+			},
+		},
+	}
+	testCases := []evaluateTestCase{
+		{
+			name:            "evaluates parenthesized equality first",
+			inputPath:       "true = (false = false)",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "evaluates parenthesized expressions in order",
+			inputPath:       "true = ('Alex' = (name.given[0]))",
+			inputCollection: []fhir.Resource{patient},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+	}
+
+	testEvaluate(t, testCases)
+}
+
+func TestFunctionInvocation_Evaluates(t *testing.T) {
+	testTime := time.Now()
+	testDateTime, _ := system.DateTimeFromProto(fhir.DateTime(testTime))
+	testCases := []evaluateTestCase{
+		{
+			name:            "returns nickname with where()",
+			inputPath:       "Patient.name.where(use = 'nickname')",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{patientChu.Name[0]},
+		},
+		{
+			name:            "returns official name with where()",
+			inputPath:       "Patient.name.where(use = 'official')",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{patientChu.Name[1]},
+		},
+		{
+			name:            "returns true with exists()",
+			inputPath:       "Patient.name.exists(use = 'official')",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "returns false with exists()",
+			inputPath:       "Patient.name.exists(use = 'random-use')",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+		{
+			name:            "returns true with exists() with BooleanExpression",
+			inputPath:       "Patient.name.exists(use = 'official' and given = 'Kang')",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "returns true with exists() with BooleanExpression",
+			inputPath:       "Patient.name.exists(use = 'random-use' or given = 'Kang')",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "returns true with where() and exists()",
+			inputPath:       "Patient.name.where(use = 'official').exists()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "chaining exists() is fine when the first exists() evaluates to true",
+			inputPath:       "Patient.name.where(use = 'official').exists().exists().exists()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "chaining exists() when the first exists() evaluates to false gives correct but ambiguous result",
+			inputPath:       "Patient.name.where(use = 'random').exists().exists()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "chaining empty() gives correct but ambiguous result",
+			inputPath:       "Patient.name.where(use = 'random').empty().empty()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+		{
+			name:            "returns false with where() and empty()",
+			inputPath:       "Patient.name.where(use = 'official').empty()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+		{
+			name:            "returns true with where() and empty()",
+			inputPath:       "Patient.name.where(use = 'random').empty()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "returns empty if no elements match where condition",
+			inputPath:       "Patient.name.where(family = 'Suresh')",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "evaluates timeOfDay() based on context, not dependent on latent factors",
+			inputPath:       "timeOfDay().delay() = timeOfDay()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+			compileOptions: []fhirpath.CompileOption{compopts.AddFunction("delay", func(in system.Collection) (system.Collection, error) {
+				time.Sleep(time.Second * 2)
+				return in, nil
+			})},
+		},
+		{
+			name:            "evaluates now() based on context, not dependent on latent factors",
+			inputPath:       "now().delay() = now()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+			compileOptions: []fhirpath.CompileOption{compopts.AddFunction("delay", func(in system.Collection) (system.Collection, error) {
+				time.Sleep(time.Second * 2)
+				return in, nil
+			})},
+		},
+		{
+			name:            "evaluates today() based on context, not dependent on latent factors",
+			inputPath:       "today().delay() = today()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+			compileOptions: []fhirpath.CompileOption{compopts.AddFunction("delay", func(in system.Collection) (system.Collection, error) {
+				time.Sleep(time.Second * 2)
+				return in, nil
+			})},
+		},
+		{
+			name:            "evaluates now() using overridden time",
+			inputPath:       "now()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{testDateTime},
+			evaluateOptions: []fhirpath.EvaluateOption{evalopts.OverrideTime(testTime)},
+		},
+		{
+			name:            "evaluate with custom function 'patient()'",
+			inputPath:       "patient() = Patient",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+			compileOptions: []fhirpath.CompileOption{compopts.AddFunction("patient", func(system.Collection) (system.Collection, error) {
+				return system.Collection{patientChu}, nil
+			})},
+		},
+		{
+			name:            "evaluate with custom function startsWith()",
+			inputPath:       "Patient.name[0].family.startsWith('Ch')",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "evaluate with custom function endsWith()",
+			inputPath:       "Patient.name[0].family.endsWith('hu')",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "evaluate with custom function length()",
+			inputPath:       "Patient.name[0].family.length()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Integer(3)},
+		},
+		{
+			name:            "evaluate with custom function upper()",
+			inputPath:       "Patient.name[0].given.upper()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.String("SENPAI")},
+		},
+		{
+			name:            "evaluate with custom function lower()",
+			inputPath:       "Patient.name[0].family.lower()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.String("chu")},
+		},
+		{
+			name:            "evaluate with custom function contains()",
+			inputPath:       "Patient.name[0].given.contains('pai')",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "evaluate with custom function toChars()",
+			inputPath:       "Patient.name[0].family.toChars()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection: system.Collection{
+				system.String('C'),
+				system.String('h'),
+				system.String('u'),
+			},
+		},
+		{
+			name:            "evaluate with custom function substring()",
+			inputPath:       "Patient.name[0].given.substring(1, 4)",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.String("enpa")},
+		},
+		{
+			name:            "evaluate with custom function indexOf()",
+			inputPath:       "Patient.name[0].given.indexOf('pa')",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Integer(3)},
+		},
+		{
+			name:            "evaluate with custom function matches()",
+			inputPath:       "Patient.name[0].family.matches('^[A-Za-z]*$')",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "evaluate with custom function replace()",
+			inputPath:       "Patient.name[0].given.replace('Senpai', 'Oppa')",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.String("Oppa")},
+		},
+		{
+			name:            "evaluate with custom function replaceMatches()",
+			inputPath:       "Patient.name[0].family.replaceMatches('[A-Z]', 'zzz')",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.String("zzzhu")},
+		},
+	}
+
+	testEvaluate(t, testCases)
+}
+
+func TestTypeExpression_Evaluates(t *testing.T) {
+	testCases := []evaluateTestCase{
+		{
+			name:            "returns true for resource type check",
+			inputPath:       "Patient is Patient",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "returns true for resource subtype relationship",
+			inputPath:       "Patient is FHIR.Resource",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "returns true for primitive type check",
+			inputPath:       "Patient.deceased is boolean",
+			inputCollection: []fhir.Resource{patientVoldemort},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "returns false for primitive type case mismatch",
+			inputPath:       "Patient.deceased is Boolean",
+			inputCollection: []fhir.Resource{patientVoldemort},
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+		{
+			name:            "returns true for system type check",
+			inputPath:       "1 is Integer",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "propagates empty collection",
+			inputPath:       "{} is Boolean",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "passes through as expression",
+			inputPath:       "Patient as Patient",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{patientChu},
+		},
+		{
+			name:            "passes through as expression for subtype relationship",
+			inputPath:       "Patient.name.use[0] as FHIR.Element",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{patientChu.Name[0].Use},
+		},
+		{
+			name:            "returns empty if as expression is not correct type",
+			inputPath:       "Patient.name.family[0] as HumanName",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "unwraps polymorphic type with as expression",
+			inputPath:       "Patient.deceased as boolean",
+			inputCollection: []fhir.Resource{patientVoldemort},
+			wantCollection:  system.Collection{fhir.Boolean(true)},
+		},
+		{
+			name:            "passes through system type with as expression",
+			inputPath:       "@2000-12-05 as Date",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.MustParseDate("2000-12-05")},
+		},
+	}
+
+	testEvaluate(t, testCases)
+}
+
+func TestBooleanExpression_Evaluates(t *testing.T) {
+	testCases := []evaluateTestCase{
+		{
+			name:            "evaluates and correctly",
+			inputPath:       "true and false",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+		{
+			name:            "evaluates boolean correctly with protos",
+			inputPath:       "Patient.active and Patient.deceased",
+			inputCollection: []fhir.Resource{patientVoldemort},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "evaluates or correctly",
+			inputPath:       "true or false",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "propogates empty collections correctly",
+			inputPath:       "false or {}",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "evaluates xor correctly",
+			inputPath:       "true xor true",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+		{
+			name:            "evaluates implies correctly",
+			inputPath:       "false implies false",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "not function inverts input",
+			inputPath:       "Patient.active.not()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+	}
+
+	testEvaluate(t, testCases)
+}
+
+func TestComparisonExpression_ReturnsBool(t *testing.T) {
+	testCases := []evaluateTestCase{
+		{
+			name:            "compares strings",
+			inputPath:       "'abc' > 'ABC'",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "compares integer with decimal",
+			inputPath:       "4 <= 4.0",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "compares quantities of the same precision",
+			inputPath:       "3.2 'kg' > 9.7 'kg'",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+		{
+			name:            "returns empty for quantities of different precision",
+			inputPath:       "99.9 'cm' < 1 'm'",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "compares dates correctly",
+			inputPath:       "@2018-03-01 >= @2018-03-01",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "returns empty for mismatched time precision",
+			inputPath:       "@T08:30 > @T08:30:00",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "correctly compares times",
+			inputPath:       "@T10:29:59.999 < @T10:30:00",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "validate the age of an individual",
+			inputPath:       "Patient.birthDate + 23 'years' <= today()",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+	}
+
+	testEvaluate(t, testCases)
+}
+
+func TestArithmetic_ReturnsResult(t *testing.T) {
+	testCases := []evaluateTestCase{
+		{
+			name:            "adds dates with quantity",
+			inputPath:       "@2012-12-12 + 12 days",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.MustParseDate("2012-12-24")},
+		},
+		{
+			name:            "concatenates strings",
+			inputPath:       "'hello ' & 'world'",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.String("hello world")},
+		},
+		{
+			name:            "subtracts integer from quantity",
+			inputPath:       "8 'kg' - 4",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.MustParseQuantity("4", "kg")},
+		},
+		{
+			name:            "multiplies values together",
+			inputPath:       "8 * 4.2",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Decimal(decimal.NewFromFloat(33.6))},
+		},
+		{
+			name:            "divides values",
+			inputPath:       "8 / 2.5",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Decimal(decimal.NewFromFloat(3.2))},
+		},
+		{
+			name:            "performs floor division",
+			inputPath:       "29 div 10",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Integer(2)},
+		},
+		{
+			name:            "performs modulo operation",
+			inputPath:       "100 mod 11",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Integer(1)},
+		},
+	}
+
+	testEvaluate(t, testCases)
+}
+
+func TestCompile_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name           string
+		inputPath      string
+		compileOptions []fhirpath.CompileOption
+	}{
+		{
+			name:      "mismatched parentheses",
+			inputPath: "Patient.name.where(use = official",
+		},
+		{
+			name:      "invalid character",
+			inputPath: "Patient.*name",
+		},
+		{
+			name:      "invalid expression (misspelling)",
+			inputPath: "Patient.name aand Patient.name",
+		},
+		{
+			name:      "invalid expression (non-existent operator)",
+			inputPath: "Patient.name nor Patient.name",
+		},
+		{
+			name:      "invalid character (lexer error)",
+			inputPath: "Patient^",
+		},
+		{
+			name:      "non-existent function",
+			inputPath: "Patient.notAFunc()",
+		},
+		{
+			name:           "expanding function table with bad function",
+			inputPath:      "Patient.badFn()",
+			compileOptions: []fhirpath.CompileOption{compopts.AddFunction("badFn", func() {})},
+		},
+		{
+			name:           "attempting to override existing function",
+			inputPath:      "Patient.where()",
+			compileOptions: []fhirpath.CompileOption{compopts.AddFunction("where", func(system.Collection) (system.Collection, error) { return nil, nil })},
+		},
+		{
+			name:      "evaluating function with mismatched arity",
+			inputPath: "Patient.name.where(use = 'official', use = 'nickname')",
+		},
+		{
+			name:      "evaluating function with invalid arguments",
+			inputPath: "Patient.name.where(invalid $ expr)",
+		},
+		{
+			name:      "resolving invalid type specifier",
+			inputPath: "1 is System.Patient",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if _, err := fhirpath.Compile(tc.inputPath, tc.compileOptions...); err == nil {
+				t.Errorf("Compiling \"%s\" doesn't raise error when expected to", tc.inputPath)
+			}
+		})
+	}
+}
+
+func TestEvaluate_ReturnsError(t *testing.T) {
+	alwaysFails := func(system.Collection) (system.Collection, error) {
+		return nil, errors.New("some error")
+	}
+	testCases := []struct {
+		name            string
+		inputPath       string
+		inputCollection []fhir.Resource
+		compileOptions  []fhirpath.CompileOption
+		evaluateOptions []fhirpath.EvaluateOption
+	}{
+		{
+			name:            "non-integer index returns error",
+			inputPath:       "Patient.name['not a number']",
+			inputCollection: []fhir.Resource{patientChu},
+		},
+		{
+			name:            "evaluating failing function propagates error",
+			inputPath:       "alwaysFails()",
+			inputCollection: []fhir.Resource{},
+			compileOptions:  []fhirpath.CompileOption{compopts.AddFunction("alwaysFails", alwaysFails)},
+		},
+		{
+			name:            "evaluating is expression on non-singleton collection",
+			inputPath:       "Patient.name is string",
+			inputCollection: []fhir.Resource{patientChu},
+		},
+		{
+			name:            "comparing unsupported types",
+			inputPath:       "true > 0",
+			inputCollection: []fhir.Resource{},
+		},
+		{
+			name:            "arithmetic on unsupported types",
+			inputPath:       "1 + true",
+			inputCollection: []fhir.Resource{},
+		},
+		{
+			name:            "misspelled identifier raises error",
+			inputPath:       "Patient.nam.given",
+			inputCollection: []fhir.Resource{patientVoldemort},
+		},
+		{
+			name:            "overriding existing constant",
+			inputPath:       "'valid fhirpath'",
+			inputCollection: []fhir.Resource{},
+			evaluateOptions: []fhirpath.EvaluateOption{
+				evalopts.EnvVariable("context", system.String("context")),
+			},
+		},
+		{
+			name:            "adding unsupported type as constant",
+			inputPath:       "%var",
+			inputCollection: []fhir.Resource{},
+			evaluateOptions: []fhirpath.EvaluateOption{
+				evalopts.EnvVariable("var", 1),
+			},
+		},
+		{
+			name:            "adding unsupported type within collection as constant",
+			inputPath:       "%collection",
+			inputCollection: []fhir.Resource{},
+			evaluateOptions: []fhirpath.EvaluateOption{
+				evalopts.EnvVariable("collection", system.Collection{system.Integer(1), 1}),
+			},
+		},
+		{
+			name:            "negating unsupported type",
+			inputPath:       "-'string'",
+			inputCollection: []fhir.Resource{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			expression, err := fhirpath.Compile(tc.inputPath, tc.compileOptions...)
+			if err != nil {
+				t.Fatalf("compiling \"%s\" raised unexpected error: %v", tc.inputPath, err)
+			}
+			if _, err = expression.Evaluate(tc.inputCollection, tc.evaluateOptions...); err == nil {
+				t.Errorf("Evaluating expression \"%s\" doesn't raise error when expected to", tc.inputPath)
+			}
+		})
+	}
+}
+
+func TestExternalConstantExpression_ReturnsConstant(t *testing.T) {
+	testCases := []evaluateTestCase{
+		{
+			name:            "system type constant",
+			inputPath:       "%var",
+			inputCollection: []fhir.Resource{},
+			evaluateOptions: []fhirpath.EvaluateOption{
+				evalopts.EnvVariable("var", system.String("hello")),
+			},
+			wantCollection: system.Collection{system.String("hello")},
+		},
+		{
+			name:            "proto type constant",
+			inputPath:       "%patient",
+			inputCollection: []fhir.Resource{},
+			evaluateOptions: []fhirpath.EvaluateOption{
+				evalopts.EnvVariable("patient", patientChu),
+			},
+			wantCollection: system.Collection{patientChu},
+		},
+		{
+			name:            "collection constant containing system and proto types",
+			inputPath:       "%collection",
+			inputCollection: []fhir.Resource{},
+			evaluateOptions: []fhirpath.EvaluateOption{
+				evalopts.EnvVariable("collection", system.Collection{system.String("hello"), patientChu}),
+			},
+			wantCollection: system.Collection{system.String("hello"), patientChu},
+		},
+		{
+			name:            "returns input as %context variable",
+			inputPath:       "%context",
+			inputCollection: []fhir.Resource{patientChu},
+			wantCollection:  system.Collection{patientChu},
+		},
+		{
+			name:            "returns ucum url as %ucum",
+			inputPath:       "%ucum",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.String("http://unitsofmeasure.org")},
+		},
+	}
+
+	testEvaluate(t, testCases)
+}
+
+func TestPolarityExpression(t *testing.T) {
+	testCases := []evaluateTestCase{
+		{
+			name:            "negates integer",
+			inputPath:       "-1",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Integer(-1)},
+		},
+		{
+			name:            "does nothing when using '+'",
+			inputPath:       "+2.45",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Decimal(decimal.NewFromFloat(2.45))},
+		},
+		{
+			name:            "negates field from proto",
+			inputPath:       "-(Patient.multipleBirth as integer)",
+			inputCollection: []fhir.Resource{patientVoldemort},
+			wantCollection:  system.Collection{system.Integer(-2)},
+		},
+		{
+			name:            "performs arithmetic correctly with negatives",
+			inputPath:       "-1 - (-2)",
+			inputCollection: []fhir.Resource{},
+			wantCollection:  system.Collection{system.Integer(1)},
+		},
+	}
+
+	testEvaluate(t, testCases)
+}
+
+func TestMustCompile_CompileError_Panics(t *testing.T) {
+	defer func() { _ = recover() }()
+
+	fhirpath.MustCompile("Patient.name.where(use = official")
+
+	t.Errorf("MustCompile: Expected panic")
+}
+
+func TestMustCompile_ValidExpression_ReturnsExpression(t *testing.T) {
+	result := fhirpath.MustCompile("Patient.name")
+
+	if result == nil {
+		t.Errorf("MustCompile: Expected result")
+	}
+}
diff --git a/fhirpath/fhirpathtest/fhirpathtest.go b/fhirpath/fhirpathtest/fhirpathtest.go
new file mode 100644
index 0000000..f9dc733
--- /dev/null
+++ b/fhirpath/fhirpathtest/fhirpathtest.go
@@ -0,0 +1,51 @@
+/*
+Package fhirpathtest provides an easy way to generate test-doubles within
+FHIRPath.
+*/
+package fhirpathtest
+
+import (
+	"github.com/verily-src/fhirpath-go/fhirpath"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+// Return creates a FHIRPath expression that will always return the given
+// values.
+func Return(args ...any) *fhirpath.Expression {
+	return ReturnCollection(system.Collection(args))
+}
+
+// ReturnCollection creates a FHIRPath expression that will always return the
+// given input collection.
+func ReturnCollection(collection system.Collection) *fhirpath.Expression {
+	return fhirpath.MustCompile("return()",
+		fhirpath.WithFunction("return", func(system.Collection) (system.Collection, error) {
+			return collection, nil
+		}),
+	)
+}
+
+// Error creates a FHIRPath expression that will always return the specified error.
+func Error(err error) *fhirpath.Expression {
+	return fhirpath.MustCompile("return()",
+		fhirpath.WithFunction("return", func(system.Collection) (system.Collection, error) {
+			return nil, err
+		}),
+	)
+}
+
+var (
+	// Empty is a FHIRPath expression that returns an empty collection when
+	// evaluated.
+	Empty = Return(system.Collection{})
+
+	// True is a FHIRPath expression that returns a collection containing a single
+	// system boolean of 'true'. This is useful for testing expected boolean
+	// logic in paths.
+	True = Return(system.Collection{system.Boolean(true)})
+
+	// False is a FHIRPath expression that returns a collection containing a single
+	// system boolean of 'false'. This is useful for testing expected boolean
+	// logic in paths.
+	False = Return(system.Collection{system.Boolean(false)})
+)
diff --git a/fhirpath/fhirpathtest/fhirpathtest_example_test.go b/fhirpath/fhirpathtest/fhirpathtest_example_test.go
new file mode 100644
index 0000000..2c0ea04
--- /dev/null
+++ b/fhirpath/fhirpathtest/fhirpathtest_example_test.go
@@ -0,0 +1,47 @@
+package fhirpathtest_test
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/fhirpathtest"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+func ExampleError() {
+	want := errors.New("example error")
+	expr := fhirpathtest.Error(want)
+
+	_, err := expr.Evaluate([]fhir.Resource{})
+	if errors.Is(err, want) {
+		fmt.Printf("err = '%v'", want)
+	}
+
+	// Output: err = 'example error'
+}
+
+func ExampleReturn() {
+	want := system.Boolean(true)
+	expr := fhirpathtest.Return(want)
+
+	got, err := expr.Evaluate([]fhir.Resource{})
+	if err != nil {
+		panic(err)
+	}
+
+	fmt.Printf("got = %v", bool(got[0].(system.Boolean)))
+	// Output: got = true
+}
+func ExampleReturnCollection() {
+	want := system.Collection{system.Boolean(true)}
+	expr := fhirpathtest.ReturnCollection(want)
+
+	got, err := expr.Evaluate([]fhir.Resource{})
+	if err != nil {
+		panic(err)
+	}
+
+	fmt.Printf("got = %v", bool(got[0].(system.Boolean)))
+	// Output: got = true
+}
diff --git a/fhirpath/fhirpathtest/fhirpathtest_test.go b/fhirpath/fhirpathtest/fhirpathtest_test.go
new file mode 100644
index 0000000..1ea6443
--- /dev/null
+++ b/fhirpath/fhirpathtest/fhirpathtest_test.go
@@ -0,0 +1,61 @@
+package fhirpathtest_test
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/fhirtest"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"github.com/verily-src/fhirpath-go/fhirpath/fhirpathtest"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func TestError_Evaluates_ReturnsErr(t *testing.T) {
+	wantErr := errors.New("test error")
+	expr := fhirpathtest.Error(wantErr)
+
+	_, err := expr.Evaluate([]fhir.Resource{})
+
+	if got, want := err, wantErr; !errors.Is(got, want) {
+		t.Errorf("Error: want err %v, got %v", want, got)
+	}
+}
+
+func TestReturn_Evaluates_ReturnsCollectionOfEntries(t *testing.T) {
+	res := fhirtest.NewResource(t, resource.Patient)
+	want := system.Collection{res}
+	expr := fhirpathtest.Return(res)
+
+	got, err := expr.Evaluate([]fhir.Resource{})
+	if err != nil {
+		t.Fatalf("Return: unexpected err: %v", err)
+	}
+
+	if len(got) != len(want) {
+		t.Fatalf("Return: mismatched size; want %v, got %v", len(got), len(want))
+	}
+	if !cmp.Equal(got[0], want[0], protocmp.Transform()) {
+		t.Errorf("Return: want %v, got %v", want, got)
+	}
+}
+
+func TestReturnCollection_Evaluates_ReturnsCollectionOfEntries(t *testing.T) {
+	res := fhirtest.NewResource(t, resource.Patient)
+	want := system.Collection{res}
+	expr := fhirpathtest.ReturnCollection(want)
+
+	got, err := expr.Evaluate([]fhir.Resource{})
+	if err != nil {
+		t.Fatalf("Return: unexpected err: %v", err)
+	}
+
+	if len(got) != len(want) {
+		t.Fatalf("ReturnCollection: mismatched size; want %v, got %v", len(got), len(want))
+	}
+	if !cmp.Equal(got[0], want[0], protocmp.Transform()) {
+		t.Errorf("ReturnCollection: want %v, got %v", want, got)
+	}
+}
diff --git a/fhirpath/internal/compile/compile.go b/fhirpath/internal/compile/compile.go
new file mode 100644
index 0000000..510e78e
--- /dev/null
+++ b/fhirpath/internal/compile/compile.go
@@ -0,0 +1,46 @@
+package compile
+
+import (
+	"github.com/antlr4-go/antlr/v4"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/grammar"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/opts"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/parser"
+)
+
+// PopulateConfig creates a CompileConfig and prepopulates it with
+// a function table and any provided options.
+func PopulateConfig(options ...opts.CompileOption) (*opts.CompileConfig, error) {
+	config := &opts.CompileConfig{
+		Table: funcs.Clone(),
+	}
+	config, err := opts.ApplyOptions(config, options...)
+	if err != nil {
+		return nil, err
+	}
+	return config, err
+}
+
+// Tree creates an ANTLR parsing context from the provided FHIRPath string.
+func Tree(expr string) (grammar.IProgContext, error) {
+	inputStream := antlr.NewInputStream(expr)
+	errorListener := &parser.FHIRPathErrorListener{}
+
+	// Lex the input stream
+	lexer := grammar.NewfhirpathLexer(inputStream)
+	lexer.RemoveErrorListeners()
+	lexer.AddErrorListener(errorListener)
+	tokens := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel)
+
+	// Parse the tokens
+	p := grammar.NewfhirpathParser(tokens)
+	p.RemoveErrorListeners()
+	p.AddErrorListener(errorListener)
+	tree := p.Prog()
+
+	if err := errorListener.Error(); err != nil {
+		return nil, err
+	}
+
+	return tree, nil
+}
diff --git a/fhirpath/internal/compile/doc.go b/fhirpath/internal/compile/doc.go
new file mode 100644
index 0000000..51a9394
--- /dev/null
+++ b/fhirpath/internal/compile/doc.go
@@ -0,0 +1,5 @@
+/*
+Package compile provides helpers for compiling
+FHIRPath strings into FHIRPath expressions.
+*/
+package compile
diff --git a/fhirpath/internal/expr/arithmetic.go b/fhirpath/internal/expr/arithmetic.go
new file mode 100644
index 0000000..a753f89
--- /dev/null
+++ b/fhirpath/internal/expr/arithmetic.go
@@ -0,0 +1,197 @@
+package expr
+
+import (
+	"fmt"
+
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+// EvaluateAdd takes in two system types, and calls the appropriate Add method.
+func EvaluateAdd(lhs, rhs system.Any) (system.Any, error) {
+	switch left := lhs.(type) {
+	case system.String:
+		if right, ok := rhs.(system.String); ok {
+			return left.Add(right), nil
+		}
+		return nil, typeMismatch(Add, lhs, rhs)
+	case system.Integer:
+		if right, ok := rhs.(system.Integer); ok {
+			return left.Add(right)
+		}
+		return nil, typeMismatch(Add, lhs, rhs)
+	case system.Decimal:
+		if right, ok := rhs.(system.Decimal); ok {
+			return left.Add(right), nil
+		}
+		return nil, typeMismatch(Add, lhs, rhs)
+	case system.Time:
+		if right, ok := rhs.(system.Quantity); ok {
+			return left.Add(right)
+		}
+		return nil, typeMismatch(Add, lhs, rhs)
+	case system.Date:
+		if right, ok := rhs.(system.Quantity); ok {
+			return left.Add(right)
+		}
+		return nil, typeMismatch(Add, lhs, rhs)
+	case system.DateTime:
+		if right, ok := rhs.(system.Quantity); ok {
+			return left.Add(right)
+		}
+		return nil, typeMismatch(Add, lhs, rhs)
+	case system.Quantity:
+		if right, ok := rhs.(system.Quantity); ok {
+			return left.Add(right)
+		}
+		return nil, typeMismatch(Add, lhs, rhs)
+	default:
+		return nil, typeMismatch(Add, lhs, rhs)
+	}
+}
+
+// EvaluateSub takes in two system types, and calls the appropriate Sub method.
+func EvaluateSub(lhs, rhs system.Any) (system.Any, error) {
+	switch left := lhs.(type) {
+	case system.Integer:
+		if right, ok := rhs.(system.Integer); ok {
+			return left.Sub(right)
+		}
+		return nil, typeMismatch(Sub, lhs, rhs)
+	case system.Decimal:
+		if right, ok := rhs.(system.Decimal); ok {
+			return left.Sub(right), nil
+		}
+		return nil, typeMismatch(Sub, lhs, rhs)
+	case system.Time:
+		if right, ok := rhs.(system.Quantity); ok {
+			return left.Sub(right)
+		}
+		return nil, typeMismatch(Sub, lhs, rhs)
+	case system.Date:
+		if right, ok := rhs.(system.Quantity); ok {
+			return left.Sub(right)
+		}
+		return nil, typeMismatch(Sub, lhs, rhs)
+	case system.DateTime:
+		if right, ok := rhs.(system.Quantity); ok {
+			return left.Sub(right)
+		}
+		return nil, typeMismatch(Sub, lhs, rhs)
+	case system.Quantity:
+		if right, ok := rhs.(system.Quantity); ok {
+			return left.Sub(right)
+		}
+		return nil, typeMismatch(Sub, lhs, rhs)
+	default:
+		return nil, typeMismatch(Sub, lhs, rhs)
+	}
+}
+
+// EvaluateMul takes in two system types, and calls the appropriate Mul method.
+func EvaluateMul(lhs, rhs system.Any) (system.Any, error) {
+	switch left := lhs.(type) {
+	case system.Integer:
+		if right, ok := rhs.(system.Integer); ok {
+			return left.Mul(right)
+		}
+		if _, ok := rhs.(system.Quantity); ok {
+			return nil, fmt.Errorf("%w: PHP-7340", ErrToBeImplemented)
+		}
+		return nil, typeMismatch(Mul, lhs, rhs)
+	case system.Decimal:
+		if right, ok := rhs.(system.Decimal); ok {
+			return left.Mul(right), nil
+		}
+		if _, ok := rhs.(system.Quantity); ok {
+			return nil, fmt.Errorf("%w: PHP-7340", ErrToBeImplemented)
+		}
+		return nil, typeMismatch(Mul, lhs, rhs)
+	case system.Quantity:
+		return nil, fmt.Errorf("%w: PHP-7171", ErrToBeImplemented)
+	default:
+		return nil, typeMismatch(Mul, lhs, rhs)
+	}
+}
+
+// EvaluateDiv takes in two system types, and calls the appropriate Div method.
+func EvaluateDiv(lhs, rhs system.Any) (system.Any, error) {
+	switch left := lhs.(type) {
+	case system.Integer:
+		if right, ok := rhs.(system.Integer); ok {
+			return left.Div(right), nil
+		}
+		if _, ok := rhs.(system.Quantity); ok {
+			return nil, fmt.Errorf("%w: PHP-7340", ErrToBeImplemented)
+		}
+		return nil, typeMismatch(Div, lhs, rhs)
+	case system.Decimal:
+		if right, ok := rhs.(system.Decimal); ok {
+			return left.Div(right), nil
+		}
+		if _, ok := rhs.(system.Quantity); ok {
+			return nil, fmt.Errorf("%w: PHP-7340", ErrToBeImplemented)
+		}
+		return nil, typeMismatch(Div, lhs, rhs)
+	case system.Quantity:
+		return nil, fmt.Errorf("%w: PHP-7171", ErrToBeImplemented)
+	default:
+		return nil, typeMismatch(Div, lhs, rhs)
+	}
+}
+
+// EvaluateFloorDiv takes in two system types, and calls the appropriate FloorDiv method.
+func EvaluateFloorDiv(lhs, rhs system.Any) (system.Any, error) {
+	switch left := lhs.(type) {
+	case system.Integer:
+		if right, ok := rhs.(system.Integer); ok {
+			return left.FloorDiv(right), nil
+		}
+		if _, ok := rhs.(system.Quantity); ok {
+			return nil, fmt.Errorf("%w: PHP-7340", ErrToBeImplemented)
+		}
+		return nil, typeMismatch(FloorDiv, lhs, rhs)
+	case system.Decimal:
+		if right, ok := rhs.(system.Decimal); ok {
+			return left.FloorDiv(right)
+		}
+		if _, ok := rhs.(system.Quantity); ok {
+			return nil, fmt.Errorf("%w: PHP-7340", ErrToBeImplemented)
+		}
+		return nil, typeMismatch(FloorDiv, lhs, rhs)
+	case system.Quantity:
+		return nil, fmt.Errorf("%w: PHP-7171", ErrToBeImplemented)
+	default:
+		return nil, typeMismatch(FloorDiv, lhs, rhs)
+	}
+}
+
+// EvaluateMod takes in two system types, and calls the appropriate Mod method.
+func EvaluateMod(lhs, rhs system.Any) (system.Any, error) {
+	switch left := lhs.(type) {
+	case system.Integer:
+		if right, ok := rhs.(system.Integer); ok {
+			return left.Mod(right), nil
+		}
+		if _, ok := rhs.(system.Quantity); ok {
+			return nil, fmt.Errorf("%w: PHP-7340", ErrToBeImplemented)
+		}
+		return nil, typeMismatch(Mod, lhs, rhs)
+	case system.Decimal:
+		if right, ok := rhs.(system.Decimal); ok {
+			return left.Mod(right), nil
+		}
+		if _, ok := rhs.(system.Quantity); ok {
+			return nil, fmt.Errorf("%w: PHP-7340", ErrToBeImplemented)
+		}
+		return nil, typeMismatch(Mod, lhs, rhs)
+	case system.Quantity:
+		return nil, fmt.Errorf("%w: PHP-7171", ErrToBeImplemented)
+	default:
+		return nil, typeMismatch(Mod, lhs, rhs)
+	}
+}
+
+// typeMismatch generates an unsupported operation error.
+func typeMismatch(op Operator, lhs, rhs system.Any) error {
+	return fmt.Errorf("%w: %T %s %T", system.ErrTypeMismatch, lhs, op, rhs)
+}
diff --git a/fhirpath/internal/expr/booleans.go b/fhirpath/internal/expr/booleans.go
new file mode 100644
index 0000000..98d44b6
--- /dev/null
+++ b/fhirpath/internal/expr/booleans.go
@@ -0,0 +1,47 @@
+package expr
+
+import "github.com/verily-src/fhirpath-go/fhirpath/system"
+
+func evaluateAnd(left []system.Boolean, right []system.Boolean) system.Collection {
+	if len(left) > 0 && len(right) > 0 {
+		result := system.Boolean(left[0] && right[0])
+		return system.Collection{result}
+	}
+	// returns false if either boolean is false, regardless of whether or not the other is empty.
+	if (len(left) == 1 && !left[0]) || (len(right) == 1 && !right[0]) {
+		return system.Collection{system.Boolean(false)}
+	}
+	return system.Collection{}
+}
+
+func evaluateOr(left []system.Boolean, right []system.Boolean) system.Collection {
+	if len(left) > 0 && len(right) > 0 {
+		result := system.Boolean(left[0] || right[0])
+		return system.Collection{result}
+	}
+	// returns false if either boolean is true, regardless of whether or not the other is empty.
+	if (len(left) == 1 && left[0]) || (len(right) == 1 && right[0]) {
+		return system.Collection{system.Boolean(true)}
+	}
+	return system.Collection{}
+}
+
+func evaluateXor(left []system.Boolean, right []system.Boolean) system.Collection {
+	if len(left) > 0 && len(right) > 0 {
+		result := system.Boolean(left[0] != right[0])
+		return system.Collection{result}
+	}
+	return system.Collection{}
+}
+
+func evaluateImplies(left []system.Boolean, right []system.Boolean) system.Collection {
+	if len(left) > 0 && len(right) > 0 {
+		result := system.Boolean(!left[0] || right[0])
+		return system.Collection{result}
+	}
+	// returns true if left is false, or if right is true, regardless of whether or not the other is empty.
+	if (len(left) > 0 && !left[0]) || (len(right) > 0 && right[0]) {
+		return system.Collection{system.Boolean(true)}
+	}
+	return system.Collection{}
+}
diff --git a/fhirpath/internal/expr/context.go b/fhirpath/internal/expr/context.go
new file mode 100644
index 0000000..b65d9da
--- /dev/null
+++ b/fhirpath/internal/expr/context.go
@@ -0,0 +1,47 @@
+package expr
+
+import (
+	"time"
+
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+// Context holds the global time and external constant
+// variable map, to enable deterministic evaluation.
+type Context struct {
+	Now               time.Time
+	ExternalConstants map[string]any
+
+	// LastResult is required for implementing most FHIRPatch operations, since
+	// a reference to the node before the one being (inserted, replaced, moved) is
+	// necessary in order to alter the containing object.
+	LastResult system.Collection
+
+	// BeforeLastResult is necessary for implementing FHIRPatch delete due to an
+	// edge-case, where deleting a specific element from a list requires a pointer
+	// to the container that holds the list. In a path like `Patient.name.given[0]`,
+	// the 'LastResult' will be the unwrapped list from 'given', but we need the
+	// 'name' element that contains the 'given' list in order to alter the list.
+	BeforeLastResult system.Collection
+}
+
+// Clone copies this Context object to produce a new instance.
+func (c *Context) Clone() *Context {
+	return &Context{
+		Now:               c.Now,
+		ExternalConstants: c.ExternalConstants,
+		LastResult:        c.LastResult,
+	}
+}
+
+// InitializeContext returns a base context, initialized with current time and initial
+// constant variables set.
+func InitializeContext(input system.Collection) *Context {
+	return &Context{
+		Now: time.Now().Local().UTC(),
+		ExternalConstants: map[string]any{
+			"context": input,
+			"ucum":    system.String("http://unitsofmeasure.org"),
+		},
+	}
+}
diff --git a/fhirpath/internal/expr/doc.go b/fhirpath/internal/expr/doc.go
new file mode 100644
index 0000000..1123e43
--- /dev/null
+++ b/fhirpath/internal/expr/doc.go
@@ -0,0 +1,5 @@
+/*
+Package expr contains all the expression types
+and related logic for FHIRPath expressions.
+*/
+package expr
diff --git a/fhirpath/internal/expr/expressions.go b/fhirpath/internal/expr/expressions.go
new file mode 100644
index 0000000..e25b9b7
--- /dev/null
+++ b/fhirpath/internal/expr/expressions.go
@@ -0,0 +1,779 @@
+package expr
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	bcrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	"github.com/iancoleman/strcase"
+	"github.com/shopspring/decimal"
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/containedresource"
+	"github.com/verily-src/fhirpath-go/internal/fhirconv"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/reflection"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+	"github.com/verily-src/fhirpath-go/internal/protofields"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+var (
+	ErrNotSingleton     = errors.New("collection is not a singleton")
+	ErrInvalidType      = errors.New("collection evaluates to incorrect type")
+	ErrInvalidOperator  = errors.New("received invalid operator")
+	ErrToBeImplemented  = errors.New("expression not yet implemented")
+	ErrInvalidField     = errors.New("invalid field")
+	ErrConstantNotFound = errors.New("external constant not found")
+)
+
+// Expression is the abstraction for all FHIRPath expressions,
+// ie. taking in some input collection and outputting some collection.
+type Expression interface {
+	Evaluate(*Context, system.Collection) (system.Collection, error)
+}
+
+// ExpressionSequence abstracts the flow of evaluation for
+// compound expressions. It consists of a sequence of expressions
+// whose outputs flow into the inputs of the next expression.
+type ExpressionSequence struct {
+	Expressions []Expression
+}
+
+// Evaluate iterates through the ExpressionSequence, feeding the output of
+// an evaluation to the next Expression.
+func (s *ExpressionSequence) Evaluate(ctx *Context, input system.Collection) (system.Collection, error) {
+	output := input
+
+	for _, expr := range s.Expressions {
+		result, err := expr.Evaluate(ctx, output)
+		// raise error as soon as one is encountered
+		if err != nil {
+			return nil, err
+		}
+		output = result
+	}
+	return output, nil
+}
+
+var _ Expression = (*ExpressionSequence)(nil)
+
+// IdentityExpression encapsulates the top-level expression, ie. the
+// first step in the chain of evaluation. A no-op expression that
+// returns itself.
+type IdentityExpression struct{}
+
+// Evaluate returns the input collection, without raising
+// an error.
+func (*IdentityExpression) Evaluate(ctx *Context, input system.Collection) (system.Collection, error) {
+	return input, nil
+}
+
+var _ Expression = (*IdentityExpression)(nil)
+
+// FieldExpression is the expression that accesses the specified
+// FieldName in the input collection.
+type FieldExpression struct {
+	FieldName  string
+	Permissive bool
+}
+
+// Evaluate filters the input collections by those that contain
+// the FieldName string, and returns the result.
+func (e *FieldExpression) Evaluate(ctx *Context, input system.Collection) (system.Collection, error) {
+	output := system.Collection{}
+
+	for _, item := range input {
+		message, ok := item.(proto.Message)
+		if !ok {
+			if e.Permissive {
+				continue
+			}
+			return nil, e.errField(item)
+		}
+
+		// Date, Time, DateTime, and Instant have "fake" fields 'value_us', 'timezone',
+		// and 'precision'. This checks to ensure that such fields aren't being accessed,
+		// since they aren't actually real and don't exist in the FHIR spec.
+		if !e.isEvaluable(message) {
+			return nil, e.errField(message)
+		}
+
+		// unwrap if a ContainedResource
+		if contained, ok := message.(*bcrpb.ContainedResource); ok {
+			message = containedresource.Unwrap(contained)
+		}
+
+		// Get desired field
+		fieldName := strcase.ToSnake(e.FieldName)
+		reflect := message.ProtoReflect()
+		field := reflect.Descriptor().Fields().ByName(protoreflect.Name(fieldName))
+
+		// extract field and append to output, flattening
+		// if the field is a list. Raises error if field doesn't exist
+		if field == nil {
+			// If the field is a reference, we need to combine the type and
+			// ID fields to create a usable reference, e.g. Type/ID. Since
+			// ID is a oneof (e.g. questionnaire_id), we need to determine
+			// which it is to find the appropriate field.
+			if fieldName == "reference" {
+				if reference, ok := message.(*dtpb.Reference); ok {
+					refString := e.unwrapReference(reference)
+					if refString != nil {
+						output = append(output, refString)
+					}
+					continue
+				}
+			}
+
+			// Attempting to get a "value" field from a Date, DateTime, Time, or Instant
+			// needs to convert the value to a System String type.
+			// The FHIR Protos model time datatypes using a "value_us" field, which
+			// is normalized here, since the FHIR spec models these types as strings
+			// with a "value" field.
+			if fieldName == "value" {
+				switch v := message.(type) {
+				case *dtpb.Date:
+					output = append(output, system.String(fhirconv.DateToString(v)))
+					continue
+				case *dtpb.DateTime:
+					output = append(output, system.String(fhirconv.DateTimeToString(v)))
+					continue
+				case *dtpb.Time:
+					output = append(output, system.String(fhirconv.TimeToString(v)))
+					continue
+				case *dtpb.Instant:
+					output = append(output, system.String(fhirconv.InstantToString(v)))
+					continue
+				}
+			}
+
+			// Try again with "_value" added because sometimes Google protos do that
+			// for primitives like:
+			// Observation.ValueX.String --> Observation_ValueX_StringValue
+			fieldName = fieldName + "_value"
+			field = reflect.Descriptor().Fields().ByName(protoreflect.Name(fieldName))
+			if field == nil {
+				return nil, fmt.Errorf("%w: %s not a field on %T", ErrInvalidField, fieldName, message)
+			}
+		}
+
+		// If the field is not a message, it is a primitive (enum or go native type).
+		// So, it can be cast to a system type. Otherwise, a field is being accessed that
+		// shouldn't be accessed, so the error is returned.
+		if field.Kind() != protoreflect.MessageKind {
+			primitive, err := system.From(message)
+			if err != nil {
+				return nil, err
+			}
+			output = append(output, primitive)
+			continue
+		}
+
+		unwrap := e.unwrapOneof
+		if e.Permissive {
+			unwrap = func(obj proto.Message) proto.Message { return obj }
+		}
+
+		if !field.IsList() {
+			message := reflect.Get(field).Message()
+			if !message.IsValid() {
+				continue
+			}
+			output = append(output, unwrap(message.Interface()))
+			continue
+		}
+		content := reflect.Get(field).List()
+		for i := 0; i < content.Len(); i++ { // flatten out list
+			result := content.Get(i).Message().Interface()
+			output = append(output, unwrap(result))
+		}
+	}
+	return output, nil
+}
+
+var nonEvaluableFields = []string{
+	"valueUs", "precision", "timezone",
+}
+
+func (e *FieldExpression) isEvaluable(msg proto.Message) bool {
+	if e.Permissive {
+		return true
+	}
+
+	// Prevent snake_case fields, since all FHIRPath fields need to be in
+	// camelCase.
+	if strcase.ToLowerCamel(e.FieldName) != e.FieldName {
+		return false
+	}
+
+	// Prevent manually accessing idiosynchratic fields from google/fhir like
+	// value_us, precision, and time_zone
+	switch msg.(type) {
+	case *dtpb.Time, *dtpb.Date, *dtpb.DateTime, *dtpb.Instant:
+		return !slices.Includes(nonEvaluableFields, e.FieldName)
+	}
+
+	return true
+}
+
+func (e *FieldExpression) errField(object any) error {
+	return fmt.Errorf("%w: %s not a field on %T", ErrInvalidField, e.FieldName, object)
+}
+
+func (e *FieldExpression) unwrapReference(ref *dtpb.Reference) *dtpb.String {
+	if ref.GetReference() == nil {
+		return nil
+	}
+	rv := ref.ProtoReflect()
+	switch ref := ref.GetReference().(type) {
+	case *dtpb.Reference_Uri:
+		return fhir.String(ref.Uri.GetValue())
+	case *dtpb.Reference_Fragment:
+		return fhir.String("#" + ref.Fragment.GetValue())
+	default:
+		descriptor := rv.Descriptor()
+		oneof := descriptor.Oneofs().ByName("reference")
+		field := rv.WhichOneof(oneof)
+		if field == nil {
+			return nil
+		}
+		refid := rv.Get(field).Message().Interface().(*dtpb.ReferenceId)
+		fieldName, ok := strings.CutSuffix(string(field.Name()), "_id")
+		if !ok {
+			return nil
+		}
+		fieldName = strcase.ToCamel(fieldName)
+		if history := refid.GetHistory(); history != nil {
+			return fhir.String(fmt.Sprintf("%v/%v/_history/%v", fieldName, refid.GetValue(), history.GetValue()))
+		}
+		return fhir.String(fmt.Sprintf("%v/%v", fieldName, refid.GetValue()))
+	}
+}
+
+func (e *FieldExpression) unwrapOneof(obj proto.Message) proto.Message {
+	message := obj.ProtoReflect()
+	descriptor := message.Descriptor()
+	if name := string(descriptor.Name()); !(strings.HasSuffix(name, "ValueX") || name == "ContainedResource") {
+		return obj
+	}
+	oneofsNum := descriptor.Oneofs().Len()
+	if oneofsNum != 1 {
+		return obj
+	}
+
+	oneof := descriptor.Oneofs().Get(0)
+	field := message.WhichOneof(oneof)
+	if oneof == nil || field == nil {
+		return obj
+	}
+	if msg := message.Get(field).Message(); msg != nil {
+		return msg.Interface()
+	}
+	return obj
+}
+
+var _ Expression = (*FieldExpression)(nil)
+
+// TypeExpression contains the FHIR Type identifier string,
+// to be able to filter the items in the input collection that have the
+// given type.
+type TypeExpression struct {
+	Type string
+}
+
+// Evaluate filters the messages in the input that are identified by the Type
+// defined in the expression.
+func (e *TypeExpression) Evaluate(ctx *Context, input system.Collection) (system.Collection, error) {
+	output := system.Collection{}
+
+	for _, item := range input {
+		message, ok := item.(proto.Message)
+		if !ok {
+			continue
+		}
+
+		// find message name, add to collection only if it matches
+		pReflect := message.ProtoReflect()
+		name := pReflect.Descriptor().Name()
+
+		if string(name) != e.Type {
+			continue
+		}
+
+		output = append(output, message)
+	}
+	return output, nil
+}
+
+var _ Expression = (*TypeExpression)(nil)
+
+// LiteralExpression abstracts FHIRPath system types, that
+// are returned from parsing literals.
+type LiteralExpression struct {
+	Literal system.Any
+}
+
+// Evaluate returns the contained literal, without
+// raising an error. Returns an empty collection if the
+// literal is nil, representing a Null literal.
+func (e *LiteralExpression) Evaluate(*Context, system.Collection) (system.Collection, error) {
+	if e.Literal != nil {
+		return system.Collection{e.Literal}, nil
+	}
+	return system.Collection{}, nil
+}
+
+var _ Expression = (*LiteralExpression)(nil)
+
+// IndexExpression allows accessing of an input system.Collection's index.
+// Contains an expression, that when evaluated, should return an integer
+// that represents the index.
+type IndexExpression struct {
+	Index Expression
+}
+
+// Evaluate indexes the input system.Collection and returns the
+// item located at the given index. If the index is negative
+// or out of bounds, returns an empty collection. Raises an error if the
+// contained expression does not evaluate to an expression, or raises an error
+// itself.
+func (e *IndexExpression) Evaluate(ctx *Context, input system.Collection) (system.Collection, error) {
+	indexResult, err := e.Index.Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	}
+	length := len(indexResult)
+	if length == 0 {
+		return system.Collection{}, nil
+	}
+	if length > 1 {
+		return nil, fmt.Errorf("%w: contains %v elements", ErrNotSingleton, length)
+	}
+	value, err := system.From(indexResult[0])
+	if err != nil {
+		return nil, err
+	}
+	index, ok := value.(system.Integer)
+	if !ok {
+		return nil, fmt.Errorf("%w: want Integer but got %T", ErrInvalidType, index)
+	}
+	if int(index) >= len(input) || int(index) < 0 {
+		return system.Collection{}, nil
+	}
+	return system.Collection{input[int(index)]}, nil
+}
+
+var _ Expression = (*IndexExpression)(nil)
+
+// EqualityExpression allows checking equality of the two contained
+// subexpressions. The two expressions should return comparable values
+// when evaluated.
+type EqualityExpression struct {
+	Left  Expression
+	Right Expression
+	Not   bool
+}
+
+// Evaluate evaluates the two subexpressions, and returns true if their
+// contents are equal, using the functionality of system.Collection.Equal. If either
+// collection is empty, returns an empty collection.
+func (e *EqualityExpression) Evaluate(ctx *Context, input system.Collection) (system.Collection, error) {
+	leftResult, err := e.Left.Evaluate(ctx.Clone(), input)
+	if err != nil {
+		return nil, err
+	}
+	rightResult, err := e.Right.Evaluate(ctx.Clone(), input)
+	if err != nil {
+		return nil, err
+	}
+	if len(leftResult) == 0 || len(rightResult) == 0 {
+		return system.Collection{}, nil
+	}
+
+	result, ok := leftResult.TryEqual(rightResult)
+	if !ok {
+		return system.Collection{}, nil
+	}
+	if err != nil {
+		return nil, err
+	}
+	if e.Not {
+		result = !result
+	}
+	return system.Collection{system.Boolean(result)}, nil
+}
+
+var _ Expression = (*EqualityExpression)(nil)
+
+// FunctionExpression enables evaluation of Function Invocation expressions.
+// It holds the function and function arguments.
+type FunctionExpression struct {
+	Fn   func(*Context, system.Collection, ...Expression) (system.Collection, error)
+	Args []Expression
+}
+
+// Evaluate evaluates the function with respect to its arguments. Returns the result
+// of the function, or an error if raised.
+func (e *FunctionExpression) Evaluate(ctx *Context, input system.Collection) (system.Collection, error) {
+	return e.Fn(ctx.Clone(), input, e.Args...)
+}
+
+var _ Expression = (*FunctionExpression)(nil)
+
+// IsExpression enables evaluation of an "is" type expression.
+type IsExpression struct {
+	Expr Expression
+	Type reflection.TypeSpecifier
+}
+
+// Evaluate evaluates the contained expression with respect to singleton evaluation
+// of collections, and determines whether or not it is the given type.
+func (e *IsExpression) Evaluate(ctx *Context, input system.Collection) (system.Collection, error) {
+	result, err := e.Expr.Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	}
+	length := len(result)
+	if length == 0 {
+		return system.Collection{}, nil
+	}
+	if length > 1 {
+		return nil, fmt.Errorf("%w: contains %v elements", ErrNotSingleton, length)
+	}
+	typeSpecifier, err := reflection.TypeOf(result[0])
+	if err != nil {
+		return nil, err
+	}
+	return system.Collection{typeSpecifier.Is(e.Type)}, nil
+}
+
+var _ Expression = (*IsExpression)(nil)
+
+// AsExpression enables evaluation of an "as" type expression.
+type AsExpression struct {
+	Expr Expression
+	Type reflection.TypeSpecifier
+}
+
+// Evaluate evaluates the contained expression with respect to singleton evaluation
+// of collections, returns the singleton if it is of the given type. Returns empty otherwise.
+func (e *AsExpression) Evaluate(ctx *Context, input system.Collection) (system.Collection, error) {
+	result, err := e.Expr.Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	}
+	length := len(result)
+	if length == 0 {
+		return system.Collection{}, nil
+	}
+	if length > 1 {
+		return nil, fmt.Errorf("%w: contains %v elements", ErrNotSingleton, length)
+	}
+	typeSpecifier, err := reflection.TypeOf(result[0])
+	if err != nil {
+		return nil, err
+	}
+	if !typeSpecifier.Is(e.Type) {
+		return system.Collection{}, nil
+	}
+	// attempt to unwrap polymorphic types
+	message, ok := result[0].(fhir.Base)
+	if !ok {
+		return result, nil
+	}
+	if oneOf := protofields.UnwrapOneofField(message, "choice"); oneOf != nil {
+		return system.Collection{oneOf}, nil
+	}
+	return result, nil
+}
+
+var _ Expression = (*AsExpression)(nil)
+
+// BooleanExpression enables evaluation of boolean expressions,
+// including "and", "or", "xor", and "implies".
+type BooleanExpression struct {
+	Left  Expression
+	Right Expression
+	Op    Operator
+}
+
+// Evaluate evaluates the subexpressions with respect to singleton evaluation of
+// collections, and performs the respective Boolean operation.
+func (e *BooleanExpression) Evaluate(ctx *Context, input system.Collection) (system.Collection, error) {
+	leftResult, err := e.Left.Evaluate(ctx.Clone(), input)
+	if err != nil {
+		return nil, err
+	}
+	rightResult, err := e.Right.Evaluate(ctx.Clone(), input)
+	if err != nil {
+		return nil, err
+	}
+
+	leftBool, err := leftResult.ToSingletonBoolean()
+	if err != nil {
+		return nil, err
+	}
+	rightBool, err := rightResult.ToSingletonBoolean()
+	if err != nil {
+		return nil, err
+	}
+
+	switch e.Op {
+	case And:
+		return evaluateAnd(leftBool, rightBool), nil
+	case Or:
+		return evaluateOr(leftBool, rightBool), nil
+	case Xor:
+		return evaluateXor(leftBool, rightBool), nil
+	case Implies:
+		return evaluateImplies(leftBool, rightBool), nil
+	default:
+		return nil, fmt.Errorf("%w: %s", ErrInvalidOperator, e.Op)
+	}
+}
+
+var _ Expression = (*BooleanExpression)(nil)
+
+type ComparisonExpression struct {
+	Left  Expression
+	Right Expression
+	Op    Operator
+}
+
+// Evaluate evaluates the subexpressions with respect to singleton evaluation of collections,
+// and performs the respective comparison operation.
+func (e *ComparisonExpression) Evaluate(ctx *Context, input system.Collection) (system.Collection, error) {
+	leftResult, err := e.Left.Evaluate(ctx.Clone(), input)
+	if err != nil {
+		return nil, err
+	}
+	rightResult, err := e.Right.Evaluate(ctx.Clone(), input)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(leftResult) == 0 || len(rightResult) == 0 {
+		return system.Collection{}, nil
+	}
+	if len(leftResult) != 1 || len(rightResult) != 1 {
+		return nil, fmt.Errorf("%w: left contains %v elements, right contains %v elements", ErrNotSingleton, len(leftResult), len(rightResult))
+	}
+
+	leftPrimitive, err := system.From(leftResult[0])
+	if err != nil {
+		return nil, fmt.Errorf("%w: %w", ErrInvalidType, err)
+	}
+	rightPrimitive, err := system.From(rightResult[0])
+	if err != nil {
+		return nil, fmt.Errorf("%w: %w", ErrInvalidType, err)
+	}
+
+	// Implicitly convert types
+	leftPrimitive = system.Normalize(leftPrimitive, rightPrimitive)
+	rightPrimitive = system.Normalize(rightPrimitive, leftPrimitive)
+
+	// Calculate both less than and greater than
+	lessThan, err := leftPrimitive.Less(rightPrimitive)
+	if errors.Is(err, system.ErrMismatchedPrecision) || errors.Is(err, system.ErrMismatchedUnit) {
+		return system.Collection{}, nil
+	}
+	if err != nil {
+		return nil, err
+	}
+	greaterThan, err := rightPrimitive.Less(leftPrimitive)
+	if errors.Is(err, system.ErrMismatchedPrecision) {
+		return system.Collection{}, nil
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	switch e.Op {
+	case Lt:
+		return system.Collection{lessThan}, nil
+	case Gt:
+		return system.Collection{greaterThan}, nil // (a > b) = (b < a)
+	case Lte:
+		return system.Collection{!greaterThan}, nil // (a <= b) = !(a > b)
+	case Gte:
+		return system.Collection{!lessThan}, nil // (a >= b) = !(a < b)
+	default:
+		return nil, fmt.Errorf("%w: %s", ErrInvalidOperator, e.Op)
+	}
+}
+
+var _ Expression = (*ComparisonExpression)(nil)
+
+// ArithmeticExpression enables mathematical arithmetic operations.
+// Includes '+', '-', '*', "/", "div", and 'mod'.
+type ArithmeticExpression struct {
+	Left  Expression
+	Right Expression
+	Op    func(system.Any, system.Any) (system.Any, error)
+}
+
+// Evaluate evaluates the two subexpressions, with respect to singleton evaluation of collections,
+// and performs the respective additive operation.
+func (e *ArithmeticExpression) Evaluate(ctx *Context, input system.Collection) (system.Collection, error) {
+	leftResult, err := e.Left.Evaluate(ctx.Clone(), input)
+	if err != nil {
+		return nil, err
+	}
+	rightResult, err := e.Right.Evaluate(ctx.Clone(), input)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(leftResult) == 0 || len(rightResult) == 0 {
+		return system.Collection{}, nil
+	}
+	if len(leftResult) != 1 || len(rightResult) != 1 {
+		return nil, fmt.Errorf("%w: left contains %v elements, right contains %v elements", ErrNotSingleton, len(leftResult), len(rightResult))
+	}
+
+	// Cast contents to system types. Addition and subtraction is not supported for protos.
+	leftPrimitive, err := system.From(leftResult[0])
+	if err != nil {
+		return nil, fmt.Errorf("%w: %w", ErrInvalidType, err)
+	}
+	rightPrimitive, err := system.From(rightResult[0])
+	if err != nil {
+		return nil, fmt.Errorf("%w: %w", ErrInvalidType, err)
+	}
+
+	// Implicitly convert types
+	leftPrimitive = system.Normalize(leftPrimitive, rightPrimitive)
+	rightPrimitive = system.Normalize(rightPrimitive, leftPrimitive)
+
+	result, err := e.Op(leftPrimitive, rightPrimitive)
+	if errors.Is(err, system.ErrIntOverflow) {
+		return system.Collection{}, nil // "Operations that cause arithmetic overflow or underflow will result in empty ( { } )".
+	}
+	if err != nil {
+		return nil, err
+	}
+	return system.Collection{result}, nil
+}
+
+var _ Expression = (*ArithmeticExpression)(nil)
+
+// ConcatExpression enables the evaluation of a string concatenation expression.
+type ConcatExpression struct {
+	Left  Expression
+	Right Expression
+}
+
+// Evaluate evaluates the two subexpressions with respect to singleton evaluation of
+// collections, and attempts to concatenate the two strings. Returns an error if the expressions
+// don't resolve to strings. This differs from string addition when either collection is empty. Rather
+// than returning empty, it will treat the empty collection as an empty string.
+func (e *ConcatExpression) Evaluate(ctx *Context, input system.Collection) (system.Collection, error) {
+	leftResult, err := e.Left.Evaluate(ctx.Clone(), input)
+	if err != nil {
+		return nil, err
+	}
+	rightResult, err := e.Right.Evaluate(ctx.Clone(), input)
+	if err != nil {
+		return nil, err
+	}
+
+	// Convert empty collection to empty string
+	if len(leftResult) == 0 {
+		leftResult = append(leftResult, system.String(""))
+	}
+	if len(rightResult) == 0 {
+		rightResult = append(rightResult, system.String(""))
+	}
+
+	if len(leftResult) > 1 || len(rightResult) > 1 {
+		return nil, fmt.Errorf("%w: left contains %v elements, right contains %v elements", ErrNotSingleton, len(leftResult), len(rightResult))
+	}
+
+	// Cast contents to system types
+	leftPrimitive, err := system.From(leftResult[0])
+	if err != nil {
+		return nil, fmt.Errorf("%w: %w", ErrInvalidType, err)
+	}
+	rightPrimitive, err := system.From(rightResult[0])
+	if err != nil {
+		return nil, fmt.Errorf("%w: %w", ErrInvalidType, err)
+	}
+
+	leftStr, ok := leftPrimitive.(system.String)
+	if !ok {
+		return nil, fmt.Errorf("%w: expected a string", ErrInvalidType)
+	}
+	rightStr, ok := rightPrimitive.(system.String)
+	if !ok {
+		return nil, fmt.Errorf("%w: expected a string", ErrInvalidType)
+	}
+
+	return system.Collection{leftStr + rightStr}, nil
+}
+
+var _ Expression = (*ConcatExpression)(nil)
+
+// ExternalConstantExpression enables evaluation of external constants.
+type ExternalConstantExpression struct {
+	Identifier string
+}
+
+// Evaluate retrieves the constant from the map located in the Context. Returns an error if the
+// constant is not present.
+func (e *ExternalConstantExpression) Evaluate(ctx *Context, input system.Collection) (system.Collection, error) {
+	constant, ok := ctx.ExternalConstants[e.Identifier]
+	if !ok {
+		return nil, fmt.Errorf("%w: %s", ErrConstantNotFound, e.Identifier)
+	}
+	if collection, ok := constant.(system.Collection); ok {
+		return collection, nil
+	}
+	return system.Collection{constant}, nil
+}
+
+var _ Expression = (*ExternalConstantExpression)(nil)
+
+// NegationExpression enables negation of number values (Integer, Decimal, Quantity).
+type NegationExpression struct {
+	Expr Expression
+}
+
+// Evaluate negates the contained expression, that is evaluated with respect to singleton evaluation.
+// If the contained value is not a number, returns an error.
+func (e *NegationExpression) Evaluate(ctx *Context, input system.Collection) (system.Collection, error) {
+	result, err := e.Expr.Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	}
+
+	length := len(result)
+	if length == 0 {
+		return system.Collection{}, nil
+	}
+	if length != 1 {
+		return nil, fmt.Errorf("%w: can't negate a collection", ErrNotSingleton)
+	}
+
+	primitive, err := system.From(result[0])
+	if err != nil {
+		return nil, fmt.Errorf("%w: can't negate complex type %T", ErrInvalidType, result[0])
+	}
+
+	// handle negation of value
+	switch v := primitive.(type) {
+	case system.Integer:
+		return system.Collection{system.Integer(-1) * v}, nil
+	case system.Decimal:
+		negative := system.Decimal(decimal.NewFromInt(-1))
+		return system.Collection{v.Mul(negative)}, nil
+	case system.Quantity:
+		return system.Collection{v.Negate()}, nil
+	default:
+		return nil, fmt.Errorf("%w: can't negate %T", ErrInvalidType, primitive)
+	}
+}
+
+var _ Expression = (*NegationExpression)(nil)
diff --git a/fhirpath/internal/expr/expressions_test.go b/fhirpath/internal/expr/expressions_test.go
new file mode 100644
index 0000000..2e4eaf0
--- /dev/null
+++ b/fhirpath/internal/expr/expressions_test.go
@@ -0,0 +1,1465 @@
+package expr_test
+
+import (
+	"errors"
+	"math"
+	"testing"
+	"time"
+
+	cpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/codes_go_proto"
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	bcrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_go_proto"
+	epb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/encounter_go_proto"
+	mrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_request_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/shopspring/decimal"
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/bundle"
+	"github.com/verily-src/fhirpath-go/internal/element/extension"
+	"github.com/verily-src/fhirpath-go/internal/fhirconv"
+	"github.com/verily-src/fhirpath-go/fhirpath"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr/exprtest"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/reflection"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+var (
+	errMock = errors.New("some error")
+)
+
+func TestIdentityExpression_Input_EqualsOutput(t *testing.T) {
+	identity := &expr.IdentityExpression{}
+	testCases := []struct {
+		name string
+		data system.Collection
+	}{
+		{
+			name: "Empty set",
+			data: system.Collection{},
+		},
+		{
+			name: "Mixed type set",
+			data: system.Collection{fhir.String("test"), fhir.Integer(1), fhir.Integer(2)},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			out, err := identity.Evaluate(&expr.Context{}, tc.data)
+
+			if err != nil {
+				t.Fatalf("Input: %s error when not expected, err: %v", tc.name, err)
+			}
+			if got, want := out, tc.data; !cmp.Equal(got, want, protocmp.Transform()) {
+				t.Errorf("Input: %s, got: %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestFieldExpression_Gets_DesiredField(t *testing.T) {
+	patientID := "123"
+	patientFirstHumanName := &dtpb.HumanName{
+		Given: []*dtpb.String{
+			fhir.String("IU"),
+			fhir.String("Amanda"),
+		},
+	}
+	patientBirthDay := &dtpb.Date{
+		ValueUs:   time.Now().UnixMicro(),
+		Precision: dtpb.Date_DAY,
+	}
+	fullName := &dtpb.HumanName{
+		Given: []*dtpb.String{
+			fhir.String("Julius"),
+		},
+		Family: fhir.String("Caesar"),
+	}
+	patientContactPoint := []*dtpb.ContactPoint{
+		{
+			System: &dtpb.ContactPoint_SystemCode{
+				Value: cpb.ContactPointSystemCode_PHONE,
+			},
+			Value: fhir.String("123-456-7890"),
+			Rank:  fhir.PositiveInt(1),
+		},
+		{
+			System: &dtpb.ContactPoint_SystemCode{
+				Value: cpb.ContactPointSystemCode_EMAIL,
+			},
+			Value: fhir.String("example@gmail.com"),
+			Rank:  fhir.PositiveInt(2),
+		},
+	}
+
+	containedPatient := &bcrpb.ContainedResource{
+		OneofResource: &bcrpb.ContainedResource_Patient{
+			Patient: &ppb.Patient{
+				Id:     fhir.ID(patientID),
+				Active: fhir.Boolean(true),
+				Gender: &ppb.Patient_GenderCode{
+					Value: cpb.AdministrativeGenderCode_FEMALE,
+				},
+				Deceased: &ppb.Patient_DeceasedX{
+					Choice: &ppb.Patient_DeceasedX_Boolean{
+						Boolean: fhir.Boolean(true),
+					},
+				},
+				MultipleBirth: &ppb.Patient_MultipleBirthX{
+					Choice: &ppb.Patient_MultipleBirthX_Integer{
+						Integer: fhir.Integer(int32(2)),
+					},
+				},
+				Meta: &dtpb.Meta{
+					Tag: []*dtpb.Coding{
+						{
+							Code: fhir.Code("#blessed"),
+						},
+					},
+				},
+				Name: []*dtpb.HumanName{
+					patientFirstHumanName,
+					fullName,
+				},
+				Telecom: patientContactPoint,
+			},
+		},
+	}
+	patientMissingName := &ppb.Patient{
+		Id:        fhir.ID(patientID),
+		Active:    fhir.Boolean(true),
+		Telecom:   patientContactPoint,
+		BirthDate: patientBirthDay,
+	}
+	patientWithOneName := &ppb.Patient{
+		Id:   fhir.ID(patientID),
+		Name: []*dtpb.HumanName{fullName},
+	}
+
+	testCases := []struct {
+		name           string
+		fieldExp       *expr.FieldExpression
+		input          system.Collection
+		wantCollection system.Collection
+		wantErr        error
+	}{
+		{
+			name:           "contained resource has collection in field",
+			fieldExp:       &expr.FieldExpression{FieldName: "name"},
+			input:          system.Collection{containedPatient},
+			wantCollection: system.Collection{patientFirstHumanName, fullName},
+		},
+		{
+			name:           "resource has empty list field",
+			fieldExp:       &expr.FieldExpression{FieldName: "name"},
+			input:          system.Collection{patientMissingName},
+			wantCollection: system.Collection{},
+		},
+		{
+			name:           "resource has empty non-list field",
+			fieldExp:       &expr.FieldExpression{FieldName: "family"},
+			input:          system.Collection{patientFirstHumanName},
+			wantCollection: system.Collection{},
+		},
+		{
+			name:           "field is appended with the suffix `value`",
+			fieldExp:       &expr.FieldExpression{FieldName: "class"},
+			input:          system.Collection{&epb.Encounter{ClassValue: fhir.Coding("class-system", "class-code")}},
+			wantCollection: system.Collection{fhir.Coding("class-system", "class-code")},
+		},
+		{
+			name:     "accessing non existent field",
+			fieldExp: &expr.FieldExpression{FieldName: "version"},
+			input:    system.Collection{containedPatient},
+			wantErr:  expr.ErrInvalidField,
+		},
+		{
+			name:           "resource has singleton in field",
+			fieldExp:       &expr.FieldExpression{FieldName: "name"},
+			input:          system.Collection{patientWithOneName},
+			wantCollection: system.Collection{fullName},
+		},
+		{
+			name:           "accessing non-list field",
+			fieldExp:       &expr.FieldExpression{FieldName: "id"},
+			input:          system.Collection{containedPatient},
+			wantCollection: system.Collection{fhir.ID(patientID)},
+		},
+		{
+			name:           "accessing family in HumanName element",
+			fieldExp:       &expr.FieldExpression{FieldName: "family"},
+			input:          system.Collection{fullName},
+			wantCollection: system.Collection{fhir.String("Caesar")},
+		},
+		{
+			name:           "accessing fields from multiple resources",
+			fieldExp:       &expr.FieldExpression{FieldName: "name"},
+			input:          system.Collection{containedPatient, patientWithOneName},
+			wantCollection: system.Collection{patientFirstHumanName, fullName, fullName},
+		},
+		{
+			name:           "accessing field of 2 words",
+			fieldExp:       &expr.FieldExpression{FieldName: "birthDate"},
+			input:          system.Collection{patientMissingName},
+			wantCollection: system.Collection{patientBirthDay},
+		},
+		{
+			name:           "(Legacy) input contains non-resource items",
+			fieldExp:       &expr.FieldExpression{FieldName: "birthDate", Permissive: true},
+			input:          system.Collection{patientMissingName, "hello"},
+			wantCollection: system.Collection{patientBirthDay},
+		},
+		{
+			name:     "input contains non-resource items",
+			fieldExp: &expr.FieldExpression{FieldName: "birthDate"},
+			input:    system.Collection{patientMissingName, "hello"},
+			wantErr:  fhirpath.ErrInvalidField,
+		},
+		{
+			name:           "accessing value field of primitive returns System primitive",
+			fieldExp:       &expr.FieldExpression{FieldName: "value"},
+			input:          system.Collection{fullName.Family},
+			wantCollection: system.Collection{system.String("Caesar")},
+		},
+		{
+			name:           "accessing value of code returns System primitive",
+			fieldExp:       &expr.FieldExpression{FieldName: "value"},
+			input:          system.Collection{patientContactPoint[0].System},
+			wantCollection: system.Collection{system.String("phone")},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.fieldExp.Evaluate(&expr.Context{}, tc.input)
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("FieldExpression.Evaluate(%s) raised unexpected error: got %v, want %v", tc.input, err, tc.wantErr)
+			}
+			if diff := cmp.Diff(tc.wantCollection, got, protocmp.Transform()); diff != "" {
+				t.Errorf("FieldExpression.Evaluate(%s) returned unexpected diff (-want, +got):\n%s", tc.input, diff)
+			}
+		})
+	}
+}
+
+func TestFieldExpression_ValidInput_GetsField(t *testing.T) {
+	tm := time.Now()
+	device1 := &device_go_proto.Device{
+		Id: fhir.RandomID(),
+	}
+	patient1 := &patient_go_proto.Patient{
+		Id: fhir.RandomID(),
+	}
+	entries := []*bundle_and_contained_resource_go_proto.Bundle_Entry{
+		bundle.NewPostEntry(device1), bundle.NewPostEntry(patient1),
+	}
+	testCases := []struct {
+		name  string
+		input system.Collection
+		field string
+		want  system.Collection
+	}{
+		{
+			name:  "Value field from primitive element",
+			input: system.Collection{fhir.Integer(32)},
+			field: "value",
+			want:  system.Collection{system.Integer(32)},
+		}, {
+			name:  "Value field from valueX type",
+			input: system.Collection{extension.New("url", fhir.Boolean(true))},
+			field: "value",
+			want:  system.Collection{fhir.Boolean(true)},
+		}, {
+			name: "Repeated field returns collection of repeated elements",
+			input: system.Collection{bundle.NewCollection(
+				bundle.WithEntries(entries...),
+			)},
+			field: "entry",
+			want: system.Collection(
+				slices.MustConvert[any](entries),
+			),
+		}, {
+			name:  "Field on empty input returns empty",
+			input: nil,
+			field: "value",
+			want:  system.Collection{},
+		}, {
+			name:  "Bundle entry contained resource",
+			input: system.Collection{bundle.NewPostEntry(device1)},
+			field: "resource",
+			want:  system.Collection{device1},
+		}, {
+			name: "Reference using URI",
+			input: system.Collection{&dtpb.Reference{
+				Reference: &dtpb.Reference_Uri{
+					Uri: fhir.String("https://some-url.com"),
+				},
+			}},
+			field: "reference",
+			want:  system.Collection{fhir.String("https://some-url.com")},
+		}, {
+			name: "Reference Patient without history ID",
+			input: system.Collection{&dtpb.Reference{
+				Reference: &dtpb.Reference_PatientId{
+					PatientId: &dtpb.ReferenceId{
+						Value: "12345",
+					},
+				},
+			}},
+			field: "reference",
+			want:  system.Collection{fhir.String("Patient/12345")},
+		}, {
+			name: "Reference using fragment",
+			input: system.Collection{&dtpb.Reference{
+				Reference: &dtpb.Reference_Fragment{
+					Fragment: fhir.String("p0"),
+				},
+			}},
+			field: "reference",
+			want:  system.Collection{fhir.String("#p0")},
+		}, {
+			name:  "Time using value_us",
+			input: system.Collection{fhir.Time(tm)},
+			field: "value",
+			want:  system.Collection{system.String(fhirconv.TimeToString(fhir.Time(tm)))},
+		}, {
+			name:  "Date using value_us",
+			input: system.Collection{fhir.Date(tm)},
+			field: "value",
+			want:  system.Collection{system.String(fhirconv.DateToString(fhir.Date(tm)))},
+		}, {
+			name:  "DateTime using value_us",
+			input: system.Collection{fhir.DateTime(tm)},
+			field: "value",
+			want:  system.Collection{system.String(fhirconv.DateTimeToString(fhir.DateTime(tm)))},
+		}, {
+			name:  "Instant using value_us",
+			input: system.Collection{fhir.Instant(tm)},
+			field: "value",
+			want:  system.Collection{system.String(fhirconv.InstantToString(fhir.Instant(tm)))},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			sut := &expr.FieldExpression{
+				FieldName: tc.field,
+			}
+
+			got, err := sut.Evaluate(expr.InitializeContext(tc.input), tc.input)
+			if err != nil {
+				t.Fatalf("FieldExpression.Evaluate(%v): got unexpected err %v", tc.name, err)
+			}
+
+			if diff := cmp.Diff(got, tc.want, protocmp.Transform()); diff != "" {
+				t.Errorf("FieldExpression.Evaluate(%v): (-got,+want):\n%v", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestTypeExpression_Filters_DesiredType(t *testing.T) {
+	medicationRequest := &mrpb.MedicationRequest{
+		Reported: &mrpb.MedicationRequest_ReportedX{
+			Choice: &mrpb.MedicationRequest_ReportedX_Boolean{
+				Boolean: fhir.Boolean(true),
+			},
+		},
+	}
+	patient := &ppb.Patient{
+		Id: fhir.ID("123"),
+		Name: []*dtpb.HumanName{
+			{
+				Given: []*dtpb.String{
+					fhir.String("Julius"),
+				},
+				Family: fhir.String("Caesar"),
+			},
+		},
+	}
+
+	testCases := []struct {
+		name           string
+		typeExp        *expr.TypeExpression
+		input          system.Collection
+		wantCollection system.Collection
+		shouldError    bool
+	}{
+		{
+			name:           "input contains only resource",
+			typeExp:        &expr.TypeExpression{Type: "Patient"},
+			input:          system.Collection{patient},
+			wantCollection: system.Collection{patient},
+		},
+		{
+			name:           "input contains more than desired resource",
+			typeExp:        &expr.TypeExpression{Type: "Patient"},
+			input:          system.Collection{patient, medicationRequest},
+			wantCollection: system.Collection{patient},
+		},
+		{
+			name:           "input doesn't contain desired resource",
+			typeExp:        &expr.TypeExpression{Type: "MedicationRequest"},
+			input:          system.Collection{patient},
+			wantCollection: system.Collection{},
+		},
+		{
+			name:           "desired type has 2 words",
+			typeExp:        &expr.TypeExpression{Type: "MedicationRequest"},
+			input:          system.Collection{medicationRequest, patient},
+			wantCollection: system.Collection{medicationRequest},
+		},
+		{
+			name:           "input collection has non-resource types with desired name",
+			typeExp:        &expr.TypeExpression{Type: "Patient"},
+			input:          system.Collection{"Patient", struct{ Patient string }{Patient: "Peter Griffin"}},
+			wantCollection: system.Collection{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.typeExp.Evaluate(&expr.Context{}, tc.input)
+
+			if err != nil {
+				t.Fatalf("TypeExpression.Evaluate(%s) got unexpected error: %s", tc.input, err)
+			}
+			if diff := cmp.Diff(tc.wantCollection, got, protocmp.Transform()); diff != "" {
+				t.Errorf("TypeExpression.Evaluate(%s) returned unexpected diff (-want, +got):\n%s", tc.input, diff)
+			}
+		})
+	}
+}
+
+func TestExpressionSequence_EvaluatesInSequence(t *testing.T) {
+	mock := &exprtest.MockExpression{
+		Eval: func(ctx *expr.Context, input system.Collection) (system.Collection, error) {
+			wasCalled := input[0].(int)
+			return system.Collection{wasCalled + 1}, nil
+		},
+	}
+
+	sequence := &expr.ExpressionSequence{[]expr.Expression{mock, mock, mock, mock}}
+	result, err := sequence.Evaluate(&expr.Context{}, system.Collection{0})
+
+	if err != nil {
+		t.Fatalf("ExpressionSequence.Evaluate raised unexpected error: %v", err)
+	}
+	if got, want := result[0].(int), len(sequence.Expressions); got != want {
+		t.Errorf("ExpressionSequence.Evaluate incorrectly accumulated values, got: %v, want: %v", got, want)
+	}
+}
+
+func TestExpressionSequence_EvaluateRaisesError(t *testing.T) {
+	sequence := &expr.ExpressionSequence{[]expr.Expression{exprtest.Return(), exprtest.Return(), exprtest.Error(errMock), exprtest.Return()}}
+
+	_, err := sequence.Evaluate(&expr.Context{}, system.Collection{})
+
+	if err == nil {
+		t.Fatal("ExpressionSequence.Evaluate didn't raise error when expected")
+	}
+}
+
+func TestLiteralExpression_EvaluateReturnsLiteral(t *testing.T) {
+	testCases := []struct {
+		name            string
+		literalExp      *expr.LiteralExpression
+		inputCollection system.Collection
+		wantCollection  system.Collection
+	}{
+		{
+			name:            "string literal expression without input",
+			literalExp:      &expr.LiteralExpression{system.String("string")},
+			inputCollection: system.Collection{"some input"},
+			wantCollection:  system.Collection{system.String("string")},
+		},
+		{
+			name:            "null literal expression",
+			literalExp:      &expr.LiteralExpression{},
+			inputCollection: system.Collection{},
+			wantCollection:  system.Collection{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.literalExp.Evaluate(&expr.Context{}, tc.inputCollection)
+
+			if err != nil {
+				t.Fatalf("LiteralExpression.Evaluate raised unexpected error %v", err)
+			}
+			if diff := cmp.Diff(tc.wantCollection, got); diff != "" {
+				t.Errorf("LiteralExpression.Evaluate returned unexpected diff: (-want, +got):\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestIndexExpression_ReturnsIndex(t *testing.T) {
+	givenName := []*dtpb.String{
+		fhir.String("He"),
+		fhir.String("Who"),
+		fhir.String("Must"),
+		fhir.String("Not"),
+		fhir.String("Be"),
+	}
+	familyName := fhir.String("Named")
+	voldemort := []*dtpb.HumanName{
+		{
+			Given:  givenName,
+			Family: familyName,
+		},
+		{
+			Given: []*dtpb.String{
+				fhir.String("Tom"),
+				fhir.String("Marvolo"),
+			},
+			Family: fhir.String("Riddle"),
+		},
+	}
+
+	testCases := []struct {
+		name            string
+		indexExpr       *expr.IndexExpression
+		inputCollection system.Collection
+		wantCollection  system.Collection
+	}{
+		{
+			name:            "indexing a proto string array",
+			indexExpr:       &expr.IndexExpression{&expr.LiteralExpression{system.Integer(1)}},
+			inputCollection: slices.MustConvert[any](givenName),
+			wantCollection:  system.Collection{fhir.String("Who")},
+		},
+		{
+			name:            "indexing a proto HumanName",
+			indexExpr:       &expr.IndexExpression{&expr.LiteralExpression{system.Integer(0)}},
+			inputCollection: slices.MustConvert[any](voldemort),
+			wantCollection:  system.Collection{voldemort[0]},
+		},
+		{
+			name:            "index out of bounds",
+			indexExpr:       &expr.IndexExpression{&expr.LiteralExpression{system.Integer(5)}},
+			inputCollection: slices.MustConvert[any](givenName),
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "negative index",
+			indexExpr:       &expr.IndexExpression{&expr.LiteralExpression{system.Integer(-1)}},
+			inputCollection: slices.MustConvert[any](givenName),
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "empty input collection",
+			indexExpr:       &expr.IndexExpression{&expr.LiteralExpression{system.Integer(0)}},
+			inputCollection: system.Collection{},
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "index that evaluates to empty returns empty",
+			indexExpr:       &expr.IndexExpression{exprtest.Return()},
+			inputCollection: slices.MustConvert[any](givenName),
+			wantCollection:  system.Collection{},
+		},
+	}
+
+	for _, tc := range testCases {
+		got, err := tc.indexExpr.Evaluate(&expr.Context{}, tc.inputCollection)
+
+		if err != nil {
+			t.Fatalf("IndexExpression.Evaluate raised unexpected error %v", err)
+		}
+		if diff := cmp.Diff(tc.wantCollection, got, protocmp.Transform()); diff != "" {
+			t.Errorf("IndexExpression.Evaluate returned unexpected diff: (-want, +got)\n%s", diff)
+		}
+	}
+}
+
+func TestIndexExpression_EvaluateRaisesError(t *testing.T) {
+	testCases := []struct {
+		name            string
+		indexExpr       *expr.IndexExpression
+		inputCollection system.Collection
+	}{
+		{
+			name:            "evaluate index raises an error",
+			indexExpr:       &expr.IndexExpression{Index: exprtest.Error(errMock)},
+			inputCollection: system.Collection{},
+		},
+		{
+			name:            "index expression doesn't evaluate to a system type",
+			indexExpr:       &expr.IndexExpression{Index: exprtest.Return(1)},
+			inputCollection: system.Collection{},
+		},
+		{
+			name:            "index doesn't evaluate to an integer",
+			indexExpr:       &expr.IndexExpression{Index: exprtest.Return("not an integer")},
+			inputCollection: system.Collection{},
+		},
+		{
+			name:            "index expression evaluates to multiple entries",
+			indexExpr:       &expr.IndexExpression{Index: exprtest.Return(system.Integer(1), system.Integer(2))},
+			inputCollection: system.Collection{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := tc.indexExpr.Evaluate(&expr.Context{}, tc.inputCollection)
+
+			if err == nil {
+				t.Fatalf("IndexExpression.Evaluate didn't return error when expected")
+			}
+		})
+	}
+}
+
+func TestEqualityExpression_ReturnsResult(t *testing.T) {
+	testCases := []struct {
+		name            string
+		inputCollection system.Collection
+		equalityExpr    *expr.EqualityExpression
+		wantCollection  system.Collection
+	}{
+		{
+			name:            "one empty collection",
+			inputCollection: system.Collection{},
+			equalityExpr:    &expr.EqualityExpression{exprtest.Return(), exprtest.Return("one"), false},
+			wantCollection:  system.Collection{},
+		},
+		{
+			name:            "comparing with != operator",
+			inputCollection: system.Collection{},
+			equalityExpr:    &expr.EqualityExpression{exprtest.Return(system.String("abc")), exprtest.Return(system.String("abcd")), true},
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.equalityExpr.Evaluate(&expr.Context{}, tc.inputCollection)
+
+			if err != nil {
+				t.Fatalf("EqualityExpression.Evaluate raised unexpected error: %v", err)
+			}
+			if diff := cmp.Diff(tc.wantCollection, got); diff != "" {
+				t.Errorf("EqualityExpression.Evaluate returned unexpected diff: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestEqualityExpression_RaisesError(t *testing.T) {
+	testCases := []struct {
+		name         string
+		equalityExpr *expr.EqualityExpression
+	}{
+		{
+			name:         "subexpression one errors",
+			equalityExpr: &expr.EqualityExpression{exprtest.Error(errMock), exprtest.Return(system.Boolean(true)), false},
+		},
+		{
+			name:         "subexpression two errors",
+			equalityExpr: &expr.EqualityExpression{exprtest.Return(system.Boolean(true)), exprtest.Error(errMock), false},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := tc.equalityExpr.Evaluate(&expr.Context{}, system.Collection{})
+
+			if err == nil {
+				t.Fatalf("EqualityExpression.Evaluate didn't propagate error when it should have")
+			}
+		})
+	}
+}
+
+func TestIsExpression_ReturnsResult(t *testing.T) {
+	testCases := []struct {
+		name           string
+		expr           *expr.IsExpression
+		wantCollection system.Collection
+	}{
+		{
+			name:           "returns boolean",
+			expr:           &expr.IsExpression{exprtest.Return(fhir.String("str")), reflection.MustCreateTypeSpecifier("FHIR", "string")},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:           "returns empty collection",
+			expr:           &expr.IsExpression{exprtest.Return(), reflection.MustCreateTypeSpecifier("FHIR", "string")},
+			wantCollection: system.Collection{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.expr.Evaluate(&expr.Context{}, []any{})
+
+			if err != nil {
+				t.Fatalf("IsExpression.Evaluate returned unexpected error: %v", err)
+			}
+			if diff := cmp.Diff(tc.wantCollection, got, protocmp.Transform()); diff != "" {
+				t.Errorf("IsExpression.Evaluate returned unexpected diff: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestIsExpression_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name string
+		expr *expr.IsExpression
+	}{
+		{
+			name: "subexpression errors",
+			expr: &expr.IsExpression{exprtest.Error(errors.New("some error")), reflection.TypeSpecifier{}},
+		},
+		{
+			name: "subexpression evaluates to non-singleton",
+			expr: &expr.IsExpression{exprtest.Return(system.Boolean(true), system.Boolean(true)), reflection.TypeSpecifier{}},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := tc.expr.Evaluate(&expr.Context{}, []any{})
+
+			if err == nil {
+				t.Fatalf("IsExpression.Evaluate doesn't return error when expected")
+			}
+		})
+	}
+}
+
+func TestAsExpression_ReturnsResult(t *testing.T) {
+	deceased := &ppb.Patient_DeceasedX{
+		Choice: &ppb.Patient_DeceasedX_Boolean{
+			Boolean: fhir.Boolean(true),
+		},
+	}
+	testCases := []struct {
+		name           string
+		expr           *expr.AsExpression
+		wantCollection system.Collection
+	}{
+		{
+			name:           "input is of specified type (returns input)",
+			expr:           &expr.AsExpression{exprtest.Return(fhir.Code("#blessed")), reflection.MustCreateTypeSpecifier("FHIR", "code")},
+			wantCollection: system.Collection{fhir.Code("#blessed")},
+		},
+		{
+			name:           "input is not of specified type (returns empty)",
+			expr:           &expr.AsExpression{exprtest.Return(fhir.Integer(12)), reflection.MustCreateTypeSpecifier("FHIR", "string")},
+			wantCollection: system.Collection{},
+		},
+		{
+			name:           "input is empty collection (returns empty)",
+			expr:           &expr.AsExpression{exprtest.Return(), reflection.MustCreateTypeSpecifier("FHIR", "string")},
+			wantCollection: system.Collection{},
+		},
+		{
+			name:           "input is a polymorphic oneOf type",
+			expr:           &expr.AsExpression{exprtest.Return(deceased), reflection.MustCreateTypeSpecifier("FHIR", "boolean")},
+			wantCollection: system.Collection{fhir.Boolean(true)},
+		},
+		{
+			name:           "input is a system type",
+			expr:           &expr.AsExpression{exprtest.Return(system.Boolean(true)), reflection.MustCreateTypeSpecifier("System", "Boolean")},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.expr.Evaluate(&expr.Context{}, []any{})
+
+			if err != nil {
+				t.Fatalf("AsExpression.Evaluate returned unexpected error: %v", err)
+			}
+			if diff := cmp.Diff(tc.wantCollection, got, protocmp.Transform()); diff != "" {
+				t.Errorf("AsExpression.Evaluate returned unexpected diff: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestAsExpression_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name string
+		expr *expr.AsExpression
+	}{
+		{
+			name: "subexpression errors",
+			expr: &expr.AsExpression{exprtest.Error(errors.New("some error")), reflection.TypeSpecifier{}},
+		},
+		{
+			name: "subexpression evaluates to non-singleton",
+			expr: &expr.AsExpression{exprtest.Return(system.Boolean(true), system.Boolean(true)), reflection.TypeSpecifier{}},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := tc.expr.Evaluate(&expr.Context{}, []any{})
+
+			if err == nil {
+				t.Fatalf("AsExpression.Evaluate doesn't return error when expected")
+			}
+		})
+	}
+}
+
+func TestBooleanExpression_ReturnsResult(t *testing.T) {
+	testCases := []struct {
+		name           string
+		expr           *expr.BooleanExpression
+		wantCollection system.Collection
+	}{
+		{
+			name:           "[and]both expressions return true",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.Boolean(true)), exprtest.Return(system.Boolean(true)), expr.And},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:           "[and]both expressions return booleans (one false)",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.Boolean(true)), exprtest.Return(system.Boolean(false)), expr.And},
+			wantCollection: system.Collection{system.Boolean(false)},
+		},
+		{
+			name:           "[and]both expressions return empty",
+			expr:           &expr.BooleanExpression{exprtest.Return(), exprtest.Return(), expr.And},
+			wantCollection: system.Collection{},
+		},
+		{
+			name:           "[and]one expression is false while other is empty",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.Boolean(false)), exprtest.Return(), expr.And},
+			wantCollection: system.Collection{system.Boolean(false)},
+		},
+		{
+			name:           "[and]one expression is true while other is empty",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.Boolean(true)), exprtest.Return(), expr.And},
+			wantCollection: system.Collection{},
+		},
+		{
+			name:           "[and]singleton evaluation of collections (expressions are not booleans)",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.String("hi")), exprtest.Return(system.String("hello")), expr.And},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:           "[or]both expressions return true",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.Boolean(true)), exprtest.Return(system.Boolean(true)), expr.Or},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:           "[or]both expressions return booleans (one true, one false)",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.Boolean(true)), exprtest.Return(system.Boolean(false)), expr.Or},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:           "[or]both expressions return empty",
+			expr:           &expr.BooleanExpression{exprtest.Return(), exprtest.Return(), expr.Or},
+			wantCollection: system.Collection{},
+		},
+		{
+			name:           "[or]one expression is true while other is empty",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.Boolean(true)), exprtest.Return(), expr.Or},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:           "[or]one expression is false while other is empty",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.Boolean(false)), exprtest.Return(), expr.Or},
+			wantCollection: system.Collection{},
+		},
+		{
+			name:           "[or]correctly compares proto booleans",
+			expr:           &expr.BooleanExpression{exprtest.Return(fhir.Boolean(true)), exprtest.Return(fhir.Boolean(false)), expr.Or},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:           "[or]singleton evaluation of collections (expressions are not booleans)",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.String("hi")), exprtest.Return(), expr.Or},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:           "[xor]both expressions are equal booleans",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.Boolean(true)), exprtest.Return(system.Boolean(true)), expr.Xor},
+			wantCollection: system.Collection{system.Boolean(false)},
+		},
+		{
+			name:           "[xor] both expressions are inequal booleans",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.Boolean(false)), exprtest.Return(system.Boolean(true)), expr.Xor},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:           "[xor]one collection is empty",
+			expr:           &expr.BooleanExpression{exprtest.Return(), exprtest.Return(system.Boolean(true)), expr.Xor},
+			wantCollection: system.Collection{},
+		},
+		{
+			name:           "[implies]false implies false",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.Boolean(false)), exprtest.Return(system.Boolean(false)), expr.Implies},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:           "[implies]false implies true",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.Boolean(false)), exprtest.Return(system.Boolean(true)), expr.Implies},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:           "[implies]false implies empty",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.Boolean(false)), exprtest.Return(), expr.Implies},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:           "[implies]true implies false",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.Boolean(true)), exprtest.Return(system.Boolean(false)), expr.Implies},
+			wantCollection: system.Collection{system.Boolean(false)},
+		},
+		{
+			name:           "[implies]true implies true",
+			expr:           &expr.BooleanExpression{exprtest.Return(system.Boolean(true)), exprtest.Return(system.Boolean(true)), expr.Implies},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:           "[implies]empty implies true",
+			expr:           &expr.BooleanExpression{exprtest.Return(), exprtest.Return(system.Boolean(true)), expr.Implies},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:           "[implies]returns empty",
+			expr:           &expr.BooleanExpression{exprtest.Return(true), exprtest.Return(), expr.Implies},
+			wantCollection: system.Collection{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.expr.Evaluate(&expr.Context{}, system.Collection{})
+
+			if err != nil {
+				t.Fatalf("BooleanExpression.Evaluate returned unexpected error: %v", err)
+			}
+			if !cmp.Equal(got, tc.wantCollection) {
+				t.Errorf("BooleanExpression.Evaluate returned unexpected result: got %v, want %v", got, tc.wantCollection)
+			}
+		})
+	}
+}
+
+func TestComparisonExpression_ReturnsResult(t *testing.T) {
+	qty, _ := system.ParseQuantity("23.3", "kg")
+	testErr := errors.New("some error")
+	testCases := []struct {
+		name           string
+		expr           *expr.ComparisonExpression
+		wantCollection system.Collection
+		wantErr        error
+	}{
+		{
+			name: "[Lt] evaluates string comparison",
+			expr: &expr.ComparisonExpression{
+				exprtest.Return(system.String("abc")),
+				exprtest.Return(system.String("def")),
+				expr.Lt,
+			},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name: "returns empty if either collection is empty",
+			expr: &expr.ComparisonExpression{
+				exprtest.Return(),
+				exprtest.Return(system.String("def")),
+				expr.Lt,
+			},
+			wantCollection: system.Collection{},
+		},
+		{
+			name: "[Gt] correctly compares Date values",
+			expr: &expr.ComparisonExpression{
+				exprtest.Return(system.MustParseDate("2023-07-22")),
+				exprtest.Return(system.MustParseDate("2023-07-21")),
+				expr.Gt,
+			},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name: "[Gte] correctly compares Time value with different precision",
+			expr: &expr.ComparisonExpression{
+				exprtest.Return(system.MustParseTime("08:31")),
+				exprtest.Return(system.MustParseTime("08:30:30")),
+				expr.Gte,
+			},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name: "[Lte] correctly compares quantity value",
+			expr: &expr.ComparisonExpression{
+				exprtest.Return(qty),
+				exprtest.Return(qty),
+				expr.Lte,
+			},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name: "[Gte] returns empty for mismatched precision on DateTime",
+			expr: &expr.ComparisonExpression{
+				exprtest.Return(system.MustParseDateTime("2020-12-20T08:30")),
+				exprtest.Return(system.MustParseDateTime("2020-12-20T08:30:05")),
+				expr.Gte,
+			},
+			wantCollection: system.Collection{},
+		},
+		{
+			name: "[Lt] correctly compares a Date with a DateTime",
+			expr: &expr.ComparisonExpression{
+				exprtest.Return(system.MustParseDate("2020-12-20")),
+				exprtest.Return(system.MustParseDateTime("2020-12-21T")),
+				expr.Lt,
+			},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name: "returns error for comparison of invalid types",
+			expr: &expr.ComparisonExpression{
+				exprtest.Return(system.String("100")),
+				exprtest.Return(system.Integer(100)),
+				expr.Gte,
+			},
+			wantErr: system.ErrTypeMismatch,
+		},
+		{
+			name: "Propogates error from one of the expressions",
+			expr: &expr.ComparisonExpression{
+				exprtest.Return(),
+				exprtest.Error(testErr),
+				expr.Lt,
+			},
+			wantErr: testErr,
+		},
+		{
+			name: "returns error if either collection is not a singleton",
+			expr: &expr.ComparisonExpression{
+				exprtest.Return(system.String("abc"), system.String("abc")),
+				exprtest.Return(system.String("hi")),
+				expr.Lt,
+			},
+			wantErr: expr.ErrNotSingleton,
+		},
+		{
+			name: "returns error if a non-system type is returned",
+			expr: &expr.ComparisonExpression{
+				exprtest.Return(123),
+				exprtest.Return(system.Integer(234)),
+				expr.Lt,
+			},
+			wantErr: system.ErrCantBeCast,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.expr.Evaluate(&expr.Context{}, system.Collection{})
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("ComparisonExpression.Evaluate returned unexpected error: got %v, want %v", err, tc.wantErr)
+			}
+			if !cmp.Equal(got, tc.wantCollection) {
+				t.Errorf("ComparisonExpression.Evaluate returned unexpected result: got %v, want %v", got, tc.wantCollection)
+			}
+		})
+	}
+}
+
+func TestAndExpression_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name string
+		expr *expr.BooleanExpression
+	}{
+		{
+			name: "subexpression errors",
+			expr: &expr.BooleanExpression{exprtest.Error(errMock), exprtest.Return(system.Boolean(true)), expr.And},
+		},
+		{
+			name: "expression returns non-singleton",
+			expr: &expr.BooleanExpression{exprtest.Return(system.Boolean(true)), exprtest.Return(system.Boolean(true), system.Boolean(true)), expr.And},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := tc.expr.Evaluate(&expr.Context{}, system.Collection{})
+
+			if err == nil {
+				t.Fatalf("AndExpression.Evaluate didn't return error when expected")
+			}
+		})
+	}
+}
+
+func TestArithmeticExpression_ReturnsResult(t *testing.T) {
+	testCases := []struct {
+		name    string
+		expr    *expr.ArithmeticExpression
+		want    system.Collection
+		wantErr error
+	}{
+		{
+			name: "adds two system types",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.String("hello ")),
+				Right: exprtest.Return(system.String("world")),
+				Op:    expr.EvaluateAdd,
+			},
+			want: system.Collection{system.String("hello world")},
+		},
+		{
+			name: "adds two proto types",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(fhir.MustParseDate("2020-02-12")),
+				Right: exprtest.Return(fhir.UCUMQuantity(1, "day")),
+				Op:    expr.EvaluateAdd,
+			},
+			want: system.Collection{system.MustParseDate("2020-02-13")},
+		},
+		{
+			name: "adds a integer with a decimal",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.Integer(25)),
+				Right: exprtest.Return(system.Decimal(decimal.NewFromFloat(1.24))),
+				Op:    expr.EvaluateAdd,
+			},
+			want: system.Collection{system.Decimal(decimal.NewFromFloat(26.24))},
+		},
+		{
+			name: "subtracts quantity from date",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.MustParseDate("2020-12")),
+				Right: exprtest.Return(system.MustParseQuantity("35", "days")),
+				Op:    expr.EvaluateSub,
+			},
+			want: system.Collection{system.MustParseDate("2020-11")},
+		},
+		{
+			name: "returns empty if either collection is empty",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(),
+				Right: exprtest.Return(system.String("hell0")),
+				Op:    expr.EvaluateAdd,
+			},
+			want: system.Collection{},
+		},
+		{
+			name: "returns error if either collection has multiple elements",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.Integer(1), system.Integer(2)),
+				Right: exprtest.Return(system.Integer(2)),
+				Op:    expr.EvaluateAdd,
+			},
+			wantErr: expr.ErrNotSingleton,
+		},
+		{
+			name: "returns error if types can not be added",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.Integer(2)),
+				Right: exprtest.Return(system.Boolean(true)),
+				Op:    expr.EvaluateAdd,
+			},
+			wantErr: system.ErrTypeMismatch,
+		},
+		{
+			name: "returns empty if integer addition overflows",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.Integer(math.MaxInt32)),
+				Right: exprtest.Return(system.Integer(1)),
+				Op:    expr.EvaluateAdd,
+			},
+			want: system.Collection{},
+		},
+		{
+			name: "returns empty if integer subtraction overflows",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.Integer(math.MinInt32)),
+				Right: exprtest.Return(system.Integer(1)),
+				Op:    expr.EvaluateSub,
+			},
+			want: system.Collection{},
+		},
+		{
+			name: "multiplies decimals together",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.Decimal(decimal.NewFromFloat(0.25))),
+				Right: exprtest.Return(system.Decimal(decimal.NewFromFloat(0.25))),
+				Op:    expr.EvaluateMul,
+			},
+			want: system.Collection{system.Decimal(decimal.NewFromFloat(0.0625))},
+		},
+		{
+			name: "implicitly converts integer to decimal",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.Decimal(decimal.NewFromFloat(1.2))),
+				Right: exprtest.Return(system.Integer(2)),
+				Op:    expr.EvaluateMul,
+			},
+			want: system.Collection{system.Decimal(decimal.NewFromFloat(2.4))},
+		},
+		{
+			name: "multiplies integers together",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.Integer(12)),
+				Right: exprtest.Return(system.Integer(12)),
+				Op:    expr.EvaluateMul,
+			},
+			want: system.Collection{system.Integer(144)},
+		},
+		{
+			name: "returns empty if multiplication causes integer overflow",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.Integer(math.MaxInt32)),
+				Right: exprtest.Return(system.Integer(2)),
+				Op:    expr.EvaluateMul,
+			},
+			want: system.Collection{},
+		},
+		{
+			name: "returns error on a type mismatch",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.Integer(2)),
+				Right: exprtest.Return(system.String("a")),
+				Op:    expr.EvaluateMul,
+			},
+			wantErr: system.ErrTypeMismatch,
+		},
+		{
+			name: "returns decimal on integer division",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.Integer(5)),
+				Right: exprtest.Return(system.Integer(2)),
+				Op:    expr.EvaluateDiv,
+			},
+			want: system.Collection{system.Decimal(decimal.NewFromFloat(2.5))},
+		},
+		{
+			name: "performs floor division",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.Integer(5)),
+				Right: exprtest.Return(system.Integer(2)),
+				Op:    expr.EvaluateFloorDiv,
+			},
+			want: system.Collection{system.Integer(2)},
+		},
+		{
+			name: "performs mod between decimals",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.Decimal(decimal.NewFromFloat(5.5))),
+				Right: exprtest.Return(system.Decimal(decimal.NewFromFloat(0.7))),
+				Op:    expr.EvaluateMod,
+			},
+			want: system.Collection{system.Decimal(decimal.NewFromFloat(0.6))},
+		},
+		{
+			name: "performs mod between integers",
+			expr: &expr.ArithmeticExpression{
+				Left:  exprtest.Return(system.Integer(19)),
+				Right: exprtest.Return(system.Integer(9)),
+				Op:    expr.EvaluateMod,
+			},
+			want: system.Collection{system.Integer(1)},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.expr.Evaluate(&expr.Context{}, []any{})
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("ArithmeticExpression.Evaluate returned unexpected error: got %v, want %v", err, tc.wantErr)
+			}
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("ArithmeticExpression.Evaluate returned unexpected diff: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestConcatExpression_AddsStrings(t *testing.T) {
+	testCases := []struct {
+		name    string
+		expr    *expr.ConcatExpression
+		want    system.Collection
+		wantErr error
+	}{
+		{
+			name: "concatenates two strings",
+			expr: &expr.ConcatExpression{
+				Left:  exprtest.Return(system.String("hello ")),
+				Right: exprtest.Return(system.String("world")),
+			},
+			want: system.Collection{system.String("hello world")},
+		},
+		{
+			name: "concatenates fhir string with system string",
+			expr: &expr.ConcatExpression{
+				Left:  exprtest.Return(fhir.String("abc")),
+				Right: exprtest.Return(system.String("def")),
+			},
+			want: system.Collection{system.String("abcdef")},
+		},
+		{
+			name: "if left collection is empty, returns the other string",
+			expr: &expr.ConcatExpression{
+				Left:  exprtest.Return(system.String("Hello")),
+				Right: exprtest.Return(),
+			},
+			want: system.Collection{system.String("Hello")},
+		},
+		{
+			name: "if right collection is empty, returns the other string",
+			expr: &expr.ConcatExpression{
+				Left:  exprtest.Return(),
+				Right: exprtest.Return(system.String("Hello")),
+			},
+			want: system.Collection{system.String("Hello")},
+		},
+		{
+			name: "returns an error if collection has multiple elements",
+			expr: &expr.ConcatExpression{
+				Left:  exprtest.Return(system.String("1"), system.String("2")),
+				Right: exprtest.Return(system.String("3")),
+			},
+			wantErr: expr.ErrNotSingleton,
+		},
+		{
+			name: "returns an error if a string is not returned",
+			expr: &expr.ConcatExpression{
+				Left:  exprtest.Return(system.Integer(1)),
+				Right: exprtest.Return(system.String("3")),
+			},
+			wantErr: expr.ErrInvalidType,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.expr.Evaluate(&expr.Context{}, system.Collection{})
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("ConcatExpression.Evaluate returned unexpected error: got %v, want %v", err, tc.wantErr)
+			}
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("ConcatExpression.Evaluate returned unexpected diff: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestExternalConstantExpression(t *testing.T) {
+	testCases := []struct {
+		name    string
+		expr    *expr.ExternalConstantExpression
+		context *expr.Context
+		want    system.Collection
+		wantErr error
+	}{
+		{
+			name: "returns constant",
+			expr: &expr.ExternalConstantExpression{Identifier: "value"},
+			context: &expr.Context{
+				ExternalConstants: map[string]any{"value": system.String("some string")},
+			},
+			want: system.Collection{system.String("some string")},
+		},
+		{
+			name: "returns error if constant doesn't exist",
+			expr: &expr.ExternalConstantExpression{Identifier: "value"},
+			context: &expr.Context{
+				ExternalConstants: map[string]any{},
+			},
+			wantErr: expr.ErrConstantNotFound,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.expr.Evaluate(tc.context, system.Collection{})
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("ExternalConstantExpression.Evaluate returned unexpected error: got %v, want %v", err, tc.wantErr)
+			}
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("ExternalConstantExpression.Evaluate returned unexpected diff: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestNegationExpression(t *testing.T) {
+	testCases := []struct {
+		name    string
+		expr    *expr.NegationExpression
+		want    system.Collection
+		wantErr error
+	}{
+		{
+			name: "negates integer",
+			expr: &expr.NegationExpression{Expr: exprtest.Return(system.Integer(4))},
+			want: system.Collection{system.Integer(-4)},
+		},
+		{
+			name: "negates decimal",
+			expr: &expr.NegationExpression{Expr: exprtest.Return(system.Decimal(decimal.NewFromFloat(1.5)))},
+			want: system.Collection{system.Decimal(decimal.NewFromFloat(-1.5))},
+		},
+		{
+			name: "negates quantity",
+			expr: &expr.NegationExpression{Expr: exprtest.Return(system.MustParseQuantity("2.5", "kg"))},
+			want: system.Collection{system.MustParseQuantity("-2.5", "kg")},
+		},
+		{
+			name: "negates proto integer",
+			expr: &expr.NegationExpression{Expr: exprtest.Return(fhir.Integer(-1))},
+			want: system.Collection{system.Integer(1)},
+		},
+		{
+			name:    "raises error on negating a collection",
+			expr:    &expr.NegationExpression{Expr: exprtest.Return(system.Integer(1), system.Integer(2))},
+			wantErr: expr.ErrNotSingleton,
+		},
+		{
+			name:    "raises error if a non-number type is negated",
+			expr:    &expr.NegationExpression{Expr: exprtest.Return(system.String("1"))},
+			wantErr: expr.ErrInvalidType,
+		},
+		{
+			name:    "raises error if complex type is negated",
+			expr:    &expr.NegationExpression{Expr: exprtest.Return(fhir.Ratio(20, 10))},
+			wantErr: expr.ErrInvalidType,
+		},
+		{
+			name: "passes through empty collection",
+			expr: &expr.NegationExpression{Expr: exprtest.Return()},
+			want: system.Collection{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.expr.Evaluate(&expr.Context{}, system.Collection{})
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("NegationExpression.Evaluate returned unexpected error: got %v, want %v", err, tc.wantErr)
+			}
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("NegationExpression.Evaluate returned unexpected diff: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
diff --git a/fhirpath/internal/expr/exprtest/doc.go b/fhirpath/internal/expr/exprtest/doc.go
new file mode 100644
index 0000000..8594ccf
--- /dev/null
+++ b/fhirpath/internal/expr/exprtest/doc.go
@@ -0,0 +1,5 @@
+/*
+Package exprtest provides some useful test dummies to make it
+easier to mock expressions to return a desired result.
+*/
+package exprtest
diff --git a/fhirpath/internal/expr/exprtest/doubles.go b/fhirpath/internal/expr/exprtest/doubles.go
new file mode 100644
index 0000000..90bdedc
--- /dev/null
+++ b/fhirpath/internal/expr/exprtest/doubles.go
@@ -0,0 +1,37 @@
+package exprtest
+
+import (
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+// MockExpression is a test double expression that calls
+// the contained function when evaluated.
+type MockExpression struct {
+	Eval func(*expr.Context, system.Collection) (system.Collection, error)
+}
+
+// Evaluate calls the contained Eval function.
+func (e *MockExpression) Evaluate(ctx *expr.Context, input system.Collection) (system.Collection, error) {
+	return e.Eval(ctx, input)
+}
+
+// Error creates a MockExpression that returns the provided
+// error when evaluated.
+func Error(input error) *MockExpression {
+	return &MockExpression{
+		func(*expr.Context, system.Collection) (system.Collection, error) {
+			return nil, input
+		},
+	}
+}
+
+// Return creates a MockExpression that returns the provided
+// inputs when evaluated.
+func Return(out ...any) *MockExpression {
+	return &MockExpression{
+		func(*expr.Context, system.Collection) (system.Collection, error) {
+			return out, nil
+		},
+	}
+}
diff --git a/fhirpath/internal/expr/operators.go b/fhirpath/internal/expr/operators.go
new file mode 100644
index 0000000..f3ef506
--- /dev/null
+++ b/fhirpath/internal/expr/operators.go
@@ -0,0 +1,29 @@
+package expr
+
+// Operator constants.
+const (
+	Equals        = "="
+	NotEquals     = "!="
+	Equivalence   = "~"
+	Inequivalence = "!~"
+	Is            = "is"
+	As            = "as"
+	And           = "and"
+	Or            = "or"
+	Xor           = "xor"
+	Implies       = "implies"
+	Lt            = "<"
+	Gt            = ">"
+	Lte           = "<="
+	Gte           = ">="
+	Add           = "+"
+	Sub           = "-"
+	Concat        = "&"
+	Mul           = "*"
+	Div           = "/"
+	FloorDiv      = "div"
+	Mod           = "mod"
+)
+
+// Operator represents a valid expression operator.
+type Operator string
diff --git a/fhirpath/internal/funcs/doc.go b/fhirpath/internal/funcs/doc.go
new file mode 100644
index 0000000..3d78d71
--- /dev/null
+++ b/fhirpath/internal/funcs/doc.go
@@ -0,0 +1,6 @@
+/*
+Package funcs provides the implementations for all
+base FHIRPath functions. Provides a wrapper, and function table
+for compilation.
+*/
+package funcs
diff --git a/fhirpath/internal/funcs/function.go b/fhirpath/internal/funcs/function.go
new file mode 100644
index 0000000..6ae174a
--- /dev/null
+++ b/fhirpath/internal/funcs/function.go
@@ -0,0 +1,94 @@
+package funcs
+
+import (
+	"errors"
+	"fmt"
+	"reflect"
+
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs/impl"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+var (
+	errNotFunc       = errors.New("value is not a function")
+	errMissingArgs   = errors.New("missing arguments")
+	errInvalidParams = errors.New("invalid input parameters")
+	errInvalidReturn = errors.New("invalid function return signature")
+)
+
+var notImplemented = Function{Func: unimplemented}
+
+// FHIRPathFunc is the common abstraction for all function types
+// supported by FHIRPath.
+type FHIRPathFunc func(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error)
+
+// Function contains the FHIRPathFunction, along with metadata
+type Function struct {
+	Func           FHIRPathFunc
+	MinArity       int
+	MaxArity       int
+	IsTypeFunction bool
+}
+
+// ToFunction takes in a function with any arguments and attempts to
+// convert it to a functions.Function type. If the conversion is successful,
+// the new function will assert the argument expressions resolve to the original
+// argument types.
+func ToFunction(fn any) (Function, error) {
+	rv := reflect.ValueOf(fn)
+	if err := validateFunc(rv); err != nil {
+		return Function{}, fmt.Errorf("constructing FHIRPathFunction: %w", err)
+	}
+
+	arity := rv.Type().NumIn() - 1 // 'True' arity, as the first argument is the input Collection.
+	fhirpathFunc := func(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+		if len(args) != arity {
+			return nil, fmt.Errorf("%w: function expects %v arguments, received %v", impl.ErrWrongArity, arity, len(args))
+		}
+		// ensure the arguments match the function signature.
+		funcArgs := []reflect.Value{reflect.ValueOf(input)}
+		for i, exp := range args {
+			result, err := exp.Evaluate(ctx, input)
+			if err != nil {
+				return nil, err
+			}
+			if len(result) != 1 {
+				return nil, fmt.Errorf("%w: doesn't return singleton", impl.ErrInvalidReturnType)
+			}
+			if expectedType, gotType := rv.Type().In(i+1), reflect.TypeOf(result[0]); !gotType.AssignableTo(expectedType) {
+				return nil, fmt.Errorf("%w: got type '%s' when type '%s' was expected", impl.ErrInvalidReturnType, gotType.String(), expectedType.Name())
+			}
+			funcArgs = append(funcArgs, reflect.ValueOf(result[0]))
+		}
+		output := rv.Call(funcArgs)
+		if err, ok := output[1].Interface().(error); ok {
+			return output[0].Interface().(system.Collection), err
+		}
+		return output[0].Interface().(system.Collection), nil
+	}
+	return Function{fhirpathFunc, arity, arity, false}, nil
+}
+
+// validateFunc verifies that the input reflect value represents a
+// valid FHIRPath function. If not, it returns an error.
+func validateFunc(rv reflect.Value) error {
+	if rv.Kind() != reflect.Func {
+		return errNotFunc
+	}
+	errs := []error{}
+	if rv.Type().NumIn() < 1 {
+		errs = append(errs, errMissingArgs)
+	} else if rv.Type().In(0) != reflect.TypeOf(system.Collection{}) {
+		errs = append(errs, errInvalidParams)
+	}
+	if rv.Type().NumOut() != 2 || rv.Type().Out(0) != reflect.TypeOf(system.Collection{}) || rv.Type().Out(1).Name() != "error" {
+		errs = append(errs, errInvalidReturn)
+	}
+	return errors.Join(errs...)
+}
+
+// unimplemented is a no-op placeholder function that satisfies the FHIRPathFunction contract
+func unimplemented(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	return nil, fmt.Errorf("function not yet implemented")
+}
diff --git a/fhirpath/internal/funcs/function_table.go b/fhirpath/internal/funcs/function_table.go
new file mode 100644
index 0000000..e4de603
--- /dev/null
+++ b/fhirpath/internal/funcs/function_table.go
@@ -0,0 +1,23 @@
+package funcs
+
+import (
+	"fmt"
+)
+
+// FunctionTable is the data structure used to store
+// valid FHIRPath functions, and maps their case-sensitive
+// names.
+type FunctionTable map[string]Function
+
+// Register attempts to add a given function to the FunctionTable t.
+func (t FunctionTable) Register(name string, fn any) error {
+	if _, ok := t[name]; ok {
+		return fmt.Errorf("function '%s' already exists in default table", name)
+	}
+	fhirpathFunc, err := ToFunction(fn)
+	if err != nil {
+		return err
+	}
+	t[name] = fhirpathFunc
+	return nil
+}
diff --git a/fhirpath/internal/funcs/function_table_test.go b/fhirpath/internal/funcs/function_table_test.go
new file mode 100644
index 0000000..24fee66
--- /dev/null
+++ b/fhirpath/internal/funcs/function_table_test.go
@@ -0,0 +1,49 @@
+package funcs_test
+
+import (
+	"testing"
+
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+func TestRegister_RaisesError(t *testing.T) {
+	testCases := []struct {
+		name     string
+		funcName string
+		fn       any
+	}{
+		{
+			name:     "raises error when trying to override existing function",
+			funcName: "where",
+			fn:       func(system.Collection) (system.Collection, error) { return nil, nil },
+		},
+		{
+			name:     "raises error when adding invalid function",
+			funcName: "someFn",
+			fn:       func() {},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			table := funcs.Clone()
+
+			if err := table.Register(tc.funcName, tc.fn); err == nil {
+				t.Fatalf("FunctionTable.Register(%s) doesn't raise error when expected", tc.funcName)
+			}
+		})
+	}
+}
+
+func TestRegister_AddsToMap(t *testing.T) {
+	table := funcs.Clone()
+	fn := func(system.Collection) (system.Collection, error) { return nil, nil }
+
+	if err := table.Register("someFn", fn); err != nil {
+		t.Fatalf("FunctionTable.Register raised unexpected error: %v", err)
+	}
+	if _, ok := table["someFn"]; !ok {
+		t.Errorf("FunctionTable.Register did not successfully add function to map")
+	}
+}
diff --git a/fhirpath/internal/funcs/function_test.go b/fhirpath/internal/funcs/function_test.go
new file mode 100644
index 0000000..b6bd8d3
--- /dev/null
+++ b/fhirpath/internal/funcs/function_test.go
@@ -0,0 +1,187 @@
+package funcs_test
+
+import (
+	"errors"
+	"reflect"
+	"testing"
+
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr/exprtest"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+func TestToFunction_EvaluatesCorrectly(t *testing.T) {
+	patient := &ppb.Patient{
+		Id: fhir.ID("1234"),
+	}
+	fns := map[string]any{
+		"take": func(input system.Collection, num system.Integer) (system.Collection, error) {
+			result := system.Collection{}
+			for i := 0; i < int(num); i++ {
+				if i >= len(input) {
+					continue
+				}
+				result = append(result, input[i])
+			}
+			return result, nil
+		},
+		"findResource": func(input system.Collection, resource fhir.Resource) (system.Collection, error) {
+			for i, elem := range input {
+				if reflect.DeepEqual(elem, resource) {
+					return system.Collection{system.Integer(i)}, nil
+				}
+			}
+			return system.Collection{}, nil
+		},
+	}
+	testCases := []struct {
+		name  string
+		fn    any
+		args  []expr.Expression
+		input system.Collection
+		want  system.Collection
+	}{
+		{
+			name:  "test custom take function",
+			fn:    fns["take"],
+			args:  []expr.Expression{&expr.LiteralExpression{Literal: system.Integer(2)}},
+			input: system.Collection{"1", "2", "3"},
+			want:  system.Collection{"1", "2"},
+		},
+		{
+			name:  "findResource returns index of desired resource",
+			fn:    fns["findResource"],
+			args:  []expr.Expression{exprtest.Return(patient)},
+			input: system.Collection{system.Boolean(true), system.Boolean(false), patient},
+			want:  system.Collection{system.Integer(2)},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			gotFunc, err := funcs.ToFunction(tc.fn)
+			if err != nil {
+				t.Fatalf("ToFunction(%T) raised unexpected invalid signature error: %v", tc.fn, err)
+			}
+			gotCollection, err := gotFunc.Func(&expr.Context{}, tc.input, tc.args...)
+			if err != nil {
+				t.Fatalf("Evaluating function generated by ToFunction raised unexpected error: %v", err)
+			}
+			if diff := cmp.Diff(tc.want, gotCollection); diff != "" {
+				t.Errorf("Evaluating function generated by ToFunction returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestToFunction_RaisesSignatureError(t *testing.T) {
+	testCases := []struct {
+		name string
+		fn   any
+	}{
+		{
+			name: "not a function",
+			fn:   4,
+		},
+		{
+			name: "no arguments",
+			fn:   func() {},
+		},
+		{
+			name: "doesn't contain an input collection as first argument",
+			fn:   func(num system.Integer) {},
+		},
+		{
+			name: "only returns one input",
+			fn:   func(in system.Collection) system.Collection { return system.Collection{} },
+		},
+		{
+			name: "doesn't return a collection",
+			fn:   func(in system.Collection) (int, error) { return 1, nil },
+		},
+		{
+			name: "doesn't return an error",
+			fn:   func(in system.Collection) (system.Collection, bool) { return system.Collection{}, false },
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := funcs.ToFunction(tc.fn)
+			if err == nil {
+				t.Fatalf("ToFunction(%T) didn't raise error when expected to on function signature mismatch", tc.fn)
+			}
+		})
+	}
+}
+
+func TestToFunction_RaisesEvaluationError(t *testing.T) {
+	testCases := []struct {
+		name  string
+		fn    any
+		args  []expr.Expression
+		input system.Collection
+	}{
+		{
+			name:  "function arity doesn't match number of arguments",
+			fn:    func(in system.Collection) (system.Collection, error) { return system.Collection{}, nil },
+			args:  []expr.Expression{&expr.LiteralExpression{Literal: system.Boolean(true)}},
+			input: system.Collection{},
+		},
+		{
+			name: "argument expression raises error",
+			fn: func(in system.Collection, num system.Integer) (system.Collection, error) {
+				return system.Collection{}, nil
+			},
+			args:  []expr.Expression{exprtest.Error(errors.New("mock error"))},
+			input: system.Collection{},
+		},
+		{
+			name: "argument expression doesn't evaluate to singleton",
+			fn: func(in system.Collection, num system.Integer) (system.Collection, error) {
+				return system.Collection{}, nil
+			},
+			args:  []expr.Expression{exprtest.Return(1, 2)},
+			input: system.Collection{},
+		},
+		{
+			name: "argument expression evaluates to different type",
+			fn: func(in system.Collection, num system.Integer) (system.Collection, error) {
+				return system.Collection{}, nil
+			},
+			args:  []expr.Expression{&expr.LiteralExpression{Literal: system.Boolean(false)}},
+			input: system.Collection{},
+		},
+		{
+			name: "second argument expression evaluates to wrong type",
+			fn: func(in system.Collection, num system.Integer, num2 system.Integer) (system.Collection, error) {
+				return system.Collection{}, nil
+			},
+			args:  []expr.Expression{&expr.LiteralExpression{Literal: system.Integer(3)}, &expr.LiteralExpression{Literal: system.Boolean(true)}},
+			input: system.Collection{},
+		},
+		{
+			name:  "function returns error",
+			fn:    func(in system.Collection) (system.Collection, error) { return nil, errors.New("some error") },
+			args:  []expr.Expression{},
+			input: system.Collection{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			gotFunc, err := funcs.ToFunction(tc.fn)
+			if err != nil {
+				t.Fatalf("ToFunction(%T) raised unexpected invalid signature error: %v", tc.fn, err)
+			}
+			_, err = gotFunc.Func(&expr.Context{}, tc.input, tc.args...)
+			if err == nil {
+				t.Fatalf("ToFunction() did not raise evaluation error when calling generated function")
+			}
+		})
+	}
+}
diff --git a/fhirpath/internal/funcs/impl/conversion.go b/fhirpath/internal/funcs/impl/conversion.go
new file mode 100644
index 0000000..047fa1f
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/conversion.go
@@ -0,0 +1,646 @@
+package impl
+
+import (
+	"errors"
+	"fmt"
+	"math"
+	"regexp"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+// DefaultQuantityUnit is defined by the following FHIRPath rules:
+// the item is an Integer, or Decimal, where the resulting quantity will have the default unit ('1')
+// the item is a Boolean, where true results in the quantity 1.0 '1', and false results in the quantity 0.0 '1'
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#toquantityunit-string-quantity
+const DefaultQuantityUnit = "1"
+
+// Based on the FHIRPath Quantity string validation regexp defined here:
+// https://hl7.org/fhirpath/N1/#convertstoquantityunit-string-boolean
+const fhirQuantityRegexp = `^(?P<value>(\+|-)?\d+(\.\d+)?)\s*('(?P<unit>[^']+)'|(?P<time>[a-zA-Z]+))?$`
+
+var regex = regexp.MustCompile(fhirQuantityRegexp)
+
+// ConvertsToBoolean checks if the input can be converted to a Boolean
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#convertstoboolean-boolean
+func ConvertsToBoolean(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validation
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Conversion validation
+	result, err := ToBoolean(ctx, input, args...)
+	if result.IsEmpty() || err != nil {
+		return system.Collection{system.Boolean(false)}, nil
+	}
+	return system.Collection{system.Boolean(true)}, nil
+}
+
+// ConvertsToDate checks if the input can be converted to a Date
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#convertstodate-boolean
+func ConvertsToDate(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validation
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Conversion validation
+	result, err := ToDate(ctx, input, args...)
+	if result.IsEmpty() || err != nil {
+		return system.Collection{system.Boolean(false)}, nil
+	}
+	return system.Collection{system.Boolean(true)}, nil
+}
+
+// ConvertsToDateTime checks if the input can be converted to a Time
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#convertstodatetime-boolean
+func ConvertsToDateTime(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validation
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Conversion validation
+	result, err := ToDateTime(ctx, input, args...)
+	if result.IsEmpty() || err != nil {
+		return system.Collection{system.Boolean(false)}, nil
+	}
+	return system.Collection{system.Boolean(true)}, nil
+}
+
+// ConvertsToDecimal checks if the input can be converted to a Decimal
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#convertstodecimal-boolean
+func ConvertsToDecimal(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validation
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Conversion validation
+	result, err := ToDecimal(ctx, input, args...)
+	if result.IsEmpty() || err != nil {
+		return system.Collection{system.Boolean(false)}, nil
+	}
+	return system.Collection{system.Boolean(true)}, nil
+}
+
+// ConvertsToInteger checks if the input can be converted to an Integer
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#convertstointeger-boolean
+func ConvertsToInteger(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validation
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Conversion validation
+	result, err := ToInteger(ctx, input, args...)
+	if result.IsEmpty() || err != nil {
+		return system.Collection{system.Boolean(false)}, nil
+	}
+	return system.Collection{system.Boolean(true)}, nil
+}
+
+// ConvertsToQuantity checks if the input can be converted to a Quantity
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#convertstoquantityunit-string-boolean
+func ConvertsToQuantity(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validation
+	if len(args) > 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1 or 0", ErrWrongArity, len(args))
+	}
+	// Conversion validation
+	result, err := ToQuantity(ctx, input, args...)
+	if result.IsEmpty() || err != nil {
+		return system.Collection{system.Boolean(false)}, nil
+	}
+	return system.Collection{system.Boolean(true)}, nil
+}
+
+// ConvertsToString checks if the input can be converted to a String
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#convertstostring-string
+func ConvertsToString(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validation
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Conversion validation
+	result, err := ToString(ctx, input, args...)
+	if result.IsEmpty() || err != nil {
+		return system.Collection{system.Boolean(false)}, nil
+	}
+	if boolean, _ := result.ToBool(); boolean == false {
+		return system.Collection{system.Boolean(false)}, nil
+	}
+	return system.Collection{system.Boolean(true)}, nil
+}
+
+// ConvertsToTime checks if the input can be converted to a Time
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#convertstotime-boolean
+func ConvertsToTime(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validation
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Conversion validation
+	result, err := ToTime(ctx, input, args...)
+	if result.IsEmpty() || err != nil {
+		return system.Collection{system.Boolean(false)}, nil
+	}
+	return system.Collection{system.Boolean(true)}, nil
+}
+
+// ToBoolean converts the input to a Boolean
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#toboolean-boolean
+func ToBoolean(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validation
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Input reading
+	value, err := system.From(input[0])
+	if err != nil {
+		return nil, err
+	}
+	// Input conversion
+	switch value := value.(type) {
+	case system.Decimal:
+		result, err := system.ParseBoolean(value.String())
+		if err != nil {
+			return system.Collection{}, nil
+		}
+		return system.Collection{result}, nil
+	case system.Integer, system.String:
+		str := fmt.Sprintf("%v", value)
+		result, err := system.ParseBoolean(str)
+		if err != nil {
+			return system.Collection{}, nil
+		}
+		return system.Collection{result}, nil
+	case system.Boolean:
+		return system.Collection{value}, nil
+	}
+	return system.Collection{}, nil
+}
+
+// ToDate converts the input to a Date
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#todate-date
+func ToDate(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validation
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Input reading
+	value, err := system.From(input[0])
+	if err != nil {
+		return system.Collection{}, nil
+	}
+	// Input conversion
+	switch value := value.(type) {
+	case system.Date:
+		return system.Collection{value}, nil
+	case system.DateTime:
+		dt := value.String()
+		result := system.MustParseDate(dt[:10])
+		return system.Collection{result}, nil
+	case system.String:
+		result, err := system.ParseDate(string(value))
+		if err != nil {
+			return system.Collection{}, nil
+		}
+		return system.Collection{result}, nil
+	}
+	return system.Collection{}, nil
+}
+
+// ToDateTime converts the input to a Date
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#todatetime-datetime
+func ToDateTime(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validations
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validations
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Reading input
+	value, err := system.From(input[0])
+	if err != nil {
+		return system.Collection{}, nil
+	}
+	// Input conversion
+	switch value := value.(type) {
+	case system.Date:
+		return system.Collection{value.ToDateTime()}, nil
+	case system.DateTime:
+		return system.Collection{value}, nil
+	case system.String:
+		result, err := system.ParseDateTime(string(value))
+		if err != nil {
+			return system.Collection{}, nil
+		}
+		return system.Collection{result}, nil
+	}
+	return system.Collection{}, nil
+}
+
+// ToDecimal converts the input to a Decimal
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#todecimal-decimal
+func ToDecimal(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validation
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Input reading
+	value, err := system.From(input[0])
+	if err != nil {
+		return nil, err
+	}
+	// Input conversion
+	switch value.(type) {
+	case system.Decimal:
+		return system.Collection{value}, nil
+	case system.Integer:
+		str := fmt.Sprintf("%v", value)
+		result, err := system.ParseDecimal(str)
+		if err != nil {
+			return system.Collection{}, nil
+		}
+		return system.Collection{result}, nil
+	case system.String:
+		str := fmt.Sprintf("%s", value)
+		result, err := system.ParseDecimal(str)
+		if err != nil {
+			return system.Collection{}, nil
+		}
+		return system.Collection{result}, nil
+	case system.Boolean:
+		if value.(system.Boolean) {
+			return system.Collection{system.MustParseDecimal("1.0")}, nil
+		}
+		return system.Collection{system.MustParseDecimal("0.0")}, nil
+	}
+	return system.Collection{}, nil
+}
+
+// ToInteger converts the input to an Integer
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#tointeger-integer
+func ToInteger(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validation
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Input reading
+	value, err := system.From(input[0])
+	if err != nil {
+		return nil, err
+	}
+	// Input conversion
+	switch value.(type) {
+	case system.Integer:
+		return system.Collection{value}, nil
+	case system.String:
+		str := fmt.Sprintf("%s", value)
+		result, err := system.ParseInteger(str)
+		if err != nil {
+			return nil, err
+		}
+		return system.Collection{result}, nil
+	case system.Boolean:
+		if value.(system.Boolean) {
+			return system.Collection{system.Integer(1)}, nil
+		}
+		return system.Collection{system.Integer(0)}, nil
+	}
+	return system.Collection{}, nil
+}
+
+// ToQuantity converts the input to a Quantity
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#toquantityunit-string-quantity
+func ToQuantity(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validation
+	if len(args) > 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1 or 0", ErrWrongArity, len(args))
+	}
+	argStr := ""
+	if len(args) == 1 {
+		argValue, err := args[0].Evaluate(ctx, input)
+		if err != nil {
+			return nil, err
+		}
+		argStr, err = argValue.ToString()
+		if err != nil {
+			return nil, err
+		}
+	}
+	// Input reading
+	value, err := system.From(input[0])
+	if err != nil {
+		return nil, err
+	}
+	// Input conversion
+	switch value := value.(type) {
+	case system.Integer:
+		matches := regex.FindStringSubmatch(fmt.Sprintf("%v %v", value, argStr))
+		if matches == nil {
+			return system.Collection{}, nil
+		}
+		if argStr != "" {
+			unit := regex.SubexpIndex("unit")
+			t := regex.SubexpIndex("time")
+			if matches[unit] != "" {
+				result := system.MustParseQuantity(fmt.Sprintf("%v", value), matches[unit])
+				return system.Collection{result}, nil
+			}
+			if matches[t] != "" {
+				result := system.MustParseQuantity(fmt.Sprintf("%v", value), matches[t])
+				return system.Collection{result}, nil
+			}
+		}
+		result := system.MustParseQuantity(fmt.Sprintf("%v", value), DefaultQuantityUnit)
+		return system.Collection{result}, nil
+	case system.Decimal:
+		str := value.String()
+		result, err := system.ParseQuantity(string(str), DefaultQuantityUnit)
+		if err != nil {
+			return nil, err
+		}
+		return system.Collection{result}, nil
+	case system.Quantity:
+		return system.Collection{value}, nil
+	case system.String:
+		matches := regex.FindStringSubmatch(string(value))
+		if matches == nil {
+			return system.Collection{}, nil
+		}
+		if argStr != "" {
+			if !isValidUnitConversion(argStr) {
+				return nil, fmt.Errorf("invalid unit of time: %v", input)
+			}
+			conversion, err := convertDuration(string(value), argStr)
+			if err != nil {
+				return nil, err
+			}
+			res := strings.SplitN(conversion, " ", 2)
+			result := system.MustParseQuantity(res[0], res[1])
+			return system.Collection{result}, nil
+		}
+		res := strings.SplitN(string(value), " ", 2)
+		unit := strings.Trim(res[1], "'")
+		result := system.MustParseQuantity(res[0], unit)
+		return system.Collection{result}, nil
+	case system.Boolean:
+		if value {
+			result := system.MustParseQuantity("1.0", DefaultQuantityUnit)
+			return system.Collection{result}, nil
+		}
+		result := system.MustParseQuantity("0.0", DefaultQuantityUnit)
+		return system.Collection{result}, nil
+	}
+	return system.Collection{}, nil
+}
+
+// ToString converts the input to a String
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#tostring-string
+func ToString(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validation
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Input reading
+	value, err := system.From(input[0])
+	if err != nil {
+		return system.Collection{system.Boolean(false)}, nil
+	}
+	// Input conversion
+	switch value := value.(type) {
+	case system.String:
+		return system.Collection{value}, nil
+	case system.Integer:
+		return system.Collection{system.String(fmt.Sprintf("%v", value))}, nil
+	case system.Decimal:
+		return system.Collection{system.String(value.String())}, nil
+	case system.Quantity:
+		return system.Collection{system.String(value.String())}, nil
+	case system.Date:
+		return system.Collection{system.String(value.String())}, nil
+	case system.Time:
+		return system.Collection{system.String(value.String())}, nil
+	case system.DateTime:
+		return system.Collection{system.String(value.String())}, nil
+	case system.Boolean:
+		if value {
+			return system.Collection{system.String("true")}, nil
+		}
+		return system.Collection{system.String("false")}, nil
+	}
+	return system.Collection{system.Boolean(false)}, nil
+}
+
+// ToTime converts the input to a Time
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#totime-time
+func ToTime(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Argument validation
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Input reading
+	value, err := system.From(input[0])
+	if err != nil {
+		return system.Collection{}, nil
+	}
+	// Input conversion
+	switch value := value.(type) {
+	case system.Time:
+		return system.Collection{value}, nil
+	case system.String:
+		result, err := system.ParseTime(fmt.Sprintf("%v", value))
+		if err != nil {
+			return system.Collection{}, nil
+		}
+		return system.Collection{result}, nil
+	}
+	return system.Collection{}, nil
+}
+
+func isValidUnitConversion(outputFormat string) bool {
+	validFormats := map[string]bool{
+		"years":   true,
+		"months":  true,
+		"days":    true,
+		"hours":   true,
+		"minutes": true,
+		"seconds": true,
+	}
+
+	return validFormats[outputFormat]
+}
+
+func convertDuration(input string, outputFormat string) (string, error) {
+	duration, err := parseHumanDuration(input)
+	if err != nil {
+		return "", err
+	}
+
+	var convertedValue float64
+	switch outputFormat {
+	case "years":
+		convertedValue = duration.Hours() / (24 * 365)
+	case "months":
+		convertedValue = duration.Hours() / (24 * 30)
+	case "days":
+		convertedValue = duration.Hours() / 24
+	case "hours":
+		convertedValue = duration.Hours()
+	case "minutes":
+		convertedValue = duration.Minutes()
+	case "seconds":
+		convertedValue = duration.Seconds()
+	}
+
+	if outputFormat == "years" {
+		convertedValue = math.Ceil(convertedValue / 12.0)
+	}
+
+	return fmt.Sprintf("%.0f %s", convertedValue, outputFormat), nil
+}
+
+func parseHumanDuration(input string) (time.Duration, error) {
+	re := regexp.MustCompile(`(\d+)\s*(\w+)`)
+	matches := re.FindAllStringSubmatch(input, -1)
+	totalSeconds := int64(0)
+
+	for _, match := range matches {
+		value, err := strconv.ParseInt(match[1], 10, 64)
+		if err != nil {
+			return 0, err
+		}
+
+		unit := strings.ToLower(match[2])
+		switch unit {
+		case "second", "seconds":
+			totalSeconds += value
+		case "minute", "minutes":
+			totalSeconds += value * 60
+		case "hour", "hours":
+			totalSeconds += value * 3600
+		case "day", "days":
+			totalSeconds += value * 86400
+		case "month", "months":
+			totalSeconds += value * 30 * 86400 // Assuming one month is 30 days
+		case "year", "years":
+			totalSeconds += value * 365 * 86400 // Assuming one year is 365 days
+		default:
+			return 0, fmt.Errorf("invalid unit: %s", unit)
+		}
+	}
+
+	return time.Duration(totalSeconds) * time.Second, nil
+}
diff --git a/fhirpath/internal/funcs/impl/conversion_test.go b/fhirpath/internal/funcs/impl/conversion_test.go
new file mode 100644
index 0000000..c083727
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/conversion_test.go
@@ -0,0 +1,2112 @@
+package impl_test
+
+import (
+	"testing"
+
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr/exprtest"
+	"google.golang.org/protobuf/testing/protocmp"
+
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs/impl"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+func TestConvertsToBoolean(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.String("T"),
+				system.String("True")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.String("false")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("200")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns false if input is not convertible",
+			input:   system.Collection{system.String("404 Kg")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Decimal '0.0'",
+			input:   system.Collection{system.MustParseDecimal("0.0")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Decimal '1.0'",
+			input:   system.Collection{system.MustParseDecimal("1.0")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Decimal '3.5'",
+			input:   system.Collection{system.MustParseDecimal("3.5")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Integer '0'",
+			input:   system.Collection{system.Integer(0)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Integer '1'",
+			input:   system.Collection{system.Integer(1)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Integer '3'",
+			input:   system.Collection{system.Integer(3)},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '2.0'",
+			input:   system.Collection{system.String("2.0")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '1.0'",
+			input:   system.Collection{system.String("1.0")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'y'",
+			input:   system.Collection{system.String("y")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'yes'",
+			input:   system.Collection{system.String("yes")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'Y'",
+			input:   system.Collection{system.String("Y")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'YES'",
+			input:   system.Collection{system.String("YES")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '0.0'",
+			input:   system.Collection{system.String("0.0")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'f'",
+			input:   system.Collection{system.String("f")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'false'",
+			input:   system.Collection{system.String("false")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'F'",
+			input:   system.Collection{system.String("F")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'FALSE'",
+			input:   system.Collection{system.String("FALSE")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'n",
+			input:   system.Collection{system.String("n")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'no'",
+			input:   system.Collection{system.String("no")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'N",
+			input:   system.Collection{system.String("N")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'NO'",
+			input:   system.Collection{system.String("NO")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Boolean 'true'",
+			input:   system.Collection{system.Boolean(true)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Boolean 'false'",
+			input:   system.Collection{system.Boolean(false)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.Integer '1'",
+			input:   system.Collection{fhir.Integer(1)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.PositiveInt '1'",
+			input:   system.Collection{fhir.PositiveInt(1)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.UnsignedInt '1'",
+			input:   system.Collection{fhir.UnsignedInt(1)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ConvertsToBoolean(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ConvertsToBoolean() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ConvertsToBoolean() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestConvertsToDate(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.String("2001-09-11"),
+				system.String("2011-05-02")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.String("false")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("minutes")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns false if input is not convertible to system.Date",
+			input:   system.Collection{system.MustParseQuantity("75", "Kg")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a system.Date",
+			input:   system.Collection{system.MustParseDate("1993-08-13")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a partial system.Date",
+			input:   system.Collection{system.MustParseDate("1993-08")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a system.DateTime",
+			input:   system.Collection{system.MustParseDateTime("1993-08-13T14:20:00")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a convertible system.String",
+			input:   system.Collection{system.String("1993-08-13")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns false for a non convertible system.String",
+			input:   system.Collection{system.String("93.08.13")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "returns false for a ppb.Patient",
+			input:   system.Collection{&ppb.Patient{}},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ConvertsToDate(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ConvertsToDate() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ConvertsToDate() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestConvertsToDateTime(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.String("2001-09-11"),
+				system.String("2011-05-02")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.String("2001-09-11")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("minutes")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns empty if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns false if input is not convertible to system.DateTime",
+			input:   system.Collection{system.MustParseQuantity("75", "Kg")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a system.DateTime",
+			input:   system.Collection{system.MustParseDateTime("1993-08-13T14:20:00")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a partial system.DateTime",
+			input:   system.Collection{system.MustParseDateTime("2012-01-01T10:00")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a system.Date",
+			input:   system.Collection{system.MustParseDate("2006-01-02")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a convertible system.String",
+			input:   system.Collection{system.String("1993-08-13T14:20:00")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns empty for a non convertible system.String",
+			input:   system.Collection{system.String("93.08.13")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "returns empty for a ppb.Patient",
+			input:   system.Collection{&ppb.Patient{}},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ConvertsToDateTime(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ConvertsToDateTime() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ConvertsToDateTime() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestConvertsToDecimal(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.String("101"),
+				system.String("102")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.String("100")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("200")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Decimal 'true'",
+			input:   system.Collection{system.MustParseDecimal("13.5")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Decimal 'false'",
+			input:   system.Collection{system.MustParseDecimal("-13.5")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Integer '13'",
+			input:   system.Collection{system.Integer(13)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input system.Integer '-13'",
+			input:   system.Collection{system.Integer(-13)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input system.String '+13.5'",
+			input:   system.Collection{system.String("+13.5")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '-13.5'",
+			input:   system.Collection{system.String("-13.5")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '3.1416 cm'",
+			input:   system.Collection{system.String("3.1416 cm")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '12.99'",
+			input:   system.Collection{system.String(" 12.99 ")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is true system.Boolean 'true'",
+			input:   system.Collection{system.Boolean(true)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Boolean 'false'",
+			input:   system.Collection{system.Boolean(false)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.Integer '10'",
+			input:   system.Collection{fhir.Integer(10)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.PositiveInt '11'",
+			input:   system.Collection{fhir.PositiveInt(11)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.UnsignedInt '12'",
+			input:   system.Collection{fhir.UnsignedInt(12)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.Boolean 'true'",
+			input:   system.Collection{fhir.Boolean(true)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ConvertsToDecimal(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ConvertsToDecimal() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ConvertsToDecimal() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestConvertsToInteger(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.String("101"),
+				system.String("102")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.String("100")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("200")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Integer '13'",
+			input:   system.Collection{system.Integer(13)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Integer '-13'",
+			input:   system.Collection{system.Integer(-13)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '13'",
+			input:   system.Collection{system.String("13")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '+13'",
+			input:   system.Collection{system.String("+13")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '404 kg'",
+			input:   system.Collection{system.String("404 Kg")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String ' 12 '",
+			input:   system.Collection{system.String(" 12 ")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '-13'",
+			input:   system.Collection{system.String("-13")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Boolean 'true'",
+			input:   system.Collection{system.Boolean(true)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Boolean 'false'",
+			input:   system.Collection{system.Boolean(false)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.Integer '10'",
+			input:   system.Collection{fhir.Integer(10)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.PositiveInt '11'",
+			input:   system.Collection{fhir.PositiveInt(11)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.UnsignedInt '12'",
+			input:   system.Collection{fhir.UnsignedInt(12)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.Boolean 'true'",
+			input:   system.Collection{fhir.Boolean(true)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ConvertsToInteger(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ConvertsToInteger() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ConvertsToInteger() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestConvertsToQuantity(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.String("10 'km/hr'"),
+				system.String("10 'mi/hr'")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns false if input is not convertible to a system.Quantity",
+			input:   system.Collection{system.String("10 km / hr")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:  "errors if args length is more than 1",
+			input: system.Collection{system.String("2 days")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("hours")),
+				exprtest.Return(system.String("minutes")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Integer '13'",
+			input:   system.Collection{system.Integer(13)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Decimal '13.5",
+			input:   system.Collection{system.MustParseDecimal("13.5")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Quantity '13.5 lbs'",
+			input:   system.Collection{system.MustParseQuantity("13.5", "lbs")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100 days'",
+			input:   system.Collection{system.String("100 days")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100 km'",
+			input:   system.Collection{system.String("100 km")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100 km/h'",
+			input:   system.Collection{system.String("100 km/h")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100 'km/h''",
+			input:   system.Collection{system.String("100 'km/h'")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100 'km per hour''",
+			input:   system.Collection{system.String("100 'km per hour'")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100 km per hour'",
+			input:   system.Collection{system.String("100 km per hour")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100           km'",
+			input:   system.Collection{system.String("100           km")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100           km/h'",
+			input:   system.Collection{system.String("100           km/h")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '10 'km per hr''",
+			input:   system.Collection{system.String("10 'km per hr'")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:  "input is system.String '2 years' with arg 'months'",
+			input: system.Collection{system.String("2 years")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("months")),
+			},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:  "input is system.String '2 months' with arg 'days'",
+			input: system.Collection{system.String("2 months")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("days")),
+			},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:  "input is system.String '2 days' with arg 'hours'",
+			input: system.Collection{system.String("2 days")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("hours")),
+			},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:  "input is system.String '2 hours' with arg 'minutes'",
+			input: system.Collection{system.String("2 hours")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("minutes")),
+			},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:  "input is system.String '2 minutes' with arg 'seconds'",
+			input: system.Collection{system.String("2 minutes")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("seconds")),
+			},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100 'km''",
+			input:   system.Collection{system.String("100 'km'")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:  "input is system.Integer '100' with arg ''km''",
+			input: system.Collection{system.Integer(100)},
+			args: []expr.Expression{
+				exprtest.Return(system.String("'km'")),
+			},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:  "input is system.Integer '100' with arg 'days''",
+			input: system.Collection{system.Integer(100)},
+			args: []expr.Expression{
+				exprtest.Return(system.String("days")),
+			},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Boolean 'true'",
+			input:   system.Collection{system.Boolean(true)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Boolean 'false'",
+			input:   system.Collection{system.Boolean(false)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.Integer '10'",
+			input:   system.Collection{fhir.Integer(10)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.PositiveInt '11'",
+			input:   system.Collection{fhir.PositiveInt(11)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.UnsignedInt '12'",
+			input:   system.Collection{fhir.UnsignedInt(12)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ConvertsToQuantity(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ConvertsToQuantity() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ConvertsToQuantity() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestConvertsToString(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input is not a singleton",
+			input: system.Collection{
+				system.String("101"),
+				system.String("102"),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns empty for and empty input collection",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a system.String",
+			input:   system.Collection{system.String("100")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a fhir.String",
+			input:   system.Collection{fhir.String("100")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a system.Integer",
+			input:   system.Collection{system.Integer(100)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a fhir.Integer",
+			input:   system.Collection{fhir.Integer(100)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a fhir.PositiveInt",
+			input:   system.Collection{fhir.PositiveInt(11)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a fhir.UnsignedInt",
+			input:   system.Collection{fhir.UnsignedInt(12)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a system.Decimal",
+			input:   system.Collection{system.MustParseDecimal("100.999")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a system.Date",
+			input:   system.Collection{system.MustParseDate("1993-08-13")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a system.Time",
+			input:   system.Collection{system.MustParseTime("14:01:45.0000001")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a system.DateTime",
+			input:   system.Collection{system.MustParseDateTime("1993-08-13T14:01:45.0000001")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a system.Boolean",
+			input:   system.Collection{system.Boolean(true)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a fhir.Boolean",
+			input:   system.Collection{fhir.Boolean(true)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a system.Quantity",
+			input:   system.Collection{system.MustParseQuantity("75", "kg")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns false for a ppb.Patient",
+			input:   system.Collection{&ppb.Patient{}},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ConvertsToString(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ConvertsToString() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ConvertsToString() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestConvertsToTime(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.String("2001-09-11"),
+				system.String("2011-05-02")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.String("2001-09-11")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("minutes")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns empty if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns an empty if input is not convertible to system.Time",
+			input:   system.Collection{system.MustParseQuantity("75", "Kg")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a systemTime",
+			input:   system.Collection{system.MustParseTime("16:20:59")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a partial system.Time",
+			input:   system.Collection{system.MustParseTime("16:20")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns true for a convertible system.String",
+			input:   system.Collection{system.String("12:59:59")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "returns false for a non convertible system.String",
+			input:   system.Collection{system.String("12/59/99")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "returns empty for a ppb.Patient",
+			input:   system.Collection{&ppb.Patient{}},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ConvertsToTime(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ConvertsToTime() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ConvertsToTime() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestToBoolean(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.String("T"),
+				system.String("True")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.String("false")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("200")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns an empty collection if input is not convertible",
+			input:   system.Collection{system.String("404 Kg")},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Decimal '0.0'",
+			input:   system.Collection{system.MustParseDecimal("0.0")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Decimal '1.0'",
+			input:   system.Collection{system.MustParseDecimal("1.0")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Decimal '3.5'",
+			input:   system.Collection{system.MustParseDecimal("3.5")},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Integer '0'",
+			input:   system.Collection{system.Integer(0)},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Integer '1'",
+			input:   system.Collection{system.Integer(1)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Integer '3'",
+			input:   system.Collection{system.Integer(3)},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '2.0'",
+			input:   system.Collection{system.String("2.0")},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '1.0'",
+			input:   system.Collection{system.String("1.0")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'y'",
+			input:   system.Collection{system.String("y")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'yes'",
+			input:   system.Collection{system.String("yes")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'Y'",
+			input:   system.Collection{system.String("Y")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'YES'",
+			input:   system.Collection{system.String("YES")},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '0.0'",
+			input:   system.Collection{system.String("0.0")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'f'",
+			input:   system.Collection{system.String("f")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'false'",
+			input:   system.Collection{system.String("false")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'F'",
+			input:   system.Collection{system.String("F")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'FALSE'",
+			input:   system.Collection{system.String("FALSE")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'n",
+			input:   system.Collection{system.String("n")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'no'",
+			input:   system.Collection{system.String("no")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'N",
+			input:   system.Collection{system.String("N")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String 'NO'",
+			input:   system.Collection{system.String("NO")},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Boolean 'true'",
+			input:   system.Collection{system.Boolean(true)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Boolean 'false",
+			input:   system.Collection{system.Boolean(false)},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.Integer '1'",
+			input:   system.Collection{fhir.Integer(1)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.PositiveInt '1'",
+			input:   system.Collection{fhir.PositiveInt(1)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.UnsignedInt '1'",
+			input:   system.Collection{fhir.UnsignedInt(1)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ToBoolean(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ToBoolean() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ToBoolean() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestToDate(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.String("2001-09-11"),
+				system.String("2011-05-02")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.String("2001-09-11")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("minutes")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns empty if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns an empty if input is not convertible to system.Date",
+			input:   system.Collection{system.MustParseQuantity("75", "Kg")},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.Date for a system.Date",
+			input:   system.Collection{system.MustParseDate("1993-08-13")},
+			want:    system.Collection{system.MustParseDate("1993-08-13")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.Date for a partial system.Date",
+			input:   system.Collection{system.MustParseDate("1993-08")},
+			want:    system.Collection{system.MustParseDate("1993-08")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.Date for a system.DateTime",
+			input:   system.Collection{system.MustParseDateTime("1993-08-13T14:20:00")},
+			want:    system.Collection{system.MustParseDate("1993-08-13")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.Date for a convertible system.String",
+			input:   system.Collection{system.String("1993-08-13")},
+			want:    system.Collection{system.MustParseDate("1993-08-13")},
+			wantErr: false,
+		},
+		{
+			name:    "returns empty for a non convertible system.String",
+			input:   system.Collection{system.String("93.08.13")},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns empty for a ppb.Patient",
+			input:   system.Collection{&ppb.Patient{}},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ToDate(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ToDate() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ToDate() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestToDateTime(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.String("2001-09-11"),
+				system.String("2011-05-02")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.String("2001-09-11")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("minutes")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns empty if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns an empty if input is not convertible to system.DateTime",
+			input:   system.Collection{system.MustParseQuantity("75", "Kg")},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.DateTime for a system.DateTime",
+			input:   system.Collection{system.MustParseDateTime("1993-08-13T14:20:00")},
+			want:    system.Collection{system.MustParseDateTime("1993-08-13T14:20:00")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.DateTime for a partial system.DateTime",
+			input:   system.Collection{system.MustParseDateTime("2012-01-01T10:00")},
+			want:    system.Collection{system.MustParseDateTime("2012-01-01T10:00")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.DateTime for a system.Date",
+			input:   system.Collection{system.MustParseDate("2006-01-02")},
+			want:    system.Collection{system.MustParseDate("2006-01-02").ToDateTime()},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.DateTime for a convertible system.String",
+			input:   system.Collection{system.String("1993-08-13T14:20:00")},
+			want:    system.Collection{system.MustParseDateTime("1993-08-13T14:20:00")},
+			wantErr: false,
+		},
+		{
+			name:    "returns empty for a non convertible system.String",
+			input:   system.Collection{system.String("93.08.13")},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns empty for a ppb.Patient",
+			input:   system.Collection{&ppb.Patient{}},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ToDateTime(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ToDateTime() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ToDateTime() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestToDecimal(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.String("101"),
+				system.String("102")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.String("100")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("200")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns and empty collection if input is not convertible to system.Decimal",
+			input:   system.Collection{system.MustParseQuantity("500", "kg")},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Decimal '13'",
+			input:   system.Collection{system.MustParseDecimal("13")},
+			want:    system.Collection{system.MustParseDecimal("13")},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Integer '13'",
+			input:   system.Collection{system.Integer(13)},
+			want:    system.Collection{system.MustParseDecimal("13")},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '13'",
+			input:   system.Collection{system.String("13")},
+			want:    system.Collection{system.MustParseDecimal("13")},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Boolean 'true'",
+			input:   system.Collection{system.Boolean(true)},
+			want:    system.Collection{system.MustParseDecimal("1")},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Boolean 'false'",
+			input:   system.Collection{system.Boolean(false)},
+			want:    system.Collection{system.MustParseDecimal("0")},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.Integer '0'",
+			input:   system.Collection{fhir.Integer(0)},
+			want:    system.Collection{system.MustParseDecimal("0")},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.PositiveInt '1'",
+			input:   system.Collection{fhir.PositiveInt(1)},
+			want:    system.Collection{system.MustParseDecimal("1")},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.UnsignedInt '2'",
+			input:   system.Collection{fhir.UnsignedInt(2)},
+			want:    system.Collection{system.MustParseDecimal("2")},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ToDecimal(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ToDecimal() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ToDecimal() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestToInteger(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.String("101"),
+				system.String("102")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "errors if input is not convertible to system.Integer",
+			input:   system.Collection{system.String("404 Kg")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.String("100")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("200")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Integer '13'",
+			input:   system.Collection{system.Integer(13)},
+			want:    system.Collection{system.Integer(13)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '13'",
+			input:   system.Collection{system.String("13")},
+			want:    system.Collection{system.Integer(13)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Boolean 'true'",
+			input:   system.Collection{system.Boolean(true)},
+			want:    system.Collection{system.Integer(1)},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Boolean 'false'",
+			input:   system.Collection{system.Boolean(false)},
+			want:    system.Collection{system.Integer(0)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.Integer '10'",
+			input:   system.Collection{fhir.Integer(10)},
+			want:    system.Collection{system.Integer(10)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.PositiveInt '11'",
+			input:   system.Collection{fhir.PositiveInt(11)},
+			want:    system.Collection{system.Integer(11)},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.UnsignedInt '12'",
+			input:   system.Collection{fhir.UnsignedInt(12)},
+			want:    system.Collection{system.Integer(12)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ToInteger(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ToInteger() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ToInteger() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestToQuantity(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.String("10 'km/hr'"),
+				system.String("10 'mi/hr'")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is not convertible to a system.Quantity",
+			input:   system.Collection{system.String("10 km / hr")},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "errors if args length is more than 1",
+			input: system.Collection{system.String("2 days")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("hours")),
+				exprtest.Return(system.String("minutes")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args is not a valid unit of time",
+			input: system.Collection{system.String("100 years")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("decades")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Integer '13'",
+			input:   system.Collection{system.Integer(13)},
+			want:    system.Collection{system.MustParseQuantity("13", "1")},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Decimal '13.5",
+			input:   system.Collection{system.MustParseDecimal("13.5")},
+			want:    system.Collection{system.MustParseQuantity("13.5", "1")},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Quantity '13.5 lbs'",
+			input:   system.Collection{system.MustParseQuantity("13.5", "lbs")},
+			want:    system.Collection{system.MustParseQuantity("13.5", "lbs")},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100 days'",
+			input:   system.Collection{system.String("100 days")},
+			want:    system.Collection{system.MustParseQuantity("100", "days")},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100 km'",
+			input:   system.Collection{system.String("100 km")},
+			want:    system.Collection{system.MustParseQuantity("100", "km")},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100 km/h'",
+			input:   system.Collection{system.String("100 km/h")},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100 'km/h''",
+			input:   system.Collection{system.String("100 'km/h'")},
+			want:    system.Collection{system.MustParseQuantity("100", "km/h")},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100 'km per hour''",
+			input:   system.Collection{system.String("100 'km per hour'")},
+			want:    system.Collection{system.MustParseQuantity("100", "km per hour")},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100 km per hour'",
+			input:   system.Collection{system.String("100 km per hour")},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100           km'",
+			input:   system.Collection{system.String("100           km")},
+			want:    system.Collection{system.MustParseQuantity("100", "          km")},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100           km/h'",
+			input:   system.Collection{system.String("100           km/h")},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '10 'km per hr''",
+			input:   system.Collection{system.String("10 'km per hr'")},
+			want:    system.Collection{system.MustParseQuantity("10", "km per hr")},
+			wantErr: false,
+		},
+		{
+			name:  "input is system.String '2 years' with arg 'months'",
+			input: system.Collection{system.String("2 years")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("months")),
+			},
+			want:    system.Collection{system.MustParseQuantity("24", "months")},
+			wantErr: false,
+		},
+		{
+			name:  "input is system.String '2 months' with arg 'days'",
+			input: system.Collection{system.String("2 months")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("days")),
+			},
+			want:    system.Collection{system.MustParseQuantity("60", "days")},
+			wantErr: false,
+		},
+		{
+			name:  "input is system.String '2 days' with arg 'hours'",
+			input: system.Collection{system.String("2 days")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("hours")),
+			},
+			want:    system.Collection{system.MustParseQuantity("48", "hours")},
+			wantErr: false,
+		},
+		{
+			name:  "input is system.String '2 hours' with arg 'minutes'",
+			input: system.Collection{system.String("2 hours")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("minutes")),
+			},
+			want:    system.Collection{system.MustParseQuantity("120", "minutes")},
+			wantErr: false,
+		},
+		{
+			name:  "input is system.String '2 minutes' with arg 'seconds'",
+			input: system.Collection{system.String("2 minutes")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("seconds")),
+			},
+			want:    system.Collection{system.MustParseQuantity("120", "seconds")},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.String '100 'km''",
+			input:   system.Collection{system.String("100 'km'")},
+			want:    system.Collection{system.MustParseQuantity("100", "km")},
+			wantErr: false,
+		},
+		{
+			name:  "input is system.Integer '100' with arg ''km''",
+			input: system.Collection{system.Integer(100)},
+			args: []expr.Expression{
+				exprtest.Return(system.String("'km'")),
+			},
+			want:    system.Collection{system.MustParseQuantity("100", "km")},
+			wantErr: false,
+		},
+		{
+			name:  "input is system.Integer '100' with arg 'days''",
+			input: system.Collection{system.Integer(100)},
+			args: []expr.Expression{
+				exprtest.Return(system.String("days")),
+			},
+			want:    system.Collection{system.MustParseQuantity("100", "days")},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Boolean 'true'",
+			input:   system.Collection{system.Boolean(true)},
+			want:    system.Collection{system.MustParseQuantity("1.0", "1")},
+			wantErr: false,
+		},
+		{
+			name:    "input is system.Boolean 'false'",
+			input:   system.Collection{system.Boolean(false)},
+			want:    system.Collection{system.MustParseQuantity("0.0", "1")},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.Integer '10'",
+			input:   system.Collection{fhir.Integer(10)},
+			want:    system.Collection{system.MustParseQuantity("10", "1")},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.PositiveInt '11'",
+			input:   system.Collection{fhir.PositiveInt(11)},
+			want:    system.Collection{system.MustParseQuantity("11", "1")},
+			wantErr: false,
+		},
+		{
+			name:    "input is fhir.UnsignedInt '12'",
+			input:   system.Collection{fhir.UnsignedInt(12)},
+			want:    system.Collection{system.MustParseQuantity("12", "1")},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ToQuantity(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ToQuantity() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ToQuantity() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestToString(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input is not a singleton",
+			input: system.Collection{
+				system.String("101"),
+				system.String("102"),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns empty for and empty input collection",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns a system.String for a system.String",
+			input:   system.Collection{system.String("100")},
+			want:    system.Collection{system.String("100")},
+			wantErr: false,
+		},
+		{
+			name:    "returns a system.String for a fhir.String",
+			input:   system.Collection{fhir.String("100")},
+			want:    system.Collection{system.String("100")},
+			wantErr: false,
+		},
+		{
+			name:    "returns a system.String for a system.Integer",
+			input:   system.Collection{system.Integer(100)},
+			want:    system.Collection{system.String("100")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.String for a fhir.Integer",
+			input:   system.Collection{fhir.Integer(100)},
+			want:    system.Collection{system.String("100")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.String for a fhir.PositiveInt",
+			input:   system.Collection{fhir.PositiveInt(11)},
+			want:    system.Collection{system.String("11")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.String for a fhir.UnsignedInt",
+			input:   system.Collection{fhir.UnsignedInt(12)},
+			want:    system.Collection{system.String("12")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.String for a system.Decimal",
+			input:   system.Collection{system.MustParseDecimal("100.999")},
+			want:    system.Collection{system.String("100.999")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.String for a system.Date",
+			input:   system.Collection{system.MustParseDate("1993-08-13")},
+			want:    system.Collection{system.String("1993-08-13")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.String for a system.Time",
+			input:   system.Collection{system.MustParseTime("14:01:45")},
+			want:    system.Collection{system.String("14:01:45")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.String for a system.DateTime",
+			input:   system.Collection{system.MustParseDateTime("1993-08-13T14:01:45")},
+			want:    system.Collection{system.String("1993-08-13T14:01:45")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.String for a system.Boolean",
+			input:   system.Collection{system.Boolean(true)},
+			want:    system.Collection{system.String("true")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.String for a fhir.Boolean",
+			input:   system.Collection{fhir.Boolean(true)},
+			want:    system.Collection{system.String("true")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.String for a system.Quantity",
+			input:   system.Collection{system.MustParseQuantity("75", "kg")},
+			want:    system.Collection{system.String("75 kg")},
+			wantErr: false,
+		},
+		{
+			name:    "returns false for a ppb.Patient",
+			input:   system.Collection{&ppb.Patient{}},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ToString(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ToString() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ToString() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestToTime(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.String("2001-09-11"),
+				system.String("2011-05-02")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.String("2001-09-11")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("minutes")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns empty if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns an empty if input is not convertible to system.Time",
+			input:   system.Collection{system.MustParseQuantity("75", "Kg")},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.Time for a systemTime",
+			input:   system.Collection{system.MustParseTime("16:20:59")},
+			want:    system.Collection{system.MustParseTime("16:20:59")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.Time for a partial system.Time",
+			input:   system.Collection{system.MustParseTime("16:20")},
+			want:    system.Collection{system.MustParseTime("16:20")},
+			wantErr: false,
+		},
+		{
+			name:    "returns system.Time for a convertible system.String",
+			input:   system.Collection{system.String("12:59:59")},
+			want:    system.Collection{system.MustParseTime("12:59:59")},
+			wantErr: false,
+		},
+		{
+			name:    "returns empty for a non convertible system.String",
+			input:   system.Collection{system.String("12/59/99")},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns empty for a ppb.Patient",
+			input:   system.Collection{&ppb.Patient{}},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ToTime(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ToTime() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("ToTime() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
diff --git a/fhirpath/internal/funcs/impl/errors.go b/fhirpath/internal/funcs/impl/errors.go
new file mode 100644
index 0000000..f4089b4
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/errors.go
@@ -0,0 +1,9 @@
+package impl
+
+import "errors"
+
+// Error constants
+var (
+	ErrWrongArity        = errors.New("incorrect function arity")
+	ErrInvalidReturnType = errors.New("invalid return type")
+)
diff --git a/fhirpath/internal/funcs/impl/existence.go b/fhirpath/internal/funcs/impl/existence.go
new file mode 100644
index 0000000..cc282bd
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/existence.go
@@ -0,0 +1,106 @@
+package impl
+
+import (
+	"fmt"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+// AllTrue Takes a collection of Boolean values and returns true if all the items are true.
+// If any items are false, the result is false. If the input is empty, the result is true.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#alltrue-boolean
+func AllTrue(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validations
+	if input.IsEmpty() {
+		return system.Collection{system.Boolean(true)}, nil
+	}
+	for _, v := range input {
+		value, _ := system.From(v)
+		if value == system.Boolean(false) {
+			return system.Collection{system.Boolean(false)}, nil
+		}
+	}
+	return system.Collection{system.Boolean(true)}, nil
+}
+
+// AnyTrue takes a collection of Boolean values and returns true if any of the items are true.
+// If all the items are false, or if the input is empty ({ }), the result is false.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#anytrue-boolean
+func AnyTrue(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validations
+	if input.IsEmpty() {
+		return system.Collection{system.Boolean(false)}, nil
+	}
+	for _, v := range input {
+		value, _ := system.From(v)
+		if value == system.Boolean(true) {
+			return system.Collection{system.Boolean(true)}, nil
+		}
+	}
+	return system.Collection{system.Boolean(false)}, nil
+}
+
+// AllFalse takes a collection of Boolean values and returns true if all the items are false.
+// If any items are true, the result is false. If the input is empty, the result is true.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#allfalse-boolean
+func AllFalse(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validations
+	if input.IsEmpty() {
+		return system.Collection{system.Boolean(true)}, nil
+	}
+	for _, v := range input {
+		value, _ := system.From(v)
+		if value == system.Boolean(true) {
+			return system.Collection{system.Boolean(false)}, nil
+		}
+	}
+	return system.Collection{system.Boolean(true)}, nil
+}
+
+// AnyFalse takes a collection of Boolean values and returns true if any of the items are false.
+// If all the items are true, or if the input is empty ({ }), the result is false.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#anyfalse-boolean
+func AnyFalse(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validations
+	if input.IsEmpty() {
+		return system.Collection{system.Boolean(false)}, nil
+	}
+	for _, v := range input {
+		value, _ := system.From(v)
+		if value == system.Boolean(false) {
+			return system.Collection{system.Boolean(true)}, nil
+		}
+	}
+	return system.Collection{system.Boolean(false)}, nil
+}
+
+// Count returns the integer count of the number of items in the input collection.
+// returns 0 when the input collection is empty.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#convertstodate-boolean
+func Count(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	return system.Collection{system.Integer(len(input))}, nil
+}
+
+// Exists evaluates the expression args[0] on each input item, returns whether
+// there exists at least one item that cause the expression to evaluate to true.
+// http://hl7.org/fhirpath/N1/#existscriteria-expression-boolean
+func Exists(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	if len(args) == 0 {
+		return system.Collection{system.Boolean(len(input) > 0)}, nil
+	}
+	whereOutput, err := Where(ctx, input, args...)
+	if err != nil {
+		return nil, fmt.Errorf("calling Where(): %w", err)
+	}
+	return system.Collection{system.Boolean(len(whereOutput) > 0)}, nil
+}
+
+// Empty evaluates the expression args[0] on each input item, returns whether
+// none of the items causes the expression to evaluate to true.
+// http://hl7.org/fhirpath/N1/#empty-boolean
+func Empty(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	if len(args) == 0 {
+		return system.Collection{system.Boolean(len(input) == 0)}, nil
+	}
+	return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, len(args))
+}
diff --git a/fhirpath/internal/funcs/impl/existence_test.go b/fhirpath/internal/funcs/impl/existence_test.go
new file mode 100644
index 0000000..a313cdd
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/existence_test.go
@@ -0,0 +1,494 @@
+package impl_test
+
+import (
+	"errors"
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr/exprtest"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs/impl"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+var coding = []*dtpb.Coding{
+	fhir.Coding("loinc-system", "loinc-code"),
+	fhir.Coding("loinc-system", "generic-code"),
+	fhir.Coding("snomed-system", "snomed-code"),
+	fhir.Coding("snomed-system", "snomed-code"),
+	fhir.Coding("icd10-system", "icd10-code"),
+	{},
+}
+
+func TestAllTrue(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "returns true if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name: "returns false if input contains a false value",
+			input: system.Collection{
+				system.Boolean(true),
+				system.Boolean(true),
+				system.Boolean(false)},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name: "returns true if input contains only true values",
+			input: system.Collection{system.Boolean(true),
+				system.Boolean(true),
+				system.Boolean(true)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name: "returns true if input (fhir.Boolean) contains only true values",
+			input: system.Collection{fhir.Boolean(true),
+				fhir.Boolean(true),
+				fhir.Boolean(true)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.AllTrue(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("AllTrue() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("AllTrue() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestAnyTrue(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "returns false if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name: "returns true if input contains a true value",
+			input: system.Collection{
+				system.Boolean(false),
+				system.Boolean(false),
+				system.Boolean(true)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name: "returns false if input contains only false values",
+			input: system.Collection{system.Boolean(false),
+				system.Boolean(false),
+				system.Boolean(false)},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name: "returns false if input (fhir.Boolean) contains only false values",
+			input: system.Collection{fhir.Boolean(false),
+				fhir.Boolean(false),
+				fhir.Boolean(false)},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.AnyTrue(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("AnyTrue() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("AnyTrue() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestAllFalse(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "returns true if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name: "returns false if input contains a true value",
+			input: system.Collection{
+				system.Boolean(false),
+				system.Boolean(true),
+				system.Boolean(false)},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name: "returns true if input contains only false values",
+			input: system.Collection{system.Boolean(false),
+				system.Boolean(false),
+				system.Boolean(false)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name: "returns true if input (fhir.Boolean) contains only false values",
+			input: system.Collection{fhir.Boolean(false),
+				fhir.Boolean(false),
+				fhir.Boolean(false)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.AllFalse(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("AllFalse() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("AllFalse() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestAnyFalse(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "returns false if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name: "returns true if input contains a false value",
+			input: system.Collection{
+				system.Boolean(true),
+				system.Boolean(true),
+				system.Boolean(false)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name: "returns true if input contains only false values",
+			input: system.Collection{system.Boolean(false),
+				system.Boolean(false),
+				system.Boolean(false)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name: "returns true if input (fhir.Boolean) contains only false values",
+			input: system.Collection{fhir.Boolean(false),
+				fhir.Boolean(false),
+				fhir.Boolean(false)},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name: "returns false if input is not boolean",
+			input: system.Collection{fhir.Integer(5),
+				fhir.Integer(6)},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.AnyFalse(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("AnyFalse() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("AnyFalse() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestCount(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "returns 0 if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{system.Integer(0)},
+			wantErr: false,
+		},
+		{
+			name:    "input 1 if input length is 1 ",
+			input:   system.Collection{system.Integer(1)},
+			want:    system.Collection{system.Integer(1)},
+			wantErr: false,
+		},
+		{
+			name: "input 5 if input length is 5 ",
+			input: system.Collection{
+				system.Integer(2),
+				system.Integer(4),
+				system.Integer(6),
+				system.Integer(8),
+				system.Integer(10)},
+			want:    system.Collection{system.Integer(5)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Count(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Count() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Count() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestExists_Evaluates(t *testing.T) {
+	testCases := []struct {
+		name            string
+		inputCollection system.Collection
+		inputArgs       []expr.Expression
+		wantCollection  system.Collection
+	}{
+		{
+			name:            "exists loinc system + loinc code",
+			inputCollection: slices.MustConvert[any](coding),
+			inputArgs: []expr.Expression{&expr.BooleanExpression{
+				Left: &expr.EqualityExpression{
+					Left:  &expr.FieldExpression{FieldName: "system"},
+					Right: &expr.LiteralExpression{Literal: system.String("loinc-system")},
+				},
+				Right: &expr.EqualityExpression{
+					Left:  &expr.FieldExpression{FieldName: "code"},
+					Right: &expr.LiteralExpression{Literal: system.String("loinc-code")},
+				},
+				Op: expr.And,
+			}},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "exists loinc system",
+			inputCollection: slices.MustConvert[any](coding),
+			inputArgs: []expr.Expression{&expr.EqualityExpression{
+				Left:  &expr.FieldExpression{FieldName: "system"},
+				Right: &expr.LiteralExpression{Literal: system.String("loinc-system")},
+			}},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "exists icd10 system + icd10 code",
+			inputCollection: slices.MustConvert[any](coding),
+			inputArgs: []expr.Expression{&expr.BooleanExpression{
+				Left: &expr.EqualityExpression{
+					Left:  &expr.FieldExpression{FieldName: "system"},
+					Right: &expr.LiteralExpression{Literal: system.String("icd10-system")},
+				},
+				Right: &expr.EqualityExpression{
+					Left:  &expr.FieldExpression{FieldName: "code"},
+					Right: &expr.LiteralExpression{Literal: system.String("icd10-code")},
+				},
+				Op: expr.And,
+			}},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "exists snomed system + snomed code",
+			inputCollection: slices.MustConvert[any](coding),
+			inputArgs: []expr.Expression{&expr.BooleanExpression{
+				Left: &expr.EqualityExpression{
+					Left:  &expr.FieldExpression{FieldName: "system"},
+					Right: &expr.LiteralExpression{Literal: system.String("snomed-system")},
+				},
+				Right: &expr.EqualityExpression{
+					Left:  &expr.FieldExpression{FieldName: "code"},
+					Right: &expr.LiteralExpression{Literal: system.String("snomed-code")},
+				},
+				Op: expr.And,
+			}},
+			wantCollection: system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "does not exist snomed system + loinc code",
+			inputCollection: slices.MustConvert[any](coding),
+			inputArgs: []expr.Expression{&expr.BooleanExpression{
+				Left: &expr.EqualityExpression{
+					Left:  &expr.FieldExpression{FieldName: "system"},
+					Right: &expr.LiteralExpression{Literal: system.String("snomed-system")},
+				},
+				Right: &expr.EqualityExpression{
+					Left:  &expr.FieldExpression{FieldName: "code"},
+					Right: &expr.LiteralExpression{Literal: system.String("loic-code")},
+				},
+				Op: expr.And,
+			}},
+			wantCollection: system.Collection{system.Boolean(false)},
+		},
+		{
+			name:            "non empty inputs with empty args",
+			inputCollection: slices.MustConvert[any](coding),
+			inputArgs:       nil,
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "empty inputs with empty args",
+			inputCollection: system.Collection{},
+			inputArgs:       nil,
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Exists(&expr.Context{}, tc.inputCollection, tc.inputArgs...)
+			if err != nil {
+				t.Fatalf("Exists function returned unexpected error: %v", err)
+			}
+			if diff := cmp.Diff(tc.wantCollection, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Exists function returned unexpected diff (-want, +got):\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestExists_RaisesError(t *testing.T) {
+	testCases := []struct {
+		name            string
+		inputArgs       []expr.Expression
+		inputCollection system.Collection
+	}{
+		{
+			name:            "multiple arguments",
+			inputArgs:       []expr.Expression{exprtest.Return(1), exprtest.Return(1)},
+			inputCollection: slices.MustConvert[any](coding),
+		},
+		{
+			name:            "argument expression raises error",
+			inputArgs:       []expr.Expression{exprtest.Error(errors.New("some error"))},
+			inputCollection: slices.MustConvert[any](coding),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if _, err := impl.Exists(&expr.Context{}, tc.inputCollection, tc.inputArgs...); err == nil {
+				t.Fatalf("evaluating Exists function didn't return error when expected")
+			}
+		})
+	}
+}
+
+func TestEmpty_Evaluates(t *testing.T) {
+	testCases := []struct {
+		name            string
+		inputCollection system.Collection
+		inputArgs       []expr.Expression
+		wantCollection  system.Collection
+	}{
+		{
+			name:            "empty inputs with empty args",
+			inputCollection: system.Collection{},
+			inputArgs:       nil,
+			wantCollection:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:            "non empty inputs",
+			inputCollection: slices.MustConvert[any](coding),
+			inputArgs:       nil,
+			wantCollection:  system.Collection{system.Boolean(false)},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Empty(&expr.Context{}, tc.inputCollection, tc.inputArgs...)
+			if err != nil {
+				t.Fatalf("Empty function returned unexpected error: %v", err)
+			}
+			if diff := cmp.Diff(tc.wantCollection, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Empty function returned unexpected diff (-want, +got):\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestEmpty_RaisesError(t *testing.T) {
+	testCases := []struct {
+		name            string
+		inputArgs       []expr.Expression
+		inputCollection system.Collection
+	}{
+		{
+			name:            "multiple arguments",
+			inputArgs:       []expr.Expression{exprtest.Return(1)},
+			inputCollection: slices.MustConvert[any](coding),
+		},
+		{
+			name:            "argument expression raises error",
+			inputArgs:       []expr.Expression{exprtest.Error(errors.New("some error"))},
+			inputCollection: slices.MustConvert[any](coding),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if _, err := impl.Empty(&expr.Context{}, tc.inputCollection, tc.inputArgs...); err == nil {
+				t.Fatalf("evaluating Empty function didn't return error when expected")
+			}
+		})
+	}
+}
diff --git a/fhirpath/internal/funcs/impl/filtering.go b/fhirpath/internal/funcs/impl/filtering.go
new file mode 100644
index 0000000..7a293d4
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/filtering.go
@@ -0,0 +1,35 @@
+package impl
+
+import (
+	"fmt"
+
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+// Where evaluates the expression args[0] on each input item, collects the items that cause
+// the expression to evaluate to true.
+func Where(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	if len(args) != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, len(args))
+	}
+	e := args[0]
+	result := system.Collection{}
+	for _, item := range input {
+		output, err := e.Evaluate(ctx, system.Collection{item})
+		if err != nil {
+			return nil, err
+		}
+		if len(output) == 0 {
+			continue
+		}
+		pass, err := output.ToSingletonBoolean()
+		if err != nil {
+			return nil, fmt.Errorf("evaluating where condition as boolean resulted in an error: %w", err)
+		}
+		if pass[0] {
+			result = append(result, item)
+		}
+	}
+	return result, nil
+}
diff --git a/fhirpath/internal/funcs/impl/filtering_test.go b/fhirpath/internal/funcs/impl/filtering_test.go
new file mode 100644
index 0000000..2030555
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/filtering_test.go
@@ -0,0 +1,114 @@
+package impl_test
+
+import (
+	"errors"
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr/exprtest"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs/impl"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+var contact = []*dtpb.ContactDetail{
+	{
+		Name: fhir.String("Vick"),
+		Id:   fhir.String("123"),
+	},
+	{
+		Name: fhir.String("Vick"),
+		Id:   fhir.String("234"),
+	},
+	{
+		Name: fhir.String("Matt"),
+		Id:   fhir.String("123"),
+	},
+}
+
+func TestWhere_Evaluates(t *testing.T) {
+	nameEquality := &expr.EqualityExpression{
+		Left:  &expr.FieldExpression{FieldName: "name"},
+		Right: &expr.LiteralExpression{Literal: system.String("Vick")},
+	}
+
+	testCases := []struct {
+		name            string
+		inputCollection system.Collection
+		inputArgs       []expr.Expression
+		wantCollection  system.Collection
+	}{
+		{
+			name:            "filters those that pass name query",
+			inputCollection: slices.MustConvert[any](contact),
+			inputArgs:       []expr.Expression{nameEquality},
+			wantCollection:  slices.MustConvert[any](contact[0:2]),
+		},
+		{
+			name:            "passes through when expression evaluates to singleton",
+			inputCollection: slices.MustConvert[any](contact),
+			inputArgs:       []expr.Expression{exprtest.Return(system.String("1"))},
+			wantCollection:  slices.MustConvert[any](contact),
+		},
+		{
+			name:            "passes through when expression evaluates to proto boolean true",
+			inputCollection: slices.MustConvert[any](contact),
+			inputArgs:       []expr.Expression{exprtest.Return(fhir.Boolean(true))},
+			wantCollection:  slices.MustConvert[any](contact),
+		},
+		{
+			name:            "filters out when expression evaluates to empty",
+			inputCollection: slices.MustConvert[any](contact),
+			inputArgs:       []expr.Expression{exprtest.Return()},
+			wantCollection:  system.Collection{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Where(&expr.Context{}, tc.inputCollection, tc.inputArgs...)
+			if err != nil {
+				t.Fatalf("Where function returned unexpected error: %v", err)
+			}
+			if diff := cmp.Diff(tc.wantCollection, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Where function returned unexpected diff (-want, +got):\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestWhere_RaisesError(t *testing.T) {
+	testCases := []struct {
+		name            string
+		inputArgs       []expr.Expression
+		inputCollection system.Collection
+	}{
+		{
+			name:            "multiple arguments",
+			inputArgs:       []expr.Expression{exprtest.Return(1), exprtest.Return(1)},
+			inputCollection: slices.MustConvert[any](contact),
+		},
+		{
+			name:            "argument expression raises error",
+			inputArgs:       []expr.Expression{exprtest.Error(errors.New("some error"))},
+			inputCollection: slices.MustConvert[any](contact),
+		},
+		{
+			name:            "argument expression returns multiple items",
+			inputArgs:       []expr.Expression{exprtest.Return(1, 2)},
+			inputCollection: slices.MustConvert[any](contact),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if _, err := impl.Where(&expr.Context{}, tc.inputCollection, tc.inputArgs...); err == nil {
+				t.Fatalf("evaluating Where function didn't return error when expected")
+			}
+		})
+	}
+}
diff --git a/fhirpath/internal/funcs/impl/math.go b/fhirpath/internal/funcs/impl/math.go
new file mode 100644
index 0000000..49fbb53
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/math.go
@@ -0,0 +1,367 @@
+package impl
+
+import (
+	"errors"
+	"fmt"
+	"math"
+	"strconv"
+	"strings"
+
+	"github.com/shopspring/decimal"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+var (
+	ErrInvalidInput = errors.New("invalid input")
+)
+
+// Abs returns the absolute value of the input.
+// When taking the absolute value of a quantity, the unit is unchanged.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#abs-integer-decimal-quantity
+func Abs(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validations
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	// Argument validations
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+
+	switch input[0].(type) {
+	case system.Integer:
+		// Input type conversion to int32
+		number, err := input.ToInt32()
+		if err != nil {
+			return nil, err
+		}
+		// Absolution number
+		res := math.Abs(float64(number))
+		return system.Collection{system.Integer(res)}, nil
+	case system.Decimal:
+		// Input type conversion to float64
+		number, err := input.ToFloat64()
+		if err != nil {
+			return nil, err
+		}
+		// Absolution number
+		res := math.Abs(number)
+		result := decimal.NewFromFloat(res)
+		return system.Collection{system.Decimal(result)}, nil
+	case system.Quantity:
+		quantity := strings.Split(input[0].(system.Quantity).String(), " ")
+		// Input type conversion
+		f, err := strconv.ParseFloat(quantity[0], 64)
+		if err != nil {
+			return nil, err
+		}
+		// Absolution number
+		res := math.Abs(f)
+		return system.Collection{system.MustParseQuantity(fmt.Sprintf("%f", res), quantity[1])}, nil
+	}
+	return nil, errors.New("input is not a number")
+}
+
+// Ceiling returns the first integer greater than or equal to the input.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#ceiling-integer
+func Ceiling(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validations
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	// Argument validations
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Input type conversion to float64
+	number, err := input.ToFloat64()
+	if err != nil {
+		return nil, err
+	}
+	// Ceiling number
+	result := math.Ceil(number)
+	return system.Collection{system.Integer(result)}, nil
+}
+
+// Exp returns e raised to the power of the input.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#exp-decimal
+func Exp(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validations
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	// Argument validations
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Reading input
+	number, err := input.ToFloat64()
+	if err != nil {
+		return nil, err
+	}
+	// Exp number
+	res := math.Pow(math.E, number)
+	result := system.MustParseDecimal(fmt.Sprintf("%v", res))
+	return system.Collection{result}, nil
+}
+
+// Floor returns the first integer less than or equal to the input.
+// FHIRPath docs here: https://hl7.org/fhirpath/n1/#floor-integer
+func Floor(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validations
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	// Argument validations
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Input type conversion to float64
+	number, err := input.ToFloat64()
+	if err != nil {
+		return nil, err
+	}
+	// Flooring number
+	result := math.Floor(number)
+	return system.Collection{system.Integer(result)}, nil
+}
+
+// Ln returns the natural logarithm of the input number.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#ln-decimal
+func Ln(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validations
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	// Argument validations
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Input type conversion to float64
+	number, err := input.ToFloat64()
+	if err != nil {
+		return nil, err
+	}
+	res := math.Log(number)
+	// Validating NaN case
+	if math.IsNaN(res) {
+		return system.Collection{}, nil
+	}
+	// Type conversion to system.Decimal
+	result := decimal.NewFromFloat(res)
+	return system.Collection{system.Decimal(result)}, nil
+}
+
+// Log returns the logarithm base of the input number.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#logbase-decimal-decimal
+func Log(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validations
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	// Input type conversion to float64
+	number, err := input.ToFloat64()
+	if err != nil {
+		return nil, err
+	}
+	// Validating args
+	if len(args) != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, len(args))
+	}
+	argValues, err := args[0].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	}
+	base, err := argValues.ToFloat64()
+	if err != nil {
+		return nil, err
+	}
+	// Log number to base
+	res := logToBase(number, base)
+	// Validating NaN case
+	if math.IsNaN(res) {
+		return system.Collection{}, nil
+	}
+	// Type conversion to system.Decimal
+	result := decimal.NewFromFloat(res)
+	return system.Collection{system.Decimal(result)}, nil
+}
+
+// Power returns a number to the exponent power.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#powerexponent-integer-decimal-integer-decimal
+func Power(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Validating input
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	// Validating args
+	if len(args) != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, len(args))
+	}
+	argValues, err := args[0].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	}
+	// Validating integers case
+	_, ok := input[0].(system.Integer)
+	_, ok2 := argValues[0].(system.Integer)
+	if ok && ok2 {
+		// Input type conversion to int32
+		number, err := input.ToInt32()
+		if err != nil {
+			return nil, err
+		}
+		// Input type conversion to int32
+		exp, err := argValues.ToInt32()
+		if err != nil {
+			return nil, err
+		}
+		// Powering ints
+		res := powInt32(number, exp)
+		return system.Collection{system.Integer(res)}, nil
+	}
+	// Input type conversion to float64
+	number, err := input.ToFloat64()
+	if err != nil {
+		return nil, err
+	}
+	// Input type conversion to float64
+	exp, err := argValues.ToFloat64()
+	if err != nil {
+		return nil, err
+	}
+	// Powering number
+	res := math.Pow(number, exp)
+	// Validating NaN case
+	if math.IsNaN(res) {
+		return system.Collection{}, nil
+	}
+	// Type conversion to system.Decimal
+	result := decimal.NewFromFloat(res)
+	return system.Collection{system.Decimal(result)}, nil
+}
+
+// Round rounds the decimal to the nearest whole number using a traditional round (i.e. 0.5 or higher will round to 1).
+// If specified, the precision argument determines the decimal place at which the rounding will occur.
+// If not specified, the rounding will default to 0 decimal places.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#roundprecision-integer-decimal
+func Round(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Validating input
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	if !input.IsSingleton() {
+		return nil, errors.New("invalid input, is not a singleton")
+	}
+	// Validating args
+	if len(args) > 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0 or 1 arguments", ErrWrongArity, len(args))
+	}
+	precision := int32(0)
+	if len(args) == 1 {
+		argValues, err := args[0].Evaluate(ctx, input)
+		if err != nil {
+			return nil, err
+		}
+		// Arg type conversion to int32
+		precision, err = argValues.ToInt32()
+		if err != nil {
+			return nil, err
+		}
+		if precision < 0 {
+			return nil, errors.New("precision must be greater or equal than 0")
+		}
+	}
+	value, err := system.From(input[0])
+	if err != nil {
+		return nil, err
+	}
+	// Rounding number
+	switch value.(type) {
+	case system.Decimal:
+		res, _ := input[0].(system.Decimal)
+		result := res.Round(precision)
+		return system.Collection{result}, nil
+	case system.Integer:
+		number, err := input.ToInt32()
+		if err != nil {
+			return nil, err
+		}
+		res := system.MustParseDecimal(fmt.Sprintf("%d", number))
+		result := res.Round(precision)
+		return system.Collection{result}, nil
+	}
+	return nil, errors.New("input is not a number")
+}
+
+// Sqrt returns the square root of the input number as a Decimal.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#sqrt-decimal
+func Sqrt(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validations
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	// Argument validations
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Input type conversion to float64
+	number, err := input.ToFloat64()
+	if err != nil {
+		return nil, err
+	}
+	// Validate negative input
+	if number < 0 {
+		return nil, fmt.Errorf("%w: unable to sqrt negative value", ErrInvalidInput)
+	}
+	// Ceiling number
+	value := math.Sqrt(number)
+	result := decimal.NewFromFloat(value)
+	return system.Collection{system.Decimal(result)}, nil
+}
+
+// Truncate returns the integer portion of the input.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#truncate-integer
+func Truncate(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validations
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	// Argument validations
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	// Input type conversion to float64
+	number, err := input.ToFloat64()
+	if err != nil {
+		return nil, err
+	}
+	// Ceiling number
+	result := math.Trunc(number)
+	return system.Collection{system.Integer(result)}, nil
+}
+
+func logToBase(number, base float64) float64 {
+	if number <= 0 || base <= 1 {
+		return math.NaN() // Return NaN for invalid inputs
+	}
+
+	return math.Log(number) / math.Log(base)
+}
+
+// powInt32 returns the powering of a number to a given exponential.
+func powInt32(base, exp int32) int32 {
+	if exp == 0 {
+		return 1
+	}
+	if exp < 0 {
+		return 0
+	}
+
+	result := base
+	for i := int32(2); i <= exp; i++ {
+		result *= base
+	}
+	return result
+}
diff --git a/fhirpath/internal/funcs/impl/math_test.go b/fhirpath/internal/funcs/impl/math_test.go
new file mode 100644
index 0000000..7ebfaf9
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/math_test.go
@@ -0,0 +1,1059 @@
+package impl_test
+
+import (
+	"testing"
+
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr/exprtest"
+	"google.golang.org/protobuf/testing/protocmp"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs/impl"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+func TestAbs(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "errors if input is not a number",
+			input:   system.Collection{system.String("1.2")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.MustParseDecimal("10.1"),
+				system.MustParseDecimal("10.5")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.MustParseDecimal("10.8")},
+			args: []expr.Expression{
+				exprtest.Return(system.String("kg")),
+				exprtest.Return(system.String("lb")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "abs a positive Decimal number",
+			input:   system.Collection{system.MustParseDecimal("10.5")},
+			want:    system.Collection{system.MustParseDecimal("10.5")},
+			wantErr: false,
+		},
+		{
+			name:    "abs a negative Decimal number",
+			input:   system.Collection{system.MustParseDecimal("-10.5")},
+			want:    system.Collection{system.MustParseDecimal("10.5")},
+			wantErr: false,
+		},
+		{
+			name:    "abs a positive Integer number",
+			input:   system.Collection{system.Integer(11)},
+			want:    system.Collection{system.Integer(11)},
+			wantErr: false,
+		},
+		{
+			name:    "abs a negative Integer number",
+			input:   system.Collection{system.Integer(-11)},
+			want:    system.Collection{system.Integer(11)},
+			wantErr: false,
+		},
+		{
+			name:    "abs a positive Quantity number",
+			input:   system.Collection{system.MustParseQuantity("10.5", "kg")},
+			want:    system.Collection{system.MustParseQuantity("10.5", "kg")},
+			wantErr: false,
+		},
+		{
+			name:    "abs a negative Quantity number",
+			input:   system.Collection{system.MustParseQuantity("-10.5", "kg")},
+			want:    system.Collection{system.MustParseQuantity("10.5", "kg")},
+			wantErr: false,
+		},
+		{
+			name:    "abs zero number",
+			input:   system.Collection{system.Integer(0)},
+			want:    system.Collection{system.Integer(0)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Abs(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Abs() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Abs() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestCeiling(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "errors if input is not a number",
+			input:   system.Collection{system.String("1.2")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.MustParseDecimal("10.1"),
+				system.MustParseDecimal("10.5")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.MustParseDecimal("10")},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("20")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "ceiling a positive float number",
+			input:   system.Collection{system.MustParseDecimal("10.5")},
+			want:    system.Collection{system.Integer(11)},
+			wantErr: false,
+		},
+		{
+			name:    "ceiling a positive float number with zero decimal",
+			input:   system.Collection{system.MustParseDecimal("10.0")},
+			want:    system.Collection{system.Integer(10)},
+			wantErr: false,
+		},
+		{
+			name:    "ceiling a negative float number",
+			input:   system.Collection{system.MustParseDecimal("-10.5")},
+			want:    system.Collection{system.Integer(-10)},
+			wantErr: false,
+		},
+		{
+			name:    "ceiling a negative float number with zero decimal",
+			input:   system.Collection{system.MustParseDecimal("-10.0")},
+			want:    system.Collection{system.Integer(-10)},
+			wantErr: false,
+		},
+		{
+			name:    "ceiling an Integer number",
+			input:   system.Collection{fhir.Integer(10)},
+			want:    system.Collection{system.Integer(10)},
+			wantErr: false,
+		},
+		{
+			name:    "ceiling a negative Integer number",
+			input:   system.Collection{fhir.Integer(-10)},
+			want:    system.Collection{system.Integer(-10)},
+			wantErr: false,
+		},
+		{
+			name:    "ceiling a PositiveInt number",
+			input:   system.Collection{fhir.PositiveInt(100)},
+			want:    system.Collection{system.Integer(100)},
+			wantErr: false,
+		},
+		{
+			name:    "ceiling an UnsignedInt number",
+			input:   system.Collection{fhir.UnsignedInt(1000)},
+			want:    system.Collection{system.Integer(1000)},
+			wantErr: false,
+		},
+		{
+			name:    "ceiling zero number",
+			input:   system.Collection{fhir.Integer(0)},
+			want:    system.Collection{system.Integer(0)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Ceiling(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Ceiling() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Ceiling() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestExp(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "errors if input is not a number",
+			input:   system.Collection{system.String("1.2")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2)},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.Integer(1)},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("2")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns a system.Decimal if input is system.Integer",
+			input:   system.Collection{system.Integer(0)},
+			want:    system.Collection{system.MustParseDecimal("1")},
+			wantErr: false,
+		},
+		{
+			name:    "returns a system.Decimal if input is system.Decimal",
+			input:   system.Collection{system.MustParseDecimal("0")},
+			want:    system.Collection{system.MustParseDecimal("1")},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Exp(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Exp() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Exp() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestFloor(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "errors if input is not a number",
+			input:   system.Collection{system.String("1.2")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.MustParseDecimal("10.1"),
+				system.MustParseDecimal("10.5")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.MustParseDecimal("10")},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("20")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "floors a positive float number",
+			input:   system.Collection{system.MustParseDecimal("10.5")},
+			want:    system.Collection{system.Integer(10)},
+			wantErr: false,
+		},
+		{
+			name:    "floors a positive float number with zero decimal",
+			input:   system.Collection{system.MustParseDecimal("10.0")},
+			want:    system.Collection{system.Integer(10)},
+			wantErr: false,
+		},
+		{
+			name:    "floors a negative float number",
+			input:   system.Collection{system.MustParseDecimal("-10.5")},
+			want:    system.Collection{system.Integer(-11)},
+			wantErr: false,
+		},
+		{
+			name:    "floors a negative float number with zero decimal",
+			input:   system.Collection{system.MustParseDecimal("-10.0")},
+			want:    system.Collection{system.Integer(-10)},
+			wantErr: false,
+		},
+		{
+			name:    "floors an Integer number",
+			input:   system.Collection{fhir.Integer(10)},
+			want:    system.Collection{system.Integer(10)},
+			wantErr: false,
+		},
+		{
+			name:    "floors a negative Integer number",
+			input:   system.Collection{fhir.Integer(-10)},
+			want:    system.Collection{system.Integer(-10)},
+			wantErr: false,
+		},
+		{
+			name:    "floors a PositiveInt number",
+			input:   system.Collection{fhir.PositiveInt(100)},
+			want:    system.Collection{system.Integer(100)},
+			wantErr: false,
+		},
+		{
+			name:    "floors an UnsignedInt number",
+			input:   system.Collection{fhir.UnsignedInt(1000)},
+			want:    system.Collection{system.Integer(1000)},
+			wantErr: false,
+		},
+		{
+			name:    "floors zero number",
+			input:   system.Collection{fhir.Integer(0)},
+			want:    system.Collection{system.Integer(0)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Floor(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Floor() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Floor() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestLn(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "errors if input is not a number",
+			input:   system.Collection{system.String("1.2")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.MustParseDecimal("10.1"),
+				system.MustParseDecimal("10.5")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.MustParseDecimal("10")},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("20")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns an empty collection if result is NaN",
+			input:   system.Collection{system.MustParseDecimal("-1.0")},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "lns a positive number",
+			input:   system.Collection{system.MustParseDecimal("2")},
+			want:    system.Collection{system.MustParseDecimal("0.6931471805599453")},
+			wantErr: false,
+		},
+		{
+			name:    "lns a positive float number",
+			input:   system.Collection{system.MustParseDecimal("0.5")},
+			want:    system.Collection{system.MustParseDecimal("-0.6931471805599453")},
+			wantErr: false,
+		},
+		{
+			name:    "lns an PositiveInt number",
+			input:   system.Collection{fhir.PositiveInt(16)},
+			want:    system.Collection{system.MustParseDecimal("2.772588722239781")},
+			wantErr: false,
+		},
+		{
+			name:    "lns an UnsignedInt number",
+			input:   system.Collection{fhir.UnsignedInt(16)},
+			want:    system.Collection{system.MustParseDecimal("2.772588722239781")},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Ln(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Ln() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Ln() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestLog(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "errors if input is not a number",
+			input:   system.Collection{system.String("1.2")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.MustParseDecimal("10.1"),
+				system.MustParseDecimal("10.5")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns an empty collection if result is NaN",
+			input: system.Collection{system.MustParseDecimal("-1.0")},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("0.5")),
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "logs a number with base 2",
+			input: system.Collection{system.MustParseDecimal("16")},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("2")),
+			},
+			want:    system.Collection{system.MustParseDecimal("4")},
+			wantErr: false,
+		},
+		{
+			name:  "logs a number with base 10",
+			input: system.Collection{system.MustParseDecimal("100")},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("10")),
+			},
+			want:    system.Collection{system.MustParseDecimal("2")},
+			wantErr: false,
+		},
+		{
+			name:  "logs a PositiveInt number with base 2",
+			input: system.Collection{fhir.PositiveInt(16)},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("2")),
+			},
+			want:    system.Collection{system.MustParseDecimal("4")},
+			wantErr: false,
+		},
+		{
+			name:  "logs an UnsignedInt number with base 2",
+			input: system.Collection{fhir.UnsignedInt(16)},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("2")),
+			},
+			want:    system.Collection{system.MustParseDecimal("4")},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Log(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Log() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Log() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestPower(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "errors if input is not a number",
+			input:   system.Collection{system.String("1.2")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.MustParseDecimal("10.1"),
+				system.MustParseDecimal("10.5")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns an empty collection if result is NaN",
+			input: system.Collection{system.MustParseDecimal("-1.0")},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("0.5")),
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "powers a positive float number to a positive float arg",
+			input: system.Collection{system.MustParseDecimal("2.5")},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("2")),
+			},
+			want:    system.Collection{system.MustParseDecimal("6.25")},
+			wantErr: false,
+		},
+		{
+			name:  "powers a positive float number to a negative float arg",
+			input: system.Collection{system.MustParseDecimal("2.5")},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("-2")),
+			},
+			want:    system.Collection{system.MustParseDecimal("0.16")},
+			wantErr: false,
+		},
+		{
+			name:  "powers a positive float number to a positive int arg",
+			input: system.Collection{system.MustParseDecimal("2.5")},
+			args: []expr.Expression{
+				exprtest.Return(system.Integer(2)),
+			},
+			want:    system.Collection{system.MustParseDecimal("6.25")},
+			wantErr: false,
+		},
+		{
+			name:  "powers a positive float number to a negative int arg",
+			input: system.Collection{system.MustParseDecimal("2.5")},
+			args: []expr.Expression{
+				exprtest.Return(system.Integer(-2)),
+			},
+			want:    system.Collection{system.MustParseDecimal("0.16")},
+			wantErr: false,
+		},
+		{
+			name:  "powers an integer float number to a positive float arg",
+			input: system.Collection{system.Integer(4)},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("2.5")),
+			},
+			want:    system.Collection{system.MustParseDecimal("32")},
+			wantErr: false,
+		},
+		{
+			name:  "powers an integer float number to a negative float arg",
+			input: system.Collection{system.Integer(4)},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("-2.5")),
+			},
+			want:    system.Collection{system.MustParseDecimal("0.03125")},
+			wantErr: false,
+		},
+		{
+			name:  "powers an integer number to a negative int arg",
+			input: system.Collection{system.Integer(4)},
+			args: []expr.Expression{
+				exprtest.Return(system.Integer(-2)),
+			},
+			want:    system.Collection{system.Integer(0)},
+			wantErr: false,
+		},
+		{
+			name:  "powers a PositiveInt number",
+			input: system.Collection{fhir.PositiveInt(10)},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("2.0")),
+			},
+			want:    system.Collection{system.MustParseDecimal("100")},
+			wantErr: false,
+		},
+		{
+			name:  "powers an UnsignedInt number",
+			input: system.Collection{fhir.UnsignedInt(20)},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("2.0")),
+			},
+			want:    system.Collection{system.MustParseDecimal("400")},
+			wantErr: false,
+		},
+		{
+			name:  "powers zero number to positive float exp",
+			input: system.Collection{fhir.Integer(0)},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("10.5")),
+			},
+			want:    system.Collection{system.MustParseDecimal("0")},
+			wantErr: false,
+		},
+		{
+			name:  "powers a positive integer number to zero exp",
+			input: system.Collection{fhir.Integer(10)},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("0")),
+			},
+			want:    system.Collection{system.MustParseDecimal("1")},
+			wantErr: false,
+		},
+		{
+			name:  "powers zero number to zero exp",
+			input: system.Collection{fhir.Integer(0)},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("0")),
+			},
+			want:    system.Collection{system.MustParseDecimal("1")},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Power(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Power() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Power() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestRound(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "errors if input is not a number",
+			input:   system.Collection{system.String("1.2")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.MustParseDecimal("10.1"),
+				system.MustParseDecimal("10.5")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args len is greater than 1",
+			input: system.Collection{system.MustParseDecimal("3.141592653589793")},
+			args: []expr.Expression{
+				exprtest.Return(system.Integer(1)),
+				exprtest.Return(system.Integer(2)),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if arg is not an Integer",
+			input: system.Collection{system.MustParseDecimal("3.141592653589793")},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("2.2")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if precision arg is negative",
+			input: system.Collection{system.MustParseDecimal("3.141592653589793")},
+			args: []expr.Expression{
+				exprtest.Return(system.Integer(-2)),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "rounds a positive Decimal",
+			input: system.Collection{system.MustParseDecimal("3.141592653589793")},
+			args: []expr.Expression{
+				exprtest.Return(system.Integer(4)),
+			},
+			want:    system.Collection{system.MustParseDecimal("3.1416")},
+			wantErr: false,
+		},
+		{
+			name:    "rounds a positive Decimal with no precision arg",
+			input:   system.Collection{system.MustParseDecimal("3.141592653589793")},
+			want:    system.Collection{system.MustParseDecimal("3")},
+			wantErr: false,
+		},
+		{
+			name:  "rounds a negative Decimal",
+			input: system.Collection{system.MustParseDecimal("-3.141592653589793")},
+			args: []expr.Expression{
+				exprtest.Return(system.Integer(4)),
+			},
+			want:    system.Collection{system.MustParseDecimal("-3.1416")},
+			wantErr: false,
+		},
+		{
+			name:    "rounds a positive Integer",
+			input:   system.Collection{system.Integer(3)},
+			want:    system.Collection{system.MustParseDecimal("3")},
+			wantErr: false,
+		},
+		{
+			name:  "rounds a positive Integer with precision arg",
+			input: system.Collection{system.Integer(3)},
+			args: []expr.Expression{
+				exprtest.Return(system.Integer(2)),
+			},
+			want:    system.Collection{system.MustParseDecimal("3")},
+			wantErr: false,
+		},
+		{
+			name:    "rounds a negative Integer",
+			input:   system.Collection{system.Integer(-7)},
+			want:    system.Collection{system.MustParseDecimal("-7")},
+			wantErr: false,
+		},
+		{
+			name:    "rounds a fhir Integer",
+			input:   system.Collection{fhir.Integer(20)},
+			want:    system.Collection{system.MustParseDecimal("20")},
+			wantErr: false,
+		},
+		{
+			name:    "rounds a fhir PositiveInt",
+			input:   system.Collection{fhir.PositiveInt(30)},
+			want:    system.Collection{system.MustParseDecimal("30")},
+			wantErr: false,
+		},
+		{
+			name:    "rounds a fhir UnsignedInt",
+			input:   system.Collection{fhir.UnsignedInt(10)},
+			want:    system.Collection{system.MustParseDecimal("10")},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Round(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Round() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Round() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestSqrt(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "errors if input is not a number",
+			input:   system.Collection{system.String("1.2")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.MustParseDecimal("10.1"),
+				system.MustParseDecimal("10.5")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "errors if input is negative",
+			input:   system.Collection{system.MustParseDecimal("-16.0")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.MustParseDecimal("10")},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("20")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "sqrt a positive float number",
+			input:   system.Collection{system.MustParseDecimal("16.5")},
+			want:    system.Collection{system.MustParseDecimal("4.06201920231798")},
+			wantErr: false,
+		},
+		{
+			name:    "sqrt a positive float number with zero decimal",
+			input:   system.Collection{system.MustParseDecimal("81.0")},
+			want:    system.Collection{system.MustParseDecimal("9.0")},
+			wantErr: false,
+		},
+		{
+			name:    "sqrt an Integer number",
+			input:   system.Collection{fhir.Integer(400)},
+			want:    system.Collection{system.MustParseDecimal("20.0")},
+			wantErr: false,
+		},
+		{
+			name:    "sqrt a PositiveInt number",
+			input:   system.Collection{fhir.PositiveInt(100)},
+			want:    system.Collection{system.MustParseDecimal("10.0")},
+			wantErr: false,
+		},
+		{
+			name:    "sqrt an UnsignedInt number",
+			input:   system.Collection{fhir.UnsignedInt(9)},
+			want:    system.Collection{system.MustParseDecimal("3.0")},
+			wantErr: false,
+		},
+		{
+			name:    "sqrt zero number",
+			input:   system.Collection{fhir.Integer(0)},
+			want:    system.Collection{system.MustParseDecimal("0.0")},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Sqrt(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Sqrt() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Sqrt() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestTruncate(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "errors if input is not a number",
+			input:   system.Collection{system.String("1.2")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name: "errors if input length is more than 1",
+			input: system.Collection{
+				system.MustParseDecimal("10.1"),
+				system.MustParseDecimal("10.5")},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args length is more than 0",
+			input: system.Collection{system.MustParseDecimal("10")},
+			args: []expr.Expression{
+				exprtest.Return(system.MustParseDecimal("20")),
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "returns an empty collection if input is empty",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "truncates a positive float number",
+			input:   system.Collection{system.MustParseDecimal("10.12345")},
+			want:    system.Collection{system.Integer(10)},
+			wantErr: false,
+		},
+		{
+			name:    "truncates a positive float number with zero decimal",
+			input:   system.Collection{system.MustParseDecimal("10.00")},
+			want:    system.Collection{system.Integer(10)},
+			wantErr: false,
+		},
+		{
+			name:    "truncates a negative float number",
+			input:   system.Collection{system.MustParseDecimal("-10.12345")},
+			want:    system.Collection{system.Integer(-10)},
+			wantErr: false,
+		},
+		{
+			name:    "truncates a negative float number with zero decimal",
+			input:   system.Collection{system.MustParseDecimal("-10.000")},
+			want:    system.Collection{system.Integer(-10)},
+			wantErr: false,
+		},
+		{
+			name:    "truncates an Integer number",
+			input:   system.Collection{fhir.Integer(10)},
+			want:    system.Collection{system.Integer(10)},
+			wantErr: false,
+		},
+		{
+			name:    "truncates a negative Integer number",
+			input:   system.Collection{fhir.Integer(-10)},
+			want:    system.Collection{system.Integer(-10)},
+			wantErr: false,
+		},
+		{
+			name:    "truncates a PositiveInt number",
+			input:   system.Collection{fhir.PositiveInt(100)},
+			want:    system.Collection{system.Integer(100)},
+			wantErr: false,
+		},
+		{
+			name:    "truncates an UnsignedInt number",
+			input:   system.Collection{fhir.UnsignedInt(1000)},
+			want:    system.Collection{system.Integer(1000)},
+			wantErr: false,
+		},
+		{
+			name:    "truncates zero number",
+			input:   system.Collection{fhir.Integer(0)},
+			want:    system.Collection{system.Integer(0)},
+			wantErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Truncate(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Truncate() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Truncate() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
diff --git a/fhirpath/internal/funcs/impl/not.go b/fhirpath/internal/funcs/impl/not.go
new file mode 100644
index 0000000..2ed5eae
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/not.go
@@ -0,0 +1,25 @@
+package impl
+
+import (
+	"fmt"
+
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+// Not returns the boolean inverse of the singleton input collection.
+func Not(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	if length := len(args); length != 0 {
+		return nil, fmt.Errorf("%w, received %v arguments, expected 0", ErrWrongArity, length)
+	}
+
+	boolean, err := input.ToSingletonBoolean()
+	if err != nil {
+		return nil, err
+	}
+	if len(boolean) == 0 {
+		return system.Collection{}, nil
+	}
+	result := system.Boolean(!boolean[0])
+	return system.Collection{result}, nil
+}
diff --git a/fhirpath/internal/funcs/impl/not_test.go b/fhirpath/internal/funcs/impl/not_test.go
new file mode 100644
index 0000000..adf90a9
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/not_test.go
@@ -0,0 +1,54 @@
+package impl_test
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs/impl"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+func TestNot_InvertsBoolean(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:  "inverts system boolean",
+			input: system.Collection{system.Boolean(true)},
+			want:  system.Collection{system.Boolean(false)},
+		},
+		{
+			name:  "inverts proto boolean",
+			input: system.Collection{fhir.Boolean(false)},
+			want:  system.Collection{system.Boolean(true)},
+		},
+		{
+			name:    "receives non-singleton collection",
+			input:   system.Collection{system.Boolean(true), system.Boolean(false)},
+			wantErr: true,
+		},
+		{
+			name:  "passes through empty collection",
+			input: system.Collection{},
+			want:  system.Collection{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Not(&expr.Context{}, tc.input)
+
+			if gotErr := err != nil; tc.wantErr != gotErr {
+				t.Fatalf("Not function got unexpected error result: gotErr %v, wantErr %v, err: %v", gotErr, tc.wantErr, err)
+			}
+			if !cmp.Equal(tc.want, got) {
+				t.Errorf("Not function returned unexpected result: got: %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
diff --git a/fhirpath/internal/funcs/impl/r4.go b/fhirpath/internal/funcs/impl/r4.go
new file mode 100644
index 0000000..112ad09
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/r4.go
@@ -0,0 +1,42 @@
+package impl
+
+import (
+	"fmt"
+
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+// Extension is syntactic sugar over `extension.where(url = ...)`, and is
+// specific to the R4 extensions for FHIRPath (as oppose to being part of the
+// N1 normative spec).
+//
+// For more details, see https://hl7.org/fhir/R4/fhirpath.html#functions
+func Extension(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	if len(args) != 1 {
+		return nil, fmt.Errorf("%w: expected 1 argument", ErrWrongArity)
+	}
+	arg, err := args[0].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	}
+	str, err := arg.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	var result system.Collection
+	for _, entry := range input {
+		entry, ok := entry.(fhir.Extendable)
+		if !ok {
+			continue
+		}
+		for _, ext := range entry.GetExtension() {
+			if url := ext.GetUrl(); url != nil && url.Value == str {
+				result = append(result, ext)
+			}
+		}
+	}
+	return result, nil
+}
diff --git a/fhirpath/internal/funcs/impl/r4_test.go b/fhirpath/internal/funcs/impl/r4_test.go
new file mode 100644
index 0000000..02b1a70
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/r4_test.go
@@ -0,0 +1,120 @@
+package impl_test
+
+import (
+	"errors"
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/element/extension"
+	"github.com/verily-src/fhirpath-go/internal/fhirtest"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr/exprtest"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs/impl"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func TestExtension_ValidInput(t *testing.T) {
+	extURL := "1234"
+	patient := fhirtest.NewResourceOf[*ppb.Patient](t)
+	ext := extension.New(extURL, fhir.String("some value"))
+	patient.Extension = append(patient.Extension, &dtpb.Extension{})
+	patient.Extension = append(patient.Extension, ext, ext)
+	testCases := []struct {
+		name  string
+		input system.Collection
+		arg   string
+		want  system.Collection
+	}{
+		{
+			name:  "empty input result in empty output",
+			input: system.Collection{},
+			arg:   "some-url",
+			want:  nil,
+		},
+		{
+			name: "entries have extensions not matched by url",
+			input: system.Collection{
+				fhirtest.NewResourceOf[*ppb.Patient](t),
+			},
+			arg:  "some-url",
+			want: nil,
+		},
+		{
+			name: "input does not have extension field",
+			input: system.Collection{
+				system.String("hello world"),
+			},
+			arg:  "some-url",
+			want: nil,
+		},
+		{
+			name:  "entries have extensions matched by url",
+			input: system.Collection{patient},
+			arg:   extURL,
+			want:  system.Collection{ext, ext},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Extension(&expr.Context{}, tc.input, exprtest.Return(system.String(tc.arg)))
+			if err != nil {
+				t.Fatalf("Extension function returned unexpected error: %v", err)
+			}
+
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Extension function returned unexpected diff (-want, +got):\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestExtension_InvalidInput_RaisesError(t *testing.T) {
+	testErr := errors.New("test error")
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		wantErr error
+	}{
+		{
+			name:  "too many arguments",
+			input: system.Collection{},
+			args: []expr.Expression{
+				exprtest.Return(system.String("")),
+				exprtest.Return(system.String("")),
+			},
+			wantErr: impl.ErrWrongArity,
+		}, {
+			name:    "too few arguments",
+			input:   system.Collection{},
+			args:    []expr.Expression{},
+			wantErr: impl.ErrWrongArity,
+		}, {
+			name:    "invalid argument type",
+			input:   system.Collection{},
+			args:    []expr.Expression{exprtest.Return(system.Integer(42))},
+			wantErr: cmpopts.AnyError,
+		}, {
+			name:    "argument errors",
+			input:   system.Collection{},
+			args:    []expr.Expression{exprtest.Error(testErr)},
+			wantErr: testErr,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := impl.Extension(&expr.Context{}, tc.input, tc.args...)
+
+			if got, want := err, tc.wantErr; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+				t.Fatalf("Extension(%v): got err %v, want err %v", tc.name, got, want)
+			}
+		})
+	}
+}
diff --git a/fhirpath/internal/funcs/impl/strings.go b/fhirpath/internal/funcs/impl/strings.go
new file mode 100644
index 0000000..30e3480
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/strings.go
@@ -0,0 +1,435 @@
+package impl
+
+import (
+	"errors"
+	"fmt"
+	"regexp"
+	"strings"
+
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+var ErrInvalidRegex = errors.New("invalid regex")
+
+// StartsWith returns true if the input string starts with the given prefix.
+func StartsWith(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Validate single string input
+	if length := len(input); length > 1 {
+		return nil, fmt.Errorf("%w: input has length %v, expected 1", ErrWrongArity, length)
+	} else if length == 0 {
+		return system.Collection{}, nil
+	}
+	fullString, err := input.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	// Validate single string argument
+	if length := len(args); length != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+	output, err := args[0].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	} else if length := len(output); length != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+	prefix, err := output.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	result := system.Boolean(strings.HasPrefix(string(fullString), string(prefix)))
+	return system.Collection{result}, nil
+}
+
+// EndsWith returns true if the input string ends with the given prefix.
+func EndsWith(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Validate single string input
+	if length := len(input); length > 1 {
+		return nil, fmt.Errorf("%w: input has length %v, expected 1", ErrWrongArity, length)
+	} else if length == 0 {
+		return system.Collection{}, nil
+	}
+	fullString, err := input.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	// Validate single string argument
+	if length := len(args); length != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+	output, err := args[0].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	} else if length := len(output); length != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+	suffix, err := output.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	result := system.Boolean(strings.HasSuffix(string(fullString), string(suffix)))
+	return system.Collection{result}, nil
+}
+
+// Length returns the length of the input string.
+func Length(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Validate single string input
+	if length := len(input); length > 1 {
+		return nil, fmt.Errorf("%w: input has length %v, expected 1", ErrWrongArity, length)
+	} else if length == 0 {
+		return system.Collection{}, nil
+	}
+	fullString, err := input.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	if length := len(args); length != 0 {
+		return nil, fmt.Errorf("%w, received %v arguments, expected 0", ErrWrongArity, length)
+	}
+
+	result := system.Integer(len(fullString))
+	return system.Collection{result}, nil
+}
+
+// Upper returns the input string with all characters converted to upper case.
+func Upper(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Validate single string input
+	if length := len(input); length > 1 {
+		return nil, fmt.Errorf("%w: input has length %v, expected 1", ErrWrongArity, length)
+	} else if length == 0 {
+		return system.Collection{}, nil
+	}
+	fullString, err := input.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	if length := len(args); length != 0 {
+		return nil, fmt.Errorf("%w, received %v arguments, expected 0", ErrWrongArity, length)
+	}
+
+	result := system.String(strings.ToUpper(fullString))
+	return system.Collection{result}, nil
+}
+
+// Lower returns the input string with all characters converted to lower case.
+func Lower(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Validate single string input
+	if length := len(input); length > 1 {
+		return nil, fmt.Errorf("%w: input has length %v, expected 1", ErrWrongArity, length)
+	} else if length == 0 {
+		return system.Collection{}, nil
+	}
+	fullString, err := input.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	if length := len(args); length != 0 {
+		return nil, fmt.Errorf("%w, received %v arguments, expected 0", ErrWrongArity, length)
+	}
+
+	result := system.String(strings.ToLower(fullString))
+	return system.Collection{result}, nil
+}
+
+// Contains returns true if the input string contains the given substring.
+func Contains(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Validate single string input
+	if length := len(input); length > 1 {
+		return nil, fmt.Errorf("%w: input has length %v, expected 1", ErrWrongArity, length)
+	} else if length == 0 {
+		return system.Collection{}, nil
+	}
+	fullString, err := input.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	// Validate single string argument
+	if length := len(args); length != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+	output, err := args[0].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	} else if length := len(output); length != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+	substring, err := output.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	result := system.Boolean(strings.Contains(string(fullString), string(substring)))
+	return system.Collection{result}, nil
+}
+
+// ToChars returns the list of characters in the input string.
+func ToChars(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Validate single string input
+	if length := len(input); length > 1 {
+		return nil, fmt.Errorf("%w: input has length %v, expected 1", ErrWrongArity, length)
+	} else if length == 0 {
+		return system.Collection{}, nil
+	}
+	fullString, err := input.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	if length := len(args); length != 0 {
+		return nil, fmt.Errorf("%w, received %v arguments, expected 0", ErrWrongArity, length)
+	}
+
+	result := system.Collection{}
+	for _, char := range strings.Split(fullString, "") {
+		result = append(result, system.String(char))
+	}
+	return result, nil
+}
+
+// Substring returns the part of the string starting at position start (zero-based).
+// If length is given, will return at most length number of characters from the input string.
+func Substring(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Validate single string input
+	if length := len(input); length > 1 {
+		return nil, fmt.Errorf("%w: input has length %v, expected 1", ErrWrongArity, length)
+	} else if length == 0 {
+		return system.Collection{}, nil
+	}
+	fullString, err := input.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	argLength := len(args)
+	if argLength < 1 || argLength > 2 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1 or 2", ErrWrongArity, argLength)
+	}
+
+	// Validate 1st integer argument (start)
+	startOutput, err := args[0].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	} else if length := len(startOutput); length != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+	start, err := startOutput.ToInt32()
+	if err != nil {
+		return nil, err
+	}
+	if int(start) >= len(fullString) {
+		return system.Collection{}, nil
+	}
+
+	// Validate optional 2nd integer argument (length).
+	var substringLength int32 = -1
+	if argLength == 2 {
+		lengthOutput, err := args[1].Evaluate(ctx, input)
+		if err != nil {
+			return nil, err
+		} else if length := len(lengthOutput); length != 1 {
+			return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+		}
+		substringLength, err = lengthOutput.ToInt32()
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	var result system.String
+	if substringLength > -1 && int(start+substringLength) < len(fullString) {
+		// Substring will not go out of bounds
+		result = system.String(fullString[start : start+substringLength])
+	} else {
+		result = system.String(fullString[start:])
+	}
+	return system.Collection{result}, nil
+}
+
+// IndexOf returns the 0-based index of the first position in which the
+// substring is found in the input string, or -1 if it is not found.
+func IndexOf(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Validate single string input
+	if length := len(input); length > 1 {
+		return nil, fmt.Errorf("%w: input has length %v, expected 1", ErrWrongArity, length)
+	} else if length == 0 {
+		return system.Collection{}, nil
+	}
+	fullString, err := input.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	// Validate single string argument
+	if length := len(args); length != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+	output, err := args[0].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	} else if length := len(output); length == 0 {
+		// Return empty for empty argument
+		return system.Collection{}, nil
+	} else if length > 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+	substring, err := output.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	result := system.Integer(strings.Index(fullString, substring))
+	return system.Collection{result}, nil
+}
+
+// Matches returns true when the value matches the given regular expression.
+func Matches(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Validate single string input
+	if length := len(input); length > 1 {
+		return nil, fmt.Errorf("%w: input has length %v, expected 1", ErrWrongArity, length)
+	} else if length == 0 {
+		return system.Collection{}, nil
+	}
+	fullString, err := input.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	// Validate single string argument
+	if length := len(args); length != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+	output, err := args[0].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	} else if length := len(output); length == 0 {
+		return system.Collection{}, nil
+	} else if length != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+	regexString, err := output.ToString()
+	if err != nil {
+		return nil, err
+	}
+	re, err := regexp.Compile(regexString)
+	if err != nil {
+		return nil, fmt.Errorf("%w: %s", ErrInvalidRegex, regexString)
+	}
+
+	result := system.Boolean(re.Match([]byte(fullString)))
+	return system.Collection{result}, nil
+}
+
+// Replace returns the input string with all instances of pattern replaced with substitution.
+func Replace(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Validate single string input
+	if length := len(input); length > 1 {
+		return nil, fmt.Errorf("%w: input has length %v, expected 1", ErrWrongArity, length)
+	} else if length == 0 {
+		return system.Collection{}, nil
+	}
+	fullString, err := input.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	if length := len(args); length != 2 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 2", ErrWrongArity, length)
+	}
+
+	// Validate 1st string argument (pattern)
+	patternOutput, err := args[0].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	} else if length := len(patternOutput); length == 0 {
+		// Empty arg
+		return system.Collection{}, nil
+	} else if length > 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+	pattern, err := patternOutput.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	// Validate 2nd string argument (substitution)
+	subOutput, err := args[1].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	} else if length := len(subOutput); length == 0 {
+		// Empty arg
+		return system.Collection{}, nil
+	} else if length > 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+	substitution, err := subOutput.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	result := system.String(strings.ReplaceAll(fullString, pattern, substitution))
+	return system.Collection{result}, nil
+}
+
+// ReplaceMatches matches the input using the regular expression in
+// regex and replaces each match with the substitution string. The
+// substitution may refer to identified match groups in the regular expression.
+func ReplaceMatches(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Validate single string input
+	if length := len(input); length > 1 {
+		return nil, fmt.Errorf("%w: input has length %v, expected 1", ErrWrongArity, length)
+	} else if length == 0 {
+		return system.Collection{}, nil
+	}
+	fullString, err := input.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	if length := len(args); length != 2 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+
+	// Validate 1st string argument (regex)
+	regexOutput, err := args[0].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	} else if length := len(regexOutput); length == 0 {
+		return system.Collection{}, nil
+	} else if length > 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+	regexString, err := regexOutput.ToString()
+	if err != nil {
+		return nil, err
+	}
+	re, err := regexp.Compile(regexString)
+	if err != nil {
+		return nil, fmt.Errorf("%w: %s", ErrInvalidRegex, regexString)
+	}
+
+	// Validate 2nd string argument (substitution)
+	subOutput, err := args[1].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	} else if length := len(subOutput); length == 0 {
+		return system.Collection{}, nil
+	} else if length > 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, length)
+	}
+	substitution, err := subOutput.ToString()
+	if err != nil {
+		return nil, err
+	}
+
+	result := system.String(re.ReplaceAllString(fullString, substitution))
+	return system.Collection{result}, nil
+}
diff --git a/fhirpath/internal/funcs/impl/strings_test.go b/fhirpath/internal/funcs/impl/strings_test.go
new file mode 100644
index 0000000..7fc06bf
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/strings_test.go
@@ -0,0 +1,1148 @@
+package impl_test
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs/impl"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+func TestStartsWith(t *testing.T) {
+	fullString := system.String("Lee Jieun")
+
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:  "returns empty for empty input",
+			input: system.Collection{},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("")},
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns true for empty prefix",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("")},
+			},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:  "returns true for match",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("Lee")},
+			},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:  "returns false for no match",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:  "errors if input length is more than 1",
+			input: system.Collection{fullString, fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if input is not a string",
+			input: system.Collection{system.Integer(516)},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "errors if args length is not 1",
+			input:   system.Collection{fullString},
+			args:    []expr.Expression{},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if arg is not a string",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(516)},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.StartsWith(&expr.Context{}, tc.input, tc.args...)
+
+			if gotErr := err != nil; tc.wantErr != gotErr {
+				t.Fatalf("StartsWith got unexpected error result: gotErr %v, wantErr %v, err: %v", gotErr, tc.wantErr, err)
+			}
+			if !cmp.Equal(tc.want, got) {
+				t.Errorf("StartsWith returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestEndsWith(t *testing.T) {
+	fullString := system.String("Lee Jieun")
+
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:  "returns empty for empty input",
+			input: system.Collection{},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("")},
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns true for empty suffix",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("")},
+			},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:  "returns true for match",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("Jieun")},
+			},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:  "returns false for no match",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:  "errors if input length is more than 1",
+			input: system.Collection{fullString, fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if input is not a string",
+			input: system.Collection{system.Integer(516)},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "errors if args length is not 1",
+			input:   system.Collection{fullString},
+			args:    []expr.Expression{},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if arg is not a string",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(516)},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.EndsWith(&expr.Context{}, tc.input, tc.args...)
+
+			if gotErr := err != nil; tc.wantErr != gotErr {
+				t.Fatalf("EndsWith got unexpected error result: gotErr %v, wantErr %v, err: %v", gotErr, tc.wantErr, err)
+			}
+			if !cmp.Equal(tc.want, got) {
+				t.Errorf("EndsWith returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestLength(t *testing.T) {
+	fullString := system.String("Lee Jieun")
+
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "returns empty for empty input",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns length of string",
+			input:   system.Collection{fullString},
+			want:    system.Collection{system.Integer(9)},
+			wantErr: false,
+		},
+		{
+			name:    "errors if input length is more than 1",
+			input:   system.Collection{fullString, fullString},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "errors if input is not a string",
+			input:   system.Collection{system.Integer(516)},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args is not empty",
+			input: system.Collection{system.String("IU")},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("516")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Length(&expr.Context{}, tc.input, tc.args...)
+
+			if gotErr := err != nil; tc.wantErr != gotErr {
+				t.Fatalf("Length got unexpected error result: gotErr %v, wantErr %v, err: %v", gotErr, tc.wantErr, err)
+			}
+			if !cmp.Equal(tc.want, got) {
+				t.Errorf("Length returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestUpper(t *testing.T) {
+	fullString := system.String("Lee Jieun")
+
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "returns empty for empty input",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns upper string",
+			input:   system.Collection{fullString},
+			want:    system.Collection{system.String("LEE JIEUN")},
+			wantErr: false,
+		},
+		{
+			name:    "errors if input length is more than 1",
+			input:   system.Collection{fullString, fullString},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "errors if input is not a string",
+			input:   system.Collection{system.Integer(516)},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args is not empty",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Upper(&expr.Context{}, tc.input, tc.args...)
+
+			if gotErr := err != nil; tc.wantErr != gotErr {
+				t.Fatalf("Upper got unexpected error result: gotErr %v, wantErr %v, err: %v", gotErr, tc.wantErr, err)
+			}
+			if !cmp.Equal(tc.want, got) {
+				t.Errorf("Upper returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestLower(t *testing.T) {
+	fullString := system.String("Lee Jieun")
+
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "returns empty for empty input",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns lower string",
+			input:   system.Collection{fullString},
+			want:    system.Collection{system.String("lee jieun")},
+			wantErr: false,
+		},
+		{
+			name:    "errors if input length is more than 1",
+			input:   system.Collection{fullString, fullString},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "errors if input is not a string",
+			input:   system.Collection{system.Integer(516)},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args is not empty",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Lower(&expr.Context{}, tc.input, tc.args...)
+
+			if gotErr := err != nil; tc.wantErr != gotErr {
+				t.Fatalf("Lower got unexpected error result: gotErr %v, wantErr %v, err: %v", gotErr, tc.wantErr, err)
+			}
+			if !cmp.Equal(tc.want, got) {
+				t.Errorf("Lower returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestContains(t *testing.T) {
+	fullString := system.String("Lee Jieun")
+
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:  "returns empty for empty input",
+			input: system.Collection{},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("")},
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns true for empty substring",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("")},
+			},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:  "returns true for match",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("Jie")},
+			},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:  "returns false for no match",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:  "errors if input length is more than 1",
+			input: system.Collection{fullString, fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if input is not a string",
+			input: system.Collection{system.Integer(516)},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "errors if args length is not 1",
+			input:   system.Collection{fullString},
+			args:    []expr.Expression{},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if arg is not a string",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(516)},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Contains(&expr.Context{}, tc.input, tc.args...)
+
+			if gotErr := err != nil; tc.wantErr != gotErr {
+				t.Fatalf("Contains got unexpected error result: gotErr %v, wantErr %v, err: %v", gotErr, tc.wantErr, err)
+			}
+			if !cmp.Equal(tc.want, got) {
+				t.Errorf("Contains returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestToChars(t *testing.T) {
+	fullString := system.String("IU")
+
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:    "returns empty for empty input",
+			input:   system.Collection{},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:    "returns chars of string",
+			input:   system.Collection{fullString},
+			want:    system.Collection{system.String("I"), system.String("U")},
+			wantErr: false,
+		},
+		{
+			name:    "errors if input length is more than 1",
+			input:   system.Collection{fullString, fullString},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "errors if input is not a string",
+			input:   system.Collection{system.Integer(516)},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if args is not empty",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(516)},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ToChars(&expr.Context{}, tc.input, tc.args...)
+
+			if gotErr := err != nil; tc.wantErr != gotErr {
+				t.Fatalf("ToChars got unexpected error result: gotErr %v, wantErr %v, err: %v", gotErr, tc.wantErr, err)
+			}
+			if !cmp.Equal(tc.want, got) {
+				t.Errorf("ToChars returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestSubstring(t *testing.T) {
+	fullString := system.String("Lee Jieun")
+
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:  "returns empty for empty input",
+			input: system.Collection{},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(0)},
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns empty is start is bigger than input string",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(50)},
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns substring with no length",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(4)},
+			},
+			want:    system.Collection{system.String("Jieun")},
+			wantErr: false,
+		},
+		{
+			name:  "returns substring with length",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(4)},
+				&expr.LiteralExpression{Literal: system.Integer(2)},
+			},
+			want:    system.Collection{system.String("Ji")},
+			wantErr: false,
+		},
+		{
+			name:  "returns remaining chars if length overflows",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(4)},
+				&expr.LiteralExpression{Literal: system.Integer(50)},
+			},
+			want:    system.Collection{system.String("Jieun")},
+			wantErr: false,
+		},
+		{
+			name:  "errors if input length is more than 1",
+			input: system.Collection{fullString, fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(4)},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if input is not a string",
+			input: system.Collection{system.Integer(516)},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(4)},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "errors if args length is not 1 or 2",
+			input:   system.Collection{fullString},
+			args:    []expr.Expression{},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if arg 1 is not an integer",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if arg 2 is not an integer",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(1)},
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Substring(&expr.Context{}, tc.input, tc.args...)
+
+			if gotErr := err != nil; tc.wantErr != gotErr {
+				t.Fatalf("Substring got unexpected error result: gotErr %v, wantErr %v, err: %v", gotErr, tc.wantErr, err)
+			}
+			if !cmp.Equal(tc.want, got) {
+				t.Errorf("Substring returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestIndexOf(t *testing.T) {
+	fullString := system.String("Lee Jieun")
+
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:  "returns empty for empty input",
+			input: system.Collection{},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("")},
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns empty for empty arg",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{},
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns 0 for empty substring",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("")},
+			},
+			want:    system.Collection{system.Integer(0)},
+			wantErr: false,
+		},
+		{
+			name:  "returns proper index for match",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("Jieun")},
+			},
+			want:    system.Collection{system.Integer(4)},
+			wantErr: false,
+		},
+		{
+			name:  "returns -1 for no match",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    system.Collection{system.Integer(-1)},
+			wantErr: false,
+		},
+		{
+			name:  "errors if input length is more than 1",
+			input: system.Collection{fullString, fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if input is not a string",
+			input: system.Collection{system.Integer(516)},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "errors if args length is not 1",
+			input:   system.Collection{fullString},
+			args:    []expr.Expression{},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if arg is not a string",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(516)},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.IndexOf(&expr.Context{}, tc.input, tc.args...)
+
+			if gotErr := err != nil; tc.wantErr != gotErr {
+				t.Fatalf("IndexOf got unexpected error result: gotErr %v, wantErr %v, err: %v", gotErr, tc.wantErr, err)
+			}
+			if !cmp.Equal(tc.want, got) {
+				t.Errorf("IndexOf returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestMatches(t *testing.T) {
+	fullString := system.String("Lee Jieun")
+
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:  "returns empty for empty input",
+			input: system.Collection{},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("")},
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns empty for empty regex arg",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{},
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns true for empty regex",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("")},
+			},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:  "returns true for match",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("^Lee[A-Za-z ]*$")},
+			},
+			want:    system.Collection{system.Boolean(true)},
+			wantErr: false,
+		},
+		{
+			name:  "returns false for no match",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("^Lee[0-9]*$")},
+			},
+			want:    system.Collection{system.Boolean(false)},
+			wantErr: false,
+		},
+		{
+			name:  "errors if input length is more than 1",
+			input: system.Collection{fullString, fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if input is not a string",
+			input: system.Collection{system.Integer(516)},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "errors if args length is not 1",
+			input:   system.Collection{fullString},
+			args:    []expr.Expression{},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if arg is not a string",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(516)},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if regex is invalid",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("^[$")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Matches(&expr.Context{}, tc.input, tc.args...)
+
+			if gotErr := err != nil; tc.wantErr != gotErr {
+				t.Fatalf("Matches got unexpected error result: gotErr %v, wantErr %v, err: %v", gotErr, tc.wantErr, err)
+			}
+			if !cmp.Equal(tc.want, got) {
+				t.Errorf("Matches returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestReplace(t *testing.T) {
+	fullString := system.String("Lee Jieun")
+
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:  "returns empty for empty input",
+			input: system.Collection{},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("")},
+				&expr.LiteralExpression{Literal: system.String("")},
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns empty for empty pattern",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{},
+				&expr.LiteralExpression{Literal: system.String("Jieun")},
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns empty for empty substitution",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("Jieun")},
+				&expr.LiteralExpression{},
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns replaced string",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("Jieun")},
+				&expr.LiteralExpression{Literal: system.String("Uaena")},
+			},
+			want:    system.Collection{system.String("Lee Uaena")},
+			wantErr: false,
+		},
+		{
+			name:  "returns original string if both args are empty string",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("")},
+				&expr.LiteralExpression{Literal: system.String("")},
+			},
+			want:    system.Collection{fullString},
+			wantErr: false,
+		},
+		{
+			name:  "removes pattern if substitution is empty string",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("e")},
+				&expr.LiteralExpression{Literal: system.String("")},
+			},
+			want:    system.Collection{system.String("L Jiun")},
+			wantErr: false,
+		},
+		{
+			name:  "surrounds all characters with subtitution if pattern is empty string",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("")},
+				&expr.LiteralExpression{Literal: system.String("!")},
+			},
+			want:    system.Collection{system.String("!L!e!e! !J!i!e!u!n!")},
+			wantErr: false,
+		},
+		{
+			name:  "errors if input length is more than 1",
+			input: system.Collection{fullString, fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if input is not a string",
+			input: system.Collection{system.Integer(516)},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "errors if args length is not 2",
+			input:   system.Collection{fullString},
+			args:    []expr.Expression{},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if arg 1 is not a string",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(516)},
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if arg 2 is not a string",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+				&expr.LiteralExpression{Literal: system.Integer(516)},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Replace(&expr.Context{}, tc.input, tc.args...)
+
+			if gotErr := err != nil; tc.wantErr != gotErr {
+				t.Fatalf("Replace got unexpected error result: gotErr %v, wantErr %v, err: %v", gotErr, tc.wantErr, err)
+			}
+			if !cmp.Equal(tc.want, got) {
+				t.Errorf("Replace returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestReplaceMatches(t *testing.T) {
+	fullString := system.String("Woo Young Woo")
+	dateRegex := `\b(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<year>\d{2,4})\b`
+
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:  "returns empty for empty input",
+			input: system.Collection{},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("")},
+				&expr.LiteralExpression{Literal: system.String("")},
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns empty for empty pattern arg",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{},
+				&expr.LiteralExpression{Literal: system.String(" to the ")},
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns empty for empty substitution arg",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String(`[ ]`)},
+				&expr.LiteralExpression{},
+			},
+			want:    system.Collection{},
+			wantErr: false,
+		},
+		{
+			name:  "returns replaced string",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String(`[ ]`)},
+				&expr.LiteralExpression{Literal: system.String(" to the ")},
+			},
+			want:    system.Collection{system.String("Woo to the Young to the Woo")},
+			wantErr: false,
+		},
+		// This test case comes directly from the FHIRPath spec.
+		// https://build.fhir.org/ig/HL7/FHIRPath/#replacematchesregex-string-substitution-string-string
+		{
+			name:  "returns replaced string with named patterns",
+			input: system.Collection{system.String("5/16/1993")},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String(dateRegex)},
+				&expr.LiteralExpression{Literal: system.String("${day}/${month}/${year}")},
+			},
+			want:    system.Collection{system.String("16/5/1993")},
+			wantErr: false,
+		},
+		{
+			name:  "errors if input length is more than 1",
+			input: system.Collection{fullString, fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if input is not a string",
+			input: system.Collection{system.Integer(516)},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "errors if args length is not 2",
+			input:   system.Collection{fullString},
+			args:    []expr.Expression{},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if arg 1 is not a string",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.Integer(516)},
+				&expr.LiteralExpression{Literal: system.String("IU")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if arg 2 is not a string",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("IU")},
+				&expr.LiteralExpression{Literal: system.Integer(516)},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:  "errors if regex is invalid",
+			input: system.Collection{fullString},
+			args: []expr.Expression{
+				&expr.LiteralExpression{Literal: system.String("^[$")},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.ReplaceMatches(&expr.Context{}, tc.input, tc.args...)
+
+			if gotErr := err != nil; tc.wantErr != gotErr {
+				t.Fatalf("ReplaceMatches got unexpected error result: gotErr %v, wantErr %v, err: %v", gotErr, tc.wantErr, err)
+			}
+			if !cmp.Equal(tc.want, got) {
+				t.Errorf("ReplaceMatches returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
diff --git a/fhirpath/internal/funcs/impl/subsetting.go b/fhirpath/internal/funcs/impl/subsetting.go
new file mode 100644
index 0000000..d469c24
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/subsetting.go
@@ -0,0 +1,238 @@
+package impl
+
+import (
+	"fmt"
+
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+	"google.golang.org/protobuf/proto"
+)
+
+// First Returns a collection containing only the first item in the input collection.
+// This function is equivalent to item[0], so it will return an empty collection if the input collection has no items.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#first-collection
+func First(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	return system.Collection{input[0]}, nil
+}
+
+// Last Returns a collection containing only the last item in the input collection.
+// Will return an empty collection if the input collection has no items.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#last-collection
+func Last(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	return system.Collection{input[len(input)-1]}, nil
+}
+
+// Tail Returns a collection containing all but the first item in the input collection.
+// Will return an empty collection if the input collection has no items, or only one item.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#tail-collection
+func Tail(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	return input[1:], nil
+}
+
+// Skip Returns a collection containing all but the first num items in the input collection.
+// Will return an empty collection if there are no items remaining after the indicated number of items have been skipped,
+// or if the input collection is empty.
+// If num is less than or equal to zero, the input collection is simply returned.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#skipnum-integer-collection
+func Skip(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	// Args validation
+	if len(args) != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, len(args))
+	}
+	argValues, err := args[0].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	}
+	skip, err := argValues.ToInt32()
+	if err != nil {
+		return nil, err
+	}
+	if skip <= 0 {
+		return input, nil
+	}
+	if skip >= int32(len(input)) {
+		return system.Collection{}, nil
+	}
+	return input[skip:], nil
+}
+
+// Take Returns a collection containing the first num items in the input collection,
+// or less if there are less than num items. If num is less than or equal to 0,
+// or if the input collection is empty ({ }), take returns an empty collection.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#takenum-integer-collection
+func Take(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	// Args validation
+	if len(args) != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, len(args))
+	}
+	argValues, err := args[0].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	}
+	take, err := argValues.ToInt32()
+	if err != nil {
+		return nil, err
+	}
+	if take <= 0 {
+		return system.Collection{}, nil
+	}
+	if take >= int32(len(input)) {
+		return input, nil
+	}
+	return input[:take], nil
+}
+
+// Intersect Returns the set of elements that are in both collections.
+// Duplicate items will be eliminated by this function.
+// Order of items is not guaranteed to be preserved in the result of this function.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#intersectother-collection-collection
+func Intersect(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	// Args validation
+	if len(args) != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, len(args))
+	}
+	argValues, err := args[0].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	}
+	var result system.Collection
+	for _, i := range input {
+		for _, c := range argValues {
+			if checkEquality(i, c) {
+				v, _ := system.From(c)
+				result = append(result, v)
+			}
+		}
+	}
+	if len(result) == 0 {
+		return system.Collection{}, nil
+	}
+	return removeDuplicates(result), nil
+}
+
+// Exclude returns the set of elements that are not in the other collection.
+// Duplicate items will not be eliminated by this function, and order will be preserved.
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#excludeother-collection-collection
+func Exclude(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	// Input validation
+	if input.IsEmpty() {
+		return system.Collection{}, nil
+	}
+	// Args validation
+	if len(args) != 1 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 1", ErrWrongArity, len(args))
+	}
+	argValues, err := args[0].Evaluate(ctx, input)
+	if err != nil {
+		return nil, err
+	}
+	var result system.Collection
+	for _, val := range input {
+		if !argValues.Contains(val) {
+			result = append(result, val)
+		}
+	}
+	for _, arg := range argValues {
+		if !input.Contains(arg) {
+			result = append(result, arg)
+		}
+	}
+	return result, nil
+}
+
+// Distinct returns the set of elements that are distinct and unique from the
+// input by applying equality-operation tests.
+//
+// If the input collection is empty ({}), the result is empty.
+// Note that the order of elements in the input collection is not guaranteed to
+// be preserved in the result.
+//
+// See the spec for this function for more details:
+// https://hl7.org/fhirpath/N1/#distinct-collection
+func Distinct(_ *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	if len(args) != 0 {
+		return nil, fmt.Errorf("%w: received %v arguments, expected 0", ErrWrongArity, len(args))
+	}
+	var result system.Collection
+	for _, v := range input {
+		if result.Contains(v) {
+			continue
+		}
+		result = append(result, v)
+	}
+	return result, nil
+}
+
+// IsDistinct queries whether the input collection is a set of fully distinct
+// and unique values. This is effectively short-hand for calling:
+//
+//	v.count() = v.distinct().count()
+//
+// See the spec for this function for more details:
+// https://hl7.org/fhirpath/N1/#isdistinct-boolean
+func IsDistinct(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	got, err := Distinct(ctx, input, args...)
+	if err != nil {
+		return nil, err
+	}
+	return system.Collection{system.Boolean(len(got) == len(input))}, nil
+}
+
+func removeDuplicates(collection system.Collection) system.Collection {
+	seen := make(map[any]bool)
+	var result system.Collection
+	for _, val := range collection {
+		if _, ok := seen[val]; !ok {
+			seen[val] = true
+			result = append(result, val)
+		}
+	}
+	return result
+}
+
+func checkEquality(lhs, rhs any) bool {
+	return checkSystemEquality(lhs, rhs) || checkProtoEquality(lhs, rhs)
+}
+
+func checkSystemEquality(lhs, rhs any) bool {
+	l, lerr := system.From(lhs)
+	r, rerr := system.From(rhs)
+	if lerr == nil && rerr == nil {
+		got, ok := system.TryEqual(l, r)
+		return got && ok
+	}
+	return false
+}
+
+func checkProtoEquality(lhs, rhs any) bool {
+	l, lok := lhs.(proto.Message)
+	r, rok := rhs.(proto.Message)
+	if lok && rok {
+		return proto.Equal(l, r)
+	}
+	return false
+}
diff --git a/fhirpath/internal/funcs/impl/subsetting_test.go b/fhirpath/internal/funcs/impl/subsetting_test.go
new file mode 100644
index 0000000..1cee610
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/subsetting_test.go
@@ -0,0 +1,677 @@
+package impl_test
+
+import (
+	"testing"
+
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr/exprtest"
+
+	"google.golang.org/protobuf/testing/protocmp"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs/impl"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+func TestFirst(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:  "returns and empty collection if input is empty",
+			input: system.Collection{},
+			want:  system.Collection{},
+		},
+		{
+			name: "returns first collection element",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+			want: system.Collection{system.Integer(1)},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.First(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("First() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("First() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestLast(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:  "returns and empty collection if input is empty",
+			input: system.Collection{},
+			want:  system.Collection{},
+		},
+		{
+			name: "returns last collection element",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+			want: system.Collection{system.Integer(5)},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Last(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Last() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Last() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestTail(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name:  "returns and empty collection if input is empty",
+			input: system.Collection{},
+			want:  system.Collection{},
+		},
+		{
+			name: "returns collection tail",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+			want: system.Collection{
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Tail(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Tail() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Tail() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestSkip(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if arg is not provided",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+			wantErr: true,
+		},
+		{
+			name:  "returns an empty collection if input is empty",
+			input: system.Collection{},
+			want:  system.Collection{},
+		},
+		{
+			name: "returns an empty collection if input arg is greater than or equal to input length",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+			args: []expr.Expression{
+				exprtest.Return(system.Integer(5)),
+			},
+			want: system.Collection{},
+		},
+		{
+			name: "returns the same input collection if arg is <= 0",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+			args: []expr.Expression{
+				exprtest.Return(system.Integer(-2)),
+			},
+			want: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+		},
+		{
+			name: "returns the skipped input collection",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+			args: []expr.Expression{
+				exprtest.Return(system.Integer(3)),
+			},
+			want: system.Collection{
+				system.Integer(4),
+				system.Integer(5)},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Skip(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Skip() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Skip() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestTake(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if arg is not provided",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+			wantErr: true,
+		},
+		{
+			name:  "returns an empty collection if input is empty",
+			input: system.Collection{},
+			want:  system.Collection{},
+		},
+		{
+			name: "returns an empty collection if arg is lower than or equal to 0 ",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+			args: []expr.Expression{
+				exprtest.Return(system.Integer(0)),
+			},
+			want: system.Collection{},
+		},
+		{
+			name: "returns the same collection if arg is greater than or equal to input length",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+			args: []expr.Expression{
+				exprtest.Return(system.Integer(7)),
+			},
+			want: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+		},
+		{
+			name: "returns the taken input collection",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+			args: []expr.Expression{
+				exprtest.Return(system.Integer(3)),
+			},
+			want: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3)},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Take(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Take() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Take() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestIntersect(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if arg is not provided",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+			wantErr: true,
+		},
+		{
+			name:  "returns an empty collection if input is empty",
+			input: system.Collection{},
+			want:  system.Collection{},
+		},
+		{
+			name: "returns the intersection of both collections",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5),
+				system.Integer(6)},
+			args: []expr.Expression{
+				exprtest.Return(
+					system.Integer(3),
+					system.Integer(6),
+					system.Integer(9),
+				),
+			},
+			want: system.Collection{
+				system.Integer(3),
+				system.Integer(6)},
+		},
+		{
+			name: "returns the intersection of both collections ignoring duplicates",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(2),
+				system.Integer(3)},
+			args: []expr.Expression{
+				exprtest.Return(
+					system.Integer(2),
+					system.Integer(2),
+					system.Integer(4),
+				),
+			},
+			want: system.Collection{
+				system.Integer(2),
+			},
+		},
+		{
+			name: "returns the intersection of both collections with fhir.Integer types",
+			input: system.Collection{
+				fhir.Integer(1),
+				fhir.Integer(2),
+				fhir.Integer(3),
+				fhir.Integer(4)},
+			args: []expr.Expression{
+				exprtest.Return(
+					fhir.Integer(2),
+					fhir.Integer(4),
+				),
+			},
+			want: system.Collection{
+				system.Integer(2),
+				system.Integer(4),
+			},
+		},
+		{
+			name: "returns an empty collection if there is no intersection",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2)},
+			args: []expr.Expression{
+				exprtest.Return(
+					system.Integer(3),
+					system.Integer(4),
+				),
+			},
+			want: system.Collection{},
+		},
+		{
+			name: "returns intersection of normalized values",
+			input: system.Collection{
+				system.Integer(1),
+				fhir.Integer(1)},
+			args: []expr.Expression{
+				exprtest.Return(
+					fhir.Integer(1),
+					system.Integer(1),
+				),
+			},
+			want: system.Collection{
+				system.Integer(1),
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Intersect(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Intersect() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Intersect() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestExclude(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		want    system.Collection
+		wantErr bool
+	}{
+		{
+			name: "errors if arg is not provided",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4),
+				system.Integer(5)},
+			wantErr: true,
+		},
+		{
+			name:  "returns an empty collection if input is empty",
+			input: system.Collection{},
+			want:  system.Collection{},
+		},
+		{
+			name: "returns the exclude of both collections",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(3),
+				system.Integer(4)},
+			args: []expr.Expression{
+				exprtest.Return(
+					system.Integer(3),
+					system.Integer(4),
+					system.Integer(5),
+					system.Integer(6),
+				),
+			},
+			want: system.Collection{
+				system.Integer(1),
+				system.Integer(2),
+				system.Integer(5),
+				system.Integer(6)},
+		},
+		{
+			name: "returns the exclude of both collections with fhir.Integer types",
+			input: system.Collection{
+				fhir.Integer(1),
+				fhir.Integer(2),
+				fhir.Integer(3),
+				fhir.Integer(4)},
+			args: []expr.Expression{
+				exprtest.Return(
+					fhir.Integer(2),
+					fhir.Integer(4),
+				),
+			},
+			want: system.Collection{
+				fhir.Integer(1),
+				fhir.Integer(3),
+			},
+		},
+		{
+			name: "returns an empty collection if there is no exclude",
+			input: system.Collection{
+				system.Integer(1),
+				system.Integer(2)},
+			args: []expr.Expression{
+				exprtest.Return(
+					system.Integer(1),
+					system.Integer(2),
+				),
+			},
+		},
+		{
+			name: "returns exclude of normalized values",
+			input: system.Collection{
+				system.Integer(1),
+				fhir.Integer(2),
+			},
+			args: []expr.Expression{
+				exprtest.Return(
+					fhir.Integer(2),
+					system.Integer(3),
+				),
+			},
+			want: system.Collection{
+				system.Integer(1),
+				system.Integer(3),
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := impl.Exclude(&expr.Context{}, tc.input, tc.args...)
+			if (err != nil) != tc.wantErr {
+				t.Errorf("Exclude() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Exclude(%v) returned unexpected diff (-want, +got)\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestDistinct(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input system.Collection
+		want  system.Collection
+	}{
+		{
+			name: "Empty input returns empty output",
+		},
+		{
+			name:  "Inputs are distinct",
+			input: system.Collection{system.String("Hello"), system.Integer(1), fhir.Integer(2)},
+			want:  system.Collection{system.String("Hello"), system.Integer(1), fhir.Integer(2)},
+		},
+		{
+			name:  "Inputs contain exact duplicate",
+			input: system.Collection{system.String("Hello"), system.Integer(1), system.Integer(1)},
+			want:  system.Collection{system.String("Hello"), system.Integer(1)},
+		}, {
+			name:  "Inputs contain system-convertible duplicates",
+			input: system.Collection{system.String("Hello"), system.Integer(1), fhir.Integer(1)},
+			want:  system.Collection{system.String("Hello"), system.Integer(1)},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ctx := expr.Context{}
+			got, err := impl.Distinct(&ctx, tc.input)
+			if err != nil {
+				t.Fatalf("Distinct(%v): got unexpected err: %v", tc.name, err)
+			}
+
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Distinct(%v) returned unexpected diff (-want, +got)\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestDistinct_BadInputs(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		wantErr error
+	}{
+		{
+			name:    "Function called with nonzero arguments",
+			input:   system.Collection{},
+			args:    []expr.Expression{exprtest.Return()},
+			wantErr: cmpopts.AnyError,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ctx := expr.Context{}
+			_, err := impl.Distinct(&ctx, tc.input, tc.args...)
+
+			if got, want := err, tc.wantErr; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+				t.Errorf("Distinct(%v): got err %v, want  err%v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestIsDistinct(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input system.Collection
+		want  bool
+	}{
+		{
+			name: "Empty input is distinct",
+			want: true,
+		},
+		{
+			name:  "Inputs are distinct",
+			input: system.Collection{system.String("Hello"), system.Integer(1), fhir.Integer(2)},
+			want:  true,
+		},
+		{
+			name:  "Inputs contain exact duplicate",
+			input: system.Collection{system.String("Hello"), system.Integer(1), system.Integer(1)},
+			want:  false,
+		}, {
+			name:  "Inputs contain system-convertible duplicates",
+			input: system.Collection{system.String("Hello"), system.Integer(1), fhir.Integer(1)},
+			want:  false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ctx := expr.Context{}
+			collection, err := impl.IsDistinct(&ctx, tc.input)
+			if err != nil {
+				t.Fatalf("IsDistinct(%v): got unexpected err: %v", tc.name, err)
+			}
+			got, err := collection.ToBool()
+			if err != nil {
+				t.Fatalf("IsDistinct(%v): got unexpected err: %v", tc.name, err)
+			}
+
+			if got != tc.want {
+				t.Errorf("IsDistinct(%v): got %v, want %v", tc.name, got, tc.want)
+			}
+		})
+	}
+}
+
+func TestIsDistinct_BadInputs(t *testing.T) {
+	testCases := []struct {
+		name    string
+		input   system.Collection
+		args    []expr.Expression
+		wantErr error
+	}{
+		{
+			name:    "Function called with nonzero arguments",
+			input:   system.Collection{},
+			args:    []expr.Expression{exprtest.Return()},
+			wantErr: cmpopts.AnyError,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ctx := expr.Context{}
+			_, err := impl.IsDistinct(&ctx, tc.input, tc.args...)
+
+			if got, want := err, tc.wantErr; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+				t.Errorf("IsDistinct(%v): got err %v, want  err%v", tc.name, got, want)
+			}
+		})
+	}
+}
diff --git a/fhirpath/internal/funcs/impl/utility.go b/fhirpath/internal/funcs/impl/utility.go
new file mode 100644
index 0000000..97dfef8
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/utility.go
@@ -0,0 +1,24 @@
+package impl
+
+import (
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+// TimeOfDay returns the current time as a system.Time object.
+func TimeOfDay(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	timeString := ctx.Now.Format("15:04:05.000")
+	return system.Collection{system.MustParseTime(timeString)}, nil
+}
+
+// Today returns the current date as a system.Date object.
+func Today(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	dateString := ctx.Now.Format("2006-01-02")
+	return system.Collection{system.MustParseDate(dateString)}, nil
+}
+
+// Now returns the current time as a system.DateTime object.
+func Now(ctx *expr.Context, input system.Collection, args ...expr.Expression) (system.Collection, error) {
+	dateTimeString := ctx.Now.Format("2006-01-02T15:04:05.000Z07:00")
+	return system.Collection{system.MustParseDateTime(dateTimeString)}, nil
+}
diff --git a/fhirpath/internal/funcs/impl/utility_test.go b/fhirpath/internal/funcs/impl/utility_test.go
new file mode 100644
index 0000000..b3faef2
--- /dev/null
+++ b/fhirpath/internal/funcs/impl/utility_test.go
@@ -0,0 +1,50 @@
+package impl_test
+
+import (
+	"testing"
+	"time"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs/impl"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+func TestTimeOfDay(t *testing.T) {
+	ctx := &expr.Context{Now: time.Date(0, time.January, 1, 19, 30, 5, 1000000, time.UTC)}
+	wantCollection := system.Collection{system.MustParseTime("19:30:05.001")}
+
+	got, err := impl.TimeOfDay(ctx, []any{})
+	if err != nil {
+		t.Fatalf("impl.TimeOfDay() returned unexpected error: %v", err)
+	}
+	if !cmp.Equal(got, wantCollection) {
+		t.Errorf("impl.TimeOfDay() returned unexpected result: got %v, want %v", got, wantCollection)
+	}
+}
+
+func TestToday(t *testing.T) {
+	ctx := &expr.Context{Now: time.Date(2010, time.February, 12, 0, 0, 0, 0, time.UTC)}
+	wantCollection := system.Collection{system.MustParseDate("2010-02-12")}
+
+	got, err := impl.Today(ctx, []any{})
+	if err != nil {
+		t.Fatalf("impl.Today() returned unexpected error: %v", err)
+	}
+	if !cmp.Equal(got, wantCollection) {
+		t.Errorf("impl.Today() returned unexpected result: got %v, want %v", got, wantCollection)
+	}
+}
+
+func TestNow(t *testing.T) {
+	ctx := &expr.Context{Now: time.Date(2010, time.February, 12, 12, 30, 34, 2000000, time.UTC)}
+	wantCollection := system.Collection{system.MustParseDateTime("2010-02-12T12:30:34.002Z")}
+
+	got, err := impl.Now(ctx, []any{})
+	if err != nil {
+		t.Fatalf("impl.Now() returned unexpected error: %v", err)
+	}
+	if !cmp.Equal(got, wantCollection) {
+		t.Errorf("impl.Now() returned unexpected result: got %v, want %v", got, wantCollection)
+	}
+}
diff --git a/fhirpath/internal/funcs/table.go b/fhirpath/internal/funcs/table.go
new file mode 100644
index 0000000..e410cc5
--- /dev/null
+++ b/fhirpath/internal/funcs/table.go
@@ -0,0 +1,392 @@
+package funcs
+
+import "github.com/verily-src/fhirpath-go/fhirpath/internal/funcs/impl"
+
+// BaseTable holds the default mapping of all
+// FHIRPath functions. Unimplemented functions return an
+// unimplemented error.
+var baseTable = FunctionTable{
+	"empty": Function{
+		impl.Empty,
+		0,
+		0,
+		false,
+	},
+	"exists": Function{
+		impl.Exists,
+		0,
+		1,
+		false,
+	},
+	"extension": Function{
+		impl.Extension,
+		1,
+		1,
+		false,
+	},
+	"all": notImplemented,
+	"allTrue": Function{
+		impl.AllTrue,
+		0,
+		0,
+		false,
+	},
+	"anyTrue": Function{
+		impl.AnyTrue,
+		0,
+		0,
+		false,
+	},
+	"allFalse": Function{
+		impl.AllFalse,
+		0,
+		0,
+		false,
+	},
+	"anyFalse": Function{
+		impl.AnyFalse,
+		0,
+		0,
+		false,
+	},
+	"subsetOf":   notImplemented,
+	"supersetOf": notImplemented,
+	"count": Function{
+		impl.Count,
+		0,
+		0,
+		false,
+	},
+	"distinct": Function{
+		impl.Distinct,
+		0,
+		0,
+		false,
+	},
+	"isDistinct": Function{
+		impl.IsDistinct,
+		0,
+		0,
+		false,
+	},
+	"where": Function{
+		impl.Where,
+		1,
+		1,
+		false,
+	},
+	"select": notImplemented,
+	"repeat": notImplemented,
+	"ofType": notImplemented,
+	"single": notImplemented,
+	"first": Function{
+		impl.First,
+		0,
+		0,
+		false,
+	},
+	"last": Function{
+		impl.Last,
+		0,
+		0,
+		false,
+	},
+	"tail": Function{
+		impl.Tail,
+		0,
+		0,
+		false,
+	},
+	"skip": Function{
+		impl.Skip,
+		1,
+		1,
+		false,
+	},
+	"take": Function{
+		impl.Take,
+		1,
+		1,
+		false,
+	},
+	"intersect": Function{
+		impl.Intersect,
+		1,
+		1,
+		false,
+	},
+	"exclude": Function{
+		impl.Exclude,
+		1,
+		1,
+		false,
+	},
+	"union":   notImplemented,
+	"combine": notImplemented,
+	"iif":     notImplemented,
+	"toBoolean": Function{
+		impl.ToBoolean,
+		0,
+		0,
+		false,
+	},
+	"convertsToBoolean": Function{
+		impl.ConvertsToBoolean,
+		0,
+		0,
+		false,
+	},
+	"toInteger": Function{
+		impl.ToInteger,
+		0,
+		0,
+		false,
+	},
+	"convertsToInteger": Function{
+		impl.ConvertsToInteger,
+		0,
+		0,
+		false,
+	},
+	"toDate": Function{
+		impl.ToDate,
+		0,
+		0,
+		false,
+	},
+	"convertsToDate": Function{
+		impl.ConvertsToDate,
+		0,
+		0,
+		false,
+	},
+	"toDateTime": Function{
+		impl.ToDateTime,
+		0,
+		0,
+		false,
+	},
+	"convertToDateTime": Function{
+		impl.ConvertsToDateTime,
+		0,
+		0,
+		false,
+	},
+	"toDecimal": Function{
+		impl.ToDecimal,
+		0,
+		0,
+		false,
+	},
+	"convertsToDecimal": Function{
+		impl.ConvertsToDecimal,
+		0,
+		0,
+		false,
+	},
+	"toQuantity": Function{
+		impl.ToInteger,
+		0,
+		1,
+		false,
+	},
+	"convertsToQuantity": Function{
+		impl.ConvertsToQuantity,
+		0,
+		1,
+		false,
+	},
+	"toString": Function{
+		impl.ToString,
+		0,
+		0,
+		false,
+	},
+	"convertsToString": Function{
+		impl.ConvertsToString,
+		0,
+		0,
+		false,
+	},
+	"toTime": Function{
+		impl.ToTime,
+		0,
+		0,
+		false,
+	},
+	"convertsToTime": Function{
+		impl.ConvertsToTime,
+		0,
+		0,
+		false,
+	},
+	"indexOf": Function{
+		impl.IndexOf,
+		1,
+		1,
+		false,
+	},
+	"substring": Function{
+		impl.Substring,
+		1,
+		2,
+		false,
+	},
+	"startsWith": Function{
+		impl.StartsWith,
+		1,
+		1,
+		false,
+	},
+	"endsWith": Function{
+		impl.EndsWith,
+		1,
+		1,
+		false,
+	},
+	"contains": Function{
+		impl.Contains,
+		1,
+		1,
+		false,
+	},
+	"upper": Function{
+		impl.Upper,
+		0,
+		0,
+		false,
+	},
+	"lower": Function{
+		impl.Lower,
+		0,
+		0,
+		false,
+	},
+	"replace": Function{
+		impl.Replace,
+		2,
+		2,
+		false,
+	},
+	"matches": Function{
+		impl.Matches,
+		1,
+		1,
+		false,
+	},
+	"replaceMatches": Function{
+		impl.ReplaceMatches,
+		2,
+		2,
+		false,
+	},
+	"length": Function{
+		impl.Length,
+		0,
+		0,
+		false,
+	},
+	"toChars": Function{
+		impl.ToChars,
+		0,
+		0,
+		false,
+	},
+	"abs": Function{
+		impl.Abs,
+		0,
+		0,
+		false,
+	},
+	"ceiling": Function{
+		impl.Ceiling,
+		0,
+		0,
+		false,
+	},
+	"exp": Function{
+		impl.Exp,
+		0,
+		0,
+		false,
+	},
+	"floor": Function{
+		impl.Floor,
+		0,
+		0,
+		false,
+	},
+	"ln": Function{
+		impl.Ln,
+		0,
+		0,
+		false,
+	},
+	"log": Function{
+		impl.Log,
+		0,
+		0,
+		false,
+	},
+	"power": Function{
+		impl.Power,
+		0,
+		0,
+		false,
+	},
+	"round": Function{
+		impl.Round,
+		0,
+		0,
+		false,
+	},
+	"sqrt": Function{
+		impl.Sqrt,
+		0,
+		0,
+		false,
+	},
+	"truncate": Function{
+		impl.Truncate,
+		0,
+		0,
+		false,
+	},
+	"children":    notImplemented,
+	"descendants": notImplemented,
+	"trace":       notImplemented,
+	"now": Function{
+		impl.Now,
+		0,
+		0,
+		false,
+	},
+	"timeOfDay": Function{
+		impl.TimeOfDay,
+		0,
+		0,
+		false,
+	},
+	"today": Function{
+		impl.Today,
+		0,
+		0,
+		false,
+	},
+	"not": Function{
+		impl.Not,
+		0,
+		0,
+		false,
+	},
+}
+
+// Clone returns a deep copy of the base
+// function table.
+func Clone() FunctionTable {
+	table := make(FunctionTable) // TODO: Optimize (PHP-6173)
+	for k, v := range baseTable {
+		table[k] = v
+	}
+	return table
+}
diff --git a/fhirpath/internal/grammar/fhirpath.g4 b/fhirpath/internal/grammar/fhirpath.g4
new file mode 100644
index 0000000..b6c993d
--- /dev/null
+++ b/fhirpath/internal/grammar/fhirpath.g4
@@ -0,0 +1,179 @@
+grammar fhirpath;
+
+// Grammar rules
+// [FHIRPath](http://hl7.org/fhirpath/N1) Normative Release
+
+//prog: line (line)*;
+//line: ID ( '(' expr ')') ':' expr '\r'? '\n';
+
+prog
+        : expression EOF
+        ;
+
+expression
+        : term                                                      #termExpression
+        | expression '.' invocation                                 #invocationExpression
+        | expression '[' expression ']'                             #indexerExpression
+        | ('+' | '-') expression                                    #polarityExpression
+        | expression ('*' | '/' | 'div' | 'mod') expression         #multiplicativeExpression
+        | expression ('+' | '-' | '&') expression                   #additiveExpression
+        | expression ('is' | 'as') typeSpecifier                    #typeExpression
+        | expression '|' expression                                 #unionExpression
+        | expression ('<=' | '<' | '>' | '>=') expression           #inequalityExpression
+        | expression ('=' | '~' | '!=' | '!~') expression           #equalityExpression
+        | expression ('in' | 'contains') expression                 #membershipExpression
+        | expression 'and' expression                               #andExpression
+        | expression ('or' | 'xor') expression                      #orExpression
+        | expression 'implies' expression                           #impliesExpression
+        //| (IDENTIFIER)? '=>' expression                           #lambdaExpression
+        ;
+
+term
+        : invocation                                            #invocationTerm
+        | literal                                               #literalTerm
+        | externalConstant                                      #externalConstantTerm
+        | '(' expression ')'                                    #parenthesizedTerm
+        ;
+
+literal
+        : '{' '}'                                               #nullLiteral
+        | ('true' | 'false')                                    #booleanLiteral
+        | STRING                                                #stringLiteral
+        | NUMBER                                                #numberLiteral
+        | DATE                                                  #dateLiteral
+        | DATETIME                                              #dateTimeLiteral
+        | TIME                                                  #timeLiteral
+        | quantity                                              #quantityLiteral
+        ;
+
+externalConstant
+        : '%' ( identifier | STRING )
+        ;
+
+invocation                          // Terms that can be used after the function/member invocation '.'
+        : identifier                                            #memberInvocation
+        | function                                              #functionInvocation
+        | '$this'                                               #thisInvocation
+        | '$index'                                              #indexInvocation
+        | '$total'                                              #totalInvocation
+        ;
+
+function
+        : identifier '(' paramList? ')'
+        ;
+
+paramList
+        : expression (',' expression)*
+        ;
+
+quantity
+        : NUMBER unit?
+        ;
+
+unit
+        : dateTimePrecision
+        | pluralDateTimePrecision
+        | STRING // UCUM syntax for units of measure
+        ;
+
+dateTimePrecision
+        : 'year' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond'
+        ;
+
+pluralDateTimePrecision
+        : 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds'
+        ;
+
+typeSpecifier
+        : qualifiedIdentifier
+        ;
+
+qualifiedIdentifier
+        : identifier ('.' identifier)*
+        ;
+
+identifier
+        : IDENTIFIER
+        | DELIMITEDIDENTIFIER
+        | 'as'
+        | 'contains'
+        | 'in'
+        | 'is'
+        ;
+
+
+/****************************************************************
+    Lexical rules
+*****************************************************************/
+
+/*
+NOTE: The goal of these rules in the grammar is to provide a date
+token to the parser. As such it is not attempting to validate that
+the date is a correct date, that task is for the parser or interpreter.
+*/
+
+DATE
+        : '@' DATEFORMAT
+        ;
+
+DATETIME
+        : '@' DATEFORMAT 'T' (TIMEFORMAT TIMEZONEOFFSETFORMAT?)?
+        ;
+
+TIME
+        : '@' 'T' TIMEFORMAT
+        ;
+
+fragment DATEFORMAT
+        : [0-9][0-9][0-9][0-9] ('-'[0-9][0-9] ('-'[0-9][0-9])?)?
+        ;
+
+fragment TIMEFORMAT
+        : [0-9][0-9] (':'[0-9][0-9] (':'[0-9][0-9] ('.'[0-9]+)?)?)?
+        ;
+
+fragment TIMEZONEOFFSETFORMAT
+        : ('Z' | ('+' | '-') [0-9][0-9]':'[0-9][0-9])
+        ;
+
+IDENTIFIER
+        : ([A-Za-z] | '_')([A-Za-z0-9] | '_')*            // Added _ to support CQL (FHIR could constrain it out)
+        ;
+
+DELIMITEDIDENTIFIER
+        : '`' (ESC | .)*? '`'
+        ;
+
+STRING
+        : '\'' (ESC | .)*? '\''
+        ;
+
+// Also allows leading zeroes now (just like CQL and XSD)
+NUMBER
+        : [0-9]+('.' [0-9]+)?
+        ;
+
+// Pipe whitespace to the HIDDEN channel to support retrieving source text through the parser.
+WS
+        : [ \r\n\t]+ -> channel(HIDDEN)
+        ;
+
+COMMENT
+        : '/*' .*? '*/' -> channel(HIDDEN)
+        ;
+
+LINE_COMMENT
+        : '//' ~[\r\n]* -> channel(HIDDEN)
+        ;
+
+fragment ESC
+        : '\\' ([`'\\/fnrt] | UNICODE)    // allow \`, \', \\, \/, \f, etc. and \uXXX
+        ;
+
+fragment UNICODE
+        : 'u' HEX HEX HEX HEX
+        ;
+
+fragment HEX
+        : [0-9a-fA-F]
+        ;
\ No newline at end of file
diff --git a/fhirpath/internal/grammar/fhirpath_lexer.go b/fhirpath/internal/grammar/fhirpath_lexer.go
new file mode 100644
index 0000000..8d80b18
--- /dev/null
+++ b/fhirpath/internal/grammar/fhirpath_lexer.go
@@ -0,0 +1,410 @@
+// Code generated from fhirpath.g4 by ANTLR 4.13.0. DO NOT EDIT.
+
+package grammar
+
+import (
+	"fmt"
+	"github.com/antlr4-go/antlr/v4"
+	"sync"
+	"unicode"
+)
+
+// Suppress unused import error
+var _ = fmt.Printf
+var _ = sync.Once{}
+var _ = unicode.IsLetter
+
+type fhirpathLexer struct {
+	*antlr.BaseLexer
+	channelNames []string
+	modeNames    []string
+	// TODO: EOF string
+}
+
+var FhirpathLexerLexerStaticData struct {
+	once                   sync.Once
+	serializedATN          []int32
+	ChannelNames           []string
+	ModeNames              []string
+	LiteralNames           []string
+	SymbolicNames          []string
+	RuleNames              []string
+	PredictionContextCache *antlr.PredictionContextCache
+	atn                    *antlr.ATN
+	decisionToDFA          []*antlr.DFA
+}
+
+func fhirpathlexerLexerInit() {
+	staticData := &FhirpathLexerLexerStaticData
+	staticData.ChannelNames = []string{
+		"DEFAULT_TOKEN_CHANNEL", "HIDDEN",
+	}
+	staticData.ModeNames = []string{
+		"DEFAULT_MODE",
+	}
+	staticData.LiteralNames = []string{
+		"", "'.'", "'['", "']'", "'+'", "'-'", "'*'", "'/'", "'div'", "'mod'",
+		"'&'", "'is'", "'as'", "'|'", "'<='", "'<'", "'>'", "'>='", "'='", "'~'",
+		"'!='", "'!~'", "'in'", "'contains'", "'and'", "'or'", "'xor'", "'implies'",
+		"'('", "')'", "'{'", "'}'", "'true'", "'false'", "'%'", "'$this'", "'$index'",
+		"'$total'", "','", "'year'", "'month'", "'week'", "'day'", "'hour'",
+		"'minute'", "'second'", "'millisecond'", "'years'", "'months'", "'weeks'",
+		"'days'", "'hours'", "'minutes'", "'seconds'", "'milliseconds'",
+	}
+	staticData.SymbolicNames = []string{
+		"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+		"", "", "", "", "DATE", "DATETIME", "TIME", "IDENTIFIER", "DELIMITEDIDENTIFIER",
+		"STRING", "NUMBER", "WS", "COMMENT", "LINE_COMMENT",
+	}
+	staticData.RuleNames = []string{
+		"T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", "T__7", "T__8",
+		"T__9", "T__10", "T__11", "T__12", "T__13", "T__14", "T__15", "T__16",
+		"T__17", "T__18", "T__19", "T__20", "T__21", "T__22", "T__23", "T__24",
+		"T__25", "T__26", "T__27", "T__28", "T__29", "T__30", "T__31", "T__32",
+		"T__33", "T__34", "T__35", "T__36", "T__37", "T__38", "T__39", "T__40",
+		"T__41", "T__42", "T__43", "T__44", "T__45", "T__46", "T__47", "T__48",
+		"T__49", "T__50", "T__51", "T__52", "T__53", "DATE", "DATETIME", "TIME",
+		"DATEFORMAT", "TIMEFORMAT", "TIMEZONEOFFSETFORMAT", "IDENTIFIER", "DELIMITEDIDENTIFIER",
+		"STRING", "NUMBER", "WS", "COMMENT", "LINE_COMMENT", "ESC", "UNICODE",
+		"HEX",
+	}
+	staticData.PredictionContextCache = antlr.NewPredictionContextCache()
+	staticData.serializedATN = []int32{
+		4, 0, 64, 523, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2,
+		4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2,
+		10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15,
+		7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7,
+		20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25,
+		2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2,
+		31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36,
+		7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7,
+		41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46,
+		2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2,
+		52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57,
+		7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7,
+		62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67,
+		2, 68, 7, 68, 2, 69, 7, 69, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1,
+		3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1,
+		8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1,
+		12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16,
+		1, 16, 1, 17, 1, 17, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1,
+		20, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22,
+		1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 25, 1,
+		25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26,
+		1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1,
+		31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33,
+		1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1,
+		35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37,
+		1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1,
+		39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41,
+		1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1,
+		43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45,
+		1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1,
+		46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47,
+		1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1,
+		49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51,
+		1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1,
+		52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53,
+		1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 55, 1,
+		55, 1, 55, 1, 55, 1, 55, 3, 55, 386, 8, 55, 3, 55, 388, 8, 55, 1, 56, 1,
+		56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57,
+		1, 57, 1, 57, 3, 57, 404, 8, 57, 3, 57, 406, 8, 57, 1, 58, 1, 58, 1, 58,
+		1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 4, 58, 418, 8, 58, 11,
+		58, 12, 58, 419, 3, 58, 422, 8, 58, 3, 58, 424, 8, 58, 3, 58, 426, 8, 58,
+		1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 435, 8, 59, 1,
+		60, 3, 60, 438, 8, 60, 1, 60, 5, 60, 441, 8, 60, 10, 60, 12, 60, 444, 9,
+		60, 1, 61, 1, 61, 1, 61, 5, 61, 449, 8, 61, 10, 61, 12, 61, 452, 9, 61,
+		1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 5, 62, 459, 8, 62, 10, 62, 12, 62, 462,
+		9, 62, 1, 62, 1, 62, 1, 63, 4, 63, 467, 8, 63, 11, 63, 12, 63, 468, 1,
+		63, 1, 63, 4, 63, 473, 8, 63, 11, 63, 12, 63, 474, 3, 63, 477, 8, 63, 1,
+		64, 4, 64, 480, 8, 64, 11, 64, 12, 64, 481, 1, 64, 1, 64, 1, 65, 1, 65,
+		1, 65, 1, 65, 5, 65, 490, 8, 65, 10, 65, 12, 65, 493, 9, 65, 1, 65, 1,
+		65, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 66, 5, 66, 504, 8, 66,
+		10, 66, 12, 66, 507, 9, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 3, 67, 514,
+		8, 67, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 3, 450,
+		460, 491, 0, 70, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17,
+		9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35,
+		18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53,
+		27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71,
+		36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89,
+		45, 91, 46, 93, 47, 95, 48, 97, 49, 99, 50, 101, 51, 103, 52, 105, 53,
+		107, 54, 109, 55, 111, 56, 113, 57, 115, 0, 117, 0, 119, 0, 121, 58, 123,
+		59, 125, 60, 127, 61, 129, 62, 131, 63, 133, 64, 135, 0, 137, 0, 139, 0,
+		1, 0, 8, 1, 0, 48, 57, 2, 0, 43, 43, 45, 45, 3, 0, 65, 90, 95, 95, 97,
+		122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32,
+		2, 0, 10, 10, 13, 13, 8, 0, 39, 39, 47, 47, 92, 92, 96, 96, 102, 102, 110,
+		110, 114, 114, 116, 116, 3, 0, 48, 57, 65, 70, 97, 102, 537, 0, 1, 1, 0,
+		0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0,
+		0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1,
+		0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25,
+		1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0,
+		33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0,
+		0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0,
+		0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0,
+		0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1,
+		0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71,
+		1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0,
+		79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0,
+		0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0,
+		0, 0, 95, 1, 0, 0, 0, 0, 97, 1, 0, 0, 0, 0, 99, 1, 0, 0, 0, 0, 101, 1,
+		0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 105, 1, 0, 0, 0, 0, 107, 1, 0, 0, 0, 0,
+		109, 1, 0, 0, 0, 0, 111, 1, 0, 0, 0, 0, 113, 1, 0, 0, 0, 0, 121, 1, 0,
+		0, 0, 0, 123, 1, 0, 0, 0, 0, 125, 1, 0, 0, 0, 0, 127, 1, 0, 0, 0, 0, 129,
+		1, 0, 0, 0, 0, 131, 1, 0, 0, 0, 0, 133, 1, 0, 0, 0, 1, 141, 1, 0, 0, 0,
+		3, 143, 1, 0, 0, 0, 5, 145, 1, 0, 0, 0, 7, 147, 1, 0, 0, 0, 9, 149, 1,
+		0, 0, 0, 11, 151, 1, 0, 0, 0, 13, 153, 1, 0, 0, 0, 15, 155, 1, 0, 0, 0,
+		17, 159, 1, 0, 0, 0, 19, 163, 1, 0, 0, 0, 21, 165, 1, 0, 0, 0, 23, 168,
+		1, 0, 0, 0, 25, 171, 1, 0, 0, 0, 27, 173, 1, 0, 0, 0, 29, 176, 1, 0, 0,
+		0, 31, 178, 1, 0, 0, 0, 33, 180, 1, 0, 0, 0, 35, 183, 1, 0, 0, 0, 37, 185,
+		1, 0, 0, 0, 39, 187, 1, 0, 0, 0, 41, 190, 1, 0, 0, 0, 43, 193, 1, 0, 0,
+		0, 45, 196, 1, 0, 0, 0, 47, 205, 1, 0, 0, 0, 49, 209, 1, 0, 0, 0, 51, 212,
+		1, 0, 0, 0, 53, 216, 1, 0, 0, 0, 55, 224, 1, 0, 0, 0, 57, 226, 1, 0, 0,
+		0, 59, 228, 1, 0, 0, 0, 61, 230, 1, 0, 0, 0, 63, 232, 1, 0, 0, 0, 65, 237,
+		1, 0, 0, 0, 67, 243, 1, 0, 0, 0, 69, 245, 1, 0, 0, 0, 71, 251, 1, 0, 0,
+		0, 73, 258, 1, 0, 0, 0, 75, 265, 1, 0, 0, 0, 77, 267, 1, 0, 0, 0, 79, 272,
+		1, 0, 0, 0, 81, 278, 1, 0, 0, 0, 83, 283, 1, 0, 0, 0, 85, 287, 1, 0, 0,
+		0, 87, 292, 1, 0, 0, 0, 89, 299, 1, 0, 0, 0, 91, 306, 1, 0, 0, 0, 93, 318,
+		1, 0, 0, 0, 95, 324, 1, 0, 0, 0, 97, 331, 1, 0, 0, 0, 99, 337, 1, 0, 0,
+		0, 101, 342, 1, 0, 0, 0, 103, 348, 1, 0, 0, 0, 105, 356, 1, 0, 0, 0, 107,
+		364, 1, 0, 0, 0, 109, 377, 1, 0, 0, 0, 111, 380, 1, 0, 0, 0, 113, 389,
+		1, 0, 0, 0, 115, 393, 1, 0, 0, 0, 117, 407, 1, 0, 0, 0, 119, 434, 1, 0,
+		0, 0, 121, 437, 1, 0, 0, 0, 123, 445, 1, 0, 0, 0, 125, 455, 1, 0, 0, 0,
+		127, 466, 1, 0, 0, 0, 129, 479, 1, 0, 0, 0, 131, 485, 1, 0, 0, 0, 133,
+		499, 1, 0, 0, 0, 135, 510, 1, 0, 0, 0, 137, 515, 1, 0, 0, 0, 139, 521,
+		1, 0, 0, 0, 141, 142, 5, 46, 0, 0, 142, 2, 1, 0, 0, 0, 143, 144, 5, 91,
+		0, 0, 144, 4, 1, 0, 0, 0, 145, 146, 5, 93, 0, 0, 146, 6, 1, 0, 0, 0, 147,
+		148, 5, 43, 0, 0, 148, 8, 1, 0, 0, 0, 149, 150, 5, 45, 0, 0, 150, 10, 1,
+		0, 0, 0, 151, 152, 5, 42, 0, 0, 152, 12, 1, 0, 0, 0, 153, 154, 5, 47, 0,
+		0, 154, 14, 1, 0, 0, 0, 155, 156, 5, 100, 0, 0, 156, 157, 5, 105, 0, 0,
+		157, 158, 5, 118, 0, 0, 158, 16, 1, 0, 0, 0, 159, 160, 5, 109, 0, 0, 160,
+		161, 5, 111, 0, 0, 161, 162, 5, 100, 0, 0, 162, 18, 1, 0, 0, 0, 163, 164,
+		5, 38, 0, 0, 164, 20, 1, 0, 0, 0, 165, 166, 5, 105, 0, 0, 166, 167, 5,
+		115, 0, 0, 167, 22, 1, 0, 0, 0, 168, 169, 5, 97, 0, 0, 169, 170, 5, 115,
+		0, 0, 170, 24, 1, 0, 0, 0, 171, 172, 5, 124, 0, 0, 172, 26, 1, 0, 0, 0,
+		173, 174, 5, 60, 0, 0, 174, 175, 5, 61, 0, 0, 175, 28, 1, 0, 0, 0, 176,
+		177, 5, 60, 0, 0, 177, 30, 1, 0, 0, 0, 178, 179, 5, 62, 0, 0, 179, 32,
+		1, 0, 0, 0, 180, 181, 5, 62, 0, 0, 181, 182, 5, 61, 0, 0, 182, 34, 1, 0,
+		0, 0, 183, 184, 5, 61, 0, 0, 184, 36, 1, 0, 0, 0, 185, 186, 5, 126, 0,
+		0, 186, 38, 1, 0, 0, 0, 187, 188, 5, 33, 0, 0, 188, 189, 5, 61, 0, 0, 189,
+		40, 1, 0, 0, 0, 190, 191, 5, 33, 0, 0, 191, 192, 5, 126, 0, 0, 192, 42,
+		1, 0, 0, 0, 193, 194, 5, 105, 0, 0, 194, 195, 5, 110, 0, 0, 195, 44, 1,
+		0, 0, 0, 196, 197, 5, 99, 0, 0, 197, 198, 5, 111, 0, 0, 198, 199, 5, 110,
+		0, 0, 199, 200, 5, 116, 0, 0, 200, 201, 5, 97, 0, 0, 201, 202, 5, 105,
+		0, 0, 202, 203, 5, 110, 0, 0, 203, 204, 5, 115, 0, 0, 204, 46, 1, 0, 0,
+		0, 205, 206, 5, 97, 0, 0, 206, 207, 5, 110, 0, 0, 207, 208, 5, 100, 0,
+		0, 208, 48, 1, 0, 0, 0, 209, 210, 5, 111, 0, 0, 210, 211, 5, 114, 0, 0,
+		211, 50, 1, 0, 0, 0, 212, 213, 5, 120, 0, 0, 213, 214, 5, 111, 0, 0, 214,
+		215, 5, 114, 0, 0, 215, 52, 1, 0, 0, 0, 216, 217, 5, 105, 0, 0, 217, 218,
+		5, 109, 0, 0, 218, 219, 5, 112, 0, 0, 219, 220, 5, 108, 0, 0, 220, 221,
+		5, 105, 0, 0, 221, 222, 5, 101, 0, 0, 222, 223, 5, 115, 0, 0, 223, 54,
+		1, 0, 0, 0, 224, 225, 5, 40, 0, 0, 225, 56, 1, 0, 0, 0, 226, 227, 5, 41,
+		0, 0, 227, 58, 1, 0, 0, 0, 228, 229, 5, 123, 0, 0, 229, 60, 1, 0, 0, 0,
+		230, 231, 5, 125, 0, 0, 231, 62, 1, 0, 0, 0, 232, 233, 5, 116, 0, 0, 233,
+		234, 5, 114, 0, 0, 234, 235, 5, 117, 0, 0, 235, 236, 5, 101, 0, 0, 236,
+		64, 1, 0, 0, 0, 237, 238, 5, 102, 0, 0, 238, 239, 5, 97, 0, 0, 239, 240,
+		5, 108, 0, 0, 240, 241, 5, 115, 0, 0, 241, 242, 5, 101, 0, 0, 242, 66,
+		1, 0, 0, 0, 243, 244, 5, 37, 0, 0, 244, 68, 1, 0, 0, 0, 245, 246, 5, 36,
+		0, 0, 246, 247, 5, 116, 0, 0, 247, 248, 5, 104, 0, 0, 248, 249, 5, 105,
+		0, 0, 249, 250, 5, 115, 0, 0, 250, 70, 1, 0, 0, 0, 251, 252, 5, 36, 0,
+		0, 252, 253, 5, 105, 0, 0, 253, 254, 5, 110, 0, 0, 254, 255, 5, 100, 0,
+		0, 255, 256, 5, 101, 0, 0, 256, 257, 5, 120, 0, 0, 257, 72, 1, 0, 0, 0,
+		258, 259, 5, 36, 0, 0, 259, 260, 5, 116, 0, 0, 260, 261, 5, 111, 0, 0,
+		261, 262, 5, 116, 0, 0, 262, 263, 5, 97, 0, 0, 263, 264, 5, 108, 0, 0,
+		264, 74, 1, 0, 0, 0, 265, 266, 5, 44, 0, 0, 266, 76, 1, 0, 0, 0, 267, 268,
+		5, 121, 0, 0, 268, 269, 5, 101, 0, 0, 269, 270, 5, 97, 0, 0, 270, 271,
+		5, 114, 0, 0, 271, 78, 1, 0, 0, 0, 272, 273, 5, 109, 0, 0, 273, 274, 5,
+		111, 0, 0, 274, 275, 5, 110, 0, 0, 275, 276, 5, 116, 0, 0, 276, 277, 5,
+		104, 0, 0, 277, 80, 1, 0, 0, 0, 278, 279, 5, 119, 0, 0, 279, 280, 5, 101,
+		0, 0, 280, 281, 5, 101, 0, 0, 281, 282, 5, 107, 0, 0, 282, 82, 1, 0, 0,
+		0, 283, 284, 5, 100, 0, 0, 284, 285, 5, 97, 0, 0, 285, 286, 5, 121, 0,
+		0, 286, 84, 1, 0, 0, 0, 287, 288, 5, 104, 0, 0, 288, 289, 5, 111, 0, 0,
+		289, 290, 5, 117, 0, 0, 290, 291, 5, 114, 0, 0, 291, 86, 1, 0, 0, 0, 292,
+		293, 5, 109, 0, 0, 293, 294, 5, 105, 0, 0, 294, 295, 5, 110, 0, 0, 295,
+		296, 5, 117, 0, 0, 296, 297, 5, 116, 0, 0, 297, 298, 5, 101, 0, 0, 298,
+		88, 1, 0, 0, 0, 299, 300, 5, 115, 0, 0, 300, 301, 5, 101, 0, 0, 301, 302,
+		5, 99, 0, 0, 302, 303, 5, 111, 0, 0, 303, 304, 5, 110, 0, 0, 304, 305,
+		5, 100, 0, 0, 305, 90, 1, 0, 0, 0, 306, 307, 5, 109, 0, 0, 307, 308, 5,
+		105, 0, 0, 308, 309, 5, 108, 0, 0, 309, 310, 5, 108, 0, 0, 310, 311, 5,
+		105, 0, 0, 311, 312, 5, 115, 0, 0, 312, 313, 5, 101, 0, 0, 313, 314, 5,
+		99, 0, 0, 314, 315, 5, 111, 0, 0, 315, 316, 5, 110, 0, 0, 316, 317, 5,
+		100, 0, 0, 317, 92, 1, 0, 0, 0, 318, 319, 5, 121, 0, 0, 319, 320, 5, 101,
+		0, 0, 320, 321, 5, 97, 0, 0, 321, 322, 5, 114, 0, 0, 322, 323, 5, 115,
+		0, 0, 323, 94, 1, 0, 0, 0, 324, 325, 5, 109, 0, 0, 325, 326, 5, 111, 0,
+		0, 326, 327, 5, 110, 0, 0, 327, 328, 5, 116, 0, 0, 328, 329, 5, 104, 0,
+		0, 329, 330, 5, 115, 0, 0, 330, 96, 1, 0, 0, 0, 331, 332, 5, 119, 0, 0,
+		332, 333, 5, 101, 0, 0, 333, 334, 5, 101, 0, 0, 334, 335, 5, 107, 0, 0,
+		335, 336, 5, 115, 0, 0, 336, 98, 1, 0, 0, 0, 337, 338, 5, 100, 0, 0, 338,
+		339, 5, 97, 0, 0, 339, 340, 5, 121, 0, 0, 340, 341, 5, 115, 0, 0, 341,
+		100, 1, 0, 0, 0, 342, 343, 5, 104, 0, 0, 343, 344, 5, 111, 0, 0, 344, 345,
+		5, 117, 0, 0, 345, 346, 5, 114, 0, 0, 346, 347, 5, 115, 0, 0, 347, 102,
+		1, 0, 0, 0, 348, 349, 5, 109, 0, 0, 349, 350, 5, 105, 0, 0, 350, 351, 5,
+		110, 0, 0, 351, 352, 5, 117, 0, 0, 352, 353, 5, 116, 0, 0, 353, 354, 5,
+		101, 0, 0, 354, 355, 5, 115, 0, 0, 355, 104, 1, 0, 0, 0, 356, 357, 5, 115,
+		0, 0, 357, 358, 5, 101, 0, 0, 358, 359, 5, 99, 0, 0, 359, 360, 5, 111,
+		0, 0, 360, 361, 5, 110, 0, 0, 361, 362, 5, 100, 0, 0, 362, 363, 5, 115,
+		0, 0, 363, 106, 1, 0, 0, 0, 364, 365, 5, 109, 0, 0, 365, 366, 5, 105, 0,
+		0, 366, 367, 5, 108, 0, 0, 367, 368, 5, 108, 0, 0, 368, 369, 5, 105, 0,
+		0, 369, 370, 5, 115, 0, 0, 370, 371, 5, 101, 0, 0, 371, 372, 5, 99, 0,
+		0, 372, 373, 5, 111, 0, 0, 373, 374, 5, 110, 0, 0, 374, 375, 5, 100, 0,
+		0, 375, 376, 5, 115, 0, 0, 376, 108, 1, 0, 0, 0, 377, 378, 5, 64, 0, 0,
+		378, 379, 3, 115, 57, 0, 379, 110, 1, 0, 0, 0, 380, 381, 5, 64, 0, 0, 381,
+		382, 3, 115, 57, 0, 382, 387, 5, 84, 0, 0, 383, 385, 3, 117, 58, 0, 384,
+		386, 3, 119, 59, 0, 385, 384, 1, 0, 0, 0, 385, 386, 1, 0, 0, 0, 386, 388,
+		1, 0, 0, 0, 387, 383, 1, 0, 0, 0, 387, 388, 1, 0, 0, 0, 388, 112, 1, 0,
+		0, 0, 389, 390, 5, 64, 0, 0, 390, 391, 5, 84, 0, 0, 391, 392, 3, 117, 58,
+		0, 392, 114, 1, 0, 0, 0, 393, 394, 7, 0, 0, 0, 394, 395, 7, 0, 0, 0, 395,
+		396, 7, 0, 0, 0, 396, 405, 7, 0, 0, 0, 397, 398, 5, 45, 0, 0, 398, 399,
+		7, 0, 0, 0, 399, 403, 7, 0, 0, 0, 400, 401, 5, 45, 0, 0, 401, 402, 7, 0,
+		0, 0, 402, 404, 7, 0, 0, 0, 403, 400, 1, 0, 0, 0, 403, 404, 1, 0, 0, 0,
+		404, 406, 1, 0, 0, 0, 405, 397, 1, 0, 0, 0, 405, 406, 1, 0, 0, 0, 406,
+		116, 1, 0, 0, 0, 407, 408, 7, 0, 0, 0, 408, 425, 7, 0, 0, 0, 409, 410,
+		5, 58, 0, 0, 410, 411, 7, 0, 0, 0, 411, 423, 7, 0, 0, 0, 412, 413, 5, 58,
+		0, 0, 413, 414, 7, 0, 0, 0, 414, 421, 7, 0, 0, 0, 415, 417, 5, 46, 0, 0,
+		416, 418, 7, 0, 0, 0, 417, 416, 1, 0, 0, 0, 418, 419, 1, 0, 0, 0, 419,
+		417, 1, 0, 0, 0, 419, 420, 1, 0, 0, 0, 420, 422, 1, 0, 0, 0, 421, 415,
+		1, 0, 0, 0, 421, 422, 1, 0, 0, 0, 422, 424, 1, 0, 0, 0, 423, 412, 1, 0,
+		0, 0, 423, 424, 1, 0, 0, 0, 424, 426, 1, 0, 0, 0, 425, 409, 1, 0, 0, 0,
+		425, 426, 1, 0, 0, 0, 426, 118, 1, 0, 0, 0, 427, 435, 5, 90, 0, 0, 428,
+		429, 7, 1, 0, 0, 429, 430, 7, 0, 0, 0, 430, 431, 7, 0, 0, 0, 431, 432,
+		5, 58, 0, 0, 432, 433, 7, 0, 0, 0, 433, 435, 7, 0, 0, 0, 434, 427, 1, 0,
+		0, 0, 434, 428, 1, 0, 0, 0, 435, 120, 1, 0, 0, 0, 436, 438, 7, 2, 0, 0,
+		437, 436, 1, 0, 0, 0, 438, 442, 1, 0, 0, 0, 439, 441, 7, 3, 0, 0, 440,
+		439, 1, 0, 0, 0, 441, 444, 1, 0, 0, 0, 442, 440, 1, 0, 0, 0, 442, 443,
+		1, 0, 0, 0, 443, 122, 1, 0, 0, 0, 444, 442, 1, 0, 0, 0, 445, 450, 5, 96,
+		0, 0, 446, 449, 3, 135, 67, 0, 447, 449, 9, 0, 0, 0, 448, 446, 1, 0, 0,
+		0, 448, 447, 1, 0, 0, 0, 449, 452, 1, 0, 0, 0, 450, 451, 1, 0, 0, 0, 450,
+		448, 1, 0, 0, 0, 451, 453, 1, 0, 0, 0, 452, 450, 1, 0, 0, 0, 453, 454,
+		5, 96, 0, 0, 454, 124, 1, 0, 0, 0, 455, 460, 5, 39, 0, 0, 456, 459, 3,
+		135, 67, 0, 457, 459, 9, 0, 0, 0, 458, 456, 1, 0, 0, 0, 458, 457, 1, 0,
+		0, 0, 459, 462, 1, 0, 0, 0, 460, 461, 1, 0, 0, 0, 460, 458, 1, 0, 0, 0,
+		461, 463, 1, 0, 0, 0, 462, 460, 1, 0, 0, 0, 463, 464, 5, 39, 0, 0, 464,
+		126, 1, 0, 0, 0, 465, 467, 7, 0, 0, 0, 466, 465, 1, 0, 0, 0, 467, 468,
+		1, 0, 0, 0, 468, 466, 1, 0, 0, 0, 468, 469, 1, 0, 0, 0, 469, 476, 1, 0,
+		0, 0, 470, 472, 5, 46, 0, 0, 471, 473, 7, 0, 0, 0, 472, 471, 1, 0, 0, 0,
+		473, 474, 1, 0, 0, 0, 474, 472, 1, 0, 0, 0, 474, 475, 1, 0, 0, 0, 475,
+		477, 1, 0, 0, 0, 476, 470, 1, 0, 0, 0, 476, 477, 1, 0, 0, 0, 477, 128,
+		1, 0, 0, 0, 478, 480, 7, 4, 0, 0, 479, 478, 1, 0, 0, 0, 480, 481, 1, 0,
+		0, 0, 481, 479, 1, 0, 0, 0, 481, 482, 1, 0, 0, 0, 482, 483, 1, 0, 0, 0,
+		483, 484, 6, 64, 0, 0, 484, 130, 1, 0, 0, 0, 485, 486, 5, 47, 0, 0, 486,
+		487, 5, 42, 0, 0, 487, 491, 1, 0, 0, 0, 488, 490, 9, 0, 0, 0, 489, 488,
+		1, 0, 0, 0, 490, 493, 1, 0, 0, 0, 491, 492, 1, 0, 0, 0, 491, 489, 1, 0,
+		0, 0, 492, 494, 1, 0, 0, 0, 493, 491, 1, 0, 0, 0, 494, 495, 5, 42, 0, 0,
+		495, 496, 5, 47, 0, 0, 496, 497, 1, 0, 0, 0, 497, 498, 6, 65, 0, 0, 498,
+		132, 1, 0, 0, 0, 499, 500, 5, 47, 0, 0, 500, 501, 5, 47, 0, 0, 501, 505,
+		1, 0, 0, 0, 502, 504, 8, 5, 0, 0, 503, 502, 1, 0, 0, 0, 504, 507, 1, 0,
+		0, 0, 505, 503, 1, 0, 0, 0, 505, 506, 1, 0, 0, 0, 506, 508, 1, 0, 0, 0,
+		507, 505, 1, 0, 0, 0, 508, 509, 6, 66, 0, 0, 509, 134, 1, 0, 0, 0, 510,
+		513, 5, 92, 0, 0, 511, 514, 7, 6, 0, 0, 512, 514, 3, 137, 68, 0, 513, 511,
+		1, 0, 0, 0, 513, 512, 1, 0, 0, 0, 514, 136, 1, 0, 0, 0, 515, 516, 5, 117,
+		0, 0, 516, 517, 3, 139, 69, 0, 517, 518, 3, 139, 69, 0, 518, 519, 3, 139,
+		69, 0, 519, 520, 3, 139, 69, 0, 520, 138, 1, 0, 0, 0, 521, 522, 7, 7, 0,
+		0, 522, 140, 1, 0, 0, 0, 24, 0, 385, 387, 403, 405, 419, 421, 423, 425,
+		434, 437, 440, 442, 448, 450, 458, 460, 468, 474, 476, 481, 491, 505, 513,
+		1, 0, 1, 0,
+	}
+	deserializer := antlr.NewATNDeserializer(nil)
+	staticData.atn = deserializer.Deserialize(staticData.serializedATN)
+	atn := staticData.atn
+	staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState))
+	decisionToDFA := staticData.decisionToDFA
+	for index, state := range atn.DecisionToState {
+		decisionToDFA[index] = antlr.NewDFA(state, index)
+	}
+}
+
+// fhirpathLexerInit initializes any static state used to implement fhirpathLexer. By default the
+// static state used to implement the lexer is lazily initialized during the first call to
+// NewfhirpathLexer(). You can call this function if you wish to initialize the static state ahead
+// of time.
+func FhirpathLexerInit() {
+	staticData := &FhirpathLexerLexerStaticData
+	staticData.once.Do(fhirpathlexerLexerInit)
+}
+
+// NewfhirpathLexer produces a new lexer instance for the optional input antlr.CharStream.
+func NewfhirpathLexer(input antlr.CharStream) *fhirpathLexer {
+	FhirpathLexerInit()
+	l := new(fhirpathLexer)
+	l.BaseLexer = antlr.NewBaseLexer(input)
+	staticData := &FhirpathLexerLexerStaticData
+	l.Interpreter = antlr.NewLexerATNSimulator(l, staticData.atn, staticData.decisionToDFA, staticData.PredictionContextCache)
+	l.channelNames = staticData.ChannelNames
+	l.modeNames = staticData.ModeNames
+	l.RuleNames = staticData.RuleNames
+	l.LiteralNames = staticData.LiteralNames
+	l.SymbolicNames = staticData.SymbolicNames
+	l.GrammarFileName = "fhirpath.g4"
+	// TODO: l.EOF = antlr.TokenEOF
+
+	return l
+}
+
+// fhirpathLexer tokens.
+const (
+	fhirpathLexerT__0                = 1
+	fhirpathLexerT__1                = 2
+	fhirpathLexerT__2                = 3
+	fhirpathLexerT__3                = 4
+	fhirpathLexerT__4                = 5
+	fhirpathLexerT__5                = 6
+	fhirpathLexerT__6                = 7
+	fhirpathLexerT__7                = 8
+	fhirpathLexerT__8                = 9
+	fhirpathLexerT__9                = 10
+	fhirpathLexerT__10               = 11
+	fhirpathLexerT__11               = 12
+	fhirpathLexerT__12               = 13
+	fhirpathLexerT__13               = 14
+	fhirpathLexerT__14               = 15
+	fhirpathLexerT__15               = 16
+	fhirpathLexerT__16               = 17
+	fhirpathLexerT__17               = 18
+	fhirpathLexerT__18               = 19
+	fhirpathLexerT__19               = 20
+	fhirpathLexerT__20               = 21
+	fhirpathLexerT__21               = 22
+	fhirpathLexerT__22               = 23
+	fhirpathLexerT__23               = 24
+	fhirpathLexerT__24               = 25
+	fhirpathLexerT__25               = 26
+	fhirpathLexerT__26               = 27
+	fhirpathLexerT__27               = 28
+	fhirpathLexerT__28               = 29
+	fhirpathLexerT__29               = 30
+	fhirpathLexerT__30               = 31
+	fhirpathLexerT__31               = 32
+	fhirpathLexerT__32               = 33
+	fhirpathLexerT__33               = 34
+	fhirpathLexerT__34               = 35
+	fhirpathLexerT__35               = 36
+	fhirpathLexerT__36               = 37
+	fhirpathLexerT__37               = 38
+	fhirpathLexerT__38               = 39
+	fhirpathLexerT__39               = 40
+	fhirpathLexerT__40               = 41
+	fhirpathLexerT__41               = 42
+	fhirpathLexerT__42               = 43
+	fhirpathLexerT__43               = 44
+	fhirpathLexerT__44               = 45
+	fhirpathLexerT__45               = 46
+	fhirpathLexerT__46               = 47
+	fhirpathLexerT__47               = 48
+	fhirpathLexerT__48               = 49
+	fhirpathLexerT__49               = 50
+	fhirpathLexerT__50               = 51
+	fhirpathLexerT__51               = 52
+	fhirpathLexerT__52               = 53
+	fhirpathLexerT__53               = 54
+	fhirpathLexerDATE                = 55
+	fhirpathLexerDATETIME            = 56
+	fhirpathLexerTIME                = 57
+	fhirpathLexerIDENTIFIER          = 58
+	fhirpathLexerDELIMITEDIDENTIFIER = 59
+	fhirpathLexerSTRING              = 60
+	fhirpathLexerNUMBER              = 61
+	fhirpathLexerWS                  = 62
+	fhirpathLexerCOMMENT             = 63
+	fhirpathLexerLINE_COMMENT        = 64
+)
diff --git a/fhirpath/internal/grammar/fhirpath_parser.go b/fhirpath/internal/grammar/fhirpath_parser.go
new file mode 100644
index 0000000..22a2909
--- /dev/null
+++ b/fhirpath/internal/grammar/fhirpath_parser.go
@@ -0,0 +1,4109 @@
+// Code generated from fhirpath.g4 by ANTLR 4.13.0. DO NOT EDIT.
+
+package grammar // fhirpath
+import (
+	"fmt"
+	"strconv"
+	"sync"
+
+	"github.com/antlr4-go/antlr/v4"
+)
+
+// Suppress unused import errors
+var _ = fmt.Printf
+var _ = strconv.Itoa
+var _ = sync.Once{}
+
+type fhirpathParser struct {
+	*antlr.BaseParser
+}
+
+var FhirpathParserStaticData struct {
+	once                   sync.Once
+	serializedATN          []int32
+	LiteralNames           []string
+	SymbolicNames          []string
+	RuleNames              []string
+	PredictionContextCache *antlr.PredictionContextCache
+	atn                    *antlr.ATN
+	decisionToDFA          []*antlr.DFA
+}
+
+func fhirpathParserInit() {
+	staticData := &FhirpathParserStaticData
+	staticData.LiteralNames = []string{
+		"", "'.'", "'['", "']'", "'+'", "'-'", "'*'", "'/'", "'div'", "'mod'",
+		"'&'", "'is'", "'as'", "'|'", "'<='", "'<'", "'>'", "'>='", "'='", "'~'",
+		"'!='", "'!~'", "'in'", "'contains'", "'and'", "'or'", "'xor'", "'implies'",
+		"'('", "')'", "'{'", "'}'", "'true'", "'false'", "'%'", "'$this'", "'$index'",
+		"'$total'", "','", "'year'", "'month'", "'week'", "'day'", "'hour'",
+		"'minute'", "'second'", "'millisecond'", "'years'", "'months'", "'weeks'",
+		"'days'", "'hours'", "'minutes'", "'seconds'", "'milliseconds'",
+	}
+	staticData.SymbolicNames = []string{
+		"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+		"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+		"", "", "", "", "DATE", "DATETIME", "TIME", "IDENTIFIER", "DELIMITEDIDENTIFIER",
+		"STRING", "NUMBER", "WS", "COMMENT", "LINE_COMMENT",
+	}
+	staticData.RuleNames = []string{
+		"prog", "expression", "term", "literal", "externalConstant", "invocation",
+		"function", "paramList", "quantity", "unit", "dateTimePrecision", "pluralDateTimePrecision",
+		"typeSpecifier", "qualifiedIdentifier", "identifier",
+	}
+	staticData.PredictionContextCache = antlr.NewPredictionContextCache()
+	staticData.serializedATN = []int32{
+		4, 1, 64, 155, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7,
+		4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7,
+		10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 1, 0, 1, 0,
+		1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 38, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 78, 8, 1,
+		10, 1, 12, 1, 81, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2,
+		90, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 101,
+		8, 3, 1, 4, 1, 4, 1, 4, 3, 4, 106, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5,
+		3, 5, 113, 8, 5, 1, 6, 1, 6, 1, 6, 3, 6, 118, 8, 6, 1, 6, 1, 6, 1, 7, 1,
+		7, 1, 7, 5, 7, 125, 8, 7, 10, 7, 12, 7, 128, 9, 7, 1, 8, 1, 8, 3, 8, 132,
+		8, 8, 1, 9, 1, 9, 1, 9, 3, 9, 137, 8, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1,
+		12, 1, 12, 1, 13, 1, 13, 1, 13, 5, 13, 148, 8, 13, 10, 13, 12, 13, 151,
+		9, 13, 1, 14, 1, 14, 1, 14, 0, 1, 2, 15, 0, 2, 4, 6, 8, 10, 12, 14, 16,
+		18, 20, 22, 24, 26, 28, 0, 12, 1, 0, 4, 5, 1, 0, 6, 9, 2, 0, 4, 5, 10,
+		10, 1, 0, 14, 17, 1, 0, 18, 21, 1, 0, 22, 23, 1, 0, 25, 26, 1, 0, 11, 12,
+		1, 0, 32, 33, 1, 0, 39, 46, 1, 0, 47, 54, 3, 0, 11, 12, 22, 23, 58, 59,
+		173, 0, 30, 1, 0, 0, 0, 2, 37, 1, 0, 0, 0, 4, 89, 1, 0, 0, 0, 6, 100, 1,
+		0, 0, 0, 8, 102, 1, 0, 0, 0, 10, 112, 1, 0, 0, 0, 12, 114, 1, 0, 0, 0,
+		14, 121, 1, 0, 0, 0, 16, 129, 1, 0, 0, 0, 18, 136, 1, 0, 0, 0, 20, 138,
+		1, 0, 0, 0, 22, 140, 1, 0, 0, 0, 24, 142, 1, 0, 0, 0, 26, 144, 1, 0, 0,
+		0, 28, 152, 1, 0, 0, 0, 30, 31, 3, 2, 1, 0, 31, 32, 5, 0, 0, 1, 32, 1,
+		1, 0, 0, 0, 33, 34, 6, 1, -1, 0, 34, 38, 3, 4, 2, 0, 35, 36, 7, 0, 0, 0,
+		36, 38, 3, 2, 1, 11, 37, 33, 1, 0, 0, 0, 37, 35, 1, 0, 0, 0, 38, 79, 1,
+		0, 0, 0, 39, 40, 10, 10, 0, 0, 40, 41, 7, 1, 0, 0, 41, 78, 3, 2, 1, 11,
+		42, 43, 10, 9, 0, 0, 43, 44, 7, 2, 0, 0, 44, 78, 3, 2, 1, 10, 45, 46, 10,
+		7, 0, 0, 46, 47, 5, 13, 0, 0, 47, 78, 3, 2, 1, 8, 48, 49, 10, 6, 0, 0,
+		49, 50, 7, 3, 0, 0, 50, 78, 3, 2, 1, 7, 51, 52, 10, 5, 0, 0, 52, 53, 7,
+		4, 0, 0, 53, 78, 3, 2, 1, 6, 54, 55, 10, 4, 0, 0, 55, 56, 7, 5, 0, 0, 56,
+		78, 3, 2, 1, 5, 57, 58, 10, 3, 0, 0, 58, 59, 5, 24, 0, 0, 59, 78, 3, 2,
+		1, 4, 60, 61, 10, 2, 0, 0, 61, 62, 7, 6, 0, 0, 62, 78, 3, 2, 1, 3, 63,
+		64, 10, 1, 0, 0, 64, 65, 5, 27, 0, 0, 65, 78, 3, 2, 1, 2, 66, 67, 10, 13,
+		0, 0, 67, 68, 5, 1, 0, 0, 68, 78, 3, 10, 5, 0, 69, 70, 10, 12, 0, 0, 70,
+		71, 5, 2, 0, 0, 71, 72, 3, 2, 1, 0, 72, 73, 5, 3, 0, 0, 73, 78, 1, 0, 0,
+		0, 74, 75, 10, 8, 0, 0, 75, 76, 7, 7, 0, 0, 76, 78, 3, 24, 12, 0, 77, 39,
+		1, 0, 0, 0, 77, 42, 1, 0, 0, 0, 77, 45, 1, 0, 0, 0, 77, 48, 1, 0, 0, 0,
+		77, 51, 1, 0, 0, 0, 77, 54, 1, 0, 0, 0, 77, 57, 1, 0, 0, 0, 77, 60, 1,
+		0, 0, 0, 77, 63, 1, 0, 0, 0, 77, 66, 1, 0, 0, 0, 77, 69, 1, 0, 0, 0, 77,
+		74, 1, 0, 0, 0, 78, 81, 1, 0, 0, 0, 79, 77, 1, 0, 0, 0, 79, 80, 1, 0, 0,
+		0, 80, 3, 1, 0, 0, 0, 81, 79, 1, 0, 0, 0, 82, 90, 3, 10, 5, 0, 83, 90,
+		3, 6, 3, 0, 84, 90, 3, 8, 4, 0, 85, 86, 5, 28, 0, 0, 86, 87, 3, 2, 1, 0,
+		87, 88, 5, 29, 0, 0, 88, 90, 1, 0, 0, 0, 89, 82, 1, 0, 0, 0, 89, 83, 1,
+		0, 0, 0, 89, 84, 1, 0, 0, 0, 89, 85, 1, 0, 0, 0, 90, 5, 1, 0, 0, 0, 91,
+		92, 5, 30, 0, 0, 92, 101, 5, 31, 0, 0, 93, 101, 7, 8, 0, 0, 94, 101, 5,
+		60, 0, 0, 95, 101, 5, 61, 0, 0, 96, 101, 5, 55, 0, 0, 97, 101, 5, 56, 0,
+		0, 98, 101, 5, 57, 0, 0, 99, 101, 3, 16, 8, 0, 100, 91, 1, 0, 0, 0, 100,
+		93, 1, 0, 0, 0, 100, 94, 1, 0, 0, 0, 100, 95, 1, 0, 0, 0, 100, 96, 1, 0,
+		0, 0, 100, 97, 1, 0, 0, 0, 100, 98, 1, 0, 0, 0, 100, 99, 1, 0, 0, 0, 101,
+		7, 1, 0, 0, 0, 102, 105, 5, 34, 0, 0, 103, 106, 3, 28, 14, 0, 104, 106,
+		5, 60, 0, 0, 105, 103, 1, 0, 0, 0, 105, 104, 1, 0, 0, 0, 106, 9, 1, 0,
+		0, 0, 107, 113, 3, 28, 14, 0, 108, 113, 3, 12, 6, 0, 109, 113, 5, 35, 0,
+		0, 110, 113, 5, 36, 0, 0, 111, 113, 5, 37, 0, 0, 112, 107, 1, 0, 0, 0,
+		112, 108, 1, 0, 0, 0, 112, 109, 1, 0, 0, 0, 112, 110, 1, 0, 0, 0, 112,
+		111, 1, 0, 0, 0, 113, 11, 1, 0, 0, 0, 114, 115, 3, 28, 14, 0, 115, 117,
+		5, 28, 0, 0, 116, 118, 3, 14, 7, 0, 117, 116, 1, 0, 0, 0, 117, 118, 1,
+		0, 0, 0, 118, 119, 1, 0, 0, 0, 119, 120, 5, 29, 0, 0, 120, 13, 1, 0, 0,
+		0, 121, 126, 3, 2, 1, 0, 122, 123, 5, 38, 0, 0, 123, 125, 3, 2, 1, 0, 124,
+		122, 1, 0, 0, 0, 125, 128, 1, 0, 0, 0, 126, 124, 1, 0, 0, 0, 126, 127,
+		1, 0, 0, 0, 127, 15, 1, 0, 0, 0, 128, 126, 1, 0, 0, 0, 129, 131, 5, 61,
+		0, 0, 130, 132, 3, 18, 9, 0, 131, 130, 1, 0, 0, 0, 131, 132, 1, 0, 0, 0,
+		132, 17, 1, 0, 0, 0, 133, 137, 3, 20, 10, 0, 134, 137, 3, 22, 11, 0, 135,
+		137, 5, 60, 0, 0, 136, 133, 1, 0, 0, 0, 136, 134, 1, 0, 0, 0, 136, 135,
+		1, 0, 0, 0, 137, 19, 1, 0, 0, 0, 138, 139, 7, 9, 0, 0, 139, 21, 1, 0, 0,
+		0, 140, 141, 7, 10, 0, 0, 141, 23, 1, 0, 0, 0, 142, 143, 3, 26, 13, 0,
+		143, 25, 1, 0, 0, 0, 144, 149, 3, 28, 14, 0, 145, 146, 5, 1, 0, 0, 146,
+		148, 3, 28, 14, 0, 147, 145, 1, 0, 0, 0, 148, 151, 1, 0, 0, 0, 149, 147,
+		1, 0, 0, 0, 149, 150, 1, 0, 0, 0, 150, 27, 1, 0, 0, 0, 151, 149, 1, 0,
+		0, 0, 152, 153, 7, 11, 0, 0, 153, 29, 1, 0, 0, 0, 12, 37, 77, 79, 89, 100,
+		105, 112, 117, 126, 131, 136, 149,
+	}
+	deserializer := antlr.NewATNDeserializer(nil)
+	staticData.atn = deserializer.Deserialize(staticData.serializedATN)
+	atn := staticData.atn
+	staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState))
+	decisionToDFA := staticData.decisionToDFA
+	for index, state := range atn.DecisionToState {
+		decisionToDFA[index] = antlr.NewDFA(state, index)
+	}
+}
+
+// fhirpathParserInit initializes any static state used to implement fhirpathParser. By default the
+// static state used to implement the parser is lazily initialized during the first call to
+// NewfhirpathParser(). You can call this function if you wish to initialize the static state ahead
+// of time.
+func FhirpathParserInit() {
+	staticData := &FhirpathParserStaticData
+	staticData.once.Do(fhirpathParserInit)
+}
+
+// NewfhirpathParser produces a new parser instance for the optional input antlr.TokenStream.
+func NewfhirpathParser(input antlr.TokenStream) *fhirpathParser {
+	FhirpathParserInit()
+	this := new(fhirpathParser)
+	this.BaseParser = antlr.NewBaseParser(input)
+	staticData := &FhirpathParserStaticData
+	this.Interpreter = antlr.NewParserATNSimulator(this, staticData.atn, staticData.decisionToDFA, staticData.PredictionContextCache)
+	this.RuleNames = staticData.RuleNames
+	this.LiteralNames = staticData.LiteralNames
+	this.SymbolicNames = staticData.SymbolicNames
+	this.GrammarFileName = "fhirpath.g4"
+
+	return this
+}
+
+// fhirpathParser tokens.
+const (
+	fhirpathParserEOF                 = antlr.TokenEOF
+	fhirpathParserT__0                = 1
+	fhirpathParserT__1                = 2
+	fhirpathParserT__2                = 3
+	fhirpathParserT__3                = 4
+	fhirpathParserT__4                = 5
+	fhirpathParserT__5                = 6
+	fhirpathParserT__6                = 7
+	fhirpathParserT__7                = 8
+	fhirpathParserT__8                = 9
+	fhirpathParserT__9                = 10
+	fhirpathParserT__10               = 11
+	fhirpathParserT__11               = 12
+	fhirpathParserT__12               = 13
+	fhirpathParserT__13               = 14
+	fhirpathParserT__14               = 15
+	fhirpathParserT__15               = 16
+	fhirpathParserT__16               = 17
+	fhirpathParserT__17               = 18
+	fhirpathParserT__18               = 19
+	fhirpathParserT__19               = 20
+	fhirpathParserT__20               = 21
+	fhirpathParserT__21               = 22
+	fhirpathParserT__22               = 23
+	fhirpathParserT__23               = 24
+	fhirpathParserT__24               = 25
+	fhirpathParserT__25               = 26
+	fhirpathParserT__26               = 27
+	fhirpathParserT__27               = 28
+	fhirpathParserT__28               = 29
+	fhirpathParserT__29               = 30
+	fhirpathParserT__30               = 31
+	fhirpathParserT__31               = 32
+	fhirpathParserT__32               = 33
+	fhirpathParserT__33               = 34
+	fhirpathParserT__34               = 35
+	fhirpathParserT__35               = 36
+	fhirpathParserT__36               = 37
+	fhirpathParserT__37               = 38
+	fhirpathParserT__38               = 39
+	fhirpathParserT__39               = 40
+	fhirpathParserT__40               = 41
+	fhirpathParserT__41               = 42
+	fhirpathParserT__42               = 43
+	fhirpathParserT__43               = 44
+	fhirpathParserT__44               = 45
+	fhirpathParserT__45               = 46
+	fhirpathParserT__46               = 47
+	fhirpathParserT__47               = 48
+	fhirpathParserT__48               = 49
+	fhirpathParserT__49               = 50
+	fhirpathParserT__50               = 51
+	fhirpathParserT__51               = 52
+	fhirpathParserT__52               = 53
+	fhirpathParserT__53               = 54
+	fhirpathParserDATE                = 55
+	fhirpathParserDATETIME            = 56
+	fhirpathParserTIME                = 57
+	fhirpathParserIDENTIFIER          = 58
+	fhirpathParserDELIMITEDIDENTIFIER = 59
+	fhirpathParserSTRING              = 60
+	fhirpathParserNUMBER              = 61
+	fhirpathParserWS                  = 62
+	fhirpathParserCOMMENT             = 63
+	fhirpathParserLINE_COMMENT        = 64
+)
+
+// fhirpathParser rules.
+const (
+	fhirpathParserRULE_prog                    = 0
+	fhirpathParserRULE_expression              = 1
+	fhirpathParserRULE_term                    = 2
+	fhirpathParserRULE_literal                 = 3
+	fhirpathParserRULE_externalConstant        = 4
+	fhirpathParserRULE_invocation              = 5
+	fhirpathParserRULE_function                = 6
+	fhirpathParserRULE_paramList               = 7
+	fhirpathParserRULE_quantity                = 8
+	fhirpathParserRULE_unit                    = 9
+	fhirpathParserRULE_dateTimePrecision       = 10
+	fhirpathParserRULE_pluralDateTimePrecision = 11
+	fhirpathParserRULE_typeSpecifier           = 12
+	fhirpathParserRULE_qualifiedIdentifier     = 13
+	fhirpathParserRULE_identifier              = 14
+)
+
+// IProgContext is an interface to support dynamic dispatch.
+type IProgContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	Expression() IExpressionContext
+	EOF() antlr.TerminalNode
+
+	// IsProgContext differentiates from other interfaces.
+	IsProgContext()
+}
+
+type ProgContext struct {
+	antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyProgContext() *ProgContext {
+	var p = new(ProgContext)
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_prog
+	return p
+}
+
+func InitEmptyProgContext(p *ProgContext) {
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_prog
+}
+
+func (*ProgContext) IsProgContext() {}
+
+func NewProgContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ProgContext {
+	var p = new(ProgContext)
+
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = fhirpathParserRULE_prog
+
+	return p
+}
+
+func (s *ProgContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *ProgContext) Expression() IExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *ProgContext) EOF() antlr.TerminalNode {
+	return s.GetToken(fhirpathParserEOF, 0)
+}
+
+func (s *ProgContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *ProgContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *ProgContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitProg(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+func (p *fhirpathParser) Prog() (localctx IProgContext) {
+	localctx = NewProgContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 0, fhirpathParserRULE_prog)
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(30)
+		p.expression(0)
+	}
+	{
+		p.SetState(31)
+		p.Match(fhirpathParserEOF)
+		if p.HasError() {
+			// Recognition error - abort rule
+			goto errorExit
+		}
+	}
+
+errorExit:
+	if p.HasError() {
+		v := p.GetError()
+		localctx.SetException(v)
+		p.GetErrorHandler().ReportError(p, v)
+		p.GetErrorHandler().Recover(p, v)
+		p.SetError(nil)
+	}
+	p.ExitRule()
+	return localctx
+	goto errorExit // Trick to prevent compiler error if the label is not used
+}
+
+// IExpressionContext is an interface to support dynamic dispatch.
+type IExpressionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+	// IsExpressionContext differentiates from other interfaces.
+	IsExpressionContext()
+}
+
+type ExpressionContext struct {
+	antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyExpressionContext() *ExpressionContext {
+	var p = new(ExpressionContext)
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_expression
+	return p
+}
+
+func InitEmptyExpressionContext(p *ExpressionContext) {
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_expression
+}
+
+func (*ExpressionContext) IsExpressionContext() {}
+
+func NewExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ExpressionContext {
+	var p = new(ExpressionContext)
+
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = fhirpathParserRULE_expression
+
+	return p
+}
+
+func (s *ExpressionContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *ExpressionContext) CopyAll(ctx *ExpressionContext) {
+	s.CopyFrom(&ctx.BaseParserRuleContext)
+}
+
+func (s *ExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *ExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+type IndexerExpressionContext struct {
+	ExpressionContext
+}
+
+func NewIndexerExpressionContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *IndexerExpressionContext {
+	var p = new(IndexerExpressionContext)
+
+	InitEmptyExpressionContext(&p.ExpressionContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*ExpressionContext))
+
+	return p
+}
+
+func (s *IndexerExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *IndexerExpressionContext) AllExpression() []IExpressionContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IExpressionContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IExpressionContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IExpressionContext); ok {
+			tst[i] = t.(IExpressionContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *IndexerExpressionContext) Expression(i int) IExpressionContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *IndexerExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitIndexerExpression(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type PolarityExpressionContext struct {
+	ExpressionContext
+}
+
+func NewPolarityExpressionContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *PolarityExpressionContext {
+	var p = new(PolarityExpressionContext)
+
+	InitEmptyExpressionContext(&p.ExpressionContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*ExpressionContext))
+
+	return p
+}
+
+func (s *PolarityExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *PolarityExpressionContext) Expression() IExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *PolarityExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitPolarityExpression(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type AdditiveExpressionContext struct {
+	ExpressionContext
+}
+
+func NewAdditiveExpressionContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *AdditiveExpressionContext {
+	var p = new(AdditiveExpressionContext)
+
+	InitEmptyExpressionContext(&p.ExpressionContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*ExpressionContext))
+
+	return p
+}
+
+func (s *AdditiveExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *AdditiveExpressionContext) AllExpression() []IExpressionContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IExpressionContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IExpressionContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IExpressionContext); ok {
+			tst[i] = t.(IExpressionContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *AdditiveExpressionContext) Expression(i int) IExpressionContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *AdditiveExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitAdditiveExpression(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type MultiplicativeExpressionContext struct {
+	ExpressionContext
+}
+
+func NewMultiplicativeExpressionContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *MultiplicativeExpressionContext {
+	var p = new(MultiplicativeExpressionContext)
+
+	InitEmptyExpressionContext(&p.ExpressionContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*ExpressionContext))
+
+	return p
+}
+
+func (s *MultiplicativeExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *MultiplicativeExpressionContext) AllExpression() []IExpressionContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IExpressionContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IExpressionContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IExpressionContext); ok {
+			tst[i] = t.(IExpressionContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *MultiplicativeExpressionContext) Expression(i int) IExpressionContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *MultiplicativeExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitMultiplicativeExpression(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type UnionExpressionContext struct {
+	ExpressionContext
+}
+
+func NewUnionExpressionContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *UnionExpressionContext {
+	var p = new(UnionExpressionContext)
+
+	InitEmptyExpressionContext(&p.ExpressionContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*ExpressionContext))
+
+	return p
+}
+
+func (s *UnionExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *UnionExpressionContext) AllExpression() []IExpressionContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IExpressionContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IExpressionContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IExpressionContext); ok {
+			tst[i] = t.(IExpressionContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *UnionExpressionContext) Expression(i int) IExpressionContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *UnionExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitUnionExpression(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type OrExpressionContext struct {
+	ExpressionContext
+}
+
+func NewOrExpressionContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *OrExpressionContext {
+	var p = new(OrExpressionContext)
+
+	InitEmptyExpressionContext(&p.ExpressionContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*ExpressionContext))
+
+	return p
+}
+
+func (s *OrExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *OrExpressionContext) AllExpression() []IExpressionContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IExpressionContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IExpressionContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IExpressionContext); ok {
+			tst[i] = t.(IExpressionContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *OrExpressionContext) Expression(i int) IExpressionContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *OrExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitOrExpression(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type AndExpressionContext struct {
+	ExpressionContext
+}
+
+func NewAndExpressionContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *AndExpressionContext {
+	var p = new(AndExpressionContext)
+
+	InitEmptyExpressionContext(&p.ExpressionContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*ExpressionContext))
+
+	return p
+}
+
+func (s *AndExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *AndExpressionContext) AllExpression() []IExpressionContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IExpressionContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IExpressionContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IExpressionContext); ok {
+			tst[i] = t.(IExpressionContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *AndExpressionContext) Expression(i int) IExpressionContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *AndExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitAndExpression(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type MembershipExpressionContext struct {
+	ExpressionContext
+}
+
+func NewMembershipExpressionContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *MembershipExpressionContext {
+	var p = new(MembershipExpressionContext)
+
+	InitEmptyExpressionContext(&p.ExpressionContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*ExpressionContext))
+
+	return p
+}
+
+func (s *MembershipExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *MembershipExpressionContext) AllExpression() []IExpressionContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IExpressionContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IExpressionContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IExpressionContext); ok {
+			tst[i] = t.(IExpressionContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *MembershipExpressionContext) Expression(i int) IExpressionContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *MembershipExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitMembershipExpression(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type InequalityExpressionContext struct {
+	ExpressionContext
+}
+
+func NewInequalityExpressionContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *InequalityExpressionContext {
+	var p = new(InequalityExpressionContext)
+
+	InitEmptyExpressionContext(&p.ExpressionContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*ExpressionContext))
+
+	return p
+}
+
+func (s *InequalityExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *InequalityExpressionContext) AllExpression() []IExpressionContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IExpressionContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IExpressionContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IExpressionContext); ok {
+			tst[i] = t.(IExpressionContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *InequalityExpressionContext) Expression(i int) IExpressionContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *InequalityExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitInequalityExpression(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type InvocationExpressionContext struct {
+	ExpressionContext
+}
+
+func NewInvocationExpressionContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *InvocationExpressionContext {
+	var p = new(InvocationExpressionContext)
+
+	InitEmptyExpressionContext(&p.ExpressionContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*ExpressionContext))
+
+	return p
+}
+
+func (s *InvocationExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *InvocationExpressionContext) Expression() IExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *InvocationExpressionContext) Invocation() IInvocationContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IInvocationContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IInvocationContext)
+}
+
+func (s *InvocationExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitInvocationExpression(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type EqualityExpressionContext struct {
+	ExpressionContext
+}
+
+func NewEqualityExpressionContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *EqualityExpressionContext {
+	var p = new(EqualityExpressionContext)
+
+	InitEmptyExpressionContext(&p.ExpressionContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*ExpressionContext))
+
+	return p
+}
+
+func (s *EqualityExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *EqualityExpressionContext) AllExpression() []IExpressionContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IExpressionContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IExpressionContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IExpressionContext); ok {
+			tst[i] = t.(IExpressionContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *EqualityExpressionContext) Expression(i int) IExpressionContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *EqualityExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitEqualityExpression(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type ImpliesExpressionContext struct {
+	ExpressionContext
+}
+
+func NewImpliesExpressionContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *ImpliesExpressionContext {
+	var p = new(ImpliesExpressionContext)
+
+	InitEmptyExpressionContext(&p.ExpressionContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*ExpressionContext))
+
+	return p
+}
+
+func (s *ImpliesExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *ImpliesExpressionContext) AllExpression() []IExpressionContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IExpressionContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IExpressionContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IExpressionContext); ok {
+			tst[i] = t.(IExpressionContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *ImpliesExpressionContext) Expression(i int) IExpressionContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *ImpliesExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitImpliesExpression(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type TermExpressionContext struct {
+	ExpressionContext
+}
+
+func NewTermExpressionContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *TermExpressionContext {
+	var p = new(TermExpressionContext)
+
+	InitEmptyExpressionContext(&p.ExpressionContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*ExpressionContext))
+
+	return p
+}
+
+func (s *TermExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *TermExpressionContext) Term() ITermContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(ITermContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(ITermContext)
+}
+
+func (s *TermExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitTermExpression(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type TypeExpressionContext struct {
+	ExpressionContext
+}
+
+func NewTypeExpressionContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *TypeExpressionContext {
+	var p = new(TypeExpressionContext)
+
+	InitEmptyExpressionContext(&p.ExpressionContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*ExpressionContext))
+
+	return p
+}
+
+func (s *TypeExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *TypeExpressionContext) Expression() IExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *TypeExpressionContext) TypeSpecifier() ITypeSpecifierContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(ITypeSpecifierContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(ITypeSpecifierContext)
+}
+
+func (s *TypeExpressionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitTypeExpression(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+func (p *fhirpathParser) Expression() (localctx IExpressionContext) {
+	return p.expression(0)
+}
+
+func (p *fhirpathParser) expression(_p int) (localctx IExpressionContext) {
+	var _parentctx antlr.ParserRuleContext = p.GetParserRuleContext()
+
+	_parentState := p.GetState()
+	localctx = NewExpressionContext(p, p.GetParserRuleContext(), _parentState)
+	var _prevctx IExpressionContext = localctx
+	var _ antlr.ParserRuleContext = _prevctx // TODO: To prevent unused variable warning.
+	_startState := 2
+	p.EnterRecursionRule(localctx, 2, fhirpathParserRULE_expression, _p)
+	var _la int
+
+	var _alt int
+
+	p.EnterOuterAlt(localctx, 1)
+	p.SetState(37)
+	p.GetErrorHandler().Sync(p)
+	if p.HasError() {
+		goto errorExit
+	}
+
+	switch p.GetTokenStream().LA(1) {
+	case fhirpathParserT__10, fhirpathParserT__11, fhirpathParserT__21, fhirpathParserT__22, fhirpathParserT__27, fhirpathParserT__29, fhirpathParserT__31, fhirpathParserT__32, fhirpathParserT__33, fhirpathParserT__34, fhirpathParserT__35, fhirpathParserT__36, fhirpathParserDATE, fhirpathParserDATETIME, fhirpathParserTIME, fhirpathParserIDENTIFIER, fhirpathParserDELIMITEDIDENTIFIER, fhirpathParserSTRING, fhirpathParserNUMBER:
+		localctx = NewTermExpressionContext(p, localctx)
+		p.SetParserRuleContext(localctx)
+		_prevctx = localctx
+
+		{
+			p.SetState(34)
+			p.Term()
+		}
+
+	case fhirpathParserT__3, fhirpathParserT__4:
+		localctx = NewPolarityExpressionContext(p, localctx)
+		p.SetParserRuleContext(localctx)
+		_prevctx = localctx
+		{
+			p.SetState(35)
+			_la = p.GetTokenStream().LA(1)
+
+			if !(_la == fhirpathParserT__3 || _la == fhirpathParserT__4) {
+				p.GetErrorHandler().RecoverInline(p)
+			} else {
+				p.GetErrorHandler().ReportMatch(p)
+				p.Consume()
+			}
+		}
+		{
+			p.SetState(36)
+			p.expression(11)
+		}
+
+	default:
+		p.SetError(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil))
+		goto errorExit
+	}
+	p.GetParserRuleContext().SetStop(p.GetTokenStream().LT(-1))
+	p.SetState(79)
+	p.GetErrorHandler().Sync(p)
+	if p.HasError() {
+		goto errorExit
+	}
+	_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 2, p.GetParserRuleContext())
+	if p.HasError() {
+		goto errorExit
+	}
+	for _alt != 2 && _alt != antlr.ATNInvalidAltNumber {
+		if _alt == 1 {
+			if p.GetParseListeners() != nil {
+				p.TriggerExitRuleEvent()
+			}
+			_prevctx = localctx
+			p.SetState(77)
+			p.GetErrorHandler().Sync(p)
+			if p.HasError() {
+				goto errorExit
+			}
+
+			switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 1, p.GetParserRuleContext()) {
+			case 1:
+				localctx = NewMultiplicativeExpressionContext(p, NewExpressionContext(p, _parentctx, _parentState))
+				p.PushNewRecursionContext(localctx, _startState, fhirpathParserRULE_expression)
+				p.SetState(39)
+
+				if !(p.Precpred(p.GetParserRuleContext(), 10)) {
+					p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 10)", ""))
+					goto errorExit
+				}
+				{
+					p.SetState(40)
+					_la = p.GetTokenStream().LA(1)
+
+					if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&960) != 0) {
+						p.GetErrorHandler().RecoverInline(p)
+					} else {
+						p.GetErrorHandler().ReportMatch(p)
+						p.Consume()
+					}
+				}
+				{
+					p.SetState(41)
+					p.expression(11)
+				}
+
+			case 2:
+				localctx = NewAdditiveExpressionContext(p, NewExpressionContext(p, _parentctx, _parentState))
+				p.PushNewRecursionContext(localctx, _startState, fhirpathParserRULE_expression)
+				p.SetState(42)
+
+				if !(p.Precpred(p.GetParserRuleContext(), 9)) {
+					p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 9)", ""))
+					goto errorExit
+				}
+				{
+					p.SetState(43)
+					_la = p.GetTokenStream().LA(1)
+
+					if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&1072) != 0) {
+						p.GetErrorHandler().RecoverInline(p)
+					} else {
+						p.GetErrorHandler().ReportMatch(p)
+						p.Consume()
+					}
+				}
+				{
+					p.SetState(44)
+					p.expression(10)
+				}
+
+			case 3:
+				localctx = NewUnionExpressionContext(p, NewExpressionContext(p, _parentctx, _parentState))
+				p.PushNewRecursionContext(localctx, _startState, fhirpathParserRULE_expression)
+				p.SetState(45)
+
+				if !(p.Precpred(p.GetParserRuleContext(), 7)) {
+					p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 7)", ""))
+					goto errorExit
+				}
+				{
+					p.SetState(46)
+					p.Match(fhirpathParserT__12)
+					if p.HasError() {
+						// Recognition error - abort rule
+						goto errorExit
+					}
+				}
+				{
+					p.SetState(47)
+					p.expression(8)
+				}
+
+			case 4:
+				localctx = NewInequalityExpressionContext(p, NewExpressionContext(p, _parentctx, _parentState))
+				p.PushNewRecursionContext(localctx, _startState, fhirpathParserRULE_expression)
+				p.SetState(48)
+
+				if !(p.Precpred(p.GetParserRuleContext(), 6)) {
+					p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 6)", ""))
+					goto errorExit
+				}
+				{
+					p.SetState(49)
+					_la = p.GetTokenStream().LA(1)
+
+					if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&245760) != 0) {
+						p.GetErrorHandler().RecoverInline(p)
+					} else {
+						p.GetErrorHandler().ReportMatch(p)
+						p.Consume()
+					}
+				}
+				{
+					p.SetState(50)
+					p.expression(7)
+				}
+
+			case 5:
+				localctx = NewEqualityExpressionContext(p, NewExpressionContext(p, _parentctx, _parentState))
+				p.PushNewRecursionContext(localctx, _startState, fhirpathParserRULE_expression)
+				p.SetState(51)
+
+				if !(p.Precpred(p.GetParserRuleContext(), 5)) {
+					p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 5)", ""))
+					goto errorExit
+				}
+				{
+					p.SetState(52)
+					_la = p.GetTokenStream().LA(1)
+
+					if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&3932160) != 0) {
+						p.GetErrorHandler().RecoverInline(p)
+					} else {
+						p.GetErrorHandler().ReportMatch(p)
+						p.Consume()
+					}
+				}
+				{
+					p.SetState(53)
+					p.expression(6)
+				}
+
+			case 6:
+				localctx = NewMembershipExpressionContext(p, NewExpressionContext(p, _parentctx, _parentState))
+				p.PushNewRecursionContext(localctx, _startState, fhirpathParserRULE_expression)
+				p.SetState(54)
+
+				if !(p.Precpred(p.GetParserRuleContext(), 4)) {
+					p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 4)", ""))
+					goto errorExit
+				}
+				{
+					p.SetState(55)
+					_la = p.GetTokenStream().LA(1)
+
+					if !(_la == fhirpathParserT__21 || _la == fhirpathParserT__22) {
+						p.GetErrorHandler().RecoverInline(p)
+					} else {
+						p.GetErrorHandler().ReportMatch(p)
+						p.Consume()
+					}
+				}
+				{
+					p.SetState(56)
+					p.expression(5)
+				}
+
+			case 7:
+				localctx = NewAndExpressionContext(p, NewExpressionContext(p, _parentctx, _parentState))
+				p.PushNewRecursionContext(localctx, _startState, fhirpathParserRULE_expression)
+				p.SetState(57)
+
+				if !(p.Precpred(p.GetParserRuleContext(), 3)) {
+					p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 3)", ""))
+					goto errorExit
+				}
+				{
+					p.SetState(58)
+					p.Match(fhirpathParserT__23)
+					if p.HasError() {
+						// Recognition error - abort rule
+						goto errorExit
+					}
+				}
+				{
+					p.SetState(59)
+					p.expression(4)
+				}
+
+			case 8:
+				localctx = NewOrExpressionContext(p, NewExpressionContext(p, _parentctx, _parentState))
+				p.PushNewRecursionContext(localctx, _startState, fhirpathParserRULE_expression)
+				p.SetState(60)
+
+				if !(p.Precpred(p.GetParserRuleContext(), 2)) {
+					p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 2)", ""))
+					goto errorExit
+				}
+				{
+					p.SetState(61)
+					_la = p.GetTokenStream().LA(1)
+
+					if !(_la == fhirpathParserT__24 || _la == fhirpathParserT__25) {
+						p.GetErrorHandler().RecoverInline(p)
+					} else {
+						p.GetErrorHandler().ReportMatch(p)
+						p.Consume()
+					}
+				}
+				{
+					p.SetState(62)
+					p.expression(3)
+				}
+
+			case 9:
+				localctx = NewImpliesExpressionContext(p, NewExpressionContext(p, _parentctx, _parentState))
+				p.PushNewRecursionContext(localctx, _startState, fhirpathParserRULE_expression)
+				p.SetState(63)
+
+				if !(p.Precpred(p.GetParserRuleContext(), 1)) {
+					p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 1)", ""))
+					goto errorExit
+				}
+				{
+					p.SetState(64)
+					p.Match(fhirpathParserT__26)
+					if p.HasError() {
+						// Recognition error - abort rule
+						goto errorExit
+					}
+				}
+				{
+					p.SetState(65)
+					p.expression(2)
+				}
+
+			case 10:
+				localctx = NewInvocationExpressionContext(p, NewExpressionContext(p, _parentctx, _parentState))
+				p.PushNewRecursionContext(localctx, _startState, fhirpathParserRULE_expression)
+				p.SetState(66)
+
+				if !(p.Precpred(p.GetParserRuleContext(), 13)) {
+					p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 13)", ""))
+					goto errorExit
+				}
+				{
+					p.SetState(67)
+					p.Match(fhirpathParserT__0)
+					if p.HasError() {
+						// Recognition error - abort rule
+						goto errorExit
+					}
+				}
+				{
+					p.SetState(68)
+					p.Invocation()
+				}
+
+			case 11:
+				localctx = NewIndexerExpressionContext(p, NewExpressionContext(p, _parentctx, _parentState))
+				p.PushNewRecursionContext(localctx, _startState, fhirpathParserRULE_expression)
+				p.SetState(69)
+
+				if !(p.Precpred(p.GetParserRuleContext(), 12)) {
+					p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 12)", ""))
+					goto errorExit
+				}
+				{
+					p.SetState(70)
+					p.Match(fhirpathParserT__1)
+					if p.HasError() {
+						// Recognition error - abort rule
+						goto errorExit
+					}
+				}
+				{
+					p.SetState(71)
+					p.expression(0)
+				}
+				{
+					p.SetState(72)
+					p.Match(fhirpathParserT__2)
+					if p.HasError() {
+						// Recognition error - abort rule
+						goto errorExit
+					}
+				}
+
+			case 12:
+				localctx = NewTypeExpressionContext(p, NewExpressionContext(p, _parentctx, _parentState))
+				p.PushNewRecursionContext(localctx, _startState, fhirpathParserRULE_expression)
+				p.SetState(74)
+
+				if !(p.Precpred(p.GetParserRuleContext(), 8)) {
+					p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 8)", ""))
+					goto errorExit
+				}
+				{
+					p.SetState(75)
+					_la = p.GetTokenStream().LA(1)
+
+					if !(_la == fhirpathParserT__10 || _la == fhirpathParserT__11) {
+						p.GetErrorHandler().RecoverInline(p)
+					} else {
+						p.GetErrorHandler().ReportMatch(p)
+						p.Consume()
+					}
+				}
+				{
+					p.SetState(76)
+					p.TypeSpecifier()
+				}
+
+			case antlr.ATNInvalidAltNumber:
+				goto errorExit
+			}
+
+		}
+		p.SetState(81)
+		p.GetErrorHandler().Sync(p)
+		if p.HasError() {
+			goto errorExit
+		}
+		_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 2, p.GetParserRuleContext())
+		if p.HasError() {
+			goto errorExit
+		}
+	}
+
+errorExit:
+	if p.HasError() {
+		v := p.GetError()
+		localctx.SetException(v)
+		p.GetErrorHandler().ReportError(p, v)
+		p.GetErrorHandler().Recover(p, v)
+		p.SetError(nil)
+	}
+	p.UnrollRecursionContexts(_parentctx)
+	return localctx
+	goto errorExit // Trick to prevent compiler error if the label is not used
+}
+
+// ITermContext is an interface to support dynamic dispatch.
+type ITermContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+	// IsTermContext differentiates from other interfaces.
+	IsTermContext()
+}
+
+type TermContext struct {
+	antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyTermContext() *TermContext {
+	var p = new(TermContext)
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_term
+	return p
+}
+
+func InitEmptyTermContext(p *TermContext) {
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_term
+}
+
+func (*TermContext) IsTermContext() {}
+
+func NewTermContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TermContext {
+	var p = new(TermContext)
+
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = fhirpathParserRULE_term
+
+	return p
+}
+
+func (s *TermContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *TermContext) CopyAll(ctx *TermContext) {
+	s.CopyFrom(&ctx.BaseParserRuleContext)
+}
+
+func (s *TermContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *TermContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+type ExternalConstantTermContext struct {
+	TermContext
+}
+
+func NewExternalConstantTermContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *ExternalConstantTermContext {
+	var p = new(ExternalConstantTermContext)
+
+	InitEmptyTermContext(&p.TermContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*TermContext))
+
+	return p
+}
+
+func (s *ExternalConstantTermContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *ExternalConstantTermContext) ExternalConstant() IExternalConstantContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExternalConstantContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExternalConstantContext)
+}
+
+func (s *ExternalConstantTermContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitExternalConstantTerm(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type LiteralTermContext struct {
+	TermContext
+}
+
+func NewLiteralTermContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *LiteralTermContext {
+	var p = new(LiteralTermContext)
+
+	InitEmptyTermContext(&p.TermContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*TermContext))
+
+	return p
+}
+
+func (s *LiteralTermContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *LiteralTermContext) Literal() ILiteralContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(ILiteralContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(ILiteralContext)
+}
+
+func (s *LiteralTermContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitLiteralTerm(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type ParenthesizedTermContext struct {
+	TermContext
+}
+
+func NewParenthesizedTermContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *ParenthesizedTermContext {
+	var p = new(ParenthesizedTermContext)
+
+	InitEmptyTermContext(&p.TermContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*TermContext))
+
+	return p
+}
+
+func (s *ParenthesizedTermContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *ParenthesizedTermContext) Expression() IExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *ParenthesizedTermContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitParenthesizedTerm(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type InvocationTermContext struct {
+	TermContext
+}
+
+func NewInvocationTermContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *InvocationTermContext {
+	var p = new(InvocationTermContext)
+
+	InitEmptyTermContext(&p.TermContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*TermContext))
+
+	return p
+}
+
+func (s *InvocationTermContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *InvocationTermContext) Invocation() IInvocationContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IInvocationContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IInvocationContext)
+}
+
+func (s *InvocationTermContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitInvocationTerm(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+func (p *fhirpathParser) Term() (localctx ITermContext) {
+	localctx = NewTermContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 4, fhirpathParserRULE_term)
+	p.SetState(89)
+	p.GetErrorHandler().Sync(p)
+	if p.HasError() {
+		goto errorExit
+	}
+
+	switch p.GetTokenStream().LA(1) {
+	case fhirpathParserT__10, fhirpathParserT__11, fhirpathParserT__21, fhirpathParserT__22, fhirpathParserT__34, fhirpathParserT__35, fhirpathParserT__36, fhirpathParserIDENTIFIER, fhirpathParserDELIMITEDIDENTIFIER:
+		localctx = NewInvocationTermContext(p, localctx)
+		p.EnterOuterAlt(localctx, 1)
+		{
+			p.SetState(82)
+			p.Invocation()
+		}
+
+	case fhirpathParserT__29, fhirpathParserT__31, fhirpathParserT__32, fhirpathParserDATE, fhirpathParserDATETIME, fhirpathParserTIME, fhirpathParserSTRING, fhirpathParserNUMBER:
+		localctx = NewLiteralTermContext(p, localctx)
+		p.EnterOuterAlt(localctx, 2)
+		{
+			p.SetState(83)
+			p.Literal()
+		}
+
+	case fhirpathParserT__33:
+		localctx = NewExternalConstantTermContext(p, localctx)
+		p.EnterOuterAlt(localctx, 3)
+		{
+			p.SetState(84)
+			p.ExternalConstant()
+		}
+
+	case fhirpathParserT__27:
+		localctx = NewParenthesizedTermContext(p, localctx)
+		p.EnterOuterAlt(localctx, 4)
+		{
+			p.SetState(85)
+			p.Match(fhirpathParserT__27)
+			if p.HasError() {
+				// Recognition error - abort rule
+				goto errorExit
+			}
+		}
+		{
+			p.SetState(86)
+			p.expression(0)
+		}
+		{
+			p.SetState(87)
+			p.Match(fhirpathParserT__28)
+			if p.HasError() {
+				// Recognition error - abort rule
+				goto errorExit
+			}
+		}
+
+	default:
+		p.SetError(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil))
+		goto errorExit
+	}
+
+errorExit:
+	if p.HasError() {
+		v := p.GetError()
+		localctx.SetException(v)
+		p.GetErrorHandler().ReportError(p, v)
+		p.GetErrorHandler().Recover(p, v)
+		p.SetError(nil)
+	}
+	p.ExitRule()
+	return localctx
+	goto errorExit // Trick to prevent compiler error if the label is not used
+}
+
+// ILiteralContext is an interface to support dynamic dispatch.
+type ILiteralContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+	// IsLiteralContext differentiates from other interfaces.
+	IsLiteralContext()
+}
+
+type LiteralContext struct {
+	antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyLiteralContext() *LiteralContext {
+	var p = new(LiteralContext)
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_literal
+	return p
+}
+
+func InitEmptyLiteralContext(p *LiteralContext) {
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_literal
+}
+
+func (*LiteralContext) IsLiteralContext() {}
+
+func NewLiteralContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *LiteralContext {
+	var p = new(LiteralContext)
+
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = fhirpathParserRULE_literal
+
+	return p
+}
+
+func (s *LiteralContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *LiteralContext) CopyAll(ctx *LiteralContext) {
+	s.CopyFrom(&ctx.BaseParserRuleContext)
+}
+
+func (s *LiteralContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *LiteralContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+type TimeLiteralContext struct {
+	LiteralContext
+}
+
+func NewTimeLiteralContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *TimeLiteralContext {
+	var p = new(TimeLiteralContext)
+
+	InitEmptyLiteralContext(&p.LiteralContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*LiteralContext))
+
+	return p
+}
+
+func (s *TimeLiteralContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *TimeLiteralContext) TIME() antlr.TerminalNode {
+	return s.GetToken(fhirpathParserTIME, 0)
+}
+
+func (s *TimeLiteralContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitTimeLiteral(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type NullLiteralContext struct {
+	LiteralContext
+}
+
+func NewNullLiteralContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *NullLiteralContext {
+	var p = new(NullLiteralContext)
+
+	InitEmptyLiteralContext(&p.LiteralContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*LiteralContext))
+
+	return p
+}
+
+func (s *NullLiteralContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *NullLiteralContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitNullLiteral(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type DateTimeLiteralContext struct {
+	LiteralContext
+}
+
+func NewDateTimeLiteralContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *DateTimeLiteralContext {
+	var p = new(DateTimeLiteralContext)
+
+	InitEmptyLiteralContext(&p.LiteralContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*LiteralContext))
+
+	return p
+}
+
+func (s *DateTimeLiteralContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *DateTimeLiteralContext) DATETIME() antlr.TerminalNode {
+	return s.GetToken(fhirpathParserDATETIME, 0)
+}
+
+func (s *DateTimeLiteralContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitDateTimeLiteral(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type StringLiteralContext struct {
+	LiteralContext
+}
+
+func NewStringLiteralContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *StringLiteralContext {
+	var p = new(StringLiteralContext)
+
+	InitEmptyLiteralContext(&p.LiteralContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*LiteralContext))
+
+	return p
+}
+
+func (s *StringLiteralContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *StringLiteralContext) STRING() antlr.TerminalNode {
+	return s.GetToken(fhirpathParserSTRING, 0)
+}
+
+func (s *StringLiteralContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitStringLiteral(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type DateLiteralContext struct {
+	LiteralContext
+}
+
+func NewDateLiteralContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *DateLiteralContext {
+	var p = new(DateLiteralContext)
+
+	InitEmptyLiteralContext(&p.LiteralContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*LiteralContext))
+
+	return p
+}
+
+func (s *DateLiteralContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *DateLiteralContext) DATE() antlr.TerminalNode {
+	return s.GetToken(fhirpathParserDATE, 0)
+}
+
+func (s *DateLiteralContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitDateLiteral(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type BooleanLiteralContext struct {
+	LiteralContext
+}
+
+func NewBooleanLiteralContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *BooleanLiteralContext {
+	var p = new(BooleanLiteralContext)
+
+	InitEmptyLiteralContext(&p.LiteralContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*LiteralContext))
+
+	return p
+}
+
+func (s *BooleanLiteralContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *BooleanLiteralContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitBooleanLiteral(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type NumberLiteralContext struct {
+	LiteralContext
+}
+
+func NewNumberLiteralContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *NumberLiteralContext {
+	var p = new(NumberLiteralContext)
+
+	InitEmptyLiteralContext(&p.LiteralContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*LiteralContext))
+
+	return p
+}
+
+func (s *NumberLiteralContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *NumberLiteralContext) NUMBER() antlr.TerminalNode {
+	return s.GetToken(fhirpathParserNUMBER, 0)
+}
+
+func (s *NumberLiteralContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitNumberLiteral(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type QuantityLiteralContext struct {
+	LiteralContext
+}
+
+func NewQuantityLiteralContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *QuantityLiteralContext {
+	var p = new(QuantityLiteralContext)
+
+	InitEmptyLiteralContext(&p.LiteralContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*LiteralContext))
+
+	return p
+}
+
+func (s *QuantityLiteralContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *QuantityLiteralContext) Quantity() IQuantityContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IQuantityContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IQuantityContext)
+}
+
+func (s *QuantityLiteralContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitQuantityLiteral(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+func (p *fhirpathParser) Literal() (localctx ILiteralContext) {
+	localctx = NewLiteralContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 6, fhirpathParserRULE_literal)
+	var _la int
+
+	p.SetState(100)
+	p.GetErrorHandler().Sync(p)
+	if p.HasError() {
+		goto errorExit
+	}
+
+	switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 4, p.GetParserRuleContext()) {
+	case 1:
+		localctx = NewNullLiteralContext(p, localctx)
+		p.EnterOuterAlt(localctx, 1)
+		{
+			p.SetState(91)
+			p.Match(fhirpathParserT__29)
+			if p.HasError() {
+				// Recognition error - abort rule
+				goto errorExit
+			}
+		}
+		{
+			p.SetState(92)
+			p.Match(fhirpathParserT__30)
+			if p.HasError() {
+				// Recognition error - abort rule
+				goto errorExit
+			}
+		}
+
+	case 2:
+		localctx = NewBooleanLiteralContext(p, localctx)
+		p.EnterOuterAlt(localctx, 2)
+		{
+			p.SetState(93)
+			_la = p.GetTokenStream().LA(1)
+
+			if !(_la == fhirpathParserT__31 || _la == fhirpathParserT__32) {
+				p.GetErrorHandler().RecoverInline(p)
+			} else {
+				p.GetErrorHandler().ReportMatch(p)
+				p.Consume()
+			}
+		}
+
+	case 3:
+		localctx = NewStringLiteralContext(p, localctx)
+		p.EnterOuterAlt(localctx, 3)
+		{
+			p.SetState(94)
+			p.Match(fhirpathParserSTRING)
+			if p.HasError() {
+				// Recognition error - abort rule
+				goto errorExit
+			}
+		}
+
+	case 4:
+		localctx = NewNumberLiteralContext(p, localctx)
+		p.EnterOuterAlt(localctx, 4)
+		{
+			p.SetState(95)
+			p.Match(fhirpathParserNUMBER)
+			if p.HasError() {
+				// Recognition error - abort rule
+				goto errorExit
+			}
+		}
+
+	case 5:
+		localctx = NewDateLiteralContext(p, localctx)
+		p.EnterOuterAlt(localctx, 5)
+		{
+			p.SetState(96)
+			p.Match(fhirpathParserDATE)
+			if p.HasError() {
+				// Recognition error - abort rule
+				goto errorExit
+			}
+		}
+
+	case 6:
+		localctx = NewDateTimeLiteralContext(p, localctx)
+		p.EnterOuterAlt(localctx, 6)
+		{
+			p.SetState(97)
+			p.Match(fhirpathParserDATETIME)
+			if p.HasError() {
+				// Recognition error - abort rule
+				goto errorExit
+			}
+		}
+
+	case 7:
+		localctx = NewTimeLiteralContext(p, localctx)
+		p.EnterOuterAlt(localctx, 7)
+		{
+			p.SetState(98)
+			p.Match(fhirpathParserTIME)
+			if p.HasError() {
+				// Recognition error - abort rule
+				goto errorExit
+			}
+		}
+
+	case 8:
+		localctx = NewQuantityLiteralContext(p, localctx)
+		p.EnterOuterAlt(localctx, 8)
+		{
+			p.SetState(99)
+			p.Quantity()
+		}
+
+	case antlr.ATNInvalidAltNumber:
+		goto errorExit
+	}
+
+errorExit:
+	if p.HasError() {
+		v := p.GetError()
+		localctx.SetException(v)
+		p.GetErrorHandler().ReportError(p, v)
+		p.GetErrorHandler().Recover(p, v)
+		p.SetError(nil)
+	}
+	p.ExitRule()
+	return localctx
+	goto errorExit // Trick to prevent compiler error if the label is not used
+}
+
+// IExternalConstantContext is an interface to support dynamic dispatch.
+type IExternalConstantContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	Identifier() IIdentifierContext
+	STRING() antlr.TerminalNode
+
+	// IsExternalConstantContext differentiates from other interfaces.
+	IsExternalConstantContext()
+}
+
+type ExternalConstantContext struct {
+	antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyExternalConstantContext() *ExternalConstantContext {
+	var p = new(ExternalConstantContext)
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_externalConstant
+	return p
+}
+
+func InitEmptyExternalConstantContext(p *ExternalConstantContext) {
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_externalConstant
+}
+
+func (*ExternalConstantContext) IsExternalConstantContext() {}
+
+func NewExternalConstantContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ExternalConstantContext {
+	var p = new(ExternalConstantContext)
+
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = fhirpathParserRULE_externalConstant
+
+	return p
+}
+
+func (s *ExternalConstantContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *ExternalConstantContext) Identifier() IIdentifierContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IIdentifierContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IIdentifierContext)
+}
+
+func (s *ExternalConstantContext) STRING() antlr.TerminalNode {
+	return s.GetToken(fhirpathParserSTRING, 0)
+}
+
+func (s *ExternalConstantContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *ExternalConstantContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *ExternalConstantContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitExternalConstant(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+func (p *fhirpathParser) ExternalConstant() (localctx IExternalConstantContext) {
+	localctx = NewExternalConstantContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 8, fhirpathParserRULE_externalConstant)
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(102)
+		p.Match(fhirpathParserT__33)
+		if p.HasError() {
+			// Recognition error - abort rule
+			goto errorExit
+		}
+	}
+	p.SetState(105)
+	p.GetErrorHandler().Sync(p)
+	if p.HasError() {
+		goto errorExit
+	}
+
+	switch p.GetTokenStream().LA(1) {
+	case fhirpathParserT__10, fhirpathParserT__11, fhirpathParserT__21, fhirpathParserT__22, fhirpathParserIDENTIFIER, fhirpathParserDELIMITEDIDENTIFIER:
+		{
+			p.SetState(103)
+			p.Identifier()
+		}
+
+	case fhirpathParserSTRING:
+		{
+			p.SetState(104)
+			p.Match(fhirpathParserSTRING)
+			if p.HasError() {
+				// Recognition error - abort rule
+				goto errorExit
+			}
+		}
+
+	default:
+		p.SetError(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil))
+		goto errorExit
+	}
+
+errorExit:
+	if p.HasError() {
+		v := p.GetError()
+		localctx.SetException(v)
+		p.GetErrorHandler().ReportError(p, v)
+		p.GetErrorHandler().Recover(p, v)
+		p.SetError(nil)
+	}
+	p.ExitRule()
+	return localctx
+	goto errorExit // Trick to prevent compiler error if the label is not used
+}
+
+// IInvocationContext is an interface to support dynamic dispatch.
+type IInvocationContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+	// IsInvocationContext differentiates from other interfaces.
+	IsInvocationContext()
+}
+
+type InvocationContext struct {
+	antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyInvocationContext() *InvocationContext {
+	var p = new(InvocationContext)
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_invocation
+	return p
+}
+
+func InitEmptyInvocationContext(p *InvocationContext) {
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_invocation
+}
+
+func (*InvocationContext) IsInvocationContext() {}
+
+func NewInvocationContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *InvocationContext {
+	var p = new(InvocationContext)
+
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = fhirpathParserRULE_invocation
+
+	return p
+}
+
+func (s *InvocationContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *InvocationContext) CopyAll(ctx *InvocationContext) {
+	s.CopyFrom(&ctx.BaseParserRuleContext)
+}
+
+func (s *InvocationContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *InvocationContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+type TotalInvocationContext struct {
+	InvocationContext
+}
+
+func NewTotalInvocationContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *TotalInvocationContext {
+	var p = new(TotalInvocationContext)
+
+	InitEmptyInvocationContext(&p.InvocationContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*InvocationContext))
+
+	return p
+}
+
+func (s *TotalInvocationContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *TotalInvocationContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitTotalInvocation(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type ThisInvocationContext struct {
+	InvocationContext
+}
+
+func NewThisInvocationContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *ThisInvocationContext {
+	var p = new(ThisInvocationContext)
+
+	InitEmptyInvocationContext(&p.InvocationContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*InvocationContext))
+
+	return p
+}
+
+func (s *ThisInvocationContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *ThisInvocationContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitThisInvocation(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type IndexInvocationContext struct {
+	InvocationContext
+}
+
+func NewIndexInvocationContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *IndexInvocationContext {
+	var p = new(IndexInvocationContext)
+
+	InitEmptyInvocationContext(&p.InvocationContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*InvocationContext))
+
+	return p
+}
+
+func (s *IndexInvocationContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *IndexInvocationContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitIndexInvocation(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type FunctionInvocationContext struct {
+	InvocationContext
+}
+
+func NewFunctionInvocationContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *FunctionInvocationContext {
+	var p = new(FunctionInvocationContext)
+
+	InitEmptyInvocationContext(&p.InvocationContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*InvocationContext))
+
+	return p
+}
+
+func (s *FunctionInvocationContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *FunctionInvocationContext) Function() IFunctionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IFunctionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IFunctionContext)
+}
+
+func (s *FunctionInvocationContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitFunctionInvocation(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+type MemberInvocationContext struct {
+	InvocationContext
+}
+
+func NewMemberInvocationContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *MemberInvocationContext {
+	var p = new(MemberInvocationContext)
+
+	InitEmptyInvocationContext(&p.InvocationContext)
+	p.parser = parser
+	p.CopyAll(ctx.(*InvocationContext))
+
+	return p
+}
+
+func (s *MemberInvocationContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *MemberInvocationContext) Identifier() IIdentifierContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IIdentifierContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IIdentifierContext)
+}
+
+func (s *MemberInvocationContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitMemberInvocation(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+func (p *fhirpathParser) Invocation() (localctx IInvocationContext) {
+	localctx = NewInvocationContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 10, fhirpathParserRULE_invocation)
+	p.SetState(112)
+	p.GetErrorHandler().Sync(p)
+	if p.HasError() {
+		goto errorExit
+	}
+
+	switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 6, p.GetParserRuleContext()) {
+	case 1:
+		localctx = NewMemberInvocationContext(p, localctx)
+		p.EnterOuterAlt(localctx, 1)
+		{
+			p.SetState(107)
+			p.Identifier()
+		}
+
+	case 2:
+		localctx = NewFunctionInvocationContext(p, localctx)
+		p.EnterOuterAlt(localctx, 2)
+		{
+			p.SetState(108)
+			p.Function()
+		}
+
+	case 3:
+		localctx = NewThisInvocationContext(p, localctx)
+		p.EnterOuterAlt(localctx, 3)
+		{
+			p.SetState(109)
+			p.Match(fhirpathParserT__34)
+			if p.HasError() {
+				// Recognition error - abort rule
+				goto errorExit
+			}
+		}
+
+	case 4:
+		localctx = NewIndexInvocationContext(p, localctx)
+		p.EnterOuterAlt(localctx, 4)
+		{
+			p.SetState(110)
+			p.Match(fhirpathParserT__35)
+			if p.HasError() {
+				// Recognition error - abort rule
+				goto errorExit
+			}
+		}
+
+	case 5:
+		localctx = NewTotalInvocationContext(p, localctx)
+		p.EnterOuterAlt(localctx, 5)
+		{
+			p.SetState(111)
+			p.Match(fhirpathParserT__36)
+			if p.HasError() {
+				// Recognition error - abort rule
+				goto errorExit
+			}
+		}
+
+	case antlr.ATNInvalidAltNumber:
+		goto errorExit
+	}
+
+errorExit:
+	if p.HasError() {
+		v := p.GetError()
+		localctx.SetException(v)
+		p.GetErrorHandler().ReportError(p, v)
+		p.GetErrorHandler().Recover(p, v)
+		p.SetError(nil)
+	}
+	p.ExitRule()
+	return localctx
+	goto errorExit // Trick to prevent compiler error if the label is not used
+}
+
+// IFunctionContext is an interface to support dynamic dispatch.
+type IFunctionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	Identifier() IIdentifierContext
+	ParamList() IParamListContext
+
+	// IsFunctionContext differentiates from other interfaces.
+	IsFunctionContext()
+}
+
+type FunctionContext struct {
+	antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyFunctionContext() *FunctionContext {
+	var p = new(FunctionContext)
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_function
+	return p
+}
+
+func InitEmptyFunctionContext(p *FunctionContext) {
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_function
+}
+
+func (*FunctionContext) IsFunctionContext() {}
+
+func NewFunctionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *FunctionContext {
+	var p = new(FunctionContext)
+
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = fhirpathParserRULE_function
+
+	return p
+}
+
+func (s *FunctionContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *FunctionContext) Identifier() IIdentifierContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IIdentifierContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IIdentifierContext)
+}
+
+func (s *FunctionContext) ParamList() IParamListContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IParamListContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IParamListContext)
+}
+
+func (s *FunctionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *FunctionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *FunctionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitFunction(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+func (p *fhirpathParser) Function() (localctx IFunctionContext) {
+	localctx = NewFunctionContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 12, fhirpathParserRULE_function)
+	var _la int
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(114)
+		p.Identifier()
+	}
+	{
+		p.SetState(115)
+		p.Match(fhirpathParserT__27)
+		if p.HasError() {
+			// Recognition error - abort rule
+			goto errorExit
+		}
+	}
+	p.SetState(117)
+	p.GetErrorHandler().Sync(p)
+	if p.HasError() {
+		goto errorExit
+	}
+	_la = p.GetTokenStream().LA(1)
+
+	if (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&4575657493346129968) != 0 {
+		{
+			p.SetState(116)
+			p.ParamList()
+		}
+
+	}
+	{
+		p.SetState(119)
+		p.Match(fhirpathParserT__28)
+		if p.HasError() {
+			// Recognition error - abort rule
+			goto errorExit
+		}
+	}
+
+errorExit:
+	if p.HasError() {
+		v := p.GetError()
+		localctx.SetException(v)
+		p.GetErrorHandler().ReportError(p, v)
+		p.GetErrorHandler().Recover(p, v)
+		p.SetError(nil)
+	}
+	p.ExitRule()
+	return localctx
+	goto errorExit // Trick to prevent compiler error if the label is not used
+}
+
+// IParamListContext is an interface to support dynamic dispatch.
+type IParamListContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	AllExpression() []IExpressionContext
+	Expression(i int) IExpressionContext
+
+	// IsParamListContext differentiates from other interfaces.
+	IsParamListContext()
+}
+
+type ParamListContext struct {
+	antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyParamListContext() *ParamListContext {
+	var p = new(ParamListContext)
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_paramList
+	return p
+}
+
+func InitEmptyParamListContext(p *ParamListContext) {
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_paramList
+}
+
+func (*ParamListContext) IsParamListContext() {}
+
+func NewParamListContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ParamListContext {
+	var p = new(ParamListContext)
+
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = fhirpathParserRULE_paramList
+
+	return p
+}
+
+func (s *ParamListContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *ParamListContext) AllExpression() []IExpressionContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IExpressionContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IExpressionContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IExpressionContext); ok {
+			tst[i] = t.(IExpressionContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *ParamListContext) Expression(i int) IExpressionContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *ParamListContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *ParamListContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *ParamListContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitParamList(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+func (p *fhirpathParser) ParamList() (localctx IParamListContext) {
+	localctx = NewParamListContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 14, fhirpathParserRULE_paramList)
+	var _la int
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(121)
+		p.expression(0)
+	}
+	p.SetState(126)
+	p.GetErrorHandler().Sync(p)
+	if p.HasError() {
+		goto errorExit
+	}
+	_la = p.GetTokenStream().LA(1)
+
+	for _la == fhirpathParserT__37 {
+		{
+			p.SetState(122)
+			p.Match(fhirpathParserT__37)
+			if p.HasError() {
+				// Recognition error - abort rule
+				goto errorExit
+			}
+		}
+		{
+			p.SetState(123)
+			p.expression(0)
+		}
+
+		p.SetState(128)
+		p.GetErrorHandler().Sync(p)
+		if p.HasError() {
+			goto errorExit
+		}
+		_la = p.GetTokenStream().LA(1)
+	}
+
+errorExit:
+	if p.HasError() {
+		v := p.GetError()
+		localctx.SetException(v)
+		p.GetErrorHandler().ReportError(p, v)
+		p.GetErrorHandler().Recover(p, v)
+		p.SetError(nil)
+	}
+	p.ExitRule()
+	return localctx
+	goto errorExit // Trick to prevent compiler error if the label is not used
+}
+
+// IQuantityContext is an interface to support dynamic dispatch.
+type IQuantityContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	NUMBER() antlr.TerminalNode
+	Unit() IUnitContext
+
+	// IsQuantityContext differentiates from other interfaces.
+	IsQuantityContext()
+}
+
+type QuantityContext struct {
+	antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyQuantityContext() *QuantityContext {
+	var p = new(QuantityContext)
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_quantity
+	return p
+}
+
+func InitEmptyQuantityContext(p *QuantityContext) {
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_quantity
+}
+
+func (*QuantityContext) IsQuantityContext() {}
+
+func NewQuantityContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *QuantityContext {
+	var p = new(QuantityContext)
+
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = fhirpathParserRULE_quantity
+
+	return p
+}
+
+func (s *QuantityContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *QuantityContext) NUMBER() antlr.TerminalNode {
+	return s.GetToken(fhirpathParserNUMBER, 0)
+}
+
+func (s *QuantityContext) Unit() IUnitContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IUnitContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IUnitContext)
+}
+
+func (s *QuantityContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *QuantityContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *QuantityContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitQuantity(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+func (p *fhirpathParser) Quantity() (localctx IQuantityContext) {
+	localctx = NewQuantityContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 16, fhirpathParserRULE_quantity)
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(129)
+		p.Match(fhirpathParserNUMBER)
+		if p.HasError() {
+			// Recognition error - abort rule
+			goto errorExit
+		}
+	}
+	p.SetState(131)
+	p.GetErrorHandler().Sync(p)
+
+	if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 9, p.GetParserRuleContext()) == 1 {
+		{
+			p.SetState(130)
+			p.Unit()
+		}
+
+	} else if p.HasError() { // JIM
+		goto errorExit
+	}
+
+errorExit:
+	if p.HasError() {
+		v := p.GetError()
+		localctx.SetException(v)
+		p.GetErrorHandler().ReportError(p, v)
+		p.GetErrorHandler().Recover(p, v)
+		p.SetError(nil)
+	}
+	p.ExitRule()
+	return localctx
+	goto errorExit // Trick to prevent compiler error if the label is not used
+}
+
+// IUnitContext is an interface to support dynamic dispatch.
+type IUnitContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	DateTimePrecision() IDateTimePrecisionContext
+	PluralDateTimePrecision() IPluralDateTimePrecisionContext
+	STRING() antlr.TerminalNode
+
+	// IsUnitContext differentiates from other interfaces.
+	IsUnitContext()
+}
+
+type UnitContext struct {
+	antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyUnitContext() *UnitContext {
+	var p = new(UnitContext)
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_unit
+	return p
+}
+
+func InitEmptyUnitContext(p *UnitContext) {
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_unit
+}
+
+func (*UnitContext) IsUnitContext() {}
+
+func NewUnitContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *UnitContext {
+	var p = new(UnitContext)
+
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = fhirpathParserRULE_unit
+
+	return p
+}
+
+func (s *UnitContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *UnitContext) DateTimePrecision() IDateTimePrecisionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IDateTimePrecisionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IDateTimePrecisionContext)
+}
+
+func (s *UnitContext) PluralDateTimePrecision() IPluralDateTimePrecisionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IPluralDateTimePrecisionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IPluralDateTimePrecisionContext)
+}
+
+func (s *UnitContext) STRING() antlr.TerminalNode {
+	return s.GetToken(fhirpathParserSTRING, 0)
+}
+
+func (s *UnitContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *UnitContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *UnitContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitUnit(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+func (p *fhirpathParser) Unit() (localctx IUnitContext) {
+	localctx = NewUnitContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 18, fhirpathParserRULE_unit)
+	p.SetState(136)
+	p.GetErrorHandler().Sync(p)
+	if p.HasError() {
+		goto errorExit
+	}
+
+	switch p.GetTokenStream().LA(1) {
+	case fhirpathParserT__38, fhirpathParserT__39, fhirpathParserT__40, fhirpathParserT__41, fhirpathParserT__42, fhirpathParserT__43, fhirpathParserT__44, fhirpathParserT__45:
+		p.EnterOuterAlt(localctx, 1)
+		{
+			p.SetState(133)
+			p.DateTimePrecision()
+		}
+
+	case fhirpathParserT__46, fhirpathParserT__47, fhirpathParserT__48, fhirpathParserT__49, fhirpathParserT__50, fhirpathParserT__51, fhirpathParserT__52, fhirpathParserT__53:
+		p.EnterOuterAlt(localctx, 2)
+		{
+			p.SetState(134)
+			p.PluralDateTimePrecision()
+		}
+
+	case fhirpathParserSTRING:
+		p.EnterOuterAlt(localctx, 3)
+		{
+			p.SetState(135)
+			p.Match(fhirpathParserSTRING)
+			if p.HasError() {
+				// Recognition error - abort rule
+				goto errorExit
+			}
+		}
+
+	default:
+		p.SetError(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil))
+		goto errorExit
+	}
+
+errorExit:
+	if p.HasError() {
+		v := p.GetError()
+		localctx.SetException(v)
+		p.GetErrorHandler().ReportError(p, v)
+		p.GetErrorHandler().Recover(p, v)
+		p.SetError(nil)
+	}
+	p.ExitRule()
+	return localctx
+	goto errorExit // Trick to prevent compiler error if the label is not used
+}
+
+// IDateTimePrecisionContext is an interface to support dynamic dispatch.
+type IDateTimePrecisionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+	// IsDateTimePrecisionContext differentiates from other interfaces.
+	IsDateTimePrecisionContext()
+}
+
+type DateTimePrecisionContext struct {
+	antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyDateTimePrecisionContext() *DateTimePrecisionContext {
+	var p = new(DateTimePrecisionContext)
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_dateTimePrecision
+	return p
+}
+
+func InitEmptyDateTimePrecisionContext(p *DateTimePrecisionContext) {
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_dateTimePrecision
+}
+
+func (*DateTimePrecisionContext) IsDateTimePrecisionContext() {}
+
+func NewDateTimePrecisionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *DateTimePrecisionContext {
+	var p = new(DateTimePrecisionContext)
+
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = fhirpathParserRULE_dateTimePrecision
+
+	return p
+}
+
+func (s *DateTimePrecisionContext) GetParser() antlr.Parser { return s.parser }
+func (s *DateTimePrecisionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *DateTimePrecisionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *DateTimePrecisionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitDateTimePrecision(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+func (p *fhirpathParser) DateTimePrecision() (localctx IDateTimePrecisionContext) {
+	localctx = NewDateTimePrecisionContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 20, fhirpathParserRULE_dateTimePrecision)
+	var _la int
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(138)
+		_la = p.GetTokenStream().LA(1)
+
+		if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&140187732541440) != 0) {
+			p.GetErrorHandler().RecoverInline(p)
+		} else {
+			p.GetErrorHandler().ReportMatch(p)
+			p.Consume()
+		}
+	}
+
+errorExit:
+	if p.HasError() {
+		v := p.GetError()
+		localctx.SetException(v)
+		p.GetErrorHandler().ReportError(p, v)
+		p.GetErrorHandler().Recover(p, v)
+		p.SetError(nil)
+	}
+	p.ExitRule()
+	return localctx
+	goto errorExit // Trick to prevent compiler error if the label is not used
+}
+
+// IPluralDateTimePrecisionContext is an interface to support dynamic dispatch.
+type IPluralDateTimePrecisionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+	// IsPluralDateTimePrecisionContext differentiates from other interfaces.
+	IsPluralDateTimePrecisionContext()
+}
+
+type PluralDateTimePrecisionContext struct {
+	antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyPluralDateTimePrecisionContext() *PluralDateTimePrecisionContext {
+	var p = new(PluralDateTimePrecisionContext)
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_pluralDateTimePrecision
+	return p
+}
+
+func InitEmptyPluralDateTimePrecisionContext(p *PluralDateTimePrecisionContext) {
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_pluralDateTimePrecision
+}
+
+func (*PluralDateTimePrecisionContext) IsPluralDateTimePrecisionContext() {}
+
+func NewPluralDateTimePrecisionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *PluralDateTimePrecisionContext {
+	var p = new(PluralDateTimePrecisionContext)
+
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = fhirpathParserRULE_pluralDateTimePrecision
+
+	return p
+}
+
+func (s *PluralDateTimePrecisionContext) GetParser() antlr.Parser { return s.parser }
+func (s *PluralDateTimePrecisionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *PluralDateTimePrecisionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *PluralDateTimePrecisionContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitPluralDateTimePrecision(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+func (p *fhirpathParser) PluralDateTimePrecision() (localctx IPluralDateTimePrecisionContext) {
+	localctx = NewPluralDateTimePrecisionContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 22, fhirpathParserRULE_pluralDateTimePrecision)
+	var _la int
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(140)
+		_la = p.GetTokenStream().LA(1)
+
+		if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&35888059530608640) != 0) {
+			p.GetErrorHandler().RecoverInline(p)
+		} else {
+			p.GetErrorHandler().ReportMatch(p)
+			p.Consume()
+		}
+	}
+
+errorExit:
+	if p.HasError() {
+		v := p.GetError()
+		localctx.SetException(v)
+		p.GetErrorHandler().ReportError(p, v)
+		p.GetErrorHandler().Recover(p, v)
+		p.SetError(nil)
+	}
+	p.ExitRule()
+	return localctx
+	goto errorExit // Trick to prevent compiler error if the label is not used
+}
+
+// ITypeSpecifierContext is an interface to support dynamic dispatch.
+type ITypeSpecifierContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	QualifiedIdentifier() IQualifiedIdentifierContext
+
+	// IsTypeSpecifierContext differentiates from other interfaces.
+	IsTypeSpecifierContext()
+}
+
+type TypeSpecifierContext struct {
+	antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyTypeSpecifierContext() *TypeSpecifierContext {
+	var p = new(TypeSpecifierContext)
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_typeSpecifier
+	return p
+}
+
+func InitEmptyTypeSpecifierContext(p *TypeSpecifierContext) {
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_typeSpecifier
+}
+
+func (*TypeSpecifierContext) IsTypeSpecifierContext() {}
+
+func NewTypeSpecifierContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TypeSpecifierContext {
+	var p = new(TypeSpecifierContext)
+
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = fhirpathParserRULE_typeSpecifier
+
+	return p
+}
+
+func (s *TypeSpecifierContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *TypeSpecifierContext) QualifiedIdentifier() IQualifiedIdentifierContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IQualifiedIdentifierContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IQualifiedIdentifierContext)
+}
+
+func (s *TypeSpecifierContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *TypeSpecifierContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *TypeSpecifierContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitTypeSpecifier(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+func (p *fhirpathParser) TypeSpecifier() (localctx ITypeSpecifierContext) {
+	localctx = NewTypeSpecifierContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 24, fhirpathParserRULE_typeSpecifier)
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(142)
+		p.QualifiedIdentifier()
+	}
+
+errorExit:
+	if p.HasError() {
+		v := p.GetError()
+		localctx.SetException(v)
+		p.GetErrorHandler().ReportError(p, v)
+		p.GetErrorHandler().Recover(p, v)
+		p.SetError(nil)
+	}
+	p.ExitRule()
+	return localctx
+	goto errorExit // Trick to prevent compiler error if the label is not used
+}
+
+// IQualifiedIdentifierContext is an interface to support dynamic dispatch.
+type IQualifiedIdentifierContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	AllIdentifier() []IIdentifierContext
+	Identifier(i int) IIdentifierContext
+
+	// IsQualifiedIdentifierContext differentiates from other interfaces.
+	IsQualifiedIdentifierContext()
+}
+
+type QualifiedIdentifierContext struct {
+	antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyQualifiedIdentifierContext() *QualifiedIdentifierContext {
+	var p = new(QualifiedIdentifierContext)
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_qualifiedIdentifier
+	return p
+}
+
+func InitEmptyQualifiedIdentifierContext(p *QualifiedIdentifierContext) {
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_qualifiedIdentifier
+}
+
+func (*QualifiedIdentifierContext) IsQualifiedIdentifierContext() {}
+
+func NewQualifiedIdentifierContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *QualifiedIdentifierContext {
+	var p = new(QualifiedIdentifierContext)
+
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = fhirpathParserRULE_qualifiedIdentifier
+
+	return p
+}
+
+func (s *QualifiedIdentifierContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *QualifiedIdentifierContext) AllIdentifier() []IIdentifierContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IIdentifierContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IIdentifierContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IIdentifierContext); ok {
+			tst[i] = t.(IIdentifierContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *QualifiedIdentifierContext) Identifier(i int) IIdentifierContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IIdentifierContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IIdentifierContext)
+}
+
+func (s *QualifiedIdentifierContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *QualifiedIdentifierContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *QualifiedIdentifierContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitQualifiedIdentifier(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+func (p *fhirpathParser) QualifiedIdentifier() (localctx IQualifiedIdentifierContext) {
+	localctx = NewQualifiedIdentifierContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 26, fhirpathParserRULE_qualifiedIdentifier)
+	var _alt int
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(144)
+		p.Identifier()
+	}
+	p.SetState(149)
+	p.GetErrorHandler().Sync(p)
+	if p.HasError() {
+		goto errorExit
+	}
+	_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 11, p.GetParserRuleContext())
+	if p.HasError() {
+		goto errorExit
+	}
+	for _alt != 2 && _alt != antlr.ATNInvalidAltNumber {
+		if _alt == 1 {
+			{
+				p.SetState(145)
+				p.Match(fhirpathParserT__0)
+				if p.HasError() {
+					// Recognition error - abort rule
+					goto errorExit
+				}
+			}
+			{
+				p.SetState(146)
+				p.Identifier()
+			}
+
+		}
+		p.SetState(151)
+		p.GetErrorHandler().Sync(p)
+		if p.HasError() {
+			goto errorExit
+		}
+		_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 11, p.GetParserRuleContext())
+		if p.HasError() {
+			goto errorExit
+		}
+	}
+
+errorExit:
+	if p.HasError() {
+		v := p.GetError()
+		localctx.SetException(v)
+		p.GetErrorHandler().ReportError(p, v)
+		p.GetErrorHandler().Recover(p, v)
+		p.SetError(nil)
+	}
+	p.ExitRule()
+	return localctx
+	goto errorExit // Trick to prevent compiler error if the label is not used
+}
+
+// IIdentifierContext is an interface to support dynamic dispatch.
+type IIdentifierContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	IDENTIFIER() antlr.TerminalNode
+	DELIMITEDIDENTIFIER() antlr.TerminalNode
+
+	// IsIdentifierContext differentiates from other interfaces.
+	IsIdentifierContext()
+}
+
+type IdentifierContext struct {
+	antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyIdentifierContext() *IdentifierContext {
+	var p = new(IdentifierContext)
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_identifier
+	return p
+}
+
+func InitEmptyIdentifierContext(p *IdentifierContext) {
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
+	p.RuleIndex = fhirpathParserRULE_identifier
+}
+
+func (*IdentifierContext) IsIdentifierContext() {}
+
+func NewIdentifierContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *IdentifierContext {
+	var p = new(IdentifierContext)
+
+	antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = fhirpathParserRULE_identifier
+
+	return p
+}
+
+func (s *IdentifierContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *IdentifierContext) IDENTIFIER() antlr.TerminalNode {
+	return s.GetToken(fhirpathParserIDENTIFIER, 0)
+}
+
+func (s *IdentifierContext) DELIMITEDIDENTIFIER() antlr.TerminalNode {
+	return s.GetToken(fhirpathParserDELIMITEDIDENTIFIER, 0)
+}
+
+func (s *IdentifierContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *IdentifierContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *IdentifierContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
+	switch t := visitor.(type) {
+	case fhirpathVisitor:
+		return t.VisitIdentifier(s)
+
+	default:
+		return t.VisitChildren(s)
+	}
+}
+
+func (p *fhirpathParser) Identifier() (localctx IIdentifierContext) {
+	localctx = NewIdentifierContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 28, fhirpathParserRULE_identifier)
+	var _la int
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(152)
+		_la = p.GetTokenStream().LA(1)
+
+		if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&864691128467724288) != 0) {
+			p.GetErrorHandler().RecoverInline(p)
+		} else {
+			p.GetErrorHandler().ReportMatch(p)
+			p.Consume()
+		}
+	}
+
+errorExit:
+	if p.HasError() {
+		v := p.GetError()
+		localctx.SetException(v)
+		p.GetErrorHandler().ReportError(p, v)
+		p.GetErrorHandler().Recover(p, v)
+		p.SetError(nil)
+	}
+	p.ExitRule()
+	return localctx
+	goto errorExit // Trick to prevent compiler error if the label is not used
+}
+
+func (p *fhirpathParser) Sempred(localctx antlr.RuleContext, ruleIndex, predIndex int) bool {
+	switch ruleIndex {
+	case 1:
+		var t *ExpressionContext = nil
+		if localctx != nil {
+			t = localctx.(*ExpressionContext)
+		}
+		return p.Expression_Sempred(t, predIndex)
+
+	default:
+		panic("No predicate with index: " + fmt.Sprint(ruleIndex))
+	}
+}
+
+func (p *fhirpathParser) Expression_Sempred(localctx antlr.RuleContext, predIndex int) bool {
+	switch predIndex {
+	case 0:
+		return p.Precpred(p.GetParserRuleContext(), 10)
+
+	case 1:
+		return p.Precpred(p.GetParserRuleContext(), 9)
+
+	case 2:
+		return p.Precpred(p.GetParserRuleContext(), 7)
+
+	case 3:
+		return p.Precpred(p.GetParserRuleContext(), 6)
+
+	case 4:
+		return p.Precpred(p.GetParserRuleContext(), 5)
+
+	case 5:
+		return p.Precpred(p.GetParserRuleContext(), 4)
+
+	case 6:
+		return p.Precpred(p.GetParserRuleContext(), 3)
+
+	case 7:
+		return p.Precpred(p.GetParserRuleContext(), 2)
+
+	case 8:
+		return p.Precpred(p.GetParserRuleContext(), 1)
+
+	case 9:
+		return p.Precpred(p.GetParserRuleContext(), 13)
+
+	case 10:
+		return p.Precpred(p.GetParserRuleContext(), 12)
+
+	case 11:
+		return p.Precpred(p.GetParserRuleContext(), 8)
+
+	default:
+		panic("No predicate with index: " + fmt.Sprint(predIndex))
+	}
+}
diff --git a/fhirpath/internal/grammar/fhirpath_visitor.go b/fhirpath/internal/grammar/fhirpath_visitor.go
new file mode 100644
index 0000000..c5e4772
--- /dev/null
+++ b/fhirpath/internal/grammar/fhirpath_visitor.go
@@ -0,0 +1,135 @@
+// Code generated from fhirpath.g4 by ANTLR 4.13.0. DO NOT EDIT.
+
+package grammar // fhirpath
+import "github.com/antlr4-go/antlr/v4"
+
+// A complete Visitor for a parse tree produced by fhirpathParser.
+type fhirpathVisitor interface {
+	antlr.ParseTreeVisitor
+
+	// Visit a parse tree produced by fhirpathParser#prog.
+	VisitProg(ctx *ProgContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#indexerExpression.
+	VisitIndexerExpression(ctx *IndexerExpressionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#polarityExpression.
+	VisitPolarityExpression(ctx *PolarityExpressionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#additiveExpression.
+	VisitAdditiveExpression(ctx *AdditiveExpressionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#multiplicativeExpression.
+	VisitMultiplicativeExpression(ctx *MultiplicativeExpressionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#unionExpression.
+	VisitUnionExpression(ctx *UnionExpressionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#orExpression.
+	VisitOrExpression(ctx *OrExpressionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#andExpression.
+	VisitAndExpression(ctx *AndExpressionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#membershipExpression.
+	VisitMembershipExpression(ctx *MembershipExpressionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#inequalityExpression.
+	VisitInequalityExpression(ctx *InequalityExpressionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#invocationExpression.
+	VisitInvocationExpression(ctx *InvocationExpressionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#equalityExpression.
+	VisitEqualityExpression(ctx *EqualityExpressionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#impliesExpression.
+	VisitImpliesExpression(ctx *ImpliesExpressionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#termExpression.
+	VisitTermExpression(ctx *TermExpressionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#typeExpression.
+	VisitTypeExpression(ctx *TypeExpressionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#invocationTerm.
+	VisitInvocationTerm(ctx *InvocationTermContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#literalTerm.
+	VisitLiteralTerm(ctx *LiteralTermContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#externalConstantTerm.
+	VisitExternalConstantTerm(ctx *ExternalConstantTermContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#parenthesizedTerm.
+	VisitParenthesizedTerm(ctx *ParenthesizedTermContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#nullLiteral.
+	VisitNullLiteral(ctx *NullLiteralContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#booleanLiteral.
+	VisitBooleanLiteral(ctx *BooleanLiteralContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#stringLiteral.
+	VisitStringLiteral(ctx *StringLiteralContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#numberLiteral.
+	VisitNumberLiteral(ctx *NumberLiteralContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#dateLiteral.
+	VisitDateLiteral(ctx *DateLiteralContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#dateTimeLiteral.
+	VisitDateTimeLiteral(ctx *DateTimeLiteralContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#timeLiteral.
+	VisitTimeLiteral(ctx *TimeLiteralContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#quantityLiteral.
+	VisitQuantityLiteral(ctx *QuantityLiteralContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#externalConstant.
+	VisitExternalConstant(ctx *ExternalConstantContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#memberInvocation.
+	VisitMemberInvocation(ctx *MemberInvocationContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#functionInvocation.
+	VisitFunctionInvocation(ctx *FunctionInvocationContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#thisInvocation.
+	VisitThisInvocation(ctx *ThisInvocationContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#indexInvocation.
+	VisitIndexInvocation(ctx *IndexInvocationContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#totalInvocation.
+	VisitTotalInvocation(ctx *TotalInvocationContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#function.
+	VisitFunction(ctx *FunctionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#paramList.
+	VisitParamList(ctx *ParamListContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#quantity.
+	VisitQuantity(ctx *QuantityContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#unit.
+	VisitUnit(ctx *UnitContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#dateTimePrecision.
+	VisitDateTimePrecision(ctx *DateTimePrecisionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#pluralDateTimePrecision.
+	VisitPluralDateTimePrecision(ctx *PluralDateTimePrecisionContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#typeSpecifier.
+	VisitTypeSpecifier(ctx *TypeSpecifierContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#qualifiedIdentifier.
+	VisitQualifiedIdentifier(ctx *QualifiedIdentifierContext) interface{}
+
+	// Visit a parse tree produced by fhirpathParser#identifier.
+	VisitIdentifier(ctx *IdentifierContext) interface{}
+}
diff --git a/fhirpath/internal/grammar/generate.go b/fhirpath/internal/grammar/generate.go
new file mode 100644
index 0000000..94a4467
--- /dev/null
+++ b/fhirpath/internal/grammar/generate.go
@@ -0,0 +1,3 @@
+package grammar
+
+//go:generate ./generate.sh
diff --git a/fhirpath/internal/grammar/generate.sh b/fhirpath/internal/grammar/generate.sh
new file mode 100755
index 0000000..08a7c80
--- /dev/null
+++ b/fhirpath/internal/grammar/generate.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+shopt -s expand_aliases
+
+antlr_jar=antlr-4.13.0-complete.jar
+
+if [ ! -f ${antlr_jar} ]; then
+    wget "https://www.antlr.org/download/${antlr_jar}"
+fi
+
+alias antlr4="java -Xmx500M -cp './$antlr_jar:\$CLASSPATH' org.antlr.v4.Tool"
+antlr4 -Dlanguage=Go -no-listener -visitor -package grammar *.g4
diff --git a/fhirpath/internal/opts/opts.go b/fhirpath/internal/opts/opts.go
new file mode 100644
index 0000000..5a39f05
--- /dev/null
+++ b/fhirpath/internal/opts/opts.go
@@ -0,0 +1,66 @@
+/*
+Package opts is an internal package that exists for setting configuration
+settings for FHIRPath. This is an internal package so that only parts of this
+may be publicly re-exported, while the implementation has access to the full
+thing.
+*/
+package opts
+
+import (
+	"errors"
+
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/parser"
+)
+
+// CompileConfig provides the configuration values for the Compile command.
+type CompileConfig struct {
+	// Table is the current function table to be called.
+	Table     funcs.FunctionTable
+	Transform parser.VisitorTransform
+
+	// Permissive is a legacy option to allow FHIRpaths with *invalid* fields to be
+	// compiled (to reduce breakages).
+	Permissive bool
+}
+
+// EvaluateConfig provides the configuration values for the Evaluate command.
+type EvaluateConfig struct {
+	// Context is the current context information.
+	Context *expr.Context
+}
+
+// Option is the base interface for FHIRPath options.
+type Option[T any] interface {
+	updateConfig(*T) error
+}
+
+// CompileOption is an Option that sets CompileConfig.
+type CompileOption = Option[CompileConfig]
+
+// EvaluateOption is an Option that sets EvaluateConfig.
+type EvaluateOption = Option[EvaluateConfig]
+
+// Transform creates either an Evaluate or Compile configuration option, done
+// as a function callback.
+func Transform[T any](callback func(cfg *T) error) Option[T] {
+	return callbackOption[T]{callback: callback}
+}
+
+// ApplyOptions applies all the options to the given configuration.
+func ApplyOptions[T any](cfg *T, opts ...Option[T]) (*T, error) {
+	var errs []error
+	for _, opt := range opts {
+		errs = append(errs, opt.updateConfig(cfg))
+	}
+	return cfg, errors.Join(errs...)
+}
+
+type callbackOption[T any] struct {
+	callback func(*T) error
+}
+
+func (o callbackOption[T]) updateConfig(cfg *T) error {
+	return o.callback(cfg)
+}
diff --git a/fhirpath/internal/parser/doc.go b/fhirpath/internal/parser/doc.go
new file mode 100644
index 0000000..d1885d2
--- /dev/null
+++ b/fhirpath/internal/parser/doc.go
@@ -0,0 +1,6 @@
+/*
+Package parser provides the logic for traversing
+the ANTLR generated parse tree. Provides a visitor
+and related functions.
+*/
+package parser
diff --git a/fhirpath/internal/parser/error_handling.go b/fhirpath/internal/parser/error_handling.go
new file mode 100644
index 0000000..f182902
--- /dev/null
+++ b/fhirpath/internal/parser/error_handling.go
@@ -0,0 +1,22 @@
+package parser
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/antlr4-go/antlr/v4"
+)
+
+type FHIRPathErrorListener struct {
+	*antlr.DefaultErrorListener
+	errors []error
+}
+
+func (l *FHIRPathErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) {
+	err := fmt.Errorf("syntax error on line %d:%d - %s", line, column, msg)
+	l.errors = append(l.errors, err)
+}
+
+func (l *FHIRPathErrorListener) Error() error {
+	return errors.Join(l.errors...)
+}
diff --git a/fhirpath/internal/parser/transforms.go b/fhirpath/internal/parser/transforms.go
new file mode 100644
index 0000000..3378a62
--- /dev/null
+++ b/fhirpath/internal/parser/transforms.go
@@ -0,0 +1,12 @@
+package parser
+
+import "github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+
+// A VisitorTransform is a function which transforms the specified
+// expression. This is used in FHIRPath Patch to modify expressions.
+type VisitorTransform func(expr.Expression) expr.Expression
+
+// IdentityTransform returns the given expression without any modification.
+func IdentityTransform(e expr.Expression) expr.Expression {
+	return e
+}
diff --git a/fhirpath/internal/parser/visitor.go b/fhirpath/internal/parser/visitor.go
new file mode 100644
index 0000000..75feb97
--- /dev/null
+++ b/fhirpath/internal/parser/visitor.go
@@ -0,0 +1,515 @@
+package parser
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+
+	"github.com/antlr4-go/antlr/v4"
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/funcs/impl"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/grammar"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/reflection"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+var (
+	errNotSupported       = errors.New("expression not currently supported")
+	errTooManyQualifiers  = errors.New("too many type qualifiers")
+	errVisitingChildren   = errors.New("error while visiting child expressions")
+	errUnresolvedFunction = errors.New("function identifier can't be resolved")
+)
+
+type FHIRPathVisitor struct {
+	*antlr.BaseParseTreeVisitor
+	visitedRoot bool
+	Functions   funcs.FunctionTable
+	Transform   VisitorTransform
+	Permissive  bool
+}
+
+type VisitResult struct {
+	Result expr.Expression
+	Error  error
+}
+
+type typeResult struct {
+	result reflection.TypeSpecifier
+	err    error
+}
+
+// clone produces a shallow-clone of the visitor, to be used when visiting sub-expressions.
+func (v *FHIRPathVisitor) clone() *FHIRPathVisitor {
+	return &FHIRPathVisitor{
+		Functions:   v.Functions,
+		Transform:   v.Transform,
+		Permissive:  v.Permissive,
+		visitedRoot: false,
+	}
+}
+
+func (v *FHIRPathVisitor) transformedVisitResult(resultExpr expr.Expression) *VisitResult {
+	if v.Transform == nil {
+		v.Transform = IdentityTransform
+	}
+	return &VisitResult{v.Transform(resultExpr), nil}
+}
+
+func (v *FHIRPathVisitor) Visit(tree antlr.ParseTree) interface{} {
+	return tree.Accept(v)
+}
+
+func (v *FHIRPathVisitor) VisitProg(ctx *grammar.ProgContext) interface{} {
+	return v.Visit(ctx.Expression()).(*VisitResult)
+}
+
+// VisitIndexerExpression visits both the left side expression and right side expression, and
+// constructs an index expression. If the right side expression does not evaluate to an Integer,
+// returns an error
+func (v *FHIRPathVisitor) VisitIndexerExpression(ctx *grammar.IndexerExpressionContext) interface{} {
+	// visit left side expression
+	leftResult := v.Visit(ctx.Expression(0)).(*VisitResult)
+	if leftResult.Error != nil {
+		return &VisitResult{nil, leftResult.Error}
+	}
+
+	// visit contained expression with new Visitor to reset root node, and construct index
+	rightResult := v.clone().Visit(ctx.Expression(1)).(*VisitResult)
+	if rightResult.Error != nil {
+		return &VisitResult{nil, rightResult.Error}
+	}
+	indexExpr := &expr.IndexExpression{Index: rightResult.Result}
+
+	sequence := &expr.ExpressionSequence{Expressions: []expr.Expression{leftResult.Result, indexExpr}}
+	return v.transformedVisitResult(sequence)
+}
+
+func (v *FHIRPathVisitor) VisitPolarityExpression(ctx *grammar.PolarityExpressionContext) interface{} {
+	operator := expr.Operator(ctx.GetChild(0).(antlr.TerminalNode).GetText())
+	result := v.Visit(ctx.Expression()).(*VisitResult)
+	if result.Error != nil {
+		return &VisitResult{nil, result.Error}
+	}
+
+	// Return initial expression if the operator is '+'
+	if operator != expr.Sub {
+		return v.transformedVisitResult(result.Result)
+	}
+
+	return v.transformedVisitResult(&expr.NegationExpression{Expr: result.Result})
+}
+
+func (v *FHIRPathVisitor) VisitAdditiveExpression(ctx *grammar.AdditiveExpressionContext) interface{} {
+	leftResult := v.Visit(ctx.Expression(0)).(*VisitResult)
+	if leftResult.Error != nil {
+		return &VisitResult{nil, leftResult.Error}
+	}
+	rightResult := v.clone().Visit(ctx.Expression(1)).(*VisitResult)
+	if rightResult.Error != nil {
+		return &VisitResult{nil, rightResult.Error}
+	}
+
+	operator := expr.Operator(ctx.GetChild(1).(antlr.TerminalNode).GetText())
+
+	var expression expr.Expression
+	switch operator {
+	case expr.Concat:
+		expression = &expr.ConcatExpression{Left: leftResult.Result, Right: rightResult.Result}
+	case expr.Add:
+		expression = &expr.ArithmeticExpression{Left: leftResult.Result, Right: rightResult.Result, Op: expr.EvaluateAdd}
+	case expr.Sub:
+		expression = &expr.ArithmeticExpression{Left: leftResult.Result, Right: rightResult.Result, Op: expr.EvaluateSub}
+	}
+	return v.transformedVisitResult(expression)
+}
+
+func (v *FHIRPathVisitor) VisitMultiplicativeExpression(ctx *grammar.MultiplicativeExpressionContext) interface{} {
+	leftResult := v.Visit(ctx.Expression(0)).(*VisitResult)
+	if leftResult.Error != nil {
+		return &VisitResult{nil, leftResult.Error}
+	}
+	rightResult := v.clone().Visit(ctx.Expression(1)).(*VisitResult)
+	if rightResult.Error != nil {
+		return &VisitResult{nil, rightResult.Error}
+	}
+
+	operator := expr.Operator(ctx.GetChild(1).(antlr.TerminalNode).GetText())
+
+	// Select correct operator function.
+	var op func(system.Any, system.Any) (system.Any, error)
+	switch operator {
+	case expr.Mul:
+		op = expr.EvaluateMul
+	case expr.Div:
+		op = expr.EvaluateDiv
+	case expr.FloorDiv:
+		op = expr.EvaluateFloorDiv
+	case expr.Mod:
+		op = expr.EvaluateMod
+	}
+
+	return v.transformedVisitResult(
+		&expr.ArithmeticExpression{Left: leftResult.Result, Right: rightResult.Result, Op: op},
+	)
+}
+
+func (v *FHIRPathVisitor) VisitUnionExpression(ctx *grammar.UnionExpressionContext) interface{} {
+	return &VisitResult{nil, errNotSupported}
+}
+
+func (v *FHIRPathVisitor) VisitOrExpression(ctx *grammar.OrExpressionContext) interface{} {
+	leftResult := v.Visit(ctx.Expression(0)).(*VisitResult)
+	if leftResult.Error != nil {
+		return &VisitResult{nil, leftResult.Error}
+	}
+	rightResult := v.clone().Visit(ctx.Expression(1)).(*VisitResult)
+	if rightResult.Error != nil {
+		return &VisitResult{nil, rightResult.Error}
+	}
+
+	operator := expr.Operator(ctx.GetChild(1).(antlr.TerminalNode).GetText())
+
+	expression := &expr.BooleanExpression{Left: leftResult.Result, Right: rightResult.Result, Op: operator}
+	return v.transformedVisitResult(expression)
+}
+
+func (v *FHIRPathVisitor) VisitAndExpression(ctx *grammar.AndExpressionContext) interface{} {
+	leftResult := v.Visit(ctx.Expression(0)).(*VisitResult)
+	if leftResult.Error != nil {
+		return &VisitResult{nil, leftResult.Error}
+	}
+	rightResult := v.clone().Visit(ctx.Expression(1)).(*VisitResult)
+	if rightResult.Error != nil {
+		return &VisitResult{nil, rightResult.Error}
+	}
+
+	expression := &expr.BooleanExpression{Left: leftResult.Result, Right: rightResult.Result, Op: expr.And}
+	return v.transformedVisitResult(expression)
+}
+
+func (v *FHIRPathVisitor) VisitMembershipExpression(ctx *grammar.MembershipExpressionContext) interface{} {
+	return &VisitResult{nil, errNotSupported}
+}
+
+func (v *FHIRPathVisitor) VisitInequalityExpression(ctx *grammar.InequalityExpressionContext) interface{} {
+	leftResult := v.Visit(ctx.Expression(0)).(*VisitResult)
+	if leftResult.Error != nil {
+		return &VisitResult{nil, leftResult.Error}
+	}
+	rightResult := v.clone().Visit(ctx.Expression(1)).(*VisitResult)
+	if rightResult.Error != nil {
+		return &VisitResult{nil, rightResult.Error}
+	}
+
+	operator := expr.Operator(ctx.GetChild(1).(antlr.TerminalNode).GetText())
+
+	expression := &expr.ComparisonExpression{Left: leftResult.Result, Right: rightResult.Result, Op: operator}
+	return v.transformedVisitResult(expression)
+}
+
+// VisitInvocationExpression visits both sides, and constructs an expression sequence.
+func (v *FHIRPathVisitor) VisitInvocationExpression(ctx *grammar.InvocationExpressionContext) interface{} {
+	// Visit left side with new visitor, raising error if necessary
+	leftResult := v.Visit(ctx.Expression()).(*VisitResult)
+	if leftResult.Error != nil {
+		return &VisitResult{nil, leftResult.Error}
+	}
+
+	// Visit right side, raising error if necessary
+	rightResult := v.Visit(ctx.Invocation()).(*VisitResult)
+	if rightResult.Error != nil {
+		return &VisitResult{nil, rightResult.Error}
+	}
+
+	// Construct and return ExpressionSequence
+	expressions := []expr.Expression{leftResult.Result, rightResult.Result}
+	sequence := &expr.ExpressionSequence{Expressions: expressions}
+	return v.transformedVisitResult(sequence)
+}
+
+// VisitEqualityExpression both equality subexpressions and constructs an Equality Expression
+// from the results of each subexpression
+func (v *FHIRPathVisitor) VisitEqualityExpression(ctx *grammar.EqualityExpressionContext) interface{} {
+	leftResult := v.Visit(ctx.Expression(0)).(*VisitResult)
+	if leftResult.Error != nil {
+		return &VisitResult{nil, leftResult.Error}
+	}
+
+	rightResult := v.clone().Visit(ctx.Expression(1)).(*VisitResult)
+	if rightResult.Error != nil {
+		return &VisitResult{nil, rightResult.Error}
+	}
+	operator := ctx.GetChild(1).(antlr.TerminalNode).GetText()
+	var expression expr.Expression
+	switch operator {
+	case expr.Equals:
+		expression = &expr.EqualityExpression{Left: leftResult.Result, Right: rightResult.Result}
+	case expr.NotEquals:
+		expression = &expr.EqualityExpression{Left: leftResult.Result, Right: rightResult.Result, Not: true}
+	case expr.Equivalence:
+		// TODO (PHP-5889): Implement equivalence expressions
+	case expr.Inequivalence:
+		// TODO (PHP-5889): Implement non-equivalence expressions
+	}
+	return v.transformedVisitResult(expression)
+}
+
+func (v *FHIRPathVisitor) VisitImpliesExpression(ctx *grammar.ImpliesExpressionContext) interface{} {
+	leftResult := v.Visit(ctx.Expression(0)).(*VisitResult)
+	if leftResult.Error != nil {
+		return &VisitResult{nil, leftResult.Error}
+	}
+	rightResult := v.clone().Visit(ctx.Expression(1)).(*VisitResult)
+	if rightResult.Error != nil {
+		return &VisitResult{nil, rightResult.Error}
+	}
+
+	expression := &expr.BooleanExpression{Left: leftResult.Result, Right: rightResult.Result, Op: expr.Implies}
+	return v.transformedVisitResult(expression)
+}
+
+func (v *FHIRPathVisitor) VisitTermExpression(ctx *grammar.TermExpressionContext) interface{} {
+	return v.Visit(ctx.Term())
+}
+
+func (v *FHIRPathVisitor) VisitTypeExpression(ctx *grammar.TypeExpressionContext) interface{} {
+	expression := v.Visit(ctx.Expression()).(*VisitResult)
+	if expression.Error != nil {
+		return &VisitResult{nil, expression.Error}
+	}
+	typeSpecifier := v.Visit(ctx.TypeSpecifier()).(*typeResult)
+	if typeSpecifier.err != nil {
+		return &VisitResult{nil, typeSpecifier.err}
+	}
+	operator := ctx.GetChild(1).(antlr.TerminalNode).GetText()
+	var typeExpression expr.Expression
+	if operator == expr.Is {
+		typeExpression = &expr.IsExpression{Expr: expression.Result, Type: typeSpecifier.result}
+	}
+	if operator == expr.As {
+		typeExpression = &expr.AsExpression{Expr: expression.Result, Type: typeSpecifier.result}
+	}
+	return v.transformedVisitResult(typeExpression)
+}
+
+func (v *FHIRPathVisitor) VisitInvocationTerm(ctx *grammar.InvocationTermContext) interface{} {
+	return v.Visit(ctx.Invocation())
+}
+
+func (v *FHIRPathVisitor) VisitLiteralTerm(ctx *grammar.LiteralTermContext) interface{} {
+	return v.Visit(ctx.Literal())
+}
+
+func (v *FHIRPathVisitor) VisitExternalConstantTerm(ctx *grammar.ExternalConstantTermContext) interface{} {
+	ident := ctx.ExternalConstant().GetText()
+	ident = strings.TrimPrefix(ident, "%")
+	return v.transformedVisitResult(&expr.ExternalConstantExpression{Identifier: ident})
+}
+
+func (v *FHIRPathVisitor) VisitParenthesizedTerm(ctx *grammar.ParenthesizedTermContext) interface{} {
+	return v.Visit(ctx.Expression())
+}
+
+// VisitNullLiteral returns a NullLiteralExpression, without any error.
+func (v *FHIRPathVisitor) VisitNullLiteral(ctx *grammar.NullLiteralContext) interface{} {
+	result := &expr.LiteralExpression{}
+	return v.transformedVisitResult(result)
+}
+
+// VisitBooleanLiteral returns a BooleanLiteralExpression, returning an error if
+// there is an error during creation of the Boolean.
+func (v *FHIRPathVisitor) VisitBooleanLiteral(ctx *grammar.BooleanLiteralContext) interface{} {
+	result, err := system.ParseBoolean(ctx.GetText())
+	if err != nil {
+		return &VisitResult{nil, err}
+	}
+	expr := &expr.LiteralExpression{Literal: result}
+	return v.transformedVisitResult(expr)
+}
+
+// VisitStringLiteral returns a StringLiteralExpression, returning an error if there is an
+// error during creation of the String.
+func (v *FHIRPathVisitor) VisitStringLiteral(ctx *grammar.StringLiteralContext) interface{} {
+	result, err := system.ParseString(ctx.STRING().GetText())
+	if err != nil {
+		return &VisitResult{nil, err}
+	}
+	expr := &expr.LiteralExpression{Literal: result}
+	return v.transformedVisitResult(expr)
+}
+
+// VisitNumberLiteral returns either an integer or decimal, depending on whether or not
+// the number contains a decimal. Returns an error if there is an error during creation
+// of the number.
+func (v *FHIRPathVisitor) VisitNumberLiteral(ctx *grammar.NumberLiteralContext) interface{} {
+	number := ctx.NUMBER().GetText()
+
+	if strings.Contains(number, ".") {
+		result, err := system.ParseDecimal(number)
+		if err != nil {
+			return &VisitResult{nil, err}
+		}
+		expr := &expr.LiteralExpression{Literal: result}
+		return v.transformedVisitResult(expr)
+	}
+
+	result, err := system.ParseInteger(number)
+	if err != nil {
+		return &VisitResult{nil, err}
+	}
+	expr := &expr.LiteralExpression{Literal: result}
+	return v.transformedVisitResult(expr)
+}
+
+// VisitDateLiteral returns a DateLiteralExpression, returning an error if there is an
+// error during creation of the Date type.
+func (v *FHIRPathVisitor) VisitDateLiteral(ctx *grammar.DateLiteralContext) interface{} {
+	date, err := system.ParseDate(ctx.DATE().GetText())
+	if err != nil {
+		return &VisitResult{nil, err}
+	}
+	expr := &expr.LiteralExpression{Literal: date}
+	return v.transformedVisitResult(expr)
+}
+
+// VisitDateTimeLiteral returns a DateTimeLiteralExpression, returning an error if there
+// is an error during creation of the DateTime type.
+func (v *FHIRPathVisitor) VisitDateTimeLiteral(ctx *grammar.DateTimeLiteralContext) interface{} {
+	dateTime, err := system.ParseDateTime(ctx.DATETIME().GetText())
+	if err != nil {
+		return &VisitResult{nil, err}
+	}
+	expr := &expr.LiteralExpression{Literal: dateTime}
+	return v.transformedVisitResult(expr)
+}
+
+// VisitTimeLiteral returns a TimeLiteralExpression, returning an error if there is an error
+// during creation of the Time type.
+func (v *FHIRPathVisitor) VisitTimeLiteral(ctx *grammar.TimeLiteralContext) interface{} {
+	time, err := system.ParseTime(ctx.TIME().GetText())
+	if err != nil {
+		return &VisitResult{nil, err}
+	}
+	expr := &expr.LiteralExpression{Literal: time}
+	return v.transformedVisitResult(expr)
+}
+
+// VisitQuantityLiteral returns a QuantityLiteralExpression, returning an error if there
+// is an error during creation of the Quantity type.
+func (v *FHIRPathVisitor) VisitQuantityLiteral(ctx *grammar.QuantityLiteralContext) interface{} {
+	// remove string quotes from unit
+	unit := ctx.Quantity().Unit().GetText()
+	unit = strings.TrimPrefix(unit, "'")
+	unit = strings.TrimSuffix(unit, "'")
+
+	quantity, err := system.ParseQuantity(ctx.Quantity().NUMBER().GetText(), unit)
+	if err != nil {
+		return &VisitResult{nil, err}
+	}
+	expr := &expr.LiteralExpression{Literal: quantity}
+	return v.transformedVisitResult(expr)
+}
+
+func (v *FHIRPathVisitor) VisitExternalConstant(ctx *grammar.ExternalConstantContext) interface{} {
+	return &VisitResult{nil, errNotSupported}
+}
+
+// VisitMemberInvocation checks to see if the identifier corresponds to a resource type and is the
+// root of the expression. If so, it will return a TypeExpression. Otherwise, it returns a FieldExpression.
+func (v *FHIRPathVisitor) VisitMemberInvocation(ctx *grammar.MemberInvocationContext) interface{} {
+	identifier := ctx.GetText()
+	var expression expr.Expression
+
+	if resource.IsType(identifier) && !v.visitedRoot {
+		expression = &expr.TypeExpression{Type: identifier}
+		v.visitedRoot = true
+	} else {
+		expression = &expr.FieldExpression{FieldName: identifier, Permissive: v.Permissive}
+	}
+
+	return v.transformedVisitResult(expression)
+}
+
+func (v *FHIRPathVisitor) VisitFunctionInvocation(ctx *grammar.FunctionInvocationContext) interface{} {
+	return v.Visit(ctx.Function())
+}
+
+func (v *FHIRPathVisitor) VisitThisInvocation(ctx *grammar.ThisInvocationContext) interface{} {
+	return &VisitResult{&expr.IdentityExpression{}, nil}
+}
+
+func (v *FHIRPathVisitor) VisitIndexInvocation(ctx *grammar.IndexInvocationContext) interface{} {
+	return &VisitResult{nil, errNotSupported}
+}
+
+func (v *FHIRPathVisitor) VisitTotalInvocation(ctx *grammar.TotalInvocationContext) interface{} {
+	return &VisitResult{nil, errNotSupported}
+}
+
+func (v *FHIRPathVisitor) VisitFunction(ctx *grammar.FunctionContext) interface{} {
+	ident := ctx.Identifier().GetText()
+	fn, ok := v.Functions[ident]
+	if !ok {
+		return &VisitResult{nil, fmt.Errorf("%w: %s", errUnresolvedFunction, ident)}
+	}
+
+	results := []*VisitResult{}
+	if args := ctx.ParamList(); args != nil {
+		results = v.Visit(args).([]*VisitResult)
+	}
+
+	errs := slices.Map(results, func(r *VisitResult) error { return r.Error })
+	if err := errors.Join(errs...); err != nil {
+		return &VisitResult{nil, fmt.Errorf("%w: %w", errVisitingChildren, err)}
+	}
+
+	expressions := slices.Map(results, func(r *VisitResult) expr.Expression { return r.Result })
+	if len(expressions) < fn.MinArity || len(expressions) > fn.MaxArity {
+		return &VisitResult{nil, fmt.Errorf("%w: input arity outside of function arity bounds", impl.ErrWrongArity)}
+	}
+	return v.transformedVisitResult(&expr.FunctionExpression{Fn: fn.Func, Args: expressions})
+}
+
+func (v *FHIRPathVisitor) VisitParamList(ctx *grammar.ParamListContext) interface{} {
+	return slices.Map(ctx.AllExpression(), func(e grammar.IExpressionContext) *VisitResult { return v.Visit(e).(*VisitResult) })
+}
+
+func (v *FHIRPathVisitor) VisitQuantity(ctx *grammar.QuantityContext) interface{} {
+	return &VisitResult{nil, errNotSupported}
+}
+
+func (v *FHIRPathVisitor) VisitUnit(ctx *grammar.UnitContext) interface{} {
+	return &VisitResult{nil, errNotSupported}
+}
+
+func (v *FHIRPathVisitor) VisitDateTimePrecision(ctx *grammar.DateTimePrecisionContext) interface{} {
+	return &VisitResult{nil, errNotSupported}
+}
+
+func (v *FHIRPathVisitor) VisitPluralDateTimePrecision(ctx *grammar.PluralDateTimePrecisionContext) interface{} {
+	return &VisitResult{nil, errNotSupported}
+}
+
+func (v *FHIRPathVisitor) VisitTypeSpecifier(ctx *grammar.TypeSpecifierContext) interface{} {
+	identifiers := v.Visit(ctx.QualifiedIdentifier()).([]string)
+	if len(identifiers) == 1 {
+		specifier, err := reflection.NewTypeSpecifier(identifiers[0])
+		return &typeResult{specifier, err}
+	}
+	if len(identifiers) == 2 {
+		specifier, err := reflection.NewQualifiedTypeSpecifier(identifiers[0], identifiers[1])
+		return &typeResult{specifier, err}
+	}
+	return &typeResult{err: fmt.Errorf("%w: %s", errTooManyQualifiers, strings.Join(identifiers, ","))}
+}
+
+func (v *FHIRPathVisitor) VisitQualifiedIdentifier(ctx *grammar.QualifiedIdentifierContext) interface{} {
+	return slices.Map(ctx.AllIdentifier(), func(i grammar.IIdentifierContext) string { return i.GetText() })
+}
+
+func (v *FHIRPathVisitor) VisitIdentifier(ctx *grammar.IdentifierContext) interface{} {
+	return &VisitResult{nil, errNotSupported}
+}
diff --git a/fhirpath/internal/reflection/consts.go b/fhirpath/internal/reflection/consts.go
new file mode 100644
index 0000000..4cd06ca
--- /dev/null
+++ b/fhirpath/internal/reflection/consts.go
@@ -0,0 +1,7 @@
+package reflection
+
+// Valid namespace constants.
+const (
+	FHIR   = "FHIR"
+	System = "System"
+)
diff --git a/fhirpath/internal/reflection/doc.go b/fhirpath/internal/reflection/doc.go
new file mode 100644
index 0000000..ed51d0b
--- /dev/null
+++ b/fhirpath/internal/reflection/doc.go
@@ -0,0 +1,5 @@
+/*
+Package reflection provides types and utility functions
+to enable FHIRPath type reflection.
+*/
+package reflection
diff --git a/fhirpath/internal/reflection/elements.go b/fhirpath/internal/reflection/elements.go
new file mode 100644
index 0000000..b182a2a
--- /dev/null
+++ b/fhirpath/internal/reflection/elements.go
@@ -0,0 +1,38 @@
+package reflection
+
+import (
+	"github.com/iancoleman/strcase"
+	"github.com/verily-src/fhirpath-go/internal/protofields"
+)
+
+// IsValidFHIRPathElement checks if the input string represents
+// a valid element name. This function is importantly case-sensitive,
+// which is a distinction that is important for primitive types.
+func IsValidFHIRPathElement(name string) bool {
+	if isPrimitive(name) || name == "BackboneElement" {
+		return true
+	}
+	return protofields.IsValidElementType(primitiveToLowercase(name))
+}
+
+func isPrimitive(name string) bool {
+	switch name {
+	case "instant", "time", "date", "dateTime", "base64Binary",
+		"decimal", "boolean", "url", "code", "string", "integer", "uri",
+		"canonical", "markdown", "id", "oid", "uuid", "unsignedInt", "positiveInt":
+		return true
+	default:
+		return false
+	}
+}
+
+func primitiveToLowercase(name string) string {
+	switch name {
+	case "Instant", "Time", "Date", "DateTime", "Base64Binary",
+		"Decimal", "Boolean", "Url", "Code", "String", "Integer", "Uri",
+		"Canonical", "Markdown", "Id", "Oid", "Uuid", "UnsignedInt", "PositiveInt":
+		return strcase.ToLowerCamel(name)
+	default:
+		return name
+	}
+}
diff --git a/fhirpath/internal/reflection/type_specifier.go b/fhirpath/internal/reflection/type_specifier.go
new file mode 100644
index 0000000..1e283c9
--- /dev/null
+++ b/fhirpath/internal/reflection/type_specifier.go
@@ -0,0 +1,138 @@
+package reflection
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+	"github.com/verily-src/fhirpath-go/internal/protofields"
+)
+
+var (
+	errInvalidType      = errors.New("invalid type name")
+	errInvalidNamespace = errors.New("invalid namespace")
+	errInvalidInput     = errors.New("invalid input type")
+)
+
+// TypeSpecifier is a FHIRPath type that enables use of
+// is and as operators. Provides a namespace and type name
+type TypeSpecifier struct {
+	namespace string
+	typeName  string
+}
+
+// NewQualifiedTypeSpecifier constructs a Qualified Type Specifier given a namespace and typeName.
+// Returns an error if the typeName is not found within the namespace, or the namespace is invalid.
+func NewQualifiedTypeSpecifier(namespace string, typeName string) (TypeSpecifier, error) {
+	switch namespace {
+	case FHIR:
+		if IsValidFHIRPathElement(typeName) || protofields.IsValidResourceType(typeName) || isBaseType(typeName) {
+			return TypeSpecifier{namespace: namespace, typeName: typeName}, nil
+		}
+		return TypeSpecifier{}, fmt.Errorf("%w: %s", errInvalidType, typeName)
+	case System:
+		if system.IsValid(typeName) {
+			return TypeSpecifier{namespace: namespace, typeName: typeName}, nil
+		}
+		return TypeSpecifier{}, fmt.Errorf("%w: %s", errInvalidType, typeName)
+	default:
+		return TypeSpecifier{}, fmt.Errorf("%w: %s", errInvalidNamespace, namespace)
+	}
+}
+
+// NewTypeSpecifier constructs a Qualified Type Specifier given a typeName. The namespace
+// is inferred with the priority rules of FHIRPath. Returns an error if the typeName cannot
+// be resolved.
+func NewTypeSpecifier(typeName string) (TypeSpecifier, error) {
+	if IsValidFHIRPathElement(typeName) || protofields.IsValidResourceType(typeName) || isBaseType(typeName) {
+		return TypeSpecifier{FHIR, typeName}, nil
+	}
+	if system.IsValid(typeName) {
+		return TypeSpecifier{System, typeName}, nil
+	}
+	return TypeSpecifier{}, fmt.Errorf("%w: %s", errInvalidType, typeName)
+}
+
+// TypeOf retrieves the Type Specifier of the input, given that it is
+// a supported FHIRPath type. Otherwise, returns an error.
+func TypeOf(input any) (TypeSpecifier, error) {
+	if item, ok := input.(system.Any); ok {
+		return TypeSpecifier{System, item.Name()}, nil
+	}
+	item, ok := input.(fhir.Base)
+	if !ok {
+		return TypeSpecifier{}, fmt.Errorf("%w: no type specifier available", errInvalidInput)
+	}
+	if oneOf := protofields.UnwrapOneofField(item, "choice"); oneOf != nil {
+		item = oneOf
+	}
+	name := string(item.ProtoReflect().Descriptor().Name())
+	if protofields.IsCodeField(item) {
+		return TypeSpecifier{FHIR, "code"}, nil
+	}
+	return TypeSpecifier{FHIR, primitiveToLowercase(name)}, nil
+}
+
+// Is returns a boolean representing whether or not the receiver type is equivalent to the
+// input type, or if it's a valid subtype.
+func (ts TypeSpecifier) Is(input TypeSpecifier) system.Boolean {
+	if ts.namespace != input.namespace {
+		return false
+	}
+	// If the root type has been reached and the equality is still false, they are not equal
+	if ts == ts.parent() && ts.typeName != input.typeName {
+		return false
+	}
+	if ts.typeName == input.typeName {
+		return true
+	}
+	return ts.parent().Is(input) // Recursively compare the parent type
+}
+
+// MustCreateTypeSpecifier creates a qualified type specifier and panics if the
+// provided namespace or typeName is invalid. Returns the created TypeSpecifier
+func MustCreateTypeSpecifier(namespace string, typeName string) TypeSpecifier {
+	typeSpecifier, err := NewQualifiedTypeSpecifier(namespace, typeName)
+	if err != nil {
+		panic(err)
+	}
+	return typeSpecifier
+}
+
+func (ts TypeSpecifier) parent() TypeSpecifier {
+	if ts.namespace == System {
+		return TypeSpecifier{"System", "Any"}
+	}
+	switch ts.typeName {
+	case "code", "markdown", "id":
+		return TypeSpecifier{FHIR, "string"}
+	case "unsignedInt", "positiveInt":
+		return TypeSpecifier{FHIR, "integer"}
+	case "url", "canonical", "uuid", "oid":
+		return TypeSpecifier{FHIR, "uri"}
+	case "Duration", "MoneyQuantity", "Age", "Count", "Distance", "SimpleQuantity":
+		return TypeSpecifier{FHIR, "Quantity"}
+	case "Timing", "Dosage", "ElementDefinition":
+		return TypeSpecifier{FHIR, "BackboneElement"}
+	case "Bundle", "Binary", "Parameters", "DomainResource":
+		return TypeSpecifier{FHIR, "Resource"}
+	case "Element":
+		return ts
+	case "Resource":
+		return ts
+	default:
+		if IsValidFHIRPathElement(ts.typeName) {
+			return TypeSpecifier{FHIR, "Element"}
+		}
+		return TypeSpecifier{FHIR, "DomainResource"}
+	}
+}
+
+func isBaseType(name string) bool {
+	switch name {
+	case "Element", "Resource", "DomainResource":
+		return true
+	}
+	return false
+}
diff --git a/fhirpath/internal/reflection/type_specifier_test.go b/fhirpath/internal/reflection/type_specifier_test.go
new file mode 100644
index 0000000..5cc170e
--- /dev/null
+++ b/fhirpath/internal/reflection/type_specifier_test.go
@@ -0,0 +1,308 @@
+package reflection_test
+
+import (
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/reflection"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+func TestTypeSpecifier_Is(t *testing.T) {
+	testCases := []struct {
+		name    string
+		typeOne reflection.TypeSpecifier
+		typeTwo reflection.TypeSpecifier
+		want    system.Boolean
+	}{
+		{
+			name:    "mismatched namespaces",
+			typeOne: reflection.MustCreateTypeSpecifier("FHIR", "Element"),
+			typeTwo: reflection.MustCreateTypeSpecifier("System", "Any"),
+			want:    false,
+		},
+		{
+			name:    "same type",
+			typeOne: reflection.MustCreateTypeSpecifier("FHIR", "DomainResource"),
+			typeTwo: reflection.MustCreateTypeSpecifier("FHIR", "DomainResource"),
+			want:    true,
+		},
+		{
+			name:    "child type is parent",
+			typeOne: reflection.MustCreateTypeSpecifier("FHIR", "markdown"),
+			typeTwo: reflection.MustCreateTypeSpecifier("FHIR", "string"),
+			want:    true,
+		},
+		{
+			name:    "child type is base",
+			typeOne: reflection.MustCreateTypeSpecifier("FHIR", "markdown"),
+			typeTwo: reflection.MustCreateTypeSpecifier("FHIR", "Element"),
+			want:    true,
+		},
+		{
+			name:    "check if type name is Resource",
+			typeOne: reflection.MustCreateTypeSpecifier("FHIR", "Patient"),
+			typeTwo: reflection.MustCreateTypeSpecifier("FHIR", "Resource"),
+			want:    true,
+		},
+		{
+			name:    "check if type name is Element",
+			typeOne: reflection.MustCreateTypeSpecifier("FHIR", "Timing"),
+			typeTwo: reflection.MustCreateTypeSpecifier("FHIR", "BackboneElement"),
+			want:    true,
+		},
+		{
+			name:    "Quantity is FHIR Element",
+			typeOne: reflection.MustCreateTypeSpecifier("FHIR", "Quantity"),
+			typeTwo: reflection.MustCreateTypeSpecifier("FHIR", "Element"),
+			want:    true,
+		},
+		{
+			name:    "System type is Any",
+			typeOne: reflection.MustCreateTypeSpecifier("System", "Decimal"),
+			typeTwo: reflection.MustCreateTypeSpecifier("System", "Any"),
+			want:    true,
+		},
+		{
+			name:    "integer types are Integers",
+			typeOne: reflection.MustCreateTypeSpecifier("FHIR", "positiveInt"),
+			typeTwo: reflection.MustCreateTypeSpecifier("FHIR", "integer"),
+			want:    true,
+		},
+		{
+			name:    "Patient is DomainResource",
+			typeOne: reflection.MustCreateTypeSpecifier("FHIR", "Patient"),
+			typeTwo: reflection.MustCreateTypeSpecifier("FHIR", "DomainResource"),
+			want:    true,
+		},
+		{
+			name:    "Mismatched types",
+			typeOne: reflection.MustCreateTypeSpecifier("FHIR", "Patient"),
+			typeTwo: reflection.MustCreateTypeSpecifier("FHIR", "Practitioner"),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if got := tc.typeOne.Is(tc.typeTwo); got != tc.want {
+				t.Errorf("TypeSpecifier.Is returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestNewQualifiedTypeSpecifier_CreatesValidTS(t *testing.T) {
+	testCases := []struct {
+		name      string
+		namespace string
+		typeName  string
+		want      reflection.TypeSpecifier
+	}{
+		{
+			name:      "Valid FHIR primitive",
+			namespace: "FHIR",
+			typeName:  "decimal",
+			want:      reflection.MustCreateTypeSpecifier("FHIR", "decimal"),
+		},
+		{
+			name:      "Valid FHIR Element",
+			namespace: "FHIR",
+			typeName:  "ContactPoint",
+			want:      reflection.MustCreateTypeSpecifier("FHIR", "ContactPoint"),
+		},
+		{
+			name:      "Valid FHIR Resource",
+			namespace: "FHIR",
+			typeName:  "Medication",
+			want:      reflection.MustCreateTypeSpecifier("FHIR", "Medication"),
+		},
+		{
+			name:      "Valid System type",
+			namespace: "System",
+			typeName:  "DateTime",
+			want:      reflection.MustCreateTypeSpecifier("System", "DateTime"),
+		},
+		{
+			name:      "Valid Base type",
+			namespace: "FHIR",
+			typeName:  "Element",
+			want:      reflection.MustCreateTypeSpecifier("FHIR", "Element"),
+		},
+		{
+			name:      "Create DomainResource",
+			namespace: "FHIR",
+			typeName:  "DomainResource",
+			want:      reflection.MustCreateTypeSpecifier("FHIR", "DomainResource"),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := reflection.NewQualifiedTypeSpecifier(tc.namespace, tc.typeName)
+
+			if err != nil {
+				t.Fatalf("NewQualifiedTypeSpecifier(%s, %s) returned unexpected error: %v", tc.namespace, tc.typeName, err)
+			}
+			if got != tc.want {
+				t.Errorf("NewQualifiedTypeSpecifier(%s, %s) returned incorrect TypeSpecifier: got %v, want %v", tc.namespace, tc.typeName, got, tc.want)
+			}
+		})
+	}
+}
+
+func TestNewQualifiedTypeSpecifier_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name      string
+		namespace string
+		typeName  string
+	}{
+		{
+			name:      "FHIR primitive with wrong case",
+			namespace: "FHIR",
+			typeName:  "Decimal",
+		},
+		{
+			name:      "Non-existent FHIR type",
+			namespace: "FHIR",
+			typeName:  "Hospital",
+		},
+		{
+			name:      "Mismatched namespace",
+			namespace: "System",
+			typeName:  "Medication",
+		},
+		{
+			name:      "System type with wrong case",
+			namespace: "System",
+			typeName:  "dateTime",
+		},
+		{
+			name:      "invalid namespace",
+			namespace: "Enrichments",
+			typeName:  "Engine",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if _, err := reflection.NewQualifiedTypeSpecifier(tc.namespace, tc.typeName); err == nil {
+				t.Fatalf("NewQualifiedTypeSpecifier(%s, %s) didn't return error when expected to", tc.namespace, tc.typeName)
+			}
+		})
+	}
+}
+
+func TestNewTypeSpecifier_CreatesValidTS(t *testing.T) {
+	testCases := []struct {
+		name     string
+		typeName string
+		want     reflection.TypeSpecifier
+	}{
+		{
+			name:     "creates resource specifier from Patient",
+			typeName: "Patient",
+			want:     reflection.MustCreateTypeSpecifier("FHIR", "Patient"),
+		},
+		{
+			name:     "creates element specifier from Element",
+			typeName: "Element",
+			want:     reflection.MustCreateTypeSpecifier("FHIR", "Element"),
+		},
+		{
+			name:     "creates system specifier from Decimal",
+			typeName: "Decimal",
+			want:     reflection.MustCreateTypeSpecifier("System", "Decimal"),
+		},
+		{
+			name:     "creates FHIR specifier from decimal",
+			typeName: "decimal",
+			want:     reflection.MustCreateTypeSpecifier("FHIR", "decimal"),
+		},
+		{
+			name:     "creates FHIR specifier from Quantity",
+			typeName: "Quantity",
+			want:     reflection.MustCreateTypeSpecifier("FHIR", "Quantity"),
+		},
+		{
+			name:     "Creates System specifier from Any",
+			typeName: "Any",
+			want:     reflection.MustCreateTypeSpecifier("System", "Any"),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := reflection.NewTypeSpecifier(tc.typeName)
+
+			if err != nil {
+				t.Fatalf("NewTypeSpecifier(%s) returned unexpected error: %v", tc.name, err)
+			}
+			if got != tc.want {
+				t.Errorf("NewTypeSpecifier(%s) returned incorrect TypeSpecifier: got %v, want %v", tc.typeName, got, tc.want)
+			}
+		})
+	}
+}
+
+func TestNewTypeSpecifier_ReturnsError(t *testing.T) {
+	if _, err := reflection.NewTypeSpecifier("notAType"); err == nil {
+		t.Fatalf("NewTypeSpecifier(%s) didn't raise error on invalid type name", "notAType")
+	}
+}
+
+func TestTypeOf_ReturnsTS(t *testing.T) {
+	quantity, _ := system.ParseQuantity("123", "kg")
+
+	testCases := []struct {
+		name  string
+		input any
+		want  reflection.TypeSpecifier
+	}{
+		{
+			name:  "Gets correct specifier for Patient type",
+			input: (*ppb.Patient)(nil),
+			want:  reflection.MustCreateTypeSpecifier("FHIR", "Patient"),
+		},
+		{
+			name:  "Gets lowercase type name for primitive type",
+			input: (*dtpb.Decimal)(nil),
+			want:  reflection.MustCreateTypeSpecifier("FHIR", "decimal"),
+		},
+		{
+			name:  "Gets correct specifier for Code type",
+			input: (*ppb.Patient_GenderCode)(nil),
+			want:  reflection.MustCreateTypeSpecifier("FHIR", "code"),
+		},
+		{
+			name:  "Gets correct specifier for Oneof type",
+			input: &ppb.Patient_DeceasedX{Choice: &ppb.Patient_DeceasedX_DateTime{DateTime: fhir.DateTimeNow()}},
+			want:  reflection.MustCreateTypeSpecifier("FHIR", "dateTime"),
+		},
+		{
+			name:  "Gets correct specifier for system type",
+			input: quantity,
+			want:  reflection.MustCreateTypeSpecifier("System", "Quantity"),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := reflection.TypeOf(tc.input)
+
+			if err != nil {
+				t.Fatalf("GetTypeSpecifier returned unexpected error: %v", err)
+			}
+			if got != tc.want {
+				t.Errorf("GetTypeSpecifier returned incorrect type specifier: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestGetTypeSpecifier_ReturnsError(t *testing.T) {
+	if _, err := reflection.TypeOf("unsupported type"); err == nil {
+		t.Fatalf("GetTypeSpecifier didn't return error for unsupported type")
+	}
+}
diff --git a/fhirpath/options.go b/fhirpath/options.go
new file mode 100644
index 0000000..6517c33
--- /dev/null
+++ b/fhirpath/options.go
@@ -0,0 +1,32 @@
+package fhirpath
+
+import (
+	"github.com/verily-src/fhirpath-go/fhirpath/compopts"
+	"github.com/verily-src/fhirpath-go/fhirpath/evalopts"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/opts"
+)
+
+// CompileOption is a function type that modifies a passed in compileOption.
+// Can define mutator functions of this type (see WithLimitation below)
+type CompileOption = opts.CompileOption
+
+// EvaluateOption is a function type that mutates the evalOptions type.
+type EvaluateOption = opts.EvaluateOption
+
+// WithFunction is a compile option that allows the addition of user-defined
+// functions to a FHIRPath expression. Function argument must match the signature
+// func(Collection, ...any) (Collection, error), or an error will be raised.
+//
+// Deprecated: Use compopts.Function instead.
+func WithFunction(name string, fn any) CompileOption {
+	return compopts.AddFunction(name, fn)
+}
+
+// WithConstant is an EvaluateOption that allows the addition of external
+// constant variables. An error will be raised if the value passed in is
+// neither a fhir proto or system type.
+//
+// Deprecated: Use evalopts.EnvVariable instead
+func WithConstant(name string, value any) EvaluateOption {
+	return evalopts.EnvVariable(name, value)
+}
diff --git a/fhirpath/patch/doc.go b/fhirpath/patch/doc.go
new file mode 100644
index 0000000..aa021e8
--- /dev/null
+++ b/fhirpath/patch/doc.go
@@ -0,0 +1,7 @@
+/*
+Package patch implements the FHIRPath Patch specification.
+
+More documentation about the specification can be found on HL7:
+https://hl7.org/fhir/R4/fhirpatch.html#3.1.5.
+*/
+package patch
diff --git a/fhirpath/patch/patch.go b/fhirpath/patch/patch.go
new file mode 100644
index 0000000..4d48b21
--- /dev/null
+++ b/fhirpath/patch/patch.go
@@ -0,0 +1,597 @@
+package patch
+
+import (
+	"errors"
+	"fmt"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/iancoleman/strcase"
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"github.com/verily-src/fhirpath-go/fhirpath"
+	"github.com/verily-src/fhirpath-go/fhirpath/compopts"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/compile"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/expr"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/opts"
+	"github.com/verily-src/fhirpath-go/fhirpath/internal/parser"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+var (
+	ErrNotImplemented     = errors.New("not implemented")
+	ErrInvalidInput       = errors.New("invalid input")
+	ErrInvalidEnum        = errors.New("invalid enum value")
+	ErrInvalidField       = fhirpath.ErrInvalidField
+	ErrInvalidUnsignedInt = errors.New("invalid value for unsigned int")
+	ErrNotSingleton       = expr.ErrNotSingleton
+	ErrNotPatchable       = errors.New("result is not patchable")
+)
+
+// Options encapsulates all possible FHIRPath options that
+// can be used in the underlying FHIRPath evaluation before
+// patching. This includes both compile-time and evaluation-time
+// options.
+type Options struct {
+	CompileOpts []opts.CompileOption
+	EvalOpts    []opts.EvaluateOption
+}
+
+// Expression is the FHIRPath Patch expression that will be
+// compiled from a FHIRPath string.
+type Expression struct {
+	expression expr.Expression
+	path       string
+}
+
+// String returns the underlying FHIRPath expression.
+func (e *Expression) String() string {
+	return e.path
+}
+
+// Compile parses and compiles the FHIRPath Patch expression down
+// to a single Expression object.
+//
+// If there are any syntax or semantic errors, this will return an
+// error indicating the reason for the compilation failure.
+func Compile(path string, options ...opts.CompileOption) (*Expression, error) {
+	options = append(options, compopts.Transform(func(e expr.Expression) expr.Expression {
+		return storeLastExpression{e}
+	}))
+
+	config, err := compile.PopulateConfig(options...)
+	if err != nil {
+		return nil, err
+	}
+
+	tree, err := compile.Tree(path)
+	if err != nil {
+		return nil, err
+	}
+
+	visitor := &parser.FHIRPathVisitor{
+		Functions:  config.Table,
+		Transform:  config.Transform,
+		Permissive: config.Permissive,
+	}
+	vr, ok := visitor.Visit(tree).(*parser.VisitResult)
+	if !ok {
+		return nil, errors.New("input expression currently unsupported")
+	}
+
+	if vr.Error != nil {
+		return nil, vr.Error
+	}
+	return &Expression{
+		expression: vr.Result,
+		path:       path,
+	}, nil
+}
+
+// Add appends a value to the given field name in the element.
+// Add can be used for non-repeating elements so long as they do not already exist.
+// The field name must be in the same case as defined in the FHIRPath spec,
+// which is in camelCase
+//
+// Note: value inputs to Add must be FHIR elements or FHIR resources.
+//
+// This function will return the following errors in the given conditions:
+//
+//   - ErrInvalidInput if the input value to add is incorrect for the result of
+//     the expression
+//   - ErrInvalidField if the specified field name does not exist in the
+//     returned element
+//   - ErrNotSingleton if the result of the evaluation returns more than one
+//     entry
+//   - ErrNotPatchable if the result of the returned entry is not a patchable
+//     type -- e.g. a non-scalar type like a list, a value that is already
+//     set, etc.
+//
+// See documentation: https://hl7.org/fhir/R4/fhirpatch.html#concept.
+func (e *Expression) Add(res fhir.Resource, name string, value fhir.Base, options ...fhirpath.EvaluateOption) error {
+	if strcase.ToLowerCamel(name) != name {
+		// All field names in FHIRPath are in camelCase, but the protos are in
+		// snake_case. To avoid accidentally accepting code like "foo_value" instead
+		// of "fooValue", we check first that we are already in the correct form,
+		// and error if it would never be possible.
+		return fmt.Errorf("%w: '%v'", ErrInvalidField, name)
+	}
+	if res == nil {
+		return fmt.Errorf("%w: nil input resource", ErrInvalidInput)
+	}
+	if value == nil {
+		return fmt.Errorf("%w: nil replacement value", ErrInvalidInput)
+	}
+
+	_, evalResult, err := e.evaluate(res, options...)
+	if err != nil {
+		return err
+	}
+
+	singleton, err := evalResult.ToSingleton()
+	if err != nil {
+		return fmt.Errorf("%w: fhirpatch add requires singleton collection", ErrNotSingleton)
+	}
+
+	proto, ok := singleton.(proto.Message)
+	if !ok {
+		return fmt.Errorf("%w: result of type '%T' is not patchable", ErrNotPatchable, singleton)
+	}
+
+	ref := proto.ProtoReflect()
+	descriptor := ref.Descriptor()
+	fieldName := strcase.ToSnake(name)
+	field := descriptor.Fields().ByName(protoreflect.Name(fieldName))
+	if field == nil {
+		fieldName += "_value"
+		field = descriptor.Fields().ByName(protoreflect.Name(fieldName))
+		if field == nil {
+			return fmt.Errorf("%w: '%v'", fhirpath.ErrInvalidField, name)
+		}
+	}
+
+	if !field.IsList() && ref.Has(field) {
+		return fmt.Errorf("%w: unable to add value to populated scalar field '%v' in %v resource", ErrNotPatchable, name, resource.TypeOf(res))
+	}
+
+	var update func(m protoreflect.ProtoMessage)
+	var valueMessage protoreflect.Message
+	if field.IsList() {
+		list := ref.Mutable(field).List()
+		update = func(m protoreflect.ProtoMessage) {
+			list.Append(protoreflect.ValueOfMessage(m.ProtoReflect()))
+		}
+		valueMessage = list.NewElement().Message()
+	} else {
+		update = func(m protoreflect.ProtoMessage) {
+			ref.Set(field, protoreflect.ValueOfMessage(m.ProtoReflect()))
+		}
+		valueMessage = ref.Get(field).Message()
+	}
+
+	// Special handling for oneof fields, like "Extension", "ContainedResource", etc.
+	if e.isSingletonOneof(valueMessage.Interface()) {
+		container := e.newSetOneof(valueMessage, value)
+		if container == nil {
+			return fmt.Errorf(
+				"%w: '%v' value provided for field '%v' (which is of type '%v')",
+				ErrInvalidInput,
+				value.ProtoReflect().Descriptor().Name(),
+				name,
+				valueMessage.Descriptor().Name(),
+			)
+		}
+		update(container.Interface())
+	} else {
+		// Normalize data being patched
+		value, err = e.normalizeAdd(valueMessage, value)
+		if err != nil {
+			return err
+		}
+
+		if valueMessage.Descriptor() != value.ProtoReflect().Descriptor() {
+			return fmt.Errorf(
+				"%w: '%v' value provided for field '%v' (which is of type '%v')",
+				ErrInvalidInput,
+				value.ProtoReflect().Descriptor().Name(),
+				name,
+				valueMessage.Descriptor().Name(),
+			)
+		}
+		update(value)
+	}
+
+	return nil
+}
+
+// stringable is an interface to check for a string-valued FHIR type.
+// code, markdown and id are all specializations of string that satisfy
+// this interface.
+// See: https://hl7.org/fhir/r4/datatypes.html
+type stringable interface {
+	GetValue() string
+}
+
+// stringable is an interface to check for a integer-valued FHIR type.
+// See: https://hl7.org/fhir/r4/datatypes.html
+type intable interface {
+	GetValue() int32
+}
+
+// normalizeAdd normalizes a value to be patched to the correct type.
+// If no normalization is required, the input value will be returned
+// unmodified from its original value.
+func (e *Expression) normalizeAdd(valueMessage protoreflect.Message, value fhir.Base) (fhir.Base, error) {
+	var newVal fhir.Base
+	var err error
+	switch value := value.(type) {
+	case stringable:
+		newVal, err = enumFromStringable(valueMessage, value)
+		if newVal == nil && err == nil {
+			// Check for a reference field - since these are dynamic,
+			// we need to patch them in after the reference is created,
+			// which is why we're only updating the ID field here.
+			valueField := valueMessage.Descriptor().Fields().ByName("value")
+			if valueField.FullName() == "google.fhir.r4.core.ReferenceId.value" {
+				newVal = &dtpb.ReferenceId{Value: value.GetValue()}
+			}
+		}
+	case intable:
+		newVal, err = intValueFromInt(valueMessage, value)
+	default:
+	}
+
+	if err != nil {
+		return nil, err
+	}
+	// The value was normalized
+	if newVal != nil {
+		return newVal, nil
+	}
+
+	// Use the original value without normalization
+	return value, nil
+}
+
+func (e *Expression) evaluate(res fhir.Resource, options ...fhirpath.EvaluateOption) (*expr.Context, system.Collection, error) {
+	collection := system.Collection{res}
+	config := &opts.EvaluateConfig{
+		Context: expr.InitializeContext(collection),
+	}
+	config, err := opts.ApplyOptions(config, options...)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	result, err := e.expression.Evaluate(config.Context, collection)
+	return config.Context, result, err
+}
+
+func (e *Expression) isSingletonOneof(msg proto.Message) bool {
+	message := msg.ProtoReflect()
+	descriptor := message.Descriptor()
+	oneofs := descriptor.Oneofs()
+	return oneofs.Len() == 1 && oneofs.ByName("reference") == nil
+}
+
+func (e *Expression) newSetOneof(msg protoreflect.Message, value proto.Message) protoreflect.Message {
+	container := msg.New()
+	descriptor := container.Descriptor()
+	fields := descriptor.Fields()
+
+	for i := 0; i < fields.Len(); i++ {
+		field := fields.Get(i)
+		if value.ProtoReflect().Descriptor() == msg.Get(field).Message().Descriptor() {
+			container.Set(field, protoreflect.ValueOfMessage(value.ProtoReflect()))
+			return container
+		}
+	}
+	return nil
+}
+
+// Delete removes the element of the evaluated expression. It can only remove
+// single elements from a resource.
+//
+// See documentation: https://hl7.org/fhir/R4/fhirpatch.html#concept.
+func (e *Expression) Delete(res fhir.Resource, options ...fhirpath.EvaluateOption) error {
+	if res == nil {
+		return fmt.Errorf("%w: nil input resource", ErrInvalidInput)
+	}
+	ctx, evalResult, err := e.evaluate(res, options...)
+	if err != nil {
+		return err
+	}
+	// If we have an empty value, it means the field is already deleted.
+	if evalResult.IsEmpty() {
+		return nil
+	}
+	toDelete, err := evalResult.ToSingleton()
+	if err != nil {
+		return fmt.Errorf("%w: fhirpatch delete can only delete a single element", ErrNotSingleton)
+	}
+
+	if err := e.tryDelete(ctx.LastResult, toDelete); err != nil {
+		if err := e.tryDelete(ctx.BeforeLastResult, toDelete); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (e *Expression) tryDelete(collection system.Collection, toDelete any) error {
+	var message protoreflect.Message
+	var field protoreflect.FieldDescriptor
+	var idx int
+	for _, entry := range collection {
+		var ok bool
+		root, ok := entry.(proto.Message)
+		if !ok {
+			continue
+		}
+
+		field, idx, ok = e.getFieldForCollection(root, system.Collection{toDelete})
+		if ok {
+			message = root.ProtoReflect()
+			break
+		}
+	}
+	if field == nil {
+		return fmt.Errorf("%w: field cannot be deleted", ErrNotPatchable)
+	}
+	if field.IsList() {
+		list := message.Get(field).List()
+		if idx == -1 {
+			if list.Len() <= 1 {
+				message.Clear(field)
+				return nil
+			}
+			return fmt.Errorf("%w: list containing more than one element cannot be deleted", ErrNotPatchable)
+		}
+		newlist := message.NewField(field).List()
+		for i := 0; i < idx; i++ {
+			newlist.Append(list.Get(i))
+		}
+		for i := idx + 1; i < list.Len(); i++ {
+			newlist.Append(list.Get(i))
+		}
+		message.Set(field, protoreflect.ValueOfList(newlist))
+	} else {
+		message.Clear(field)
+	}
+	return nil
+}
+
+// Insert inserts a value into the expression's list, at the 0-based index specified.
+// Prefer Add() if you are inserting at the end of a list.
+//
+// See documentation: https://hl7.org/fhir/R4/fhirpatch.html#concept.
+func (e *Expression) Insert(res fhir.Resource, value fhir.Base, index int, options ...fhirpath.EvaluateOption) error {
+	if res == nil {
+		return fmt.Errorf("%w: nil input resource", ErrInvalidInput)
+	}
+	ctx, evalResult, err := e.evaluate(res, options...)
+	if err != nil {
+		return err
+	}
+	last, err := ctx.LastResult.ToSingleton()
+	if err != nil {
+		return fmt.Errorf("%w: fhirpatch insert requires single element to operate on", ErrNotSingleton)
+	}
+	root, ok := last.(proto.Message)
+	if !ok {
+		return fmt.Errorf("%w: %T type is not a FHIR type", ErrNotPatchable, last)
+	}
+	field, _, ok := e.getFieldForCollection(root, evalResult)
+	if !ok {
+		return fmt.Errorf("%w: field is empty", ErrNotPatchable)
+	}
+	if !field.IsList() {
+		return fmt.Errorf("%w: named field is not a list", ErrNotPatchable)
+	}
+
+	reflect := root.ProtoReflect()
+	existing := reflect.Get(field).List()
+	if index > existing.Len() || index < 0 {
+		return fmt.Errorf("%w: index %v is out of range", ErrNotPatchable, index)
+	}
+	if elem := existing.NewElement(); elem.Message().Descriptor() != value.ProtoReflect().Descriptor() {
+		return fmt.Errorf("%w: Element %T is not assignable to %T", ErrNotPatchable, value, elem)
+	}
+	// Recreate the list at this field
+	list := reflect.NewField(field).List()
+
+	// Rebuild the list in order
+	for i := 0; i < existing.Len(); i++ {
+		if i == index {
+			list.Append(protoreflect.ValueOfMessage(value.ProtoReflect()))
+		}
+		list.Append(existing.Get(i))
+	}
+	// Handle insertion at the end
+	if index == existing.Len() {
+		list.Append(protoreflect.ValueOfMessage(value.ProtoReflect()))
+	}
+	reflect.Set(field, protoreflect.ValueOfList(list))
+
+	return nil
+}
+
+// getFieldForCollection returns the FieldDescriptor that corresponds to the field
+// that contains the entries in `collection`.
+func (e *Expression) getFieldForCollection(root proto.Message, collection system.Collection) (protoreflect.FieldDescriptor, int, bool) {
+	if len(collection) == 0 {
+		return nil, -1, false
+	}
+	ref := root.ProtoReflect()
+	descriptor := ref.Descriptor()
+	for _, entry := range collection {
+		msg, ok := entry.(proto.Message)
+		if !ok {
+			continue
+		}
+		fields := descriptor.Fields()
+		for i := 0; i < fields.Len(); i++ {
+			field := fields.Get(i)
+			if !ref.Has(field) {
+				continue
+			}
+
+			value := ref.Get(field)
+			if field.Cardinality() == protoreflect.Repeated {
+				list := value.List()
+				for i := 0; i < list.Len(); i++ {
+					entry := list.Get(i).Message().Interface()
+					if entry == msg {
+						return field, i, true
+					}
+				}
+			} else if field.Kind() == protoreflect.MessageKind {
+				if value.Message().Interface() == msg {
+					return field, -1, true
+				}
+			} else {
+				return nil, -1, false
+			}
+		}
+	}
+	return nil, -1, false
+}
+
+// Move moves an element within the expression's list from one index to another.
+//
+// See documentation: https://hl7.org/fhir/R4/fhirpatch.html#concept.
+func (e *Expression) Move(resource fhir.Resource, sourceIndex, destIndex int, options ...fhirpath.EvaluateOption) error {
+	return ErrNotImplemented
+}
+
+// Replace replaces the original value of the expression with the provided value.
+//
+// See documentation: https://hl7.org/fhir/R4/fhirpatch.html#concept.
+func (e *Expression) Replace(resource fhir.Resource, value any, options ...fhirpath.EvaluateOption) error {
+	return ErrNotImplemented
+}
+
+// Add appends a value to the element identified in the path, using the name specified.
+// Add can be used for non-repeating elements so long as they do not already exist.
+//
+// See documentation: https://hl7.org/fhir/R4/fhirpatch.html#concept.
+func Add(resource fhir.Resource, path, name string, value fhir.Base, opts *Options) error {
+	expr, err := Compile(path, opts.CompileOpts...)
+	if err != nil {
+		return err
+	}
+	return expr.Add(resource, name, value, opts.EvalOpts...)
+}
+
+// Delete removes the element at the specified path. It can only remove
+// single elements from a resource.
+//
+// See documentation: https://hl7.org/fhir/R4/fhirpatch.html#concept.
+func Delete(resource fhir.Resource, path string, options ...opts.CompileOption) error {
+	expr, err := Compile(path, options...)
+	if err != nil {
+		return err
+	}
+	return expr.Delete(resource)
+}
+
+// Insert inserts a value into the specified list, at the 0-based index specified.
+// Prefer Add() if you are inserting at the end of a list.
+//
+// See documentation: https://hl7.org/fhir/R4/fhirpatch.html#concept.
+func Insert(resource fhir.Resource, path string, value fhir.Base, index int, options ...opts.CompileOption) error {
+	expr, err := Compile(path, options...)
+	if err != nil {
+		return err
+	}
+	return expr.Insert(resource, value, index)
+}
+
+// Move moves an element within the specified list from one index to another.
+//
+// See documentation: https://hl7.org/fhir/R4/fhirpatch.html#concept.
+func Move(resource fhir.Resource, path string, sourceIndex, destIndex int, options ...opts.CompileOption) error {
+	expr, err := Compile(path, options...)
+	if err != nil {
+		return err
+	}
+	return expr.Move(resource, sourceIndex, destIndex)
+}
+
+// Replace replaces the original value at the specified path with the provided value.
+//
+// See documentation: https://hl7.org/fhir/R4/fhirpatch.html#concept.
+func Replace(resource fhir.Resource, path string, value any, options ...opts.CompileOption) error {
+	expr, err := Compile(path, options...)
+	if err != nil {
+		return err
+	}
+	return expr.Replace(resource, value)
+}
+
+// storeLastExpression is a simple Expression object that can be used to store
+// the last result of an evaluation (e.g. the last returned collection that
+// occurs before the last evaluation node).
+type storeLastExpression struct {
+	delegate expr.Expression
+}
+
+func (e storeLastExpression) Evaluate(ctx *expr.Context, in system.Collection) (system.Collection, error) {
+	// Only store the last result if the slice is not identical to the previous one.
+	// This exists in case an intermediate or final node is a no-op that does not
+	// alter the slice, e.g.: `Patient.name.trace('something')` -- which would
+	// yield the same output as `Patient.name` would.
+	if !slices.IsIdentical(ctx.LastResult, in) {
+		ctx.BeforeLastResult = ctx.LastResult
+		ctx.LastResult = in
+	}
+	return e.delegate.Evaluate(ctx, in)
+}
+
+// enumFromStringable parses a string value into an enum if
+// the value field's type is an enum.
+func enumFromStringable(msg protoreflect.Message, val stringable) (fhir.Base, error) {
+	strVal := val.GetValue()
+	container := msg.New()
+	valueField := container.Descriptor().Fields().ByName("value")
+	if valueField != nil && valueField.Kind() == protoreflect.EnumKind {
+		if strcase.ToKebab(strVal) != strVal {
+			return nil, fmt.Errorf("%w: %q", ErrInvalidEnum, strVal)
+		}
+		enumValueStr := protoreflect.Name(strcase.ToScreamingSnake(strVal))
+		enum := valueField.Enum().Values().ByName(enumValueStr)
+		if enum == nil {
+			return nil, fmt.Errorf("%w: %q", ErrInvalidEnum, enumValueStr)
+		}
+		enumVal := protoreflect.ValueOfEnum(protoreflect.EnumNumber(enum.Number()))
+		container.Set(valueField, enumVal)
+		return container.Interface(), nil
+	}
+	return nil, nil
+}
+
+// intValueFromInt converts an integer to the appropriate type.
+// The type could be integer, unsignedInt, or positiveInt.
+func intValueFromInt(msg protoreflect.Message, val intable) (fhir.Base, error) {
+	container := msg.New()
+	valueField := container.Descriptor().Fields().ByName("value")
+	if valueField != nil {
+		var intValue protoreflect.Value
+		switch valueField.Kind() {
+		case protoreflect.Int32Kind:
+			intValue = protoreflect.ValueOfInt32(val.GetValue())
+		case protoreflect.Uint32Kind:
+			if val.GetValue() < 0 {
+				return nil, fmt.Errorf("%w: %v", ErrInvalidUnsignedInt, val.GetValue())
+			}
+			intValue = protoreflect.ValueOfUint32(uint32(val.GetValue()))
+		default:
+		}
+		container.Set(valueField, intValue)
+		return container.Interface(), nil
+	}
+	return nil, nil
+}
diff --git a/fhirpath/patch/patch_test.go b/fhirpath/patch/patch_test.go
new file mode 100644
index 0000000..7ee5e4d
--- /dev/null
+++ b/fhirpath/patch/patch_test.go
@@ -0,0 +1,824 @@
+package patch_test
+
+import (
+	"errors"
+	"testing"
+
+	cpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/codes_go_proto"
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	bcrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	epb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/encounter_go_proto"
+	ispb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/imaging_study_go_proto"
+	opb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/observation_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	rgpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/request_group_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/containedresource"
+	"github.com/verily-src/fhirpath-go/internal/element/extension"
+	"github.com/verily-src/fhirpath-go/internal/element/reference"
+	"github.com/verily-src/fhirpath-go/fhirpath"
+	"github.com/verily-src/fhirpath-go/fhirpath/patch"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+var patientWithBirthDate = &ppb.Patient{
+	BirthDate: fhir.MustParseDate("1993-05-16"),
+}
+
+func TestAdd_ValidInputs_ModifiesResource(t *testing.T) {
+	patientRef, _ := reference.Typed("Patient", "123")
+
+	testCases := []struct {
+		name  string
+		path  string
+		field string
+		input fhir.Resource
+		value fhir.Base
+		want  fhir.Resource
+	}{
+		{
+			name:  "Adds scalar field",
+			path:  "Patient",
+			field: "birthDate",
+			input: &ppb.Patient{},
+			value: fhir.MustParseDate("1993-05-16"),
+			want: &ppb.Patient{
+				BirthDate: fhir.MustParseDate("1993-05-16"),
+			},
+		}, {
+			name:  "Adds scalar field with reserved name",
+			path:  "Encounter",
+			field: "class",
+			input: &epb.Encounter{},
+			value: fhir.Coding("", ""),
+			want: &epb.Encounter{
+				ClassValue: fhir.Coding("", ""),
+			},
+		}, {
+			name:  "Adds non-enum string field",
+			path:  "Patient.maritalStatus",
+			field: "text",
+			input: &ppb.Patient{
+				MaritalStatus: &dtpb.CodeableConcept{},
+			},
+			value: fhir.String("H0H0H0"),
+			want: &ppb.Patient{
+				MaritalStatus: fhir.CodeableConcept("H0H0H0"),
+			},
+		}, {
+			name:  "Adds enum field",
+			path:  "Patient",
+			field: "gender",
+			input: &ppb.Patient{},
+			value: fhir.String("male"),
+			want: &ppb.Patient{
+				Gender: &ppb.Patient_GenderCode{
+					Value: cpb.AdministrativeGenderCode_MALE,
+				},
+			},
+		}, {
+			name:  "Adds valid integer to positiveInt field",
+			path:  "Patient.telecom[0]",
+			field: "rank",
+			input: &ppb.Patient{
+				Telecom: []*dtpb.ContactPoint{{}},
+			},
+			value: fhir.Integer(1),
+			want: &ppb.Patient{
+				Telecom: []*dtpb.ContactPoint{
+					{
+						Rank: fhir.PositiveInt(1),
+					},
+				},
+			},
+		}, {
+			name:  "Adds negative integer to integer field",
+			path:  "Patient.extension[0]",
+			field: "value",
+			input: &ppb.Patient{
+				Extension: []*dtpb.Extension{
+					{
+						Url: fhir.URI(""),
+					},
+				},
+			},
+			value: fhir.Integer(-10),
+			want: &ppb.Patient{
+				Extension: []*dtpb.Extension{
+					extension.New("", fhir.Integer(-10)),
+				},
+			},
+		}, {
+			name:  "Adds integer to unsigned integer field",
+			path:  "ImagingStudy",
+			field: "numberOfSeries",
+			input: &ispb.ImagingStudy{},
+			value: fhir.Integer(0),
+			want: &ispb.ImagingStudy{
+				NumberOfSeries: fhir.UnsignedInt(0),
+			},
+		}, {
+			name:  "Appends extension field",
+			path:  "Patient",
+			field: "extension",
+			input: &ppb.Patient{},
+			value: extension.New("", fhir.String("hello world")),
+			want: &ppb.Patient{
+				Extension: []*dtpb.Extension{
+					extension.New("", fhir.String("hello world")),
+				},
+			},
+		},
+		{
+			name:  "Adds reference field",
+			path:  "Observation",
+			field: "subject",
+			input: &opb.Observation{},
+			value: patientRef,
+			want: &opb.Observation{
+				Subject: patientRef,
+			},
+		},
+		{
+			name:  "Adds id to existing reference field",
+			path:  "Observation.subject",
+			field: "patientId",
+			input: &opb.Observation{
+				Subject: &dtpb.Reference{
+					Type: fhir.URI("Patient"),
+				},
+			},
+			value: fhir.String("123"),
+			want: &opb.Observation{
+				Subject: patientRef,
+			},
+		},
+		{
+			name:  "Adds extension oneof field",
+			path:  "Patient.extension[0]",
+			field: "value",
+			input: &ppb.Patient{
+				Extension: []*dtpb.Extension{
+					{},
+				},
+			},
+			value: fhir.String("hello world"),
+			want: &ppb.Patient{
+				Extension: []*dtpb.Extension{
+					{
+						Value: &dtpb.Extension_ValueX{
+							Choice: &dtpb.Extension_ValueX_StringValue{
+								StringValue: fhir.String("hello world"),
+							},
+						},
+					},
+				},
+			},
+		}, {
+			name:  "Adds contained resource oneof field",
+			path:  "Bundle.entry[0]",
+			field: "resource",
+			input: &bcrpb.Bundle{
+				Entry: []*bcrpb.Bundle_Entry{
+					{},
+				},
+			},
+			value: &ppb.Patient{},
+			want: &bcrpb.Bundle{
+				Entry: []*bcrpb.Bundle_Entry{
+					{
+						Resource: containedresource.Wrap(&ppb.Patient{}),
+					},
+				},
+			},
+		}, {
+			name:  "Appends bundle entry",
+			path:  "Bundle",
+			field: "entry",
+			input: &bcrpb.Bundle{
+				Entry: []*bcrpb.Bundle_Entry{
+					{},
+				},
+			},
+			value: &bcrpb.Bundle_Entry{
+				Resource: containedresource.Wrap(&ppb.Patient{}),
+			},
+			want: &bcrpb.Bundle{
+				Entry: []*bcrpb.Bundle_Entry{
+					{},
+					{
+						Resource: containedresource.Wrap(&ppb.Patient{}),
+					},
+				},
+			},
+		}, {
+			name:  "Setting start field of RequestGroup extension period",
+			path:  "RequestGroup.extension.where(url='123').value as FHIR.Period",
+			field: "start",
+			input: &rgpb.RequestGroup{
+				Extension: []*dtpb.Extension{
+					{},
+					{
+						Url: fhir.URI("123"),
+						Value: &dtpb.Extension_ValueX{
+							Choice: &dtpb.Extension_ValueX_Period{
+								Period: &dtpb.Period{},
+							},
+						},
+					},
+					{},
+				},
+			},
+			value: fhir.MustParseDateTime("2006-01-02T15:04:05Z"),
+			want: &rgpb.RequestGroup{
+				Extension: []*dtpb.Extension{
+					{},
+					{
+						Url: fhir.URI("123"),
+						Value: &dtpb.Extension_ValueX{
+							Choice: &dtpb.Extension_ValueX_Period{
+								Period: &dtpb.Period{
+									Start: fhir.MustParseDateTime("2006-01-02T15:04:05Z"),
+								},
+							},
+						},
+					},
+					{},
+				},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			err := patch.Add(tc.input, tc.path, tc.field, tc.value, &patch.Options{})
+			if err != nil {
+				t.Fatalf("Add(%s): unexpected err = %v", tc.name, err)
+			}
+
+			got, want := tc.input, tc.want
+			if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+				t.Errorf("Add(%s): (-got +want):\n%v", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestAdd_InvalidInputs(t *testing.T) {
+	testCases := []struct {
+		name    string
+		path    string
+		field   string
+		input   fhir.Resource
+		value   fhir.Base
+		wantErr error
+	}{
+		{
+			name:    "Invalid text case",
+			path:    "Patient",
+			field:   "birth_date",
+			input:   &ppb.Patient{},
+			value:   fhir.MustParseDate("1993-05-16"),
+			wantErr: patch.ErrInvalidField,
+		}, {
+			name:    "Underlying evaluation error",
+			path:    "Patient.i_dont_exist",
+			field:   "thisDoesntMatter",
+			input:   &ppb.Patient{},
+			value:   fhir.String(""),
+			wantErr: patch.ErrInvalidField,
+		}, {
+			name:    "Field does not exist",
+			path:    "Patient",
+			field:   "badField",
+			input:   &ppb.Patient{},
+			value:   fhir.MustParseDate("1993-05-16"),
+			wantErr: patch.ErrInvalidField,
+		}, {
+			name:  "Non-singleton result",
+			path:  "Patient.name",
+			field: "family",
+			input: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{},
+					{},
+				},
+			},
+			value:   &dtpb.HumanName{},
+			wantErr: patch.ErrNotSingleton,
+		}, {
+			name:    "enum value with bad casing",
+			path:    "Patient",
+			field:   "gender",
+			input:   &ppb.Patient{},
+			value:   fhir.String("MALE"),
+			wantErr: patch.ErrInvalidEnum,
+		}, {
+			name:    "Invalid enum value",
+			path:    "Patient",
+			field:   "gender",
+			input:   &ppb.Patient{},
+			value:   fhir.String("not_a_gender"),
+			wantErr: patch.ErrInvalidEnum,
+		}, {
+			name:  "Invalid int for positiveInt field",
+			path:  "Patient.telecom[0]",
+			field: "rank",
+			input: &ppb.Patient{
+				Telecom: []*dtpb.ContactPoint{{}},
+			},
+			value:   fhir.Integer(-1),
+			wantErr: patch.ErrInvalidUnsignedInt,
+		}, {
+			name:  "Unpatchable result",
+			path:  "Patient.active.value",
+			field: "something",
+			input: &ppb.Patient{
+				Active: fhir.Boolean(true),
+			},
+			value:   fhir.Boolean(false),
+			wantErr: patch.ErrNotPatchable,
+		}, {
+			name:  "Field already exists",
+			path:  "Patient",
+			field: "active",
+			input: &ppb.Patient{
+				Active: fhir.Boolean(true),
+			},
+			value:   fhir.Boolean(false),
+			wantErr: patch.ErrNotPatchable,
+		}, {
+			name:    "Wrong input type",
+			path:    "Patient",
+			field:   "active",
+			input:   &ppb.Patient{},
+			value:   fhir.String("true"),
+			wantErr: patch.ErrInvalidInput,
+		}, {
+			name:  "Invalid oneof entry",
+			path:  "Bundle.entry[0]",
+			field: "resource",
+			input: &bcrpb.Bundle{
+				Entry: []*bcrpb.Bundle_Entry{
+					{},
+				},
+			},
+			value:   fhir.String("I am not a resource"),
+			wantErr: patch.ErrInvalidInput,
+		}, {
+			name:  "Nil replacement value",
+			path:  "Bundle.entry[0]",
+			field: "resource",
+			input: &bcrpb.Bundle{
+				Entry: []*bcrpb.Bundle_Entry{
+					{},
+				},
+			},
+			value:   nil,
+			wantErr: patch.ErrInvalidInput,
+		}, {
+			name:    "Nil input resource value",
+			path:    "Bundle.entry[0]",
+			field:   "resource",
+			input:   nil,
+			value:   fhir.String(""),
+			wantErr: patch.ErrInvalidInput,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			err := patch.Add(tc.input, tc.path, tc.field, tc.value, &patch.Options{})
+
+			if got, want := err, tc.wantErr; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+				t.Fatalf("Add(%s): got err '%v', want err '%v'", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestDelete(t *testing.T) {
+	testCases := []struct {
+		name string
+		res  fhir.Resource
+		path string
+		want fhir.Resource
+	}{
+		{
+			name: "Deletes scalar field",
+			res: &ppb.Patient{
+				BirthDate: fhir.MustParseDate("1993-05-16"),
+			},
+			path: "Patient.birthDate",
+			want: &ppb.Patient{},
+		},
+		{
+			name: "Deletes single entry from end of list",
+			res: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("Betty"), fhir.String("Sue")},
+					},
+				},
+			},
+			path: "Patient.name.given[1]",
+			want: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("Betty")},
+					},
+				},
+			},
+		},
+		{
+			name: "Deletes single entry from beginning of list",
+			res: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("Betty"), fhir.String("Sue")},
+					},
+				},
+			},
+			path: "Patient.name.given[0]",
+			want: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("Sue")},
+					},
+				},
+			},
+		},
+		{
+			name: "Deletes list containing single entry",
+			res: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("Betty")},
+					},
+				},
+			},
+			path: "Patient.name.given",
+			want: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{},
+				},
+			},
+		},
+		{
+			name: "No-ops on empty but valid field",
+			res:  &ppb.Patient{},
+			path: "Patient.birthDate",
+			want: &ppb.Patient{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			err := patch.Delete(tc.res, tc.path)
+			if err != nil {
+				t.Fatalf("Delete(%v): got unexpected err = %v", tc.name, err)
+			}
+
+			got, want := tc.res, tc.want
+			if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+				t.Errorf("Delete(%s): (-got +want):\n%v", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestDelete_BadInput_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name    string
+		res     fhir.Resource
+		path    string
+		wantErr error
+	}{
+		{
+			name:    "Nil input",
+			res:     nil,
+			path:    "Patient.birthDate",
+			wantErr: patch.ErrInvalidInput,
+		},
+		{
+			name:    "Evaluation fails",
+			res:     &ppb.Patient{},
+			path:    "Patient.no_exist",
+			wantErr: fhirpath.ErrInvalidField,
+		},
+		{
+			name: "Attempting to delete more than one value",
+			res: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("Jieun"), fhir.String("IU")},
+					},
+				},
+			},
+			path:    "Patient.name.given",
+			wantErr: patch.ErrNotSingleton,
+		},
+		{
+			name: "Attempting to delete primitive value",
+			res: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("Jieun"), fhir.String("IU")},
+					},
+				},
+			},
+			path:    "Patient.name.given[0].value",
+			wantErr: patch.ErrNotPatchable,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			err := patch.Delete(tc.res, tc.path)
+
+			if got, want := err, tc.wantErr; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+				t.Errorf("Delete(%s): got error %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestInsert(t *testing.T) {
+	testCases := []struct {
+		name  string
+		res   fhir.Resource
+		path  string
+		value fhir.Base
+		index int
+		want  fhir.Resource
+	}{
+		{
+			name: "Inserts name at beginning",
+			res: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("IU")},
+					},
+				},
+			},
+			path:  "Patient.name[0].given",
+			value: fhir.String("Jieun"),
+			index: 0,
+			want: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("Jieun"), fhir.String("IU")},
+					},
+				},
+			},
+		}, {
+			name: "Inserts name at end",
+			res: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("IU")},
+					},
+				},
+			},
+			path:  "Patient.name[0].given",
+			value: fhir.String("Jieun"),
+			index: 1,
+			want: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("IU"), fhir.String("Jieun")},
+					},
+				},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			err := patch.Insert(tc.res, tc.path, tc.value, tc.index)
+			if err != nil {
+				t.Fatalf("Insert(%v): got unexpected err = %v", tc.name, err)
+			}
+
+			got, want := tc.res, tc.want
+			if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+				t.Errorf("Insert(%s): (-got +want):\n%v", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestInsert_InvalidCondition_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name    string
+		res     fhir.Resource
+		path    string
+		value   fhir.Base
+		index   int
+		wantErr error
+	}{
+		{
+			name:    "Nil Input",
+			res:     nil,
+			path:    "Patient.name[0].given",
+			value:   fhir.String("Jieun"),
+			index:   0,
+			wantErr: patch.ErrInvalidInput,
+		},
+		{
+			name:    "Evaluation fails",
+			res:     &ppb.Patient{},
+			path:    "Patient.no_exist",
+			value:   fhir.String("Jieun"),
+			index:   0,
+			wantErr: fhirpath.ErrInvalidField,
+		},
+		{
+			name: "Ambiguous insertion",
+			res: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("Jonathan")},
+					},
+					{
+						Given: []*dtpb.String{fhir.String("Jon")},
+					},
+				},
+			},
+			path:    "Patient.name.given",
+			value:   fhir.String("Jonny-Boy"),
+			index:   0,
+			wantErr: patch.ErrNotSingleton,
+		},
+		{
+			name: "Extraction type is not FHIR type",
+			res: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("Jonathan")},
+					},
+				},
+			},
+			path:    "Patient.name.given.value.toString()",
+			value:   fhir.String("Jonny-Boy"),
+			index:   0,
+			wantErr: patch.ErrNotPatchable,
+		},
+		{
+			name: "Output is disconnected value",
+			res: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("Jonathan")},
+					},
+				},
+			},
+			path:    "Patient.name.given.now()",
+			value:   fhir.String("Jonny-Boy"),
+			index:   0,
+			wantErr: patch.ErrNotPatchable,
+		},
+		{
+			name: "Insert target is not a list",
+			res: &ppb.Patient{
+				BirthDate: fhir.DateNow(),
+			},
+			path:    "Patient.birthDate.value",
+			value:   fhir.DateNow(),
+			index:   0,
+			wantErr: patch.ErrNotPatchable,
+		},
+		{
+			name: "Index is out of range",
+			res: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("IU")},
+					},
+				},
+			},
+			path:    "Patient.name[0].given",
+			value:   fhir.String("Jieun"),
+			index:   42,
+			wantErr: patch.ErrNotPatchable,
+		},
+		{
+			name: "Index is negative",
+			res: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("IU")},
+					},
+				},
+			},
+			path:    "Patient.name[0].given",
+			value:   fhir.String("Jieun"),
+			index:   -1,
+			wantErr: patch.ErrNotPatchable,
+		},
+		{
+			name: "Input value is wrong type",
+			res: &ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("IU")},
+					},
+				},
+			},
+			path:    "Patient.name[0].given",
+			value:   fhir.ID("Jieun"),
+			index:   0,
+			wantErr: patch.ErrNotPatchable,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			err := patch.Insert(tc.res, tc.path, tc.value, tc.index)
+
+			if got, want := err, tc.wantErr; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+				t.Errorf("Insert(%s): error got = %v, want = %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestMove(t *testing.T) {
+	testCases := []struct {
+		name     string
+		res      fhir.Resource
+		path     string
+		srcIndex int
+		dstIndex int
+		wantRes  fhir.Resource
+		wantErr  error
+	}{
+		{
+			"moves name",
+			&ppb.Patient{
+				Name: []*dtpb.HumanName{
+					{
+						Given: []*dtpb.String{fhir.String("IU"), fhir.String("Jieun")},
+					},
+				},
+			},
+			"Patient.name[0].given",
+			0,
+			1,
+			nil,
+			patch.ErrNotImplemented,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			err := patch.Move(tc.res, tc.path, tc.srcIndex, tc.dstIndex)
+
+			if got, want := err, tc.wantErr; !errors.Is(got, want) {
+				t.Fatalf("Move(%s): error got = %v, want = %v", tc.name, got, want)
+			}
+
+			got, want := fhir.Resource(nil), tc.wantRes
+			if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+				t.Errorf("Move(%s): (-got +want):\n%v", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestReplace(t *testing.T) {
+	testCases := []struct {
+		name    string
+		res     fhir.Resource
+		path    string
+		value   any
+		wantRes fhir.Resource
+		wantErr error
+	}{
+		{
+			"replaces birthDate",
+			patientWithBirthDate,
+			"Patient.birthDate",
+			"2007-07-05",
+			nil,
+			patch.ErrNotImplemented,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			err := patch.Replace(tc.res, tc.path, tc.value)
+
+			if got, want := err, tc.wantErr; !errors.Is(got, want) {
+				t.Fatalf("Replace(%s): error got = %v, want = %v", tc.name, got, want)
+			}
+
+			got, want := fhir.Resource(nil), tc.wantRes
+			if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+				t.Errorf("Replace(%s): (-got +want):\n%v", tc.name, diff)
+			}
+		})
+	}
+}
diff --git a/fhirpath/system/cmp.go b/fhirpath/system/cmp.go
new file mode 100644
index 0000000..f9128b9
--- /dev/null
+++ b/fhirpath/system/cmp.go
@@ -0,0 +1,81 @@
+package system
+
+import "reflect"
+
+// Equal compares two FHIRPath System types for equality. This uses standard
+// equality semantics and will return true if the value should yield a value
+// that is true, and false otherwise.
+//
+// This is effectively sugar over calling:
+//
+//	result, ok := TryEqual(lhs, rhs)
+//	return result && ok
+//
+// See https://hl7.org/fhirpath/n1/#equality
+func Equal(lhs, rhs Any) bool {
+	result, ok := TryEqual(lhs, rhs)
+	return ok && result
+}
+
+// TryEqual compares two FHIRPath System types for equality. This returns a
+// value if and only if the comparison of the underlying System types should
+// also yield a value, as defined in FHIRPath's equality operation.
+//
+// See https://hl7.org/fhirpath/n1/#equality
+//
+// For system types that define a custom "Equal" function, this will call the
+// underlying function. For system types that define a custom "TryEqual" function
+// this will call the underlying function. Otherwise, this will compare the raw
+// representation instead.
+func TryEqual(lhs, rhs Any) (bool, bool) {
+	lhs, rhs = Normalize(lhs, rhs), Normalize(rhs, lhs)
+	if result, has, ok := callTryEqual(lhs, rhs); ok {
+		return result, has
+	}
+	if result, ok := callEqual(lhs, rhs); ok {
+		return result, true
+	}
+	return lhs == rhs, true
+}
+
+func callTryEqual(lhs, rhs Any) (bool, bool, bool) {
+	if eq, ok := reflect.TypeOf(lhs).MethodByName("TryEqual"); ok {
+		funcType := eq.Func.Type()
+		arg0, arg1 := funcType.In(0), funcType.In(1)
+		if arg0 != reflect.TypeOf(lhs) || arg1.ConvertibleTo(reflect.TypeOf(rhs)) {
+			return false, false, true
+		}
+		args := []reflect.Value{
+			reflect.ValueOf(lhs),
+			reflect.ValueOf(rhs),
+		}
+		result := eq.Func.Call(args)
+		return result[0].Bool(), result[1].Bool(), true
+	}
+	return false, false, false
+}
+
+func callEqual(lhs, rhs Any) (bool, bool) {
+	if got, ok := callBinaryComparator("Equal", lhs, rhs); ok {
+		return got.(bool), true
+	}
+	return false, false
+}
+
+// callBinaryComparator invokes a binary comparator operator
+func callBinaryComparator(name string, lhs, rhs any) (any, bool) {
+	if eq, ok := reflect.TypeOf(lhs).MethodByName(name); ok {
+		args := []reflect.Value{
+			reflect.ValueOf(lhs),
+			reflect.ValueOf(rhs),
+		}
+		funcType := eq.Func.Type()
+		arg0, arg1 := funcType.In(0), funcType.In(1)
+		if arg0 != reflect.TypeOf(lhs) || arg1.ConvertibleTo(reflect.TypeOf(rhs)) {
+			return nil, true
+		}
+		result := eq.Func.Call(args)
+		return result[0].Bool(), true
+	}
+	return nil, false
+}
diff --git a/fhirpath/system/collection.go b/fhirpath/system/collection.go
new file mode 100644
index 0000000..ae2f447
--- /dev/null
+++ b/fhirpath/system/collection.go
@@ -0,0 +1,239 @@
+package system
+
+import (
+	"errors"
+	"fmt"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/shopspring/decimal"
+	"github.com/verily-src/fhirpath-go/internal/narrow"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"google.golang.org/protobuf/proto"
+)
+
+var (
+	// ErrNotConvertible is an error raised when attempting to call Collection.To*
+	// to a type that is not convertible.
+	ErrNotConvertible = errors.New("not convertible")
+)
+
+// Collection abstracts the input and output type for
+// FHIRPath expressions as a collection that can contain anything.
+type Collection []any
+
+// IsSingleton is a utility function to determine if a collection is a
+// "Singleton" collection (i.e. contains 1 value).
+func (c Collection) IsSingleton() bool {
+	return len(c) == 1
+}
+
+// IsEmpty is a utility function to determine if a collection is empty.
+func (c Collection) IsEmpty() bool {
+	return len(c) == 0
+}
+
+// TryEqual compares this collection to the supplied collection for, comparing each entry
+// with the corresponding entry in c2. Returns true if every entry
+// in c1 is equivalent to that of c2. If the lengths are mismatched,
+// returns false.
+func (c Collection) TryEqual(other Collection) (bool, bool) {
+	if len(c) != len(other) {
+		return false, true
+	}
+
+	for i := range other {
+		okOne := IsPrimitive(c[i])
+		okTwo := IsPrimitive(other[i])
+		if okOne != okTwo {
+			return false, true
+		}
+		if !okOne && !proto.Equal(c[i].(fhir.Base), other[i].(fhir.Base)) {
+			return false, true
+		}
+		if !okOne {
+			return true, true
+		}
+		primitiveOne, err := From(c[i])
+		if err != nil {
+			return false, true
+		}
+		primitiveTwo, err := From(other[i])
+		if err != nil {
+			return false, true
+		}
+		primitiveOne = Normalize(primitiveOne, primitiveTwo)
+		primitiveTwo = Normalize(primitiveTwo, primitiveOne)
+		equal, ok := TryEqual(primitiveOne, primitiveTwo)
+		if !ok {
+			return false, false
+		}
+		if !equal {
+			return false, true
+		}
+	}
+	return true, true
+}
+
+// ToSingletonBoolean evaluates a collection as a boolean with singleton evaluation of
+// collection rules. Returns a collection containing a single Boolean, or empty if the
+// input is empty.
+func (c Collection) ToSingletonBoolean() ([]Boolean, error) {
+	length := len(c)
+	if length == 0 {
+		return []Boolean{}, nil
+	}
+	if length > 1 {
+		return nil, fmt.Errorf("collection can't evaluate to bool, contains %v elements", length)
+	}
+	val, _ := From(c[0])
+	if boolean, ok := val.(Boolean); ok {
+		return []Boolean{boolean}, nil
+	}
+	return []Boolean{true}, nil
+}
+
+// ToSingleton evaluates a collection as a single value, returning an error if the
+// collection contains 0 or more than 1 entry.
+func (c Collection) ToSingleton() (any, error) {
+	if !c.IsSingleton() {
+		return nil, fmt.Errorf("collection is not singleton")
+	}
+	return c[0], nil
+}
+
+// ToBool converts this Collection into a Go native 'bool' type, following the
+// logic of singleton evaluation of booleans. If this collection is empty,
+// it returns false, if it contains more than 1 entry, it will return an error.
+func (c Collection) ToBool() (bool, error) {
+	if c.IsEmpty() {
+		return false, nil
+	}
+	v, err := c.ToSingleton()
+	if err != nil {
+		return false, err
+	}
+	switch val := v.(type) {
+	case Boolean:
+		return bool(val), nil
+	case *dtpb.Boolean:
+		return val.GetValue(), nil
+	}
+	// A single value that is not bool is always implicitly 'true'
+	return true, nil
+}
+
+// ToInt32 converts this Collection into a Go native 'int32' type.
+// If this collection is empty, or contains more than 1 entry, it will return
+// an error. If the type in the collection is not a System.Integer, or something
+// derived from a FHIR.Integer, this will raise an ErrNotConvertible.
+func (c Collection) ToInt32() (int32, error) {
+	v, err := c.ToSingleton()
+	if err != nil {
+		return 0, err
+	}
+	switch val := v.(type) {
+	case Integer:
+		return int32(val), nil
+	case *dtpb.Integer:
+		return val.GetValue(), nil
+	case *dtpb.PositiveInt:
+		if val, ok := narrow.ToInt32(val.GetValue()); ok {
+			return val, nil
+		}
+		return 0, c.convertErr(val.GetValue(), "int32")
+	case *dtpb.UnsignedInt:
+		if val, ok := narrow.ToInt32(val.GetValue()); ok {
+			return val, nil
+		}
+		return 0, c.convertErr(val.GetValue(), "int32")
+	}
+	return 0, c.convertErr(v, "int32")
+}
+
+// ToFloat64 converts this Collection into a Go native 'float64' type.
+// If this collection is empty, or contains more than 1 entry, it will return
+// an error. If the type in the collection is not a System.Integer, or something
+// derived from a FHIR.Integer, this will raise an ErrNotConvertible.
+func (c Collection) ToFloat64() (float64, error) {
+	v, err := c.ToSingleton()
+	if err != nil {
+		return 0, err
+	}
+	switch val := v.(type) {
+	case Decimal:
+		return decimal.Decimal(val).InexactFloat64(), nil
+	case Integer:
+		return float64(val), nil
+	case *dtpb.Integer:
+		return float64(val.GetValue()), nil
+	case *dtpb.PositiveInt:
+		return float64(val.GetValue()), nil
+	case *dtpb.UnsignedInt:
+		return float64(val.GetValue()), nil
+	}
+	return 0, c.convertErr(v, "float64")
+}
+
+// ToString converts this Collection into a Go native 'string' type.
+// If this collection is empty, or contains more than 1 entry, it will return
+// an error. If the type in the collection is not a System.String, or something
+// derived from a FHIR.String, this will raise an ErrNotConvertible.
+func (c Collection) ToString() (string, error) {
+	v, err := c.ToSingleton()
+	if err != nil {
+		return "", err
+	}
+	result, err := From(v)
+	if err != nil {
+		return "", c.convertErr(v, "string")
+	}
+	if str, ok := result.(String); ok {
+		return string(str), nil
+	}
+	return "", c.convertErr(v, "string")
+}
+
+// Contains returns true if the specified value is contained within this
+// collection. This will normalize types to system-types if necessary to check
+// for containment.
+func (c Collection) Contains(value any) bool {
+	sys, err := From(value)
+	if err == nil {
+		return c.containsSystem(sys)
+	}
+	msg, ok := value.(proto.Message)
+	if ok {
+		return c.containsProto(msg)
+	}
+	return false
+}
+
+func (c Collection) containsSystem(value Any) bool {
+	for _, v := range c {
+		sys, err := From(v)
+		if err != nil {
+			continue
+		}
+		if Equal(sys, value) {
+			return true
+		}
+	}
+	return false
+}
+
+func (c Collection) containsProto(value proto.Message) bool {
+	for _, v := range c {
+		msg, ok := v.(proto.Message)
+		if !ok {
+			continue
+		}
+		if proto.Equal(msg, value) {
+			return true
+		}
+	}
+	return false
+}
+
+func (c Collection) convertErr(got any, want string) error {
+	return fmt.Errorf("type %T %w to %v", got, ErrNotConvertible, want)
+}
diff --git a/fhirpath/system/collection_test.go b/fhirpath/system/collection_test.go
new file mode 100644
index 0000000..61641fc
--- /dev/null
+++ b/fhirpath/system/collection_test.go
@@ -0,0 +1,320 @@
+package system_test
+
+import (
+	"testing"
+
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"google.golang.org/protobuf/proto"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+func TestEqual_ReturnsResult(t *testing.T) {
+	patient1 := &ppb.Patient{
+		Id: fhir.ID("123"),
+	}
+	patient2 := &ppb.Patient{
+		Id: fhir.ID("234"),
+	}
+
+	testCases := []struct {
+		name            string
+		leftCollection  system.Collection
+		rightCollection system.Collection
+		equal           bool
+	}{
+		{
+			name:            "mismatched collection lengths",
+			leftCollection:  system.Collection{"one"},
+			rightCollection: system.Collection{"one", "two"},
+			equal:           false,
+		},
+		{
+			name:            "mismatched types (primitive and complex)",
+			leftCollection:  system.Collection{system.String("abc")},
+			rightCollection: system.Collection{&ppb.Patient{}},
+			equal:           false,
+		},
+		{
+			name:            "system types not equal",
+			leftCollection:  system.Collection{system.String("abc")},
+			rightCollection: system.Collection{system.String("abcd")},
+			equal:           false,
+		},
+		{
+			name:            "proto types not equal",
+			leftCollection:  system.Collection{patient1},
+			rightCollection: system.Collection{patient2},
+			equal:           false,
+		},
+		{
+			name:            "equal system types",
+			leftCollection:  system.Collection{system.String("sausage")},
+			rightCollection: system.Collection{system.String("sausage")},
+			equal:           true,
+		},
+		{
+			name:            "equal proto types",
+			leftCollection:  system.Collection{fhir.Code("#blessed")},
+			rightCollection: system.Collection{fhir.Code("#blessed")},
+			equal:           true,
+		},
+		{
+			name:            "equal complex types",
+			leftCollection:  system.Collection{patient1},
+			rightCollection: system.Collection{patient1},
+			equal:           true,
+		},
+		{
+			name:            "full collection not equal",
+			leftCollection:  system.Collection{system.String("Not"), system.String("Equal")},
+			rightCollection: system.Collection{system.String("Not"), system.String("Equivalent")},
+			equal:           false,
+		},
+		{
+			name:            "equal collection",
+			leftCollection:  system.Collection{system.String("Is"), system.String("Equal")},
+			rightCollection: system.Collection{system.String("Is"), system.String("Equal")},
+			equal:           true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, ok := tc.leftCollection.TryEqual(tc.rightCollection)
+
+			if !ok {
+				t.Fatalf("Collection.TryEqual: did not return value when one expected")
+			}
+			if got, want := got, tc.equal; got != want {
+				t.Errorf("Collection.TryEqual returned incorrect result, got: %v, want %v", got, want)
+			}
+		})
+	}
+}
+
+func TestToSingletonBoolean_ConvertsToBool(t *testing.T) {
+	testCases := []struct {
+		name            string
+		inputCollection system.Collection
+		want            []system.Boolean
+		shouldError     bool
+	}{
+		{
+			name:            "returns error on non-singleton collection",
+			inputCollection: system.Collection{1, 2},
+			shouldError:     true,
+		},
+		{
+			name:            "returns contained proto boolean",
+			inputCollection: system.Collection{fhir.Boolean(false)},
+			want:            []system.Boolean{false},
+		},
+		{
+			name:            "returns contained system boolean",
+			inputCollection: system.Collection{system.Boolean(true)},
+			want:            []system.Boolean{true},
+		},
+		{
+			name:            "returns true on singleton non-bool collection",
+			inputCollection: system.Collection{system.String("1")},
+			want:            []system.Boolean{true},
+		},
+		{
+			name:            "propagates empty collection input",
+			inputCollection: system.Collection{},
+			want:            []system.Boolean{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.inputCollection.ToSingletonBoolean()
+
+			if gotErr, wantErr := err != nil, tc.shouldError; gotErr != wantErr {
+				t.Fatalf("Collection.SingletonBoolean() returned unexpected error result: gotErr %v, wantErr %v", gotErr, wantErr)
+			}
+			if !cmp.Equal(got, tc.want) {
+				t.Errorf("Collection.SingletonBoolean() returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestCollection_ToFloat64(t *testing.T) {
+	tests := []struct {
+		name    string
+		c       system.Collection
+		want    float64
+		wantErr bool
+	}{
+		{
+			name:    "errors if input is not a number",
+			c:       system.Collection{system.String("10.1")},
+			want:    0,
+			wantErr: true,
+		},
+		{
+			name:    "errors if input length is more than 1",
+			c:       system.Collection{system.MustParseDecimal("10.1"), system.String("10.2")},
+			want:    0,
+			wantErr: true,
+		},
+		{
+			name:    "converts Decimal into float64 successfully",
+			c:       system.Collection{system.MustParseDecimal("10.5")},
+			want:    10.5,
+			wantErr: false,
+		},
+		{
+			name:    "converts Integer into float64 successfully",
+			c:       system.Collection{fhir.Integer(-100)},
+			want:    -100,
+			wantErr: false,
+		},
+		{
+			name:    "converts PositiveInt into float64 successfully",
+			c:       system.Collection{fhir.PositiveInt(100)},
+			want:    100,
+			wantErr: false,
+		},
+		{
+			name:    "converts UnsignedInt into float64 successfully",
+			c:       system.Collection{fhir.UnsignedInt(10)},
+			want:    10,
+			wantErr: false,
+		},
+	}
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.c.ToFloat64()
+			if (err != nil) != tc.wantErr {
+				t.Errorf("ToFloat64() error = %v, wantErr %v", err, tc.wantErr)
+				return
+			}
+			if got != tc.want {
+				t.Errorf("ToFloat64() got = %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestCollection_ToInt32(t *testing.T) {
+	tests := []struct {
+		name    string
+		c       system.Collection
+		want    int32
+		wantErr bool
+	}{
+		{
+			name:    "errors if input is not a number",
+			c:       system.Collection{system.String("10.1")},
+			want:    0,
+			wantErr: true,
+		},
+		{
+			name:    "errors if input length is more than 1",
+			c:       system.Collection{system.Integer(1), system.String("10.2")},
+			want:    0,
+			wantErr: true,
+		},
+		{
+			name:    "converts positive Integer into int32 successfully",
+			c:       system.Collection{system.Integer(100)},
+			want:    100,
+			wantErr: false,
+		},
+		{
+			name:    "converts negative Integer into int32 successfully",
+			c:       system.Collection{fhir.Integer(-100)},
+			want:    -100,
+			wantErr: false,
+		},
+		{
+			name:    "converts PositiveInt into int32 successfully",
+			c:       system.Collection{fhir.PositiveInt(1000)},
+			want:    1000,
+			wantErr: false,
+		},
+		{
+			name:    "converts UnsignedInt into int32 successfully",
+			c:       system.Collection{fhir.UnsignedInt(10)},
+			want:    10,
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := tt.c.ToInt32()
+			if (err != nil) != tt.wantErr {
+				t.Errorf("ToInt32() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if got != tt.want {
+				t.Errorf("ToInt32() got = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestContains(t *testing.T) {
+	patient := &ppb.Patient{
+		Name: []*dtpb.HumanName{
+			{
+				Given:  fhir.Strings("Foo", "Barl"),
+				Family: fhir.String("Bazington"),
+			},
+		},
+	}
+	testCases := []struct {
+		name     string
+		haystack system.Collection
+		needle   any
+		want     bool
+	}{
+		{
+			name:     "Haystack contains exact match",
+			haystack: system.Collection{system.Integer(42), system.String("Hello")},
+			needle:   system.String("Hello"),
+			want:     true,
+		}, {
+			name:     "Needle can convert to haystack value",
+			haystack: system.Collection{system.Integer(42), system.String("Hello")},
+			needle:   fhir.String("Hello"),
+			want:     true,
+		}, {
+			name:     "Haystack can convert to needle value",
+			haystack: system.Collection{system.Integer(42), fhir.String("Hello")},
+			needle:   system.String("Hello"),
+			want:     true,
+		}, {
+			name:     "Haystack does not contain needle",
+			haystack: system.Collection{system.Integer(42), fhir.String("Hello")},
+			needle:   system.String("World"),
+			want:     false,
+		}, {
+			name:     "Needle is a proto value in haystack",
+			haystack: system.Collection{system.Integer(42), patient, system.String("Foo")},
+			needle:   proto.Clone(patient),
+			want:     true,
+		}, {
+			name:     "Needle is a proto value not in haystack",
+			haystack: system.Collection{system.Integer(42), patient, system.String("Foo")},
+			needle:   &ppb.Patient{},
+			want:     false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := tc.haystack.Contains(tc.needle)
+
+			if got != tc.want {
+				t.Errorf("Collection.Contains(%v): got %v, want %v", tc.name, got, tc.want)
+			}
+		})
+	}
+}
diff --git a/fhirpath/system/consts.go b/fhirpath/system/consts.go
new file mode 100644
index 0000000..17d6cdf
--- /dev/null
+++ b/fhirpath/system/consts.go
@@ -0,0 +1,26 @@
+package system
+
+import "errors"
+
+// Common errors.
+var (
+	ErrTypeMismatch = errors.New("operation not defined between given types")
+	// Date, Time, DateTime, and Quantity have special cases for equality and inequality logic
+	// where an empty collection should be returned when their precisions/units are mismatched.
+	ErrMismatchedPrecision = errors.New("mismatched precision")
+	ErrMismatchedUnit      = errors.New("mismatched unit")
+	ErrIntOverflow         = errors.New("operation resulted in integer overflow")
+)
+
+// Type names.
+const (
+	stringType   = "String"
+	booleanType  = "Boolean"
+	integerType  = "Integer"
+	decimalType  = "Decimal"
+	dateType     = "Date"
+	dateTimeType = "DateTime"
+	timeType     = "Time"
+	quantityType = "Quantity"
+	anyType      = "Any"
+)
diff --git a/fhirpath/system/date.go b/fhirpath/system/date.go
new file mode 100644
index 0000000..acf29f0
--- /dev/null
+++ b/fhirpath/system/date.go
@@ -0,0 +1,269 @@
+package system
+
+import (
+	"fmt"
+	"strings"
+	"time"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/shopspring/decimal"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/fhirconv"
+)
+
+// Date represents Date values in the range 0001-01-01 to
+// 9999-12-31 with a 1 day step size. It uses YYYY-MM-DD with
+// optional month and day parts.
+type Date struct {
+	date time.Time
+	l    layout
+}
+
+// ParseDate takes as input a string and returns a Date
+// if the input is a valid FHIRPath Date string, otherwise
+// returns an error.
+func ParseDate(value string) (Date, error) {
+	dateLayouts := []string{
+		dayLayout,
+		monthLayout,
+		yearLayout,
+	}
+
+	var t time.Time
+	var err error
+	value = strings.TrimPrefix(value, "@")
+	for _, l := range dateLayouts {
+		if t, err = time.Parse(l, value); err == nil {
+			return Date{t, layout(l)}, nil
+		}
+	}
+	return Date{}, fmt.Errorf("unable to parse date '%s': %w", value, err)
+}
+
+// MustParseDate takes as input a string and returns a Date if
+// the input is valid. Panics if the string can't be parsed.
+func MustParseDate(value string) Date {
+	date, err := ParseDate(value)
+	if err != nil {
+		panic(err)
+	}
+	return date
+}
+
+// DateFromProto takes a proto Date as input and returns a system Date.
+func DateFromProto(proto *dtpb.Date) (Date, error) {
+	t, err := fhirconv.DateToTime(proto)
+	if err != nil {
+		return Date{}, err
+	}
+	var l layout
+	switch proto.Precision {
+	case dtpb.Date_DAY:
+		l = dayLayout
+	case dtpb.Date_MONTH:
+		l = monthLayout
+	case dtpb.Date_YEAR:
+		l = yearLayout
+	}
+	return Date{t, l}, nil
+}
+
+// ToProtoDate returns a proto Date based on a system Date.
+func (d Date) ToProtoDate() *dtpb.Date {
+	date := fhir.Date(d.date)
+	var p dtpb.Date_Precision
+	switch d.l {
+	case dayLayout:
+		p = dtpb.Date_DAY
+	case monthLayout:
+		p = dtpb.Date_MONTH
+	case yearLayout:
+		p = dtpb.Date_YEAR
+	}
+	date.Precision = p
+	return date
+}
+
+// String formats the time as a date string.
+func (d Date) String() string {
+	return d.date.Format(string(d.l))
+}
+
+// TryEqual returns a bool representing whether or not
+// the value of d is equal to input.(Date).
+// This function may not return a value, depending on the precision of
+// the other value; represented by the second bool return value.
+func (d Date) TryEqual(input Any) (bool, bool) {
+	val, ok := input.(Date)
+	if !ok {
+		return false, true
+	}
+	if d.l == val.l {
+		return d.date.Equal(val.date), true
+	}
+
+	dComponents := d.getComponents()
+	valComponents := val.getComponents()
+
+	minPrecision := min(int(dateMap[d.l]), int(dateMap[val.l]))
+
+	for i := 0; i <= minPrecision; i++ {
+		if dComponents[i] == valComponents[i] {
+			continue
+		}
+		return false, true
+	}
+	return false, false
+}
+
+// Less returns true if the value of d is less than input.(Date).
+// Compares component by component, and returns an error if there is a
+// precision mismatch. If input is not a Date, returns an error.
+func (d Date) Less(input Any) (Boolean, error) {
+	val, ok := input.(Date)
+	if !ok {
+		return false, fmt.Errorf("%w: %T, %T", ErrTypeMismatch, d, input)
+	}
+	if d.l == val.l {
+		return Boolean(d.date.Before(val.date)), nil
+	}
+
+	dComponents := d.getComponents()
+	valComponents := val.getComponents()
+
+	minPrecision := min(int(dateMap[d.l]), int(dateMap[val.l]))
+
+	for i := 0; i <= minPrecision; i++ {
+		if dComponents[i] == valComponents[i] {
+			continue
+		}
+		return dComponents[i] < valComponents[i], nil
+	}
+	return false, ErrMismatchedPrecision
+}
+
+// Add returns the result of d + input. Returns an
+// error if it is not a valid time-valued quantity.
+func (d Date) Add(input Quantity) (Date, error) {
+	var result time.Time
+	value := int(decimal.Decimal(input.value).IntPart())
+	switch input.unit {
+	case "year", "years":
+		result = addYear(d.date, value)
+	case "month", "months":
+		result = addMonth(d.date, value)
+	case "week", "weeks":
+		value = 7 * value
+		result = d.date.AddDate(0, 0, value)
+	case "day", "days":
+		result = d.date.AddDate(0, 0, value)
+	default:
+		return Date{}, fmt.Errorf("%w: can't add to date", ErrMismatchedUnit)
+	}
+
+	// Reformat to truncate date to initial precision. This causes the addition result
+	// to round down to the highest precision value.
+	result, err := time.Parse(string(d.l), result.Format(string(d.l)))
+	if err != nil {
+		return Date{}, err
+	}
+	return Date{result, d.l}, nil
+}
+
+// Sub returns the result of d - input. Returns an error if the
+// input does not represent a valid time-valued quantity.
+func (d Date) Sub(input Quantity) (Date, error) {
+	// Handle partial dates by rounding quantity to appropriate precision.
+	// Subtraction is not symmetric with addition, so the solution of truncation
+	// as done in Add, cannot be applied here.
+	if d.l == yearLayout {
+		years, err := input.toYears()
+		if err != nil {
+			return Date{}, err
+		}
+		return Date{d.date.AddDate(-years, 0, 0), d.l}, nil
+	}
+	if d.l == monthLayout {
+		months, err := input.toMonths()
+		if err != nil {
+			return Date{}, err
+		}
+		return Date{d.date.AddDate(0, -months, 0), d.l}, nil
+	}
+
+	// subtract appropriate position of date, for non-partial dates.
+	var result time.Time
+	value := -int(decimal.Decimal(input.value).IntPart())
+	switch input.unit {
+	case "year", "years":
+		result = addYear(d.date, value)
+	case "month", "months":
+		result = addMonth(d.date, value)
+	case "week", "weeks":
+		value = 7 * value
+		result = d.date.AddDate(0, 0, value)
+	case "day", "days":
+		result = d.date.AddDate(0, 0, value)
+	default:
+		return Date{}, fmt.Errorf("%w: can't add to date", ErrMismatchedUnit)
+	}
+
+	return Date{result, d.l}, nil
+}
+
+// Name returns the type name.
+func (d Date) Name() string {
+	return dateType
+}
+
+// Equal method to override cmp.Equal.
+func (d Date) Equal(d2 Date) bool {
+	return d.date.Format(string(d.l)) == d2.date.Format(string(d2.l))
+}
+
+func (d Date) getComponents() []int {
+	return []int{
+		d.date.Year(),
+		int(d.date.Month()),
+		d.date.Day(),
+	}
+}
+
+func (d Date) ToDateTime() DateTime {
+	var dateToDateTime = map[layout]layout{
+		dayLayout:   dtDayLayout,
+		monthLayout: dtMonthLayout,
+		yearLayout:  dtYearLayout,
+	}
+
+	return DateTime{d.date, dateToDateTime[d.l]}
+}
+
+func min(x, y int) int {
+	if x < y {
+		return x
+	}
+	return y
+}
+
+// addMonth adds m months to the given time, without
+// normalizing to the next month.
+// eg. 2020-01-30 + 1 month = 2020-02-28.
+func addMonth(t time.Time, m int) time.Time {
+	added := t.AddDate(0, m, 0)
+	if day := added.Day(); day != t.Day() {
+		return added.AddDate(0, 0, -day)
+	}
+	return added
+}
+
+// addYear adds y years to the given time, without
+// normalizing to the next month in the special case of leap years.
+// eg. 2020-02-29 + 1 year = 2020-02-28.
+func addYear(t time.Time, y int) time.Time {
+	added := t.AddDate(y, 0, 0)
+	if day := added.Day(); day != t.Day() {
+		return added.AddDate(0, 0, -day)
+	}
+	return added
+}
diff --git a/fhirpath/system/date_test.go b/fhirpath/system/date_test.go
new file mode 100644
index 0000000..b663045
--- /dev/null
+++ b/fhirpath/system/date_test.go
@@ -0,0 +1,437 @@
+package system_test
+
+import (
+	"errors"
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+	"google.golang.org/protobuf/testing/protocmp"
+	"testing"
+)
+
+func TestParseDate_ReturnsDate(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input string
+	}{
+		{"Year", "2012"},
+		{"Month", "2012-05"},
+		{"Day", "2012-05-21"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if _, err := system.ParseDate(tc.input); err != nil {
+				t.Fatalf("ParseDate(%s) returned unexpected error: %v", tc.input, err)
+			}
+		})
+	}
+}
+
+func TestParseDate_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input string
+	}{
+		{"Bad format", "05-01-2007"},
+		{"Bad month", "2023-13-21"},
+		{"Bad day", "2023-12-34"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if _, err := system.ParseDate(tc.input); err == nil {
+				t.Fatalf("ParseDate(%s) didn't return error when expected", tc.input)
+			}
+		})
+	}
+}
+
+func TestDate_Equal(t *testing.T) {
+	testCases := []struct {
+		name        string
+		dateOne     system.Date
+		dateTwo     system.Any
+		shouldEqual bool
+		wantOk      bool
+	}{
+		{
+			name:    "same date different precision",
+			dateOne: system.MustParseDate("2023"),
+			dateTwo: system.MustParseDate("2023-01-01"),
+			wantOk:  false,
+		},
+		{
+			name:        "different date",
+			dateOne:     system.MustParseDate("2023-01-01"),
+			dateTwo:     system.MustParseDate("2023-01-02"),
+			shouldEqual: false,
+			wantOk:      true,
+		},
+		{
+			name:        "different type",
+			dateOne:     system.MustParseDate("2023-01-01"),
+			dateTwo:     system.String("2023-01-01"),
+			shouldEqual: false,
+			wantOk:      true,
+		},
+		{
+			name:        "mismatched precision but not equal",
+			dateOne:     system.MustParseDate("2023-02"),
+			dateTwo:     system.MustParseDate("2023-01-29"),
+			shouldEqual: false,
+			wantOk:      true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, ok := tc.dateOne.TryEqual(tc.dateTwo)
+
+			if ok != tc.wantOk {
+				t.Errorf("Date.Equal: ok got %v, want %v", ok, tc.wantOk)
+			}
+			if got != tc.shouldEqual {
+				t.Errorf("Date.Equal returned unexpected equality: got %v, want %v", got, tc.shouldEqual)
+			}
+		})
+	}
+}
+
+func TestDateFromProto_Converts(t *testing.T) {
+	testCases := []struct {
+		name      string
+		dateProto *dtpb.Date
+		wantDate  system.Date
+	}{
+		{
+			name:      "converts day precision",
+			dateProto: fhir.MustParseDate("2022-05-23"),
+			wantDate:  system.MustParseDate("2022-05-23"),
+		},
+		{
+			name:      "converts month precision",
+			dateProto: fhir.MustParseDate("2012-12"),
+			wantDate:  system.MustParseDate("2012-12"),
+		},
+		{
+			name:      "converts year precision",
+			dateProto: fhir.MustParseDate("2002"),
+			wantDate:  system.MustParseDate("2002"),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := system.DateFromProto(tc.dateProto)
+			if err != nil {
+				t.Fatalf("DateFromProto(%s) raises unexpected error: %v", tc.dateProto.String(), err)
+			}
+			if diff := cmp.Diff(tc.wantDate, got); diff != "" {
+				t.Errorf("DateFromProto(%s) incorrectly converts Date: (-want, +got)\n%s", tc.dateProto.String(), diff)
+			}
+		})
+	}
+}
+
+func TestDateToProtoDate_Converts(t *testing.T) {
+	testCases := []struct {
+		name string
+		date system.Date
+		want *dtpb.Date
+	}{
+		{
+			name: "day precision",
+			date: system.MustParseDate("2025-01-30"),
+			want: fhir.MustParseDate("2025-01-30"),
+		},
+		{
+			name: "month precision",
+			date: system.MustParseDate("2025-01"),
+			want: fhir.MustParseDate("2025-01"),
+		},
+		{
+			name: "year precision",
+			date: system.MustParseDate("2025"),
+			want: fhir.MustParseDate("2025"),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := tc.date.ToProtoDate()
+
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Date.ToProtoDate returned unexpected result: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestDateLess_ReturnsBoolean(t *testing.T) {
+	testCases := []struct {
+		name    string
+		dateOne system.Date
+		dateTwo system.Date
+		want    system.Boolean
+	}{
+		{
+			name:    "returns true for an earlier date",
+			dateOne: system.MustParseDate("2020-05-01"),
+			dateTwo: system.MustParseDate("2020-06-01"),
+			want:    true,
+		},
+		{
+			name:    "returns true for an earlier date with less precision",
+			dateOne: system.MustParseDate("2020-05"),
+			dateTwo: system.MustParseDate("2020-07-28"),
+			want:    true,
+		},
+		{
+			name:    "returns false for a later date",
+			dateOne: system.MustParseDate("2023-05-01"),
+			dateTwo: system.MustParseDate("2022-02-02"),
+			want:    false,
+		},
+		{
+			name:    "returns false for a later date with less precision",
+			dateOne: system.MustParseDate("2020"),
+			dateTwo: system.MustParseDate("2019-05"),
+			want:    false,
+		},
+		{
+			name:    "returns false for equivalent dates",
+			dateOne: system.MustParseDate("2008-02-09"),
+			dateTwo: system.MustParseDate("2008-02-09"),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.dateOne.Less(tc.dateTwo)
+
+			if err != nil {
+				t.Fatalf("Date.Less returned unexpected error: %v", err)
+			}
+			if got != tc.want {
+				t.Errorf("Date.Less returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestDateLt_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name    string
+		dateOne system.Date
+		input   system.Any
+		wantErr error
+	}{
+		{
+			name:    "wrong input type",
+			dateOne: system.MustParseDate("2020-12-31"),
+			input:   system.String("asdf"),
+			wantErr: system.ErrTypeMismatch,
+		},
+		{
+			name:    "date precision shorter than input",
+			dateOne: system.MustParseDate("2020-04"),
+			input:   system.MustParseDate("2020-04-01"),
+			wantErr: system.ErrMismatchedPrecision,
+		},
+		{
+			name:    "input precision shorter than date",
+			dateOne: system.MustParseDate("2023-05-31"),
+			input:   system.MustParseDate("2023-05"),
+			wantErr: system.ErrMismatchedPrecision,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := tc.dateOne.Less(tc.input)
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("Date.Less returned incorrect error: got %v, want %v", err, tc.wantErr)
+			}
+		})
+	}
+}
+
+func TestDateAdd_ReturnsSum(t *testing.T) {
+	testCases := []struct {
+		name    string
+		date    system.Date
+		input   system.Quantity
+		want    system.Date
+		wantErr error
+	}{
+		{
+			name:  "overflows months",
+			date:  system.MustParseDate("2025-11-30"),
+			input: system.MustParseQuantity("2", "months"),
+			want:  system.MustParseDate("2026-01-30"),
+		},
+		{
+			name:  "correctly adds days",
+			date:  system.MustParseDate("2021-02-28"),
+			input: system.MustParseQuantity("2", "days"),
+			want:  system.MustParseDate("2021-03-02"),
+		},
+		{
+			name:  "correctly adds years",
+			date:  system.MustParseDate("2002-04-19"),
+			input: system.MustParseQuantity("18", "years"),
+			want:  system.MustParseDate("2020-04-19"),
+		},
+		{
+			name:  "adds weeks as days",
+			date:  system.MustParseDate("1974-10-30"),
+			input: system.MustParseQuantity("18", "weeks"),
+			want:  system.MustParseDate("1975-03-05"),
+		},
+		{
+			name:  "disregards decimal part of quantity",
+			date:  system.MustParseDate("2002-04-19"),
+			input: system.MustParseQuantity("2.5", "months"),
+			want:  system.MustParseDate("2002-06-19"),
+		},
+		{
+			name:  "returns correct result when input month is longer than result month",
+			date:  system.MustParseDate("2021-01-31"),
+			input: system.MustParseQuantity("1", "month"),
+			want:  system.MustParseDate("2021-02-28"),
+		},
+		{
+			name:  "adds partials by rounding down to the highest precision",
+			date:  system.MustParseDate("1997"),
+			input: system.MustParseQuantity("23", "months"),
+			want:  system.MustParseDate("1998"),
+		},
+		{
+			name:    "returns error on unsupported time-valued quantity",
+			date:    system.MustParseDate("2019-03-31"),
+			input:   system.MustParseQuantity("12", "hours"),
+			wantErr: system.ErrMismatchedUnit,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.date.Add(tc.input)
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("Date.Add returned unexpected error: got %v, want %v", err, tc.wantErr)
+			}
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("Date.Add returned unexpected result: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestDateSub_ReturnsSum(t *testing.T) {
+	testCases := []struct {
+		name    string
+		date    system.Date
+		input   system.Quantity
+		want    system.Date
+		wantErr error
+	}{
+		{
+			name:  "overflows months",
+			date:  system.MustParseDate("2025-01-30"),
+			input: system.MustParseQuantity("2", "months"),
+			want:  system.MustParseDate("2024-11-30"),
+		},
+		{
+			name:  "correctly subtracts days",
+			date:  system.MustParseDate("2021-03-02"),
+			input: system.MustParseQuantity("2", "days"),
+			want:  system.MustParseDate("2021-02-28"),
+		},
+		{
+			name:  "correctly subtracts years",
+			date:  system.MustParseDate("2020-04-19"),
+			input: system.MustParseQuantity("18", "years"),
+			want:  system.MustParseDate("2002-04-19"),
+		},
+		{
+			name:  "disregards decimal part of quantity",
+			date:  system.MustParseDate("2002-04-19"),
+			input: system.MustParseQuantity("2.5", "months"),
+			want:  system.MustParseDate("2002-02-19"),
+		},
+		{
+			name:  "subtracts year partial by rounding down to the highest precision",
+			date:  system.MustParseDate("1997"),
+			input: system.MustParseQuantity("23", "months"),
+			want:  system.MustParseDate("1996"),
+		},
+		{
+			name:  "subtracts month partial by rounding down to multiples of 30 days",
+			date:  system.MustParseDate("1997-04"),
+			input: system.MustParseQuantity("29", "days"),
+			want:  system.MustParseDate("1997-04"),
+		},
+		{
+			name:  "returns correct result when input month is longer than result month",
+			date:  system.MustParseDate("2019-03-31"),
+			input: system.MustParseQuantity("1", "month"),
+			want:  system.MustParseDate("2019-02-28"),
+		},
+		{
+			name:    "returns error on non time-valued quantity",
+			date:    system.MustParseDate("2019-03-31"),
+			input:   system.MustParseQuantity("12", "kg"),
+			wantErr: system.ErrMismatchedUnit,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.date.Sub(tc.input)
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("Date.Sub returned unexpected error: got %v, want %v", err, tc.wantErr)
+			}
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("Date.Sub returned unexpected result: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestDate_ToDateTime(t *testing.T) {
+	tests := []struct {
+		name  string
+		input system.Date
+		want  system.DateTime
+	}{
+		{
+			name:  "returns a DateTime for a Date with day layout",
+			input: system.MustParseDate("2006-01-02"),
+			want:  system.MustParseDate("2006-01-02").ToDateTime(),
+		},
+		{
+			name:  "returns a DateTime for a Date with month layout",
+			input: system.MustParseDate("2006-01"),
+			want:  system.MustParseDate("2006-01").ToDateTime(),
+		},
+		{
+			name:  "returns a DateTime for a Date with year layout",
+			input: system.MustParseDate("2006"),
+			want:  system.MustParseDate("2006").ToDateTime(),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := tt.input.ToDateTime()
+			if diff := cmp.Diff(tt.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Date_ToDateTime() returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
diff --git a/fhirpath/system/date_time.go b/fhirpath/system/date_time.go
new file mode 100644
index 0000000..5308fcb
--- /dev/null
+++ b/fhirpath/system/date_time.go
@@ -0,0 +1,289 @@
+package system
+
+import (
+	"fmt"
+	"strings"
+	"time"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/shopspring/decimal"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/fhirconv"
+)
+
+// DateTime represents date/time values and partial date/time values
+// in the format YYYY-MM-DDThh:mm:ss.fff(+|-)hh:mm format.
+type DateTime struct {
+	dateTime time.Time
+	l        layout
+}
+
+// ParseDateTime parses a string and returns a DateTime object if
+// the string is a valid FHIRPath DateTime string, or an error otherwise.
+func ParseDateTime(value string) (DateTime, error) {
+	dateTimeLayouts := []string{
+		dtMillisecondLayoutTZ,
+		dtMillisecondLayout,
+		dtSecondLayoutTZ,
+		dtSecondLayout,
+		dtMinuteLayoutTZ,
+		dtMinuteLayout,
+		dtHourLayoutTZ,
+		dtHourLayout,
+		dtDayLayout,
+		dtMonthLayout,
+		dtYearLayout,
+	}
+
+	var t time.Time
+	var err error
+	value = strings.TrimPrefix(value, "@")
+	for _, l := range dateTimeLayouts {
+		if t, err = time.Parse(l, value); err == nil {
+			return DateTime{t, layout(l)}, nil
+		}
+	}
+	return DateTime{}, fmt.Errorf("unable to parse DateTime '%s': %w", value, err)
+}
+
+// MustParseDateTime returns a DateTime if the string represents a
+// valid DateTime. Otherwise, panics.
+func MustParseDateTime(value string) DateTime {
+	dt, err := ParseDateTime(value)
+	if err != nil {
+		panic(err)
+	}
+	return dt
+}
+
+// DateTimeFromProto takes a proto DateTime as input and returns a System DateTime.
+// Note that the highest precision supported by System.DateTime is Millisecond.
+func DateTimeFromProto(proto *dtpb.DateTime) (DateTime, error) {
+	t, err := fhirconv.DateTimeToTime(proto)
+	if err != nil {
+		return DateTime{}, err
+	}
+	var l layout
+	switch proto.Precision {
+	case dtpb.DateTime_MICROSECOND:
+		fallthrough
+	case dtpb.DateTime_MILLISECOND:
+		l = dtMillisecondLayoutTZ
+	case dtpb.DateTime_SECOND:
+		l = dtSecondLayoutTZ
+	case dtpb.DateTime_DAY:
+		l = dtDayLayout
+	case dtpb.DateTime_MONTH:
+		l = dtMonthLayout
+	case dtpb.DateTime_YEAR:
+		l = dtYearLayout
+	}
+	return DateTime{t, l}, nil
+}
+
+// ToProtoDateTime returns a proto DateTime based on a system DateTime.
+// Note that the highest precision supported by System.DateTime is Millisecond.
+func (dt DateTime) ToProtoDateTime() *dtpb.DateTime {
+	dateTime := fhir.DateTime(dt.dateTime)
+	var p dtpb.DateTime_Precision
+	switch dt.l {
+	case dtMillisecondLayoutTZ, dtMillisecondLayout:
+		p = dtpb.DateTime_MILLISECOND
+	case dtSecondLayoutTZ, dtSecondLayout:
+		p = dtpb.DateTime_SECOND
+	case dtDayLayout:
+		p = dtpb.DateTime_DAY
+	case dtMonthLayout:
+		p = dtpb.DateTime_MONTH
+	case dtYearLayout:
+		p = dtpb.DateTime_YEAR
+	}
+	dateTime.Precision = p
+	return dateTime
+}
+
+// TryEqual returns a boolean representing whether or not
+// the value of dt is equal to the value of dt2.
+// Not intended to be used for cmp.Equal. The comparison is
+// not symmetric and may cause unexpected behaviour.
+func (dt DateTime) TryEqual(input Any) (bool, bool) {
+	val, ok := input.(DateTime)
+	if !ok {
+		return false, true
+	}
+	if dt.l == val.l {
+		return dt.dateTime.Equal(val.dateTime), true
+	}
+
+	// normalize time zone
+	dt.dateTime = dt.dateTime.UTC()
+	val.dateTime = val.dateTime.UTC()
+
+	dtComponents := dt.getComponents()
+	valComponents := val.getComponents()
+
+	minPrecision := min(int(dateTimeMap[dt.l]), int(dateTimeMap[val.l]))
+
+	for i := 0; i <= minPrecision; i++ {
+		if dtComponents[i] == valComponents[i] && i != int(dtSecond) {
+			continue
+		}
+		return dtComponents[i] == valComponents[i], true
+	}
+	return false, false
+}
+
+// Less returns true if the value of dt is less than input.(DateTime).
+// Compares component by component, and returns an error if there is a
+// precision mismatch. If input is not a Date, returns an error.
+func (dt DateTime) Less(input Any) (Boolean, error) {
+	val, ok := input.(DateTime)
+	if !ok {
+		return false, fmt.Errorf("%w, %T, %T", ErrTypeMismatch, dt, input)
+	}
+	if dt.l == val.l {
+		return Boolean(dt.dateTime.Before(val.dateTime)), nil
+	}
+
+	// normalize time zone
+	dt.dateTime = dt.dateTime.UTC()
+	val.dateTime = val.dateTime.UTC()
+
+	dtComponents := dt.getComponents()
+	valComponents := val.getComponents()
+
+	minPrecision := min(int(dateTimeMap[dt.l]), int(dateTimeMap[val.l]))
+
+	for i := 0; i <= minPrecision; i++ {
+		// precisions below second are irrelevant, and should be treated the same.
+		if dtComponents[i] == valComponents[i] && i != int(dtSecond) {
+			continue
+		}
+		return dtComponents[i] < valComponents[i], nil
+	}
+	return false, ErrMismatchedPrecision
+}
+
+// Add returns the result of dt + input. Returns an
+// error if input does not represent a valid time valued quantity.
+func (dt DateTime) Add(input Quantity) (DateTime, error) {
+	var result time.Time
+	value := int(decimal.Decimal(input.value).IntPart())
+	switch input.unit {
+	case "year", "years":
+		result = addYear(dt.dateTime, value)
+	case "month", "months":
+		result = addMonth(dt.dateTime, value)
+	case "week", "weeks":
+		value = 7 * value
+		result = dt.dateTime.AddDate(0, 0, value)
+	case "day", "days":
+		result = dt.dateTime.AddDate(0, 0, value)
+	default:
+		duration, err := input.timeDuration()
+		if err != nil {
+			return DateTime{}, err
+		}
+		result = dt.dateTime.Add(duration)
+	}
+
+	// Reformat to truncate DateTime to initial precision, rounding down to
+	// highest precision value.
+	result, err := time.Parse(string(dt.l), result.Format(string(dt.l)))
+	if err != nil {
+		return DateTime{}, err
+	}
+	return DateTime{result, dt.l}, nil
+}
+
+// Sub returns the result of dt - input.(Quantity). Returns an
+// error if the input is not a Quantity, or if it does not represent
+// a valid time duration.
+func (dt DateTime) Sub(input Quantity) (DateTime, error) {
+	// Handle partial dates by rounding quantity to appropriate precision.
+	// Subtraction is not symmetric with addition, so truncation cannot be
+	// applied here.
+	if dt.l == dtYearLayout {
+		years, err := input.toYears()
+		if err != nil {
+			return DateTime{}, err
+		}
+		return DateTime{dt.dateTime.AddDate(-years, 0, 0), dt.l}, nil
+	}
+	if dt.l == dtMonthLayout {
+		months, err := input.toMonths()
+		if err != nil {
+			return DateTime{}, err
+		}
+		return DateTime{dt.dateTime.AddDate(0, -months, 0), dt.l}, nil
+	}
+
+	// Handles non-partial dates here.
+	var result time.Time
+	value := -int(decimal.Decimal(input.value).IntPart())
+	switch input.unit {
+	case "year", "years":
+		result = addYear(dt.dateTime, value)
+	case "month", "months":
+		result = addMonth(dt.dateTime, value)
+	case "week", "weeks":
+		value = 7 * value
+		result = dt.dateTime.AddDate(0, 0, value)
+	case "day", "days":
+		result = dt.dateTime.AddDate(0, 0, value)
+	default:
+		// Get time valued duration, and round down to appropriate precision.
+		duration, err := input.timeDuration()
+		if err != nil {
+			return DateTime{}, err
+		}
+		duration = roundToDateTimePrecision(dateTimeMap[dt.l], duration)
+		result = dt.dateTime.Add(-duration)
+	}
+	return DateTime{result, dt.l}, nil
+}
+
+// Name returns the type name.
+func (dt DateTime) Name() string {
+	return dateTimeType
+}
+
+// String returns a formatted DateTime string.
+func (dt DateTime) String() string {
+	return dt.dateTime.Format(string(dt.l))
+}
+
+// Equal method to override cmp.Equal.
+func (dt DateTime) Equal(dt2 DateTime) bool {
+	return dt.dateTime.Format(string(dt.l)) == dt2.dateTime.Format(string(dt2.l))
+}
+
+func (dt DateTime) getComponents() []int {
+	return []int{
+		dt.dateTime.Year(),
+		int(dt.dateTime.Month()),
+		dt.dateTime.Day(),
+		dt.dateTime.Hour(),
+		dt.dateTime.Minute(),
+		dt.dateTime.Second()*1000000000 + dt.dateTime.Nanosecond(),
+	}
+}
+
+// roundToDateTimePrecision rounds the duration down to the appropriate precision.
+// Eg. 2012-03-20T + 23 'hours' = 2012-03-20T but 2012-03-20T + 24 'hours' = 2012-03-21T.
+func roundToDateTimePrecision(p dateTimePrecision, d time.Duration) time.Duration {
+	switch p {
+	case dtYear:
+		return d / (time.Hour * 24 * 365)
+	case dtMonth:
+		return d / (time.Hour * 24 * 30)
+	case dtDay:
+		return d / (time.Hour * 24)
+	case dtHour:
+		return d / time.Hour
+	case dtMinute:
+		return d / time.Minute
+	default:
+		return d
+	}
+}
diff --git a/fhirpath/system/date_time_test.go b/fhirpath/system/date_time_test.go
new file mode 100644
index 0000000..fa81d3e
--- /dev/null
+++ b/fhirpath/system/date_time_test.go
@@ -0,0 +1,474 @@
+package system_test
+
+import (
+	"errors"
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func TestParseDateTime_ReturnsTime(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input string
+	}{
+		{"Full DateTime with offset", "2009-03-06T12:09:45.556-04:30"},
+		{"DateTime without offset", "2009-03-06T12:09:45.556"},
+		{"Second with offset", "2006-01-02T15:04:05Z"},
+		{"Minute", "2010-02-04T14:05"},
+		{"Year", "2021T"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if _, err := system.ParseDateTime(tc.input); err != nil {
+				t.Fatalf("ParseTime(%s) returned unexpected error: %v", tc.input, err)
+			}
+		})
+	}
+}
+
+func TestParseDateTime_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input string
+	}{
+		{"Bad format", "2010-12-21T08-05"},
+		{"Bad hour", "2010-12-25T25"},
+		{"Bad minute", "2010-12-25T23:61"},
+		{"Offset without minutes", "2010-12-25T23:59+04"},
+		{"Date without T", "2010-12-25"},
+		{"Time without date", "08:30Z"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if _, err := system.ParseDateTime(tc.input); err == nil {
+				t.Fatalf("ParseDate(%s) didn't return error when expected", tc.input)
+			}
+		})
+	}
+}
+
+func TestDateTime_Equal(t *testing.T) {
+	testCases := []struct {
+		name        string
+		dateTimeOne system.DateTime
+		dateTimeTwo system.Any
+		shouldEqual bool
+		wantOk      bool
+	}{
+		{
+			name:        "same time different format",
+			dateTimeOne: system.MustParseDateTime("2023-01-01T"),
+			dateTimeTwo: system.MustParseDateTime("2023-01-01T00:00:00.000"),
+			wantOk:      false,
+		},
+		{
+			name:        "different date",
+			dateTimeOne: system.MustParseDateTime("2023-01-01T00:00:00.000"),
+			dateTimeTwo: system.MustParseDateTime("2023-01-02T00:00:00.000Z"),
+			shouldEqual: false,
+			wantOk:      true,
+		},
+		{
+			name:        "different type",
+			dateTimeOne: system.MustParseDateTime("2023-01-01T00:00:00.000"),
+			dateTimeTwo: system.String("2023-01-01T00:00:00.000"),
+			shouldEqual: false,
+			wantOk:      true,
+		},
+		{
+			name:        "same time different offset format",
+			dateTimeOne: system.MustParseDateTime("2023-01-02T00:00:00.000Z"),
+			dateTimeTwo: system.MustParseDateTime("2023-01-02T00:00:00.000-00:00"),
+			shouldEqual: true,
+			wantOk:      true,
+		},
+		{
+			name:        "same time different time zone",
+			dateTimeOne: system.MustParseDateTime("2023-01-01T08:30:00+03:00"),
+			dateTimeTwo: system.MustParseDateTime("2023-01-01T05:30:00Z"),
+			shouldEqual: true,
+			wantOk:      true,
+		},
+		{
+			name:        "not equal with mismatched precision",
+			dateTimeOne: system.MustParseDateTime("2023-02-01T08:30"),
+			dateTimeTwo: system.MustParseDateTime("2023-01-01T08:30:45"),
+			shouldEqual: false,
+			wantOk:      true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, ok := tc.dateTimeOne.TryEqual(tc.dateTimeTwo)
+
+			if ok != tc.wantOk {
+				t.Fatalf("DateTime.Equal: ok got %v, want %v", ok, tc.wantOk)
+			}
+			if got != tc.shouldEqual {
+				t.Errorf("DateTime.Equal returned unexpected equality: got %v, want %v", got, tc.shouldEqual)
+			}
+		})
+	}
+}
+
+func TestDateTimeFromProto_Converts(t *testing.T) {
+	almostY2K, _ := system.ParseDateTime("1999-12-31T23:59:59.999Z")
+	dec2004, _ := system.ParseDateTime("2004-12T")
+	pandemic, _ := system.ParseDateTime("2020-03-15T08:30:05Z")
+
+	testCases := []struct {
+		name    string
+		dtProto *dtpb.DateTime
+		wantDT  system.DateTime
+	}{
+		{
+			name:    "converts microsecond precision",
+			dtProto: fhir.MustParseDateTime("1999-12-31T23:59:59.999999Z"),
+			wantDT:  almostY2K,
+		},
+		{
+			name:    "converts partial date precision",
+			dtProto: fhir.MustParseDateTime("2004-12"),
+			wantDT:  dec2004,
+		},
+		{
+			name:    "converts second precision",
+			dtProto: fhir.MustParseDateTime("2020-03-15T08:30:05Z"),
+			wantDT:  pandemic,
+		},
+		{
+			name:    "converts with alternate offset format",
+			dtProto: fhir.MustParseDateTime("2020-03-15T08:30:05-00:00"),
+			wantDT:  pandemic,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := system.DateTimeFromProto(tc.dtProto)
+
+			if err != nil {
+				t.Fatalf("DateTimeFromProto(%s) raised unexpected error: %v", tc.dtProto.String(), err)
+			}
+			if diff := cmp.Diff(tc.wantDT, got); diff != "" {
+				t.Errorf("DateTimeFromProto(%s) incorrectly converts DateTime: (-want, +got)\n%s", tc.dtProto.String(), diff)
+			}
+		})
+	}
+}
+
+func TestDateTimeToProtoDateTime_Converts(t *testing.T) {
+	testCases := []struct {
+		name     string
+		dateTime system.DateTime
+		want     *dtpb.DateTime
+	}{
+		{
+			name:     "converts millisecond precision with timezone",
+			dateTime: system.MustParseDateTime("1999-12-31T23:59:59.999Z"),
+			want:     fhir.MustParseDateTime("1999-12-31T23:59:59.999Z"),
+		},
+		{
+			name:     "converts to second precision with timezone",
+			dateTime: system.MustParseDateTime("1999-12-31T23:59:59Z"),
+			want:     fhir.MustParseDateTime("1999-12-31T23:59:59Z"),
+		},
+		{
+			name:     "converts to day precision",
+			dateTime: system.MustParseDateTime("1999-12-31T"),
+			want:     fhir.MustParseDateTime("1999-12-31"),
+		},
+		{
+			name:     "converts to month precision",
+			dateTime: system.MustParseDateTime("1999-12T"),
+			want:     fhir.MustParseDateTime("1999-12"),
+		},
+		{
+			name:     "converts to year precision",
+			dateTime: system.MustParseDateTime("1999T"),
+			want:     fhir.MustParseDateTime("1999"),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := tc.dateTime.ToProtoDateTime()
+
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("DateTime.ToProtoDateTime returned unexpected result: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestDateTimeLess_ReturnsBoolean(t *testing.T) {
+	testCases := []struct {
+		name        string
+		dateTimeOne system.DateTime
+		dateTimeTwo system.DateTime
+		want        system.Boolean
+	}{
+		{
+			name:        "returns true for an earlier date",
+			dateTimeOne: system.MustParseDateTime("2012-11-15T08:12:12"),
+			dateTimeTwo: system.MustParseDateTime("2012-11-15T08:30:12"),
+			want:        true,
+		},
+		{
+			name:        "returns false for a later time",
+			dateTimeOne: system.MustParseDateTime("2013-11-15T00:30:00.000Z"),
+			dateTimeTwo: system.MustParseDateTime("2012-11-15T00:30:00.000Z"),
+			want:        false,
+		},
+		{
+			name:        "returns true for an earlier date with mismatched precision",
+			dateTimeOne: system.MustParseDateTime("2022-11-11T18"),
+			dateTimeTwo: system.MustParseDateTime("2022-11-12T18:30"),
+			want:        true,
+		},
+		{
+			name:        "returns false for a later time with mismatched precision",
+			dateTimeOne: system.MustParseDateTime("2021-11-12T18:30"),
+			dateTimeTwo: system.MustParseDateTime("2021-11-04T14:30:25"),
+			want:        false,
+		},
+		{
+			name:        "returns false (doesn't error) for equal times with mismatched millisecond precision",
+			dateTimeOne: system.MustParseDateTime("2021-11-04T14:30:25.000"),
+			dateTimeTwo: system.MustParseDateTime("2021-11-04T14:30:25"),
+		},
+		{
+			name:        "returns true for earlier time with mismatched millisecond precision",
+			dateTimeOne: system.MustParseDateTime("2000-01-01T18:30:01"),
+			dateTimeTwo: system.MustParseDateTime("2000-01-01T18:30:01.001"),
+			want:        true,
+		},
+		{
+			name:        "respects time zone offset",
+			dateTimeOne: system.MustParseDateTime("2000-12-19T18:30:01+05:00"),
+			dateTimeTwo: system.MustParseDateTime("2000-12-19T13:30:02"),
+			want:        true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.dateTimeOne.Less(tc.dateTimeTwo)
+
+			if err != nil {
+				t.Fatalf("DateTime.Less returned unexpected error: %v", err)
+			}
+			if got != tc.want {
+				t.Errorf("DateTime.Less returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestDateTimeLess_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name        string
+		dateTimeOne system.DateTime
+		input       system.Any
+		wantErr     error
+	}{
+		{
+			name:        "incorrect input type",
+			dateTimeOne: system.MustParseDateTime("2020-09-02T08:30:30"),
+			input:       system.String("2020-09-02T08:30:30"),
+			wantErr:     system.ErrTypeMismatch,
+		},
+		{
+			name:        "equal until precision mismatch",
+			dateTimeOne: system.MustParseDateTime("2020-09-02T08:30:30"),
+			input:       system.MustParseDateTime("2020-09-02T08:30"),
+			wantErr:     system.ErrMismatchedPrecision,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := tc.dateTimeOne.Less(tc.input)
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("DateTime.Less returned incorrect error: got %v, want %v", err, tc.wantErr)
+			}
+		})
+	}
+}
+
+func TestDateTimeAdd_ReturnsSum(t *testing.T) {
+	testCases := []struct {
+		name        string
+		dateTimeOne system.DateTime
+		input       system.Quantity
+		want        system.DateTime
+		wantErr     error
+	}{
+		{
+			name:        "overflows hours",
+			dateTimeOne: system.MustParseDateTime("2012-11-15T08:12:12"),
+			input:       system.MustParseQuantity("24", "hours"),
+			want:        system.MustParseDateTime("2012-11-16T08:12:12"),
+		},
+		{
+			name:        "adds years correctly when start date is a leap year",
+			dateTimeOne: system.MustParseDateTime("2020-02-29T08:12:12"),
+			input:       system.MustParseQuantity("1", "year"),
+			want:        system.MustParseDateTime("2021-02-28T08:12:12"),
+		},
+		{
+			name:        "adds decimal of seconds",
+			dateTimeOne: system.MustParseDateTime("2013-11-15T00:30:00.000Z"),
+			input:       system.MustParseQuantity("1.232", "seconds"),
+			want:        system.MustParseDateTime("2013-11-15T00:30:01.232Z"),
+		},
+		{
+			name:        "rounds quantity down to highest precision",
+			dateTimeOne: system.MustParseDateTime("2022-11-11T18"),
+			input:       system.MustParseQuantity("59", "minutes"),
+			want:        system.MustParseDateTime("2022-11-11T18"),
+		},
+		{
+			name:        "respects adding months when the result month is of a different length",
+			dateTimeOne: system.MustParseDateTime("2021-10-31T18:30"),
+			input:       system.MustParseQuantity("1", "month"),
+			want:        system.MustParseDateTime("2021-11-30T18:30"),
+		},
+		{
+			name:        "disregards decimal part of quantity",
+			dateTimeOne: system.MustParseDateTime("2021-11-04T14:30:25.000"),
+			input:       system.MustParseQuantity("3.23", "months"),
+			want:        system.MustParseDateTime("2022-02-04T14:30:25.000"),
+		},
+		{
+			name:        "adds days correctly",
+			dateTimeOne: system.MustParseDateTime("2021-11-04T14:30:25.000"),
+			input:       system.MustParseQuantity("32", "days"),
+			want:        system.MustParseDateTime("2021-12-06T14:30:25.000"),
+		},
+		{
+			name:        "adds weeks correctly",
+			dateTimeOne: system.MustParseDateTime("2021-11-01T14:30:25.000"),
+			input:       system.MustParseQuantity("18", "weeks"),
+			want:        system.MustParseDateTime("2022-03-07T14:30:25.000"),
+		},
+		{
+			name:        "returns error when adding non time-valued quantity",
+			dateTimeOne: system.MustParseDateTime("2021-11-01T14:30:25.000"),
+			input:       system.MustParseQuantity("18", "lbs"),
+			wantErr:     system.ErrMismatchedUnit,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.dateTimeOne.Add(tc.input)
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("DateTime.Add returned unexpected error: got: %v, want %v", err, tc.wantErr)
+			}
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("DateTime.Add returned unexpected diff: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestDateTimeSub_ReturnsResults(t *testing.T) {
+	testCases := []struct {
+		name        string
+		dateTimeOne system.DateTime
+		input       system.Quantity
+		want        system.DateTime
+		wantErr     error
+	}{
+		{
+			name:        "overflows hours",
+			dateTimeOne: system.MustParseDateTime("2012-11-15T08:12:12"),
+			input:       system.MustParseQuantity("24", "hours"),
+			want:        system.MustParseDateTime("2012-11-14T08:12:12"),
+		},
+		{
+			name:        "subtracts years correctly when start date is a leap year",
+			dateTimeOne: system.MustParseDateTime("2020-02-29T08:12:12"),
+			input:       system.MustParseQuantity("1", "year"),
+			want:        system.MustParseDateTime("2019-02-28T08:12:12"),
+		},
+		{
+			name:        "correctly subtracts from a month partial",
+			dateTimeOne: system.MustParseDateTime("2020-02T"),
+			input:       system.MustParseQuantity("12", "years"),
+			want:        system.MustParseDateTime("2008-02T"),
+		},
+		{
+			name:        "correctly subtracts weeks",
+			dateTimeOne: system.MustParseDateTime("2020-02-03T08"),
+			input:       system.MustParseQuantity("12", "weeks"),
+			want:        system.MustParseDateTime("2019-11-11T08"),
+		},
+		{
+			name:        "subtracts decimal of seconds",
+			dateTimeOne: system.MustParseDateTime("2013-11-15T00:30:01.232Z"),
+			input:       system.MustParseQuantity("1.232", "seconds"),
+			want:        system.MustParseDateTime("2013-11-15T00:30:00.000Z"),
+		},
+		{
+			name:        "subtracts years correctly if in a leap year",
+			dateTimeOne: system.MustParseDateTime("2020-02-29T"),
+			input:       system.MustParseQuantity("1", "year"),
+			want:        system.MustParseDateTime("2019-02-28T"),
+		},
+		{
+			name:        "rounds quantity down to highest precision for hour partial",
+			dateTimeOne: system.MustParseDateTime("2022-11-11T18"),
+			input:       system.MustParseQuantity("59", "minutes"),
+			want:        system.MustParseDateTime("2022-11-11T18"),
+		},
+		{
+			name:        "rounds quantity down to to highest precision for year partial",
+			dateTimeOne: system.MustParseDateTime("2021T"),
+			input:       system.MustParseQuantity("23", "months"),
+			want:        system.MustParseDateTime("2020T"),
+		},
+		{
+			name:        "respects subtracting months when the result month is of a different length",
+			dateTimeOne: system.MustParseDateTime("2021-10-31T18:30"),
+			input:       system.MustParseQuantity("1", "month"),
+			want:        system.MustParseDateTime("2021-09-30T18:30"),
+		},
+		{
+			name:        "disregards decimal part of quantity",
+			dateTimeOne: system.MustParseDateTime("2021-11-04T14:30:25.000"),
+			input:       system.MustParseQuantity("3.23", "months"),
+			want:        system.MustParseDateTime("2021-08-04T14:30:25.000"),
+		},
+		{
+			name:        "returns error when subtracting non time-valued quantity",
+			dateTimeOne: system.MustParseDateTime("2021-11-01T14:30:25.000"),
+			input:       system.MustParseQuantity("18", "lbs"),
+			wantErr:     system.ErrMismatchedUnit,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.dateTimeOne.Sub(tc.input)
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("DateTime.Sub returned unexpected error: got: %v, want %v", err, tc.wantErr)
+			}
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("DateTime.Sub returned unexpected diff: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
diff --git a/fhirpath/system/doc.go b/fhirpath/system/doc.go
new file mode 100644
index 0000000..4efe87a
--- /dev/null
+++ b/fhirpath/system/doc.go
@@ -0,0 +1,8 @@
+/*
+Package system provides types and related utility functions
+for all the valid FHIRPath System types to be used for literals.
+
+More documentation about FHIRPath System types can be found here:
+- http://hl7.org/fhirpath/N1/#literals
+*/
+package system
diff --git a/fhirpath/system/layouts.go b/fhirpath/system/layouts.go
new file mode 100644
index 0000000..cc5bfad
--- /dev/null
+++ b/fhirpath/system/layouts.go
@@ -0,0 +1,86 @@
+package system
+
+// datePrecision enumerates date precision constants.
+type datePrecision int
+
+// timePrecision enumerates time precision constants.
+type timePrecision int
+
+// dateTimePrecision enumerates dateTime precision constants.
+type dateTimePrecision int
+
+// layout represents a layout string for parsing.
+type layout string
+
+// Date precision constants.
+const (
+	year datePrecision = iota
+	month
+	day
+)
+
+// Time precision constants.
+const (
+	hour timePrecision = iota
+	minute
+	second
+)
+
+// DateTime precision constants.
+const (
+	dtYear dateTimePrecision = iota
+	dtMonth
+	dtDay
+	dtHour
+	dtMinute
+	dtSecond
+)
+
+// layout constants.
+const (
+	yearLayout            = "2006"
+	monthLayout           = "2006-01"
+	dayLayout             = "2006-01-02"
+	hourLayout            = "15"
+	minuteLayout          = "15:04"
+	secondLayout          = "15:04:05"
+	millisecondLayout     = "15:04:05.000"
+	dtMillisecondLayoutTZ = "2006-01-02T15:04:05.000Z07:00"
+	dtMillisecondLayout   = "2006-01-02T15:04:05.000"
+	dtSecondLayoutTZ      = "2006-01-02T15:04:05Z07:00"
+	dtSecondLayout        = "2006-01-02T15:04:05"
+	dtMinuteLayoutTZ      = "2006-01-02T15:04Z07:00"
+	dtMinuteLayout        = "2006-01-02T15:04"
+	dtHourLayoutTZ        = "2006-01-02T15Z07:00"
+	dtHourLayout          = "2006-01-02T15"
+	dtDayLayout           = "2006-01-02T"
+	dtMonthLayout         = "2006-01T"
+	dtYearLayout          = "2006T"
+)
+
+var dateMap = map[layout]datePrecision{
+	dayLayout:   day,
+	monthLayout: month,
+	yearLayout:  year,
+}
+
+var timeMap = map[layout]timePrecision{
+	millisecondLayout: second,
+	secondLayout:      second,
+	minuteLayout:      minute,
+	hourLayout:        hour,
+}
+
+var dateTimeMap = map[layout]dateTimePrecision{
+	dtMillisecondLayoutTZ: dtSecond,
+	dtMillisecondLayout:   dtSecond,
+	dtSecondLayoutTZ:      dtSecond,
+	dtSecondLayout:        dtSecond,
+	dtMinuteLayoutTZ:      dtMinute,
+	dtMinuteLayout:        dtMinute,
+	dtHourLayoutTZ:        dtHour,
+	dtHourLayout:          dtHour,
+	dtDayLayout:           dtDay,
+	dtMonthLayout:         dtMonth,
+	dtYearLayout:          dtYear,
+}
diff --git a/fhirpath/system/primitives.go b/fhirpath/system/primitives.go
new file mode 100644
index 0000000..24a8297
--- /dev/null
+++ b/fhirpath/system/primitives.go
@@ -0,0 +1,303 @@
+package system
+
+import (
+	"fmt"
+	"math"
+	"strconv"
+	"strings"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/shopspring/decimal"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+)
+
+// Boolean is a representation for a true or false value.
+type Boolean bool
+
+// ParseBoolean parses a boolean string and returns a Boolean value
+// FHIRPath docs here: https://hl7.org/fhirpath/N1/#toboolean-boolean
+func ParseBoolean(input string) (Boolean, error) {
+	switch strings.ToLower(input) {
+	case "true", "t", "yes", "y", "1", "1.0":
+		return true, nil
+	case "false", "f", "no", "n", "0", "0.0":
+		return false, nil
+	default:
+		return false, fmt.Errorf("%v can't be parsed to boolean", input)
+	}
+}
+
+// Equal returns true if the input value is a System Boolean,
+// and contains the same value.
+func (b Boolean) Equal(input Any) bool {
+	val, ok := input.(Boolean)
+	if !ok {
+		return false
+	}
+	return b == val
+}
+
+// Less returns error for Boolean comparison.
+func (b Boolean) Less(input Any) (Boolean, error) {
+	return false, fmt.Errorf("%w: %T, %T", ErrTypeMismatch, b, input)
+}
+
+// Name returns the type name.
+func (b Boolean) Name() string {
+	return booleanType
+}
+
+// String represents string values.
+type String string
+
+// ParseString parses the input string and replaces FHIRPath
+// escape sequences with their Go-equivalent escape characters.
+func ParseString(input string) (String, error) {
+	escSequences := []string{
+		"\\'", "'",
+		"\\\"", "\"",
+		"\\`", "`",
+		"\\r", "\r",
+		"\\t", "\t",
+		"\\n", "\n",
+		"\\f", "\f",
+		"\\\\", "\\",
+		"\\", "",
+		// TODO PHP-5581
+	}
+	input = strings.TrimPrefix(input, "'")
+	input = strings.TrimSuffix(input, "'")
+	replacer := strings.NewReplacer(escSequences...)
+	escapedString := replacer.Replace(input)
+	return String(escapedString), nil
+}
+
+// Equal returns true if the input value is a System String,
+// and contains the same string value.
+func (s String) Equal(input Any) bool {
+	val, ok := input.(String)
+	if !ok {
+		return false
+	}
+	return s == val
+}
+
+// Name returns the type name.
+func (s String) Name() string {
+	return stringType
+}
+
+// Less returns true if s is less than input.(String), by lexicographic
+// comparison. If input is not a String, returns an error.
+func (s String) Less(input Any) (Boolean, error) {
+	val, ok := input.(String)
+	if !ok {
+		return false, fmt.Errorf("%w: %T, %T,", ErrTypeMismatch, s, input)
+	}
+	return string(s) < string(val), nil
+}
+
+// Add concatenates the input String to the right of s.
+func (s String) Add(input String) String {
+	return s + input
+}
+
+// Integer represents integer values in the range 0 to 2^31 - 1.
+// Negative integers in FHIRPath are denoted with the
+type Integer int32
+
+// ParseInteger parses a string into an int32 value, and returns
+// an error if the input does not represent a valid int32.
+func ParseInteger(value string) (Integer, error) {
+	i, err := strconv.ParseInt(value, 10, 32)
+	if err != nil {
+		return Integer(0), err
+	}
+	return Integer(i), nil
+}
+
+// Equal returns true if the input value is a System Integer, and
+// contains the same int32 value.
+func (i Integer) Equal(input Any) bool {
+	val, ok := input.(Integer)
+	if !ok {
+		return false
+	}
+	return i == val
+}
+
+// Name returns the type name.
+func (i Integer) Name() string {
+	return integerType
+}
+
+// Less returns true if i is less than input.(Integer).
+// If input is not an Integer, returns an error.
+func (i Integer) Less(input Any) (Boolean, error) {
+	val, ok := input.(Integer)
+	if !ok {
+		return false, fmt.Errorf("%w: %T, %T,", ErrTypeMismatch, i, input)
+	}
+	return i < val, nil
+}
+
+// Add adds i to the input Integer. Returns an error
+// if the result overflows.
+func (i Integer) Add(input Integer) (Integer, error) {
+	result := i + input
+	// If i is incremented (result > i), input must be positive.
+	// Similarly, if i is decremented (result <= i), input must be 0 or negative.
+	// Otherwise, an overflow must have occured.
+	if (result > i) == (input > 0) {
+		return result, nil
+	}
+	return 0, ErrIntOverflow
+}
+
+// Sub subtracts the input Integer from i. Returns an error
+// if the result overflows.
+func (i Integer) Sub(input Integer) (Integer, error) {
+	result := i - input
+	// If i is decremented (result < i), input must be positive.
+	// Similarly, if i is incremented (result >= i), input must be 0 or negative.
+	// Otherwise, an overflow must have occured.
+	if (result < i) == (input > 0) {
+		return result, nil
+	}
+	return 0, ErrIntOverflow
+}
+
+// Mul multiplies the two integers together. Returns an
+// error if the result overflows.
+func (i Integer) Mul(input Integer) (Integer, error) {
+	if i == 0 || input == 0 {
+		return 0, nil
+	}
+	result := i * input
+	// Check if the sign of the result aligns with what it should be ( -ve == +ve * -ve )
+	if (result < 0) == ((i < 0) != (input < 0)) && (result/input) == i {
+		return result, nil
+	}
+	return 0, ErrIntOverflow
+}
+
+// Div divides i by input. Returns a Decimal.
+func (i Integer) Div(input Integer) Decimal {
+	lhs, rhs := decimal.NewFromInt32(int32(i)), decimal.NewFromInt32(int32(input))
+	return Decimal(lhs.Div(rhs))
+}
+
+// FloorDiv divides i by input and rounds down.
+func (i Integer) FloorDiv(input Integer) Integer {
+	return i / input
+}
+
+// Mod returns i % integer.
+func (i Integer) Mod(input Integer) Integer {
+	return i % input
+}
+
+// ToProtoInteger returns the proto representation of the system integer.
+func (i Integer) ToProtoInteger() *dtpb.Integer {
+	return fhir.Integer(int32(i))
+}
+
+// Decimal represents fixed-point decimals. Must use
+// utilities provided by "github.com/shopspring/decimal" to
+// perform arithmetic.
+type Decimal decimal.Decimal
+
+// ParseDecimal parses a string representing a decimal, and
+// returns an error if the input is invalid.
+func ParseDecimal(value string) (Decimal, error) {
+	d, err := decimal.NewFromString(value)
+	if err != nil {
+		return Decimal(decimal.Zero), err
+	}
+	return Decimal(d), nil
+}
+
+// MustParseDecimal converts a string into a Decimal type.
+// If the string is not parseable it will throw a panic().
+func MustParseDecimal(str string) Decimal {
+	dec, err := ParseDecimal(str)
+	if err != nil {
+		panic(err)
+	}
+	return dec
+}
+
+// Equal returns a bool representing whether or not
+// the two Decimals being compared are equal. Uses the API
+// provided by the decimal library.
+func (d Decimal) Equal(input Any) bool {
+	val, ok := input.(Decimal)
+	if !ok {
+		return false
+	}
+	return decimal.Decimal(d).Equal(decimal.Decimal(val))
+}
+
+// Name returns the type name.
+func (d Decimal) Name() string {
+	return decimalType
+}
+
+// Less returns true if d is less than input.(Decimal). If input
+// is not a Decimal, returns an error.
+func (d Decimal) Less(input Any) (Boolean, error) {
+	val, ok := input.(Decimal)
+	if !ok {
+		return false, fmt.Errorf("%w, %T, %T", ErrTypeMismatch, d, input)
+	}
+	return Boolean(decimal.Decimal(d).LessThan(decimal.Decimal(val))), nil
+}
+
+// Add adds d to the input Decimal
+func (d Decimal) Add(input Decimal) Decimal {
+	return Decimal(decimal.Decimal(d).Add(decimal.Decimal(input)))
+}
+
+// Sub subtracts the input Decimal from d.
+func (d Decimal) Sub(input Decimal) Decimal {
+	return Decimal(decimal.Decimal(d).Sub(decimal.Decimal(input)))
+}
+
+// String returns the string value from d.
+func (d Decimal) String() string {
+	return decimal.Decimal(d).String()
+}
+
+// Mul multiples the two decimal values together.
+func (d Decimal) Mul(input Decimal) Decimal {
+	return Decimal(decimal.Decimal(d).Mul(decimal.Decimal(input)))
+}
+
+// Div divides d by input.
+func (d Decimal) Div(input Decimal) Decimal {
+	return Decimal(decimal.Decimal(d).Div(decimal.Decimal(input)))
+}
+
+// FloorDiv divides d by input and rounds down.
+func (d Decimal) FloorDiv(input Decimal) (Integer, error) {
+	result := decimal.Decimal(d).Div(decimal.Decimal(input)).IntPart()
+	if (result < math.MinInt32) || (result > math.MaxInt32) {
+		return 0, ErrIntOverflow
+	}
+	return Integer(int32(result)), nil
+}
+
+// Mod computes d % input.
+func (d Decimal) Mod(input Decimal) Decimal {
+	return Decimal(decimal.Decimal(d).Mod(decimal.Decimal(input)))
+}
+
+// ToProtoDecimal returns the proto Decimal representation of decimal.
+func (d Decimal) ToProtoDecimal() *dtpb.Decimal {
+	return fhir.Decimal(decimal.Decimal(d).InexactFloat64())
+}
+
+// Round rounds a Decimal at the provided precision.
+func (d Decimal) Round(precision int32) Decimal {
+	return Decimal(decimal.Decimal(d).Round(precision))
+}
diff --git a/fhirpath/system/primitives_test.go b/fhirpath/system/primitives_test.go
new file mode 100644
index 0000000..f00ae45
--- /dev/null
+++ b/fhirpath/system/primitives_test.go
@@ -0,0 +1,402 @@
+package system_test
+
+import (
+	"errors"
+	"math"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+func TestParseString_ReplacesEscapeSequences(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input string
+		want  string
+	}{
+		{
+			name:  "replaces single quote",
+			input: `I\'m testing`,
+			want:  `I'm testing`,
+		},
+		{
+			name:  "replaces double quote",
+			input: `"This is a quote: \""`,
+			want:  `"This is a quote: ""`,
+		},
+		{
+			name:  "replaces backtick",
+			input: "let foo = \\`${bar}\\`",
+			want:  "let foo = `${bar}`",
+		},
+		{
+			name:  "replaces carriage return",
+			input: `here's a return \r`,
+			want:  "here's a return \r",
+		},
+		{
+			name:  "replaces newline",
+			input: `here's a newline \n`,
+			want:  "here's a newline \n",
+		},
+		{
+			name:  "replaces a tab",
+			input: `\t indent`,
+			want:  "\t indent",
+		},
+		{
+			name:  "replaces a form feed",
+			input: `\f`,
+			want:  "\f",
+		},
+		{
+			name:  "replaces backslashes w/ multiple escapes",
+			input: `escape \n\ \p \\p`,
+			want:  "escape \n p \\p",
+		},
+	}
+
+	for _, tc := range testCases {
+		str, err := system.ParseString(tc.input)
+
+		if err != nil {
+			t.Fatalf("ParseString(%s) raised error when not expected: %v", tc.input, err)
+		}
+		if got, want := string(str), tc.want; got != want {
+			t.Errorf("ParseString(%s) returned mismatch: got %#v, want %#v", tc.input, got, want)
+		}
+	}
+}
+
+func TestParseBoolean_ReturnsBoolean(t *testing.T) {
+	testCases := []struct {
+		name      string
+		input     string
+		want      system.Boolean
+		wantError bool
+	}{
+		{
+			name:  "parse 'true'",
+			input: "true",
+			want:  system.Boolean(true),
+		},
+		{
+			name:  "parse 'TRUE'",
+			input: "TRUE",
+			want:  system.Boolean(true),
+		},
+		{
+			name:  "parse 't'",
+			input: "t",
+			want:  system.Boolean(true),
+		},
+		{
+			name:  "parse 'T'",
+			input: "T",
+			want:  system.Boolean(true),
+		},
+		{
+			name:  "parse 'yes'",
+			input: "yes",
+			want:  system.Boolean(true),
+		},
+		{
+			name:  "parse 'YES'",
+			input: "YES",
+			want:  system.Boolean(true),
+		},
+		{
+			name:  "parse 'y'",
+			input: "y",
+			want:  system.Boolean(true),
+		},
+		{
+			name:  "parse 'Y'",
+			input: "Y",
+			want:  system.Boolean(true),
+		},
+		{
+			name:  "parse '1'",
+			input: "1",
+			want:  system.Boolean(true),
+		},
+		{
+			name:  "parse '1.0'",
+			input: "1.0",
+			want:  system.Boolean(true),
+		},
+		{
+			name:  "parse 'false'",
+			input: "false",
+			want:  system.Boolean(false),
+		},
+		{
+			name:  "parse 'FALSE'",
+			input: "FALSE",
+			want:  system.Boolean(false),
+		},
+		{
+			name:  "parse 'f'",
+			input: "f",
+			want:  system.Boolean(false),
+		},
+		{
+			name:  "parse 'F'",
+			input: "F",
+			want:  system.Boolean(false),
+		},
+		{
+			name:  "parse 'no'",
+			input: "no",
+			want:  system.Boolean(false),
+		},
+		{
+			name:  "parse 'NO'",
+			input: "NO",
+			want:  system.Boolean(false),
+		},
+		{
+			name:  "parse 'n'",
+			input: "n",
+			want:  system.Boolean(false),
+		},
+		{
+			name:  "parse 'N'",
+			input: "N",
+			want:  system.Boolean(false),
+		},
+		{
+			name:  "parse '0'",
+			input: "0",
+			want:  system.Boolean(false),
+		},
+		{
+			name:  "parse '0.0'",
+			input: "0.0",
+			want:  system.Boolean(false),
+		},
+		{
+			name:      "parse '2'",
+			input:     "2",
+			want:      system.Boolean(false),
+			wantError: true,
+		},
+		{
+			name:      "parse '2.0'",
+			input:     "2",
+			want:      system.Boolean(false),
+			wantError: true,
+		},
+		{
+			name:      "parse '2.5'",
+			input:     "2.5",
+			want:      system.Boolean(false),
+			wantError: true,
+		},
+		{
+			name:      "parse '2.5 kg'",
+			input:     "2.5 kg",
+			want:      system.Boolean(false),
+			wantError: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := system.ParseBoolean(tc.input)
+			if (err != nil) != tc.wantError {
+				t.Errorf("ParseBoolean(%s) error = %v, wantErr %v", tc.input, err, tc.wantError)
+				return
+			}
+			if got != tc.want {
+				t.Errorf("ParseBoolean(%s) parsed incorrectly, got: %v, want %v", tc.input, got, tc.want)
+				return
+			}
+		})
+	}
+}
+
+func TestParseInteger_ReturnsInteger(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input string
+		want  system.Integer
+	}{
+		{
+			name:  "positive edge",
+			input: "2147483647",
+			want:  system.Integer(2147483647),
+		},
+		{
+			name:  "negative edge",
+			input: "-2147483648",
+			want:  system.Integer(-2147483648),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			i, err := system.ParseInteger(tc.input)
+
+			if err != nil {
+				t.Fatalf("ParseInteger(%s) returns unexpected error: %v", tc.input, err)
+			}
+			if got, want := i, tc.want; got != want {
+				t.Errorf("ParseInteger(%s) parsed incorrectly: got %v, want %v", tc.input, got, want)
+			}
+		})
+	}
+}
+
+func TestParseInteger_ReturnsError_IfOutOfRange(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input string
+	}{
+		{
+			name:  "positive edge",
+			input: "2147483648",
+		},
+		{
+			name:  "negative edge",
+			input: "-2147483649",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := system.ParseInteger(tc.input)
+
+			if err == nil {
+				t.Fatalf("ParseInteger(%s) doesn't return error when expected to", tc.input)
+			}
+		})
+	}
+}
+
+func TestIntegerAdd(t *testing.T) {
+	testCases := []struct {
+		name    string
+		left    system.Integer
+		right   system.Integer
+		want    system.Integer
+		wantErr error
+	}{
+		{
+			name:  "adds two integers",
+			left:  2000,
+			right: 4001,
+			want:  6001,
+		},
+		{
+			name:    "returns error when addition overflows positively",
+			left:    math.MaxInt32,
+			right:   12120,
+			wantErr: system.ErrIntOverflow,
+		},
+		{
+			name:    "returns error when addition overflows negatively",
+			left:    math.MinInt32,
+			right:   -1,
+			wantErr: system.ErrIntOverflow,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.left.Add(tc.right)
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("Integer.Add returned unexpected error: got %v, want %v", err, tc.wantErr)
+			}
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("Integer.Add returned unexpected result: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestIntegerSub(t *testing.T) {
+	testCases := []struct {
+		name    string
+		left    system.Integer
+		right   system.Integer
+		want    system.Integer
+		wantErr error
+	}{
+		{
+			name:  "subtracts two integers",
+			left:  2000,
+			right: 4001,
+			want:  -2001,
+		},
+		{
+			name:    "returns error when subtraction overflows negatively",
+			left:    math.MinInt32,
+			right:   1,
+			wantErr: system.ErrIntOverflow,
+		},
+		{
+			name:    "returns error when subtraction overflows positively",
+			left:    math.MaxInt32,
+			right:   -1,
+			wantErr: system.ErrIntOverflow,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.left.Sub(tc.right)
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("Integer.Sub returned unexpected error: got %v, want %v", err, tc.wantErr)
+			}
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("Integer.Sub returned unexpected result: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestIntegerMul_ReturnsResult(t *testing.T) {
+	testCases := []struct {
+		name    string
+		left    system.Integer
+		right   system.Integer
+		want    system.Integer
+		wantErr error
+	}{
+		{
+			name:  "multiplies two integers",
+			left:  14,
+			right: 2,
+			want:  28,
+		},
+		{
+			name:    "returns error if multiplication causes overflow",
+			left:    1312312312,
+			right:   10,
+			wantErr: system.ErrIntOverflow,
+		},
+		{
+			name:    "returns overflow error if MinInt32 is multiplied",
+			left:    math.MinInt32,
+			right:   -1,
+			wantErr: system.ErrIntOverflow,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.left.Mul(tc.right)
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("Integer.Mul returned unexpected error: got %v, want %v", err, tc.wantErr)
+			}
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("Integer.Mul returned unexpected result: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
diff --git a/fhirpath/system/quantity.go b/fhirpath/system/quantity.go
new file mode 100644
index 0000000..802dcff
--- /dev/null
+++ b/fhirpath/system/quantity.go
@@ -0,0 +1,211 @@
+package system
+
+import (
+	"fmt"
+	"time"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/shopspring/decimal"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+)
+
+// Quantity type represents a decimal value along with a UCUM unit or
+// a calendar duration keyword.
+type Quantity struct {
+	value Decimal
+	unit  string
+}
+
+// ParseQuantity takes as input a number string and a unit string and constructs
+// a Quantity object. Returns error if the input does not fit into a valid Quantity
+func ParseQuantity(number string, unit string) (Quantity, error) {
+	d, err := ParseDecimal(number)
+	if err != nil {
+		return Quantity{}, err
+	}
+	q, err := newQuantity(d, unit)
+	if err != nil {
+		return Quantity{}, err
+	}
+	return q, nil
+}
+
+// MustParseQuantity constructs a Quantity object from a number string and unit string.
+// Panics if the inputs do not fit into a valid Quantity.
+func MustParseQuantity(number string, unit string) Quantity {
+	q, err := ParseQuantity(number, unit)
+	if err != nil {
+		panic(err)
+	}
+	return q
+}
+
+// newQuantity constructs a system Quantity type, given a decimal
+// value and a UCUM unit identifier.
+func newQuantity(value Decimal, unit string) (Quantity, error) {
+	// check isValidUnit(unit) *To be implemented
+	return Quantity{value, unit}, nil
+}
+
+// TryEqual returns a bool representing whether or not the
+// value represented by q is equal to the value of q2.
+// The comparison is not symmetric and may not return a value, represented by
+// the second boolean being set to false.
+func (q Quantity) TryEqual(input Any) (bool, bool) {
+	val, ok := input.(Quantity)
+	if !ok {
+		return false, true
+	}
+	if q.unit != val.unit {
+		return false, false
+	}
+	return q.value.Equal(val.value), true
+}
+
+// Less returns true if q is less than input.(Quantity). If the units
+// are mismatched, returns an error. If input is not a Quantity, returns
+// an error.
+func (q Quantity) Less(input Any) (Boolean, error) {
+	val, ok := input.(Quantity)
+	if !ok {
+		return false, fmt.Errorf("%w: %T, %T", ErrTypeMismatch, q, input)
+	}
+	if q.unit != val.unit {
+		return false, ErrMismatchedUnit
+	}
+	return q.value.Less(val.value)
+}
+
+// Add returns q + input. Returns an error if the units are mismatched.
+func (q Quantity) Add(input Quantity) (Quantity, error) {
+	if q.unit != input.unit {
+		return Quantity{}, ErrMismatchedUnit
+	}
+	value := Decimal(decimal.Decimal(q.value).Add(decimal.Decimal(input.value)))
+	return Quantity{value, q.unit}, nil
+}
+
+// Sub returns q - input. Returns an error if the units are mismatched.
+func (q Quantity) Sub(input Quantity) (Quantity, error) {
+	if q.unit != input.unit {
+		return Quantity{}, ErrMismatchedUnit
+	}
+	value := Decimal(decimal.Decimal(q.value).Sub(decimal.Decimal(input.value)))
+	return Quantity{value, q.unit}, nil
+}
+
+// Name returns the type name.
+func (q Quantity) Name() string {
+	return quantityType
+}
+
+// String returns the quantity value formatted as a string.
+func (q Quantity) String() string {
+	if q.unit != "" {
+		return fmt.Sprintf("%s %s", decimal.Decimal(q.value).String(), q.unit)
+	}
+	return fmt.Sprintf("%s", decimal.Decimal(q.value).String())
+}
+
+// ToProtoQuantity returns a proto Quantity based on a system Quantity.
+func (q Quantity) ToProtoQuantity() *dtpb.Quantity {
+	res := &dtpb.Quantity{
+		Value: q.value.ToProtoDecimal(),
+	}
+
+	if q.unit != "" {
+		res.Unit = fhir.String(q.unit)
+	}
+
+	return res
+}
+
+// Equal method to override cmp.Equal.
+func (q Quantity) Equal(q2 Quantity) bool {
+	return q.value.Equal(q2.value) && q.unit == q2.unit
+}
+
+// Negate returns the quantity multiplied by -1.
+func (q Quantity) Negate() Quantity {
+	negative := Decimal(decimal.NewFromInt(-1))
+	return Quantity{q.value.Mul(negative), q.unit}
+}
+
+// timeDuration returns the time.Duration represented by
+// a time-valued Quantity. Returns an error if the Quantity
+// doesn't represent a valid time duration.
+func (q Quantity) timeDuration() (time.Duration, error) {
+	value := decimal.Decimal(q.value).IntPart()
+
+	var duration time.Duration
+	switch q.unit {
+	case "hour", "hours":
+		duration = time.Hour * time.Duration(value)
+	case "minute", "minutes":
+		duration = time.Minute * time.Duration(value)
+	case "second", "seconds":
+		milliseconds := decimal.Decimal(q.value).Round(3).Shift(3).IntPart() // Keep decimal precision below seconds
+		duration = time.Millisecond * time.Duration(milliseconds)
+	case "millisecond":
+		duration = time.Millisecond * time.Duration(value)
+	default:
+		return time.Duration(0), fmt.Errorf("%w: not a time-valued unit", ErrMismatchedUnit)
+	}
+	return duration, nil
+}
+
+// Converts valid time based quantities to a number of years,
+// by rounding down.
+func (q Quantity) toYears() (int, error) {
+	value := int(decimal.Decimal(q.value).IntPart())
+
+	switch q.unit {
+	case "year", "years":
+		return value, nil
+	case "month", "months":
+		return value / 12, nil
+	case "week", "weeks":
+		value = value * 7
+		fallthrough
+	case "day", "days":
+		return value / 365, nil
+	case "hour", "hours":
+		return value / (365 * 24), nil
+	case "minute", "minutes":
+		return value / (365 * 24 * 60), nil
+	case "second", "seconds":
+		return value / (365 * 24 * 60 * 60), nil
+	case "millisecond", "milliseconds":
+		return value / (365 * 24 * 60 * 60) / 1000, nil
+	default:
+		return 0, fmt.Errorf("%w: not a time-valued unit", ErrMismatchedUnit)
+	}
+}
+
+// Converts a valid time based quantity to a number of months,
+// by rounding down.
+func (q Quantity) toMonths() (int, error) {
+	value := int(decimal.Decimal(q.value).IntPart())
+
+	switch q.unit {
+	case "year", "years":
+		return value * 12, nil
+	case "month", "months":
+		return value, nil
+	case "week", "weeks":
+		value = value * 7
+		fallthrough
+	case "day", "days":
+		return value / 30, nil
+	case "hour", "hours":
+		return value / (30 * 24), nil
+	case "minute", "minutes":
+		return value / (30 * 24 * 60), nil
+	case "second", "seconds":
+		return value / (30 * 24 * 60 * 60), nil
+	case "millisecond", "milliseconds":
+		return value / (30 * 24 * 60 * 60) / 1000, nil
+	default:
+		return 0, fmt.Errorf("%w: not a time-valued unit", ErrMismatchedUnit)
+	}
+}
diff --git a/fhirpath/system/quantity_test.go b/fhirpath/system/quantity_test.go
new file mode 100644
index 0000000..c288fe2
--- /dev/null
+++ b/fhirpath/system/quantity_test.go
@@ -0,0 +1,117 @@
+package system_test
+
+import (
+	"testing"
+
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+func TestParseQuantity_ErrorsOnInvalidString(t *testing.T) {
+	testCases := []struct {
+		name   string
+		number string
+		unit   string
+	}{
+		{
+			name:   "non-number",
+			number: "word",
+			unit:   "kg",
+		},
+		{
+			name:   "empty strings",
+			number: "",
+			unit:   "",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if _, err := system.ParseQuantity(tc.number, tc.unit); err == nil {
+				t.Fatalf("ParseQuantity(%s, %s) didn't raise error when expected", tc.number, tc.unit)
+			}
+		})
+	}
+}
+
+func TestParseQuantity_ReturnsQuantity(t *testing.T) {
+	testCases := []struct {
+		name   string
+		number string
+		unit   string
+	}{
+		{
+			name:   "unit quantity",
+			number: "100",
+			unit:   "lbs",
+		},
+		{
+			name:   "time quantity",
+			number: "3",
+			unit:   "minutes",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if _, err := system.ParseQuantity(tc.number, tc.unit); err != nil {
+				t.Fatalf("ParseQuantity(%s, %s) returned unexpected error: %v", tc.number, tc.unit, err)
+			}
+		})
+	}
+}
+
+func TestQuantity_Equal(t *testing.T) {
+	onePound, _ := system.ParseQuantity("1", "lbs")
+	oneLb, _ := system.ParseQuantity("1", "lbs")
+	oneKg, _ := system.ParseQuantity("1", "kg")
+	twoLbs, _ := system.ParseQuantity("2", "lbs")
+
+	testCases := []struct {
+		name        string
+		quantityOne system.Quantity
+		quantityTwo system.Any
+		shouldEqual bool
+		wantOk      bool
+	}{
+		{
+			name:        "same quantity different objects",
+			quantityOne: onePound,
+			quantityTwo: oneLb,
+			shouldEqual: true,
+			wantOk:      true,
+		},
+		{
+			name:        "same number different unit",
+			quantityOne: oneLb,
+			quantityTwo: oneKg,
+			wantOk:      false,
+		},
+		{
+			name:        "same unit different number",
+			quantityOne: oneLb,
+			quantityTwo: twoLbs,
+			shouldEqual: false,
+			wantOk:      true,
+		},
+		{
+			name:        "different type",
+			quantityOne: oneLb,
+			quantityTwo: system.String("1 lbs"),
+			shouldEqual: false,
+			wantOk:      true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, ok := tc.quantityOne.TryEqual(tc.quantityTwo)
+
+			if ok != tc.wantOk {
+				t.Fatalf("Quantity.Equal: ok got %v, want %v", ok, tc.wantOk)
+			}
+			if got != tc.shouldEqual {
+				t.Errorf("Quantity.Equal returned unexpected equality: got %v, want %v", got, tc.shouldEqual)
+			}
+		})
+	}
+}
diff --git a/fhirpath/system/time.go b/fhirpath/system/time.go
new file mode 100644
index 0000000..14beff2
--- /dev/null
+++ b/fhirpath/system/time.go
@@ -0,0 +1,192 @@
+package system
+
+import (
+	"fmt"
+	"strings"
+	"time"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/fhirconv"
+)
+
+// Time represents a time of day in the range 00:00:00.000
+// to 23:59:59.999 with a step size of 1ms. It uses the format
+// hh:mm:ss.fff format to parse times.
+type Time struct {
+	time time.Time
+	l    layout
+}
+
+// ParseTime takes an input string and returns a Time object if
+// formatted correctly according to the FHIRPath spec, otherwise
+// an error.
+func ParseTime(value string) (Time, error) {
+	timeLayouts := []string{
+		millisecondLayout,
+		secondLayout,
+		minuteLayout,
+		hourLayout,
+	}
+
+	var t time.Time
+	var err error
+	value = strings.TrimPrefix(value, "@T")
+	for _, l := range timeLayouts {
+		if t, err = time.Parse(l, value); err == nil {
+			return Time{t, layout(l)}, nil
+		}
+	}
+	return Time{}, fmt.Errorf("unable to parse time '%s': %w", value, err)
+}
+
+// MustParseTime takes an input string and returns a Time object
+// if formatted correctly. Otherwise, panics.
+func MustParseTime(value string) Time {
+	t, err := ParseTime(value)
+	if err != nil {
+		panic(err)
+	}
+	return t
+}
+
+// TimeFromProto takes a proto Time and returns a System Time.
+func TimeFromProto(proto *dtpb.Time) Time {
+	duration := fhirconv.TimeToDuration(proto)
+	t := time.UnixMicro(duration.Microseconds()).In(time.UTC)
+	var l layout
+	switch proto.Precision {
+	case dtpb.Time_MICROSECOND:
+		fallthrough
+	case dtpb.Time_MILLISECOND:
+		l = millisecondLayout
+	case dtpb.Time_SECOND:
+		l = secondLayout
+	}
+	return Time{t, l}
+}
+
+// ToProtoTime returns a proto Time based on a system Time.
+func (t Time) ToProtoTime() *dtpb.Time {
+	tp := fhir.Time(t.time)
+	switch t.l {
+	case millisecondLayout:
+		tp.Precision = dtpb.Time_MILLISECOND
+	default:
+		tp.Precision = dtpb.Time_SECOND
+	}
+	return tp
+}
+
+// TryEqual returns a boolean representing whether or not
+// the value of t is equal to the value of t2.
+// Not intended to be used for cmp.Equal. The comparison is
+// not symmetric and may cause unexpected behaviour.
+func (t Time) TryEqual(input Any) (bool, bool) {
+	val, ok := input.(Time)
+	if !ok {
+		return false, true
+	}
+	if t.l == val.l {
+		return t.time.Equal(val.time), true
+	}
+
+	tComponents := t.getComponents()
+	valComponents := val.getComponents()
+
+	minPrecision := min(int(timeMap[t.l]), int(timeMap[val.l]))
+
+	for i := 0; i <= minPrecision; i++ {
+		if tComponents[i] == valComponents[i] && i != int(second) {
+			continue
+		}
+		return tComponents[i] == valComponents[i], true
+	}
+	return false, false
+}
+
+// Name returns the type name.
+func (t Time) Name() string {
+	return timeType
+}
+
+// Equal method to override cmp.Equal.
+func (t Time) Equal(t2 Time) bool {
+	return t.time.Format(string(t.l)) == t2.time.Format(string(t2.l))
+}
+
+// String formats the time as a time string.
+func (t Time) String() string {
+	return t.time.Format(string(t.l))
+}
+
+// Less returns true if the value of t is less than input.(Time).
+// Compares component by component, and returns an error if there is a
+// precision mismatch. If input is not a Time, returns an error.
+func (t Time) Less(input Any) (Boolean, error) {
+	val, ok := input.(Time)
+	if !ok {
+		return false, fmt.Errorf("%w: %T, %T", ErrTypeMismatch, t, input)
+	}
+	if t.l == val.l {
+		return Boolean(t.time.Before(val.time)), nil
+	}
+
+	tComponents := t.getComponents()
+	valComponents := val.getComponents()
+
+	minPrecision := min(int(timeMap[t.l]), int(timeMap[val.l]))
+
+	for i := 0; i <= minPrecision; i++ {
+		// precisions below second are irrelevant, and should be treated the same.
+		if tComponents[i] == valComponents[i] && i != int(second) {
+			continue
+		}
+		return tComponents[i] < valComponents[i], nil
+	}
+	return false, ErrMismatchedPrecision
+}
+
+// Add returns the result of t with the time-valued quantity added to it.
+// Returns an error if the Quantity does not represent a valid duration.
+func (t Time) Add(input Quantity) (Time, error) {
+	duration, err := input.timeDuration()
+	if err != nil {
+		return Time{}, err
+	}
+	duration = roundToTimePrecision(timeMap[t.l], duration)
+	return Time{t.time.Add(duration), t.l}, nil
+}
+
+// Sub returns the result of the time-valued quantity subtracted from t.
+// Returns an error if the Quantity does not represent a valid duration.
+func (t Time) Sub(input Quantity) (Time, error) {
+	duration, err := input.timeDuration()
+	if err != nil {
+		return Time{}, err
+	}
+	duration = roundToTimePrecision(timeMap[t.l], duration)
+	return Time{t.time.Add(-duration), t.l}, nil
+}
+
+// roundToTimePrecision is used to round down to the highest precision of
+// the time value.
+// Eg. 08:30 + 59 'seconds' = 08:30, but 08:30 + 60 'seconds' = 08:31
+func roundToTimePrecision(p timePrecision, d time.Duration) time.Duration {
+	switch p {
+	case hour:
+		return d / time.Hour
+	case minute:
+		return d / time.Minute
+	default:
+		return d
+	}
+}
+
+func (t Time) getComponents() []int {
+	return []int{
+		t.time.Hour(),
+		t.time.Minute(),
+		t.time.Second()*1000000000 + t.time.Nanosecond(),
+	}
+}
diff --git a/fhirpath/system/time_test.go b/fhirpath/system/time_test.go
new file mode 100644
index 0000000..57395b2
--- /dev/null
+++ b/fhirpath/system/time_test.go
@@ -0,0 +1,383 @@
+package system_test
+
+import (
+	"errors"
+	"os"
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+func TestParseTime_ReturnsTime(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input string
+	}{
+		{"Hour", "14"},
+		{"Minute", "14:23"},
+		{"Second", "14:23:21"},
+		{"Millisecond", "14:23:21.999"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if _, err := system.ParseTime(tc.input); err != nil {
+				t.Fatalf("ParseTime(%s) returned unexpected error: %v", tc.input, err)
+			}
+		})
+	}
+}
+
+func TestParseTime_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name  string
+		input string
+	}{
+		{"Bad format", "14-23-21.556"},
+		{"Bad hour", "25:23:21.999"},
+		{"Bad minute", "15:65:21.999"},
+		{"Bad second", "15:23:65.999"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if _, err := system.ParseTime(tc.input); err == nil {
+				t.Fatalf("ParseDate(%s) didn't raise error when expected", tc.input)
+			}
+		})
+	}
+}
+
+func TestTime_Equal(t *testing.T) {
+	testCases := []struct {
+		name        string
+		timeOne     system.Time
+		timeTwo     system.Any
+		shouldEqual bool
+		wantOk      bool
+	}{
+		{
+			name:    "same time different format",
+			timeOne: system.MustParseTime("08"),
+			timeTwo: system.MustParseTime("08:00:00"),
+			wantOk:  false,
+		},
+		{
+			name:        "different time with mismatched precision",
+			timeOne:     system.MustParseTime("08:24:30"),
+			timeTwo:     system.MustParseTime("08:30"),
+			shouldEqual: false,
+			wantOk:      true,
+		},
+		{
+			name:        "treats millisecond and second as same precision",
+			timeOne:     system.MustParseTime("08:30:10.000"),
+			timeTwo:     system.MustParseTime("08:30:10"),
+			shouldEqual: true,
+			wantOk:      true,
+		},
+		{
+			name:        "different time",
+			timeOne:     system.MustParseTime("08:00:00"),
+			timeTwo:     system.MustParseTime("08:30:00"),
+			shouldEqual: false,
+			wantOk:      true,
+		},
+		{
+			name:    "different type",
+			timeOne: system.MustParseTime("08:00:00"),
+			timeTwo: system.String("08:00:00"),
+			wantOk:  true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, ok := tc.timeOne.TryEqual(tc.timeTwo)
+
+			if ok != tc.wantOk {
+				t.Fatalf("Time.Equal: ok got %v, want %v", ok, tc.wantOk)
+			}
+			if got != tc.shouldEqual {
+				t.Errorf("Time.Equal returned unexpected equality: got %v, want %v", got, tc.shouldEqual)
+			}
+		})
+	}
+}
+
+func TestTimeFromProto_Converts(t *testing.T) {
+	testCases := []struct {
+		name      string
+		timeProto *dtpb.Time
+		wantTime  system.Time
+	}{
+		{
+			name:      "converts microsecond precision",
+			timeProto: fhir.MustParseTime("08:30:00.212123"),
+			wantTime:  system.MustParseTime("08:30:00.212"),
+		},
+		{
+			name:      "converts second precision",
+			timeProto: fhir.MustParseTime("16:45:00"),
+			wantTime:  system.MustParseTime("16:45:00"),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			changeLocale(t)
+			got := system.TimeFromProto(tc.timeProto)
+
+			if diff := cmp.Diff(tc.wantTime, got); diff != "" {
+				t.Errorf("TimeFromProto(%s) incorrectly converts Time: (-want, +got)\n%s", tc.timeProto.String(), diff)
+			}
+		})
+	}
+}
+
+func TestTimeLess_ReturnsBoolean(t *testing.T) {
+	testCases := []struct {
+		name    string
+		timeOne system.Time
+		timeTwo system.Time
+		want    system.Boolean
+	}{
+		{
+			name:    "returns true for an earlier time",
+			timeOne: system.MustParseTime("08:30:05"),
+			timeTwo: system.MustParseTime("12:03:05"),
+			want:    true,
+		},
+		{
+			name:    "returns false for a later time",
+			timeOne: system.MustParseTime("18:30:05"),
+			timeTwo: system.MustParseTime("12:03:05"),
+			want:    false,
+		},
+		{
+			name:    "returns true for an earlier time with mismatched precision",
+			timeOne: system.MustParseTime("18:30"),
+			timeTwo: system.MustParseTime("18:45:05"),
+			want:    true,
+		},
+		{
+			name:    "returns false for a later time with mismatched precision",
+			timeOne: system.MustParseTime("18:30:34"),
+			timeTwo: system.MustParseTime("12:20"),
+			want:    false,
+		},
+		{
+			name:    "returns false (doesn't error) for equal times with mismatched millisecond precision",
+			timeOne: system.MustParseTime("04:30:25"),
+			timeTwo: system.MustParseTime("04:30:25.000"),
+			want:    false,
+		},
+		{
+			name:    "returns true for earlier time with mismatched millisecond precision",
+			timeOne: system.MustParseTime("18:30:01"),
+			timeTwo: system.MustParseTime("18:30:01.001"),
+			want:    true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.timeOne.Less(tc.timeTwo)
+
+			if err != nil {
+				t.Fatalf("Time.Less returned unexpected error: %v", err)
+			}
+			if got != tc.want {
+				t.Errorf("Time.Less returned unexpected result: got %v, want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestTimeLess_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name    string
+		timeOne system.Time
+		input   system.Any
+		wantErr error
+	}{
+		{
+			name:    "incorrect input type",
+			timeOne: system.MustParseTime("08:30:30"),
+			input:   system.String("08:30:30"),
+			wantErr: system.ErrTypeMismatch,
+		},
+		{
+			name:    "equal until precision mismatch",
+			timeOne: system.MustParseTime("11:11:11"),
+			input:   system.MustParseTime("11:11"),
+			wantErr: system.ErrMismatchedPrecision,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := tc.timeOne.Less(tc.input)
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("Time.Less returned incorrect error: got %v, want %v", err, tc.wantErr)
+			}
+		})
+	}
+}
+
+func TestTimeAdd_ReturnsSum(t *testing.T) {
+	testCases := []struct {
+		name    string
+		time    system.Time
+		input   system.Quantity
+		want    system.Time
+		wantErr error
+	}{
+		{
+			name:  "overflows time hours",
+			time:  system.MustParseTime("23:30:00"),
+			input: system.MustParseQuantity("2", "hours"),
+			want:  system.MustParseTime("01:30:00"),
+		},
+		{
+			name:  "correctly adds minutes",
+			time:  system.MustParseTime("19:45:44"),
+			input: system.MustParseQuantity("25", "minutes"),
+			want:  system.MustParseTime("20:10:44"),
+		},
+		{
+			name:  "correctly adds seconds",
+			time:  system.MustParseTime("08:00:00"),
+			input: system.MustParseQuantity("23", "seconds"),
+			want:  system.MustParseTime("08:00:23"),
+		},
+		{
+			name:  "disregards decimal part of minutes",
+			time:  system.MustParseTime("08:00:00"),
+			input: system.MustParseQuantity("15.5", "minutes"),
+			want:  system.MustParseTime("08:15:00"),
+		},
+		{
+			name:  "adds decimal part of seconds",
+			time:  system.MustParseTime("08:00:00.000"),
+			input: system.MustParseQuantity("12.24", "seconds"),
+			want:  system.MustParseTime("08:00:12.240"),
+		},
+		{
+			name:  "adds partials by rounding down to the highest precision",
+			time:  system.MustParseTime("08"),
+			input: system.MustParseQuantity("59", "minutes"),
+			want:  system.MustParseTime("08"),
+		},
+		{
+			name:    "returns error on non time-valued quantity",
+			time:    system.MustParseTime("08:30:00"),
+			input:   system.MustParseQuantity("12", "kg"),
+			wantErr: system.ErrMismatchedUnit,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.time.Add(tc.input)
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("Time.Add returned unexpected error: got %v, want %v", err, tc.wantErr)
+			}
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("Time.Add returned unexpected result: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func TestTimeSub_ReturnsResult(t *testing.T) {
+	testCases := []struct {
+		name    string
+		time    system.Time
+		input   system.Quantity
+		want    system.Time
+		wantErr error
+	}{
+		{
+			name:  "overflows time hours",
+			time:  system.MustParseTime("00:30:00"),
+			input: system.MustParseQuantity("2", "hours"),
+			want:  system.MustParseTime("22:30:00"),
+		},
+		{
+			name:  "correctly subtracts minutes",
+			time:  system.MustParseTime("19:45:44"),
+			input: system.MustParseQuantity("25", "minutes"),
+			want:  system.MustParseTime("19:20:44"),
+		},
+		{
+			name:  "correctly subtracts seconds",
+			time:  system.MustParseTime("08:00:00"),
+			input: system.MustParseQuantity("23", "seconds"),
+			want:  system.MustParseTime("07:59:37"),
+		},
+		{
+			name:  "disregards decimal part of minutes",
+			time:  system.MustParseTime("07:45:00"),
+			input: system.MustParseQuantity("15.5", "minutes"),
+			want:  system.MustParseTime("07:30:00"),
+		},
+		{
+			name:  "subtracts decimal part of seconds",
+			time:  system.MustParseTime("08:00:12.240"),
+			input: system.MustParseQuantity("12.24", "seconds"),
+			want:  system.MustParseTime("08:00:00.000"),
+		},
+		{
+			name:  "subtracts partials by rounding down to highest precision",
+			time:  system.MustParseTime("08:00"),
+			input: system.MustParseQuantity("119", "seconds"),
+			want:  system.MustParseTime("07:59"),
+		},
+		{
+			name:    "returns error on non time-valued quantity",
+			time:    system.MustParseTime("08:30:00"),
+			input:   system.MustParseQuantity("12", "kg"),
+			wantErr: system.ErrMismatchedUnit,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.time.Sub(tc.input)
+
+			if !errors.Is(err, tc.wantErr) {
+				t.Fatalf("Time.Add returned unexpected error: got %v, want %v", err, tc.wantErr)
+			}
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("Time.Add returned unexpected result: (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
+
+func changeLocale(t *testing.T) {
+	t.Helper()
+
+	// find a new locale
+	tz := os.Getenv("TZ")
+	newLocale := "Asia/Tokyo"
+	if tz == newLocale {
+		newLocale = "Africa/Cairo"
+	}
+	if err := os.Setenv("TZ", newLocale); err != nil {
+		t.Fatalf("error setting locale: %v", err)
+	}
+
+	// revert locale back to original
+	t.Cleanup(func() {
+		if err := os.Setenv("TZ", tz); err != nil {
+			t.Fatalf("error setting locale: %v", err)
+		}
+	})
+}
diff --git a/fhirpath/system/types.go b/fhirpath/system/types.go
new file mode 100644
index 0000000..8ab5c37
--- /dev/null
+++ b/fhirpath/system/types.go
@@ -0,0 +1,166 @@
+package system
+
+import (
+	"encoding/base64"
+	"errors"
+	"fmt"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/shopspring/decimal"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/fhirconv"
+	"github.com/verily-src/fhirpath-go/internal/protofields"
+)
+
+var ErrCantBeCast = errors.New("value can't be cast to system type")
+
+// Any is the root abstraction for all FHIRPath system types.
+type Any interface {
+	isSystemType()
+	Name() string
+	Less(input Any) (Boolean, error)
+}
+
+// Stub methods on each type to implement interface Any.
+func (s String) isSystemType()   {}
+func (b Boolean) isSystemType()  {}
+func (i Integer) isSystemType()  {}
+func (d Decimal) isSystemType()  {}
+func (d Date) isSystemType()     {}
+func (t Time) isSystemType()     {}
+func (d DateTime) isSystemType() {}
+func (q Quantity) isSystemType() {}
+
+// IsValid validates whether the input string represents
+// a valid system type name.
+func IsValid(typeName string) bool {
+	switch typeName {
+	case stringType, booleanType, integerType, decimalType,
+		dateType, timeType, dateTimeType, quantityType, anyType:
+		return true
+	default:
+		return false
+	}
+}
+
+// IsPrimitive evaluates to check whether or not the input
+// is a primitive FHIR type. If so, returns true, otherwise
+// returns false
+func IsPrimitive(input any) bool {
+	switch v := input.(type) {
+	case *dtpb.Boolean, *dtpb.String, *dtpb.Uri, *dtpb.Url, *dtpb.Canonical, *dtpb.Code, *dtpb.Oid, *dtpb.Id, *dtpb.Uuid, *dtpb.Markdown,
+		*dtpb.Base64Binary, *dtpb.Integer, *dtpb.UnsignedInt, *dtpb.PositiveInt, *dtpb.Decimal, *dtpb.Date,
+		*dtpb.Time, *dtpb.DateTime, *dtpb.Instant, *dtpb.Quantity, Any:
+		return true
+	case fhir.Base:
+		return protofields.IsCodeField(v)
+	default:
+		return false
+	}
+}
+
+// From converts primitive FHIR types to System types.
+// Returns the input if already a System type, and an error
+// if the input is not convertible.
+func From(input any) (Any, error) {
+	switch v := input.(type) {
+	case *dtpb.Boolean:
+		return Boolean(v.Value), nil
+	case *dtpb.String:
+		return String(v.Value), nil
+	case *dtpb.Uri:
+		return String(v.Value), nil
+	case *dtpb.Url:
+		return String(v.Value), nil
+	case *dtpb.Code:
+		return String(v.Value), nil
+	case *dtpb.Oid:
+		return String(v.Value), nil
+	case *dtpb.Id:
+		return String(v.Value), nil
+	case *dtpb.Uuid:
+		return String(v.Value), nil
+	case *dtpb.Markdown:
+		return String(v.Value), nil
+	case *dtpb.Base64Binary:
+		return String(base64.StdEncoding.EncodeToString(v.Value)), nil
+	case *dtpb.Canonical:
+		return String(v.Value), nil
+	case *dtpb.Integer:
+		return Integer(v.Value), nil
+	case *dtpb.UnsignedInt:
+		return Integer(v.Value), nil
+	case *dtpb.PositiveInt:
+		return Integer(v.Value), nil
+	case *dtpb.Decimal:
+		value, err := decimal.NewFromString(v.Value)
+		if err != nil {
+			return nil, err
+		}
+		return Decimal(value), nil
+	case *dtpb.Date:
+		value, err := DateFromProto(v)
+		if err != nil {
+			return nil, err
+		}
+		return value, nil
+	case *dtpb.Time:
+		return TimeFromProto(v), nil
+	case *dtpb.DateTime:
+		value, err := DateTimeFromProto(v)
+		if err != nil {
+			return nil, err
+		}
+		return value, nil
+	case *dtpb.Instant:
+		value, err := ParseDateTime(fhirconv.InstantToString(v))
+		if err != nil {
+			return nil, err
+		}
+		return value, nil
+	case *dtpb.Quantity:
+		value, err := decimal.NewFromString(v.Value.Value)
+		if err != nil {
+			return nil, err
+		}
+		unit := v.GetCode().GetValue()
+		return Quantity{Decimal(value), unit}, nil
+	case Any:
+		return v, nil
+	case fhir.Base:
+		value, ok := protofields.StringValueFromCodeField(v)
+		if !ok {
+			return nil, fmt.Errorf("%w: complex type %T", ErrCantBeCast, input)
+		}
+		return String(value), nil
+	default:
+		return nil, fmt.Errorf("%w: %T", ErrCantBeCast, input)
+	}
+}
+
+// Normalize casts the "from" type to the "to" type if implicit casting
+// is supported between the types. Otherwise, it returns the from input.
+func Normalize(from Any, to Any) Any {
+	switch v := from.(type) {
+	case Integer:
+		if _, ok := to.(Decimal); ok {
+			return Decimal(decimal.NewFromInt32(int32(v)))
+		}
+		if q, ok := to.(Quantity); ok {
+			dec := Decimal(decimal.NewFromInt32(int32(v)))
+			return Quantity{dec, q.unit}
+		}
+	case Decimal:
+		if q, ok := to.(Quantity); ok {
+			return Quantity{v, q.unit}
+		}
+	case Date:
+		if _, ok := to.(DateTime); ok {
+			newLayout := v.l + "T"
+			return DateTime{v.date, newLayout}
+		}
+	default:
+		return from
+	}
+	return from
+}
diff --git a/fhirpath/system/types_test.go b/fhirpath/system/types_test.go
new file mode 100644
index 0000000..6ed1731
--- /dev/null
+++ b/fhirpath/system/types_test.go
@@ -0,0 +1,281 @@
+package system_test
+
+import (
+	"testing"
+
+	cpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/codes_go_proto"
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	mrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_request_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	qpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/questionnaire_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/shopspring/decimal"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/element/canonical"
+	"github.com/verily-src/fhirpath-go/fhirpath/system"
+)
+
+var date, _ = system.ParseDate("2012-12-31")
+var time, _ = system.ParseTime("08:30:05")
+var dateTime, _ = system.ParseDateTime("2002-04-20T08:30:00Z")
+var quantity, _ = system.ParseQuantity("1.234", "m")
+
+type testCase struct {
+	name       string
+	input      any
+	want       system.Any
+	shouldCast bool
+}
+
+var testCases []testCase = []testCase{
+	{
+		name:       "converts Boolean",
+		input:      fhir.Boolean(true),
+		want:       system.Boolean(true),
+		shouldCast: true,
+	},
+	{
+		name:       "converts String",
+		input:      fhir.String("string"),
+		want:       system.String("string"),
+		shouldCast: true,
+	},
+	{
+		name:       "converts Canonical",
+		input:      canonical.New("string"),
+		want:       system.String("string"),
+		shouldCast: true,
+	},
+	{
+		name:       "converts Uri",
+		input:      fhir.URI("Uri"),
+		want:       system.String("Uri"),
+		shouldCast: true,
+	},
+	{
+		name:       "converts Url",
+		input:      fhir.URL("Url"),
+		want:       system.String("Url"),
+		shouldCast: true,
+	},
+	{
+		name:       "converts Oid",
+		input:      fhir.OID("Oid"),
+		want:       system.String("urn:oid:Oid"),
+		shouldCast: true,
+	},
+	{
+		name:       "converts Id",
+		input:      fhir.ID("Id"),
+		want:       system.String("Id"),
+		shouldCast: true,
+	},
+	{
+		name:       "converts code",
+		input:      fhir.Code("Code"),
+		want:       system.String("Code"),
+		shouldCast: true,
+	},
+	{
+		name:       "converts uuid",
+		input:      fhir.UUID("Uuid"),
+		want:       system.String("urn:uuid:Uuid"),
+		shouldCast: true,
+	},
+	{
+		name:       "converts markdown",
+		input:      fhir.Markdown("Markdown"),
+		want:       system.String("Markdown"),
+		shouldCast: true,
+	},
+	{
+		name:       "converts base64 binary",
+		input:      fhir.Base64Binary([]byte("hello world")),
+		want:       system.String("aGVsbG8gd29ybGQ="),
+		shouldCast: true,
+	},
+	{
+		name:       "converts integer",
+		input:      fhir.Integer(123),
+		want:       system.Integer(123),
+		shouldCast: true,
+	},
+	{
+		name:       "converts positive integer",
+		input:      fhir.PositiveInt(212),
+		want:       system.Integer(212),
+		shouldCast: true,
+	},
+	{
+		name:       "converts unsigned integer",
+		input:      fhir.UnsignedInt(10000),
+		want:       system.Integer(10000),
+		shouldCast: true,
+	},
+	{
+		name:       "converts decimal",
+		input:      fhir.Decimal(1.234),
+		want:       system.Decimal(decimal.NewFromFloat(1.234)),
+		shouldCast: true,
+	},
+	{
+		name:       "converts date",
+		input:      fhir.MustParseDate("2012-12-31"),
+		want:       date,
+		shouldCast: true,
+	},
+	{
+		name:       "converts time",
+		input:      fhir.MustParseTime("08:30:05"),
+		want:       time,
+		shouldCast: true,
+	},
+	{
+		name:       "converts dateTime",
+		input:      fhir.MustParseDateTime("2002-04-20T08:30:00Z"),
+		want:       dateTime,
+		shouldCast: true,
+	},
+	{
+		name:       "converts instant",
+		input:      fhir.MustParseInstant("2002-04-20T08:30:00Z"),
+		want:       dateTime,
+		shouldCast: true,
+	},
+	{
+		name:       "converts quantity",
+		input:      fhir.UCUMQuantity(float64(1.234), "m"),
+		want:       quantity,
+		shouldCast: true,
+	},
+	{
+		name:       "passes through system type",
+		input:      system.String("pass through"),
+		want:       system.String("pass through"),
+		shouldCast: true,
+	},
+	{
+		name:       "doesn't cast complex type",
+		input:      &ppb.Patient{},
+		shouldCast: false,
+	},
+	{
+		name:       "converts gender code",
+		input:      &ppb.Patient_GenderCode{Value: cpb.AdministrativeGenderCode_MALE},
+		want:       system.String("male"),
+		shouldCast: true,
+	},
+	{
+		name:       "converts PublicationStatus",
+		input:      &qpb.Questionnaire_StatusCode{Value: cpb.PublicationStatusCode_RETIRED},
+		want:       system.String("retired"),
+		shouldCast: true,
+	},
+	{
+		name:       "converts IntentCode",
+		input:      &mrpb.MedicationRequest_IntentCode{Value: cpb.MedicationRequestIntentCode_ORIGINAL_ORDER},
+		want:       system.String("original-order"),
+		shouldCast: true,
+	},
+	{
+		name:       "converts a code with a string value",
+		input:      &dtpb.Attachment_ContentTypeCode{Value: "image"},
+		want:       system.String("image"),
+		shouldCast: true,
+	},
+	{
+		name:       "converts Priority",
+		input:      &mrpb.MedicationRequest_PriorityCode{Value: cpb.RequestPriorityCode_URGENT},
+		want:       system.String("urgent"),
+		shouldCast: true,
+	},
+	{
+		name:       "doesn't cast non-code type with value field",
+		input:      &dtpb.ContactPoint{Value: fhir.String("123-456-7890")},
+		shouldCast: false,
+	},
+	{
+		name:       "doesn't cast non-fhir type",
+		input:      12,
+		shouldCast: false,
+	},
+}
+
+func TestIsPrimitive_ReturnsBoolean(t *testing.T) {
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ok := system.IsPrimitive(tc.input)
+
+			if ok != tc.shouldCast {
+				t.Errorf("system.IsPrimitive(%v) returns unexpected result, casts: %v, shouldCast: %v", tc.input, ok, tc.shouldCast)
+			}
+		})
+	}
+}
+
+func TestFrom_CastsCorrectly(t *testing.T) {
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if tc.shouldCast {
+				got, err := system.From(tc.input)
+
+				if err != nil {
+					t.Fatalf("system.From(%v) returns unexpected error: %v", tc.input, err)
+				}
+				if diff := cmp.Diff(tc.want, got); diff != "" {
+					t.Errorf("system.From(%v) returns unexpected diff: (-want, +got)\n%s", tc.input, diff)
+				}
+			}
+		})
+	}
+}
+
+func TestNormalize(t *testing.T) {
+	wantQuantity, _ := system.ParseQuantity("4", "m")
+	wantDateTime, _ := system.ParseDateTime("2012-12-31T")
+	testCases := []struct {
+		name string
+		from system.Any
+		to   system.Any
+		want system.Any
+	}{
+		{
+			name: "converts integer to decimal",
+			from: system.Integer(16),
+			to:   system.Decimal(decimal.NewFromInt32(20)),
+			want: system.Decimal(decimal.NewFromInt32(16)),
+		},
+		{
+			name: "converts decimal to quantity",
+			from: system.Decimal(decimal.NewFromFloat(1.234)),
+			to:   quantity,
+			want: quantity,
+		},
+		{
+			name: "converts integer to quantity",
+			from: system.Integer(4),
+			to:   quantity,
+			want: wantQuantity,
+		},
+		{
+			name: "converts Date to DateTime",
+			from: date,
+			to:   dateTime,
+			want: wantDateTime,
+		},
+		{
+			name: "passes through types that can't be converted",
+			from: system.String("2012-12-31"),
+			to:   date,
+			want: system.String("2012-12-31"),
+		},
+	}
+
+	for _, tc := range testCases {
+		got := system.Normalize(tc.from, tc.to)
+
+		if diff := cmp.Diff(tc.want, got); diff != "" {
+			t.Errorf("system.Normalize(%T, %T) returns unexpected diff: (-want, +got)\n%s", tc.from, tc.to, diff)
+		}
+	}
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..e7bd02d
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,28 @@
+module github.com/verily-src/fhirpath-go
+
+go 1.22.2
+
+require (
+	github.com/antlr4-go/antlr/v4 v4.13.0
+	github.com/google/fhir/go v0.7.4
+	github.com/google/go-cmp v0.6.0
+	github.com/google/uuid v1.6.0
+	github.com/iancoleman/strcase v0.3.0
+	github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38
+	github.com/shopspring/decimal v1.4.0
+	golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f
+	google.golang.org/protobuf v1.29.0
+)
+
+require (
+	bitbucket.org/creachadair/stringset v0.0.9 // indirect
+	github.com/golang/protobuf v1.5.2 // indirect
+	github.com/json-iterator/go v1.1.10 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.1 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e // indirect
+	github.com/stretchr/testify v1.8.1 // indirect
+	golang.org/x/net v0.7.0 // indirect
+	golang.org/x/sys v0.5.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..e873cf8
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,715 @@
+bitbucket.org/creachadair/stringset v0.0.9 h1:L4vld9nzPt90UZNrXjNelTshD74ps4P5NGs3Iq6yN3o=
+bitbucket.org/creachadair/stringset v0.0.9/go.mod h1:t+4WcQ4+PXTa8aQdNKe40ZP6iwesoMFWAxPGd3UGjyY=
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
+github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0=
+github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
+github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
+github.com/Azure/go-autorest/autorest v0.10.0/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
+github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
+github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
+github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
+github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
+github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
+github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
+github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
+github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
+github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k=
+github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
+github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
+github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
+github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic=
+github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
+github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
+github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
+github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
+github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/TylerBrock/colorjson v0.0.0-20180527164720-95ec53f28296/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w=
+github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
+github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
+github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
+github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
+github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+github.com/aws/aws-sdk-go v1.28.8/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/bazelbuild/rules_go v0.24.5 h1:8S5qilf+Il5/TPMZQIOfzQDAZtkhB4jALiAnwRuisDM=
+github.com/bazelbuild/rules_go v0.24.5/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/buger/jsonparser v0.0.0-20200322175846-f7e751efca13/go.mod h1:tgcrVJ81GPSF0mz+0nu1Xaz0fazGPrmmJfJtxjbHhUQ=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
+github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
+github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/corpix/uarand v0.1.1/go.mod h1:SFKZvkcRoLqVRFZ4u25xPmp6m9ktANfbpXZ7SJ0/FNU=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/creachadair/staticfile v0.1.3/go.mod h1:a3qySzCIXEprDGxk6tSxSI+dBBdLzqeBOMhZ+o2d3pM=
+github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
+github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
+github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
+github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
+github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
+github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
+github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
+github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
+github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
+github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
+github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
+github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
+github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
+github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
+github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
+github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
+github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
+github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
+github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
+github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
+github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
+github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
+github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
+github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
+github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
+github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
+github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
+github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
+github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
+github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
+github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
+github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
+github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
+github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
+github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
+github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
+github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
+github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
+github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/fhir/go v0.7.4 h1:DZv5LOcX8JIO1hdWESHNm3CD0PiWREuxjYDYE6gmzn0=
+github.com/google/fhir/go v0.7.4/go.mod h1:WF6g9QjYPqcQed319oPaRT5IcYWIRz610X3mxIt5TgU=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-github/v27 v27.0.4/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
+github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
+github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/hashicorp/consul/api v1.5.0/go.mod h1:LqwrLNW876eYSuUOo4ZLHBcdKc038txr/IMfbLPATa4=
+github.com/hashicorp/consul/sdk v0.5.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
+github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
+github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
+github.com/hashicorp/memberlist v0.2.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
+github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
+github.com/hashicorp/serf v0.9.0/go.mod h1:YL0HO+FifKOW2u1ke99DGVu1zhcpZzNwrLIqBC7vbYU=
+github.com/hashicorp/serf v0.9.2/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
+github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
+github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
+github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo=
+github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/krishicks/yaml-patch v0.0.10/go.mod h1:Sm5TchwZS6sm7RJoyg87tzxm2ZcKzdRE4Q7TjNhPrME=
+github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/manifoldco/promptui v0.7.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ=
+github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38 h1:hQWBtNqRYrI7CWIaUSXXtNKR90KzcUA5uiuxFVWw7sU=
+github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
+github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
+github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1/go.mod h1:vuvdOZLJuf5HmJAJrKV64MmozrSsk+or0PB5dzdfspg=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
+github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.2.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
+github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM=
+github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/olekukonko/tablewriter v0.0.5-0.20200416053754-163badb3bac6/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
+github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
+github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02/go.mod h1:JNdpVEzCpXBgIiv4ds+TzhN1hrtxq6ClLrTlT9OQRSc=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
+github.com/pires/go-proxyproto v0.0.0-20191211124218-517ecdf5bb2b/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
+github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e h1:zWKUYT07mGmVBH+9UgnHXd/ekCK99C8EbDSAt5qsjXE=
+github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
+github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/tchap/go-patricia v0.0.0-20160729071656-dd168db6051b/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
+github.com/tebeka/selenium v0.9.9/go.mod h1:5Fr8+pUvU6B1OiPfkdCKdXZyr5znvVkxuPd0NOdZCQc=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tinylib/msgp v1.1.1/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
+github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
+github.com/uber/jaeger-client-go v2.16.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
+github.com/uber/jaeger-lib v2.0.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
+github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
+github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/z-division/go-zookeeper v0.0.0-20190128072838-6d7457066b9b/go.mod h1:JNALoWa+nCXR8SmgLluHcBNVJgyejzpKPZk9pX2yXXE=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190128193316-c7b33c32a30b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
+golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191219041853-979b82bfef62/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
+gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
+gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190926190326-7ee9db18f195/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0=
+google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/DataDog/dd-trace-go.v1 v1.17.0/go.mod h1:DVp8HmDh8PuTu2Z0fVVlBsyWaC++fzwVCaGWylTe3tg=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/ini.v1 v1.41.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ldap.v2 v2.5.0/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+k8s.io/api v0.17.3/go.mod h1:YZ0OTkuw7ipbe305fMpIdf3GLXZKRigjtZaV5gzC2J0=
+k8s.io/apiextensions-apiserver v0.17.3/go.mod h1:CJbCyMfkKftAd/X/V6OTHYhVn7zXnDdnkUjS1h0GTeY=
+k8s.io/apimachinery v0.17.3/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
+k8s.io/apiserver v0.17.3/go.mod h1:iJtsPpu1ZpEnHaNawpSV0nYTGBhhX2dUlnn7/QS7QiY=
+k8s.io/client-go v0.17.3/go.mod h1:cLXlTMtWHkuK4tD360KpWz2gG2KtdWEr/OT02i3emRQ=
+k8s.io/code-generator v0.17.3/go.mod h1:l8BLVwASXQZTo2xamW5mQNFCe1XPiAesVq7Y1t7PiQQ=
+k8s.io/component-base v0.17.3/go.mod h1:GeQf4BrgelWm64PXkIXiPh/XS0hnO42d9gx9BtbZRp8=
+k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
+k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
+k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
+k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
+k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
+modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
+modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
+modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
+modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
+modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
+sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
+sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+vitess.io/vitess v0.7.0/go.mod h1:MjQFT3yaDsYxY+fwUwxqD0d7MRx7c8+wx0nMeXC9U/s=
diff --git a/gotchas.md b/gotchas.md
new file mode 100644
index 0000000..d45587d
--- /dev/null
+++ b/gotchas.md
@@ -0,0 +1,25 @@
+# FHIRPath Gotcha’s
+
+## Empty collections are propagated
+
+* In FHIRPath, whenever an empty collection is encountered, rather than raising an error it gets propagated throughout the rest of the expression. This may make some issues difficult to catch.
+* Eg. given `Patient.name` -> `{}`,  `Patient.name.family + ' MD'` -> `{}`
+
+## Equality sometimes returns an empty collection { }, rather than false
+
+* If either collection is empty
+* If the **precision_ _**of Date, Time, or DateTime objects are mismatched
+* If the **dimension** of a Quantity unit is mismatched
+
+## FHIR type specifiers are case-sensitive
+
+* **Primitive** types are denoted with lower case specifiers.
+* **Primitive** types that are written as upper case will be resolved as **System** types, not **FHIR** types.
+* Eg. `Patient.birthDate is date = **true**` but `Patient.birthDate is Date = **false**`
+* Case should match what’s listed [here](https://www.hl7.org/fhir/r4/datatypes.html)
+* System types always begin with an uppercase letter
+
+## `As` Expression is _not_ a filter, expects singleton input
+
+* The as expression (`Observation.value as integer`) expects a singleton as input. For example, if you pass in a resource with multiple value fields, it will raise an error.
+* It doesn’t filter out things that don’t match the type. For this purpose, the `where()` function should be used -> `where(value is integer)`
diff --git a/internal/bundle/bundle.go b/internal/bundle/bundle.go
new file mode 100644
index 0000000..ee44885
--- /dev/null
+++ b/internal/bundle/bundle.go
@@ -0,0 +1,79 @@
+/*
+Package bundle provides utilities for working with FHIR R4 Bundle proto
+definitions. This includes functionality for constructing/wrapping and
+unwrapping bundle/entry objects.
+*/
+package bundle
+
+import (
+	cpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/codes_go_proto"
+	bcrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/bundleopt"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+)
+
+// New creates a new New by building it from the bundle options.
+// Use of this function directly is discouraged; prefer to use the various
+// *New functions instead for the explicit types.
+func New(bundleType cpb.BundleTypeCode_Value, options ...Option) *bcrpb.Bundle {
+	bundle := &bcrpb.Bundle{
+		Type: &bcrpb.Bundle_TypeCode{
+			Value: bundleType,
+		},
+	}
+	return Extend(bundle, options...)
+}
+
+// Extend extends an existing bundle with the provided bundle options.
+// The options will be extended in-place; the return value is not necessary to be
+// looked at, but is available for convenience when used in fluent APIs.
+//
+// This decision was made to avoid cloning the bundle per-invocation, since in a
+// loop this would grow the cost involved with calling this function substantially.
+func Extend(bundle *bcrpb.Bundle, opts ...Option) *bcrpb.Bundle {
+	bundleopt.Apply(bundle, opts...)
+	return bundle
+}
+
+// NewTransaction is a helper function for building a transaction bundle.
+func NewTransaction(options ...Option) *bcrpb.Bundle {
+	return New(cpb.BundleTypeCode_TRANSACTION, options...)
+}
+
+// NewCollection is a helper function for building a collection bundle.
+func NewCollection(options ...Option) *bcrpb.Bundle {
+	return New(cpb.BundleTypeCode_COLLECTION, options...)
+}
+
+// NewBatch is a helper function for building a batch bundle.
+func NewBatch(options ...Option) *bcrpb.Bundle {
+	return New(cpb.BundleTypeCode_BATCH, options...)
+}
+
+// NewHistory is a helper function for building a history bundle.
+func NewHistory(options ...Option) *bcrpb.Bundle {
+	return New(cpb.BundleTypeCode_HISTORY, options...)
+}
+
+// NewSearchset is a helper function for building a searchset bundle.
+func NewSearchset(options ...Option) *bcrpb.Bundle {
+	return New(cpb.BundleTypeCode_SEARCHSET, options...)
+}
+
+// Unwrap unwraps a bundle into a slice of resources.
+func Unwrap(bundle *bcrpb.Bundle) []fhir.Resource {
+	return slices.Map(bundle.GetEntry(), UnwrapEntry)
+}
+
+// UnwrapMap unwraps a bundle into a map indexed by resource type.
+func UnwrapMap(bundle *bcrpb.Bundle) map[resource.Type][]fhir.Resource {
+	resourceMap := map[resource.Type][]fhir.Resource{}
+	resources := Unwrap(bundle)
+	for _, res := range resources {
+		resourceType := resource.TypeOf(res)
+		resourceMap[resourceType] = append(resourceMap[resourceType], res)
+	}
+	return resourceMap
+}
diff --git a/internal/bundle/bundle_entry.go b/internal/bundle/bundle_entry.go
new file mode 100644
index 0000000..28f0977
--- /dev/null
+++ b/internal/bundle/bundle_entry.go
@@ -0,0 +1,285 @@
+package bundle
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+
+	cpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/codes_go_proto"
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	bpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/binary_go_proto"
+	bcrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	"github.com/google/uuid"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/containedresource"
+	"github.com/verily-src/fhirpath-go/internal/element/reference"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+)
+
+var (
+	ErrInvalidIdentity = fmt.Errorf("invaild resource identity")
+	ErrInvalidPayload  = fmt.Errorf("invalid payload")
+	ErrMissingPayload  = fmt.Errorf("%w: nil or empty payload", ErrInvalidPayload)
+)
+
+// EntryOption is an option interface for constructing bundle entries
+// from raw data.
+type EntryOption interface {
+	updateEntry(entry *bcrpb.Bundle_Entry)
+}
+
+// fullURLOpt is a bundle entry option for including a full url.
+type fullURLOpt string
+
+func (o fullURLOpt) updateEntry(entry *bcrpb.Bundle_Entry) {
+	if o != "" {
+		entry.FullUrl = &dtpb.Uri{
+			Value: string(o),
+		}
+	}
+}
+
+// ifNoneExistOpt is a bundle entry option for including the If-None-Exist header
+type ifNoneExistOpt struct {
+	identifier *dtpb.Identifier
+}
+
+// ifNoneExistOpt.updateEntry sets the If-None-Exist header of a POST entry
+// in the format `identifier=system|value`
+// Either system or value can be empty (see https://hl7.org/fhir/R4/search.html#token)
+// If system is empty, only `identifier=value` supported; not `identifier=|value`
+func (o ifNoneExistOpt) updateEntry(entry *bcrpb.Bundle_Entry) {
+	req := entry.Request
+	if req == nil || req.GetMethod().GetValue() != cpb.HTTPVerbCode_POST {
+		return
+	}
+	var sysVal string
+	if sys := o.identifier.GetSystem().GetValue(); sys != "" {
+		sysVal += sys + "|"
+	}
+	sysVal += o.identifier.GetValue().GetValue()
+	if sysVal == "" {
+		return
+	}
+	req.IfNoneExist = fhir.String(
+		fmt.Sprintf("identifier=%s", sysVal),
+	)
+}
+
+// WithFullURL adds a FullUrl field to a BundleEntry.
+func WithFullURL(url string) EntryOption {
+	return fullURLOpt(url)
+}
+
+// WithGeneratedFullURL adds a randomly generated FullUrl
+// field to a BundleEntry, taking the form urn:uuid:$UUID.
+func WithGeneratedFullURL() EntryOption {
+	url := fmt.Sprintf("urn:uuid:%s", uuid.NewString())
+	return fullURLOpt(url)
+}
+
+// WithIfNoneExist adds an identifier to the If-None-Exist request header of
+// a POST BundleEntry, in the format `identifier=system|value`
+func WithIfNoneExist(identifier *dtpb.Identifier) EntryOption {
+	return ifNoneExistOpt{identifier: identifier}
+}
+
+// NewGetEntry constructs a bundle entry with a GET request for the head value
+// of a resource. This is the FHIR "read" interaction.
+//
+// For use within a batch or transaction bundle.
+//
+// Clients should not request a versioned resource by crafting a special id
+// argument; instead client should use VersionedGetEntry() below.
+func NewGetEntry(typeName resource.Type, id string, opts ...EntryOption) *bcrpb.Bundle_Entry {
+	return NewVersionedGetEntry(typeName, id, "" /*version*/, opts...)
+}
+
+// NewVersionedGetEntry constructs a bundle entry with a GET request
+// for the versioned value of a resource. This is the FHIR "vread" interaction.
+//
+// For use within a batch or transaction bundle.
+//
+// If version is empty, the returned entry requests the head version (a simple GET).
+//
+// The Bundle_Entry.FullUrl element is set to the requested resource's
+// relative URL (without version). This is "good enough", despite questionable
+// compliance with the standard. Clients may override this using WithFullUrl().
+//
+// This function does NOT support the "history-*" (eg "history-instance")
+// interactions. Those interaction return all the historical versions
+// for one or more resources, and are not supported within a batch
+// or transaction bundle.
+func NewVersionedGetEntry(typeName resource.Type, id string, version string, opts ...EntryOption) *bcrpb.Bundle_Entry {
+	url := string(typeName) + "/" + id
+	requestUrl := url
+	if version != "" {
+		requestUrl += "/_history/" + version
+	}
+	entry := bundleEntry(cpb.HTTPVerbCode_GET, nil /*resource*/, requestUrl)
+	entry.FullUrl = fhir.URI(url)
+	return applyOptions(entry, opts)
+}
+
+// NewPostEntry wraps a FHIR Resource for a transaction bundle to be written to
+// storage via a POST request.
+//
+// This is based on GCP documentation here:
+// https://cloud.google.com/healthcare-api/docs/how-tos/fhir-bundles#resolving_references_to_resources_created_in_a_bundle
+func NewPostEntry(res fhir.Resource, opts ...EntryOption) *bcrpb.Bundle_Entry {
+	typeString := resource.TypeOf(res)
+	entry := bundleEntry(cpb.HTTPVerbCode_POST, res, string(typeString))
+	return applyOptions(entry, opts)
+}
+
+// NewPutEntry wraps a FHIR Resource for a transaction bundle to be written to
+// storage via a PUT request. BundleEntry.fullUrl is not populated.
+//
+// This is based on GCP documentation here:
+// https://cloud.google.com/healthcare-api/docs/how-tos/fhir-bundles#resolving_references_to_resources_created_in_a_bundle
+func NewPutEntry(res fhir.Resource, opts ...EntryOption) *bcrpb.Bundle_Entry {
+	uri := resource.URIString(res)
+	entry := bundleEntry(cpb.HTTPVerbCode_PUT, res, uri)
+	return applyOptions(entry, opts)
+}
+
+// NewDeleteEntry constructs a delete resource operation via a DELETE request.
+//
+// For use within a batch or transaction bundle.
+func NewDeleteEntry(typeName resource.Type, id string, opts ...EntryOption) *bcrpb.Bundle_Entry {
+	url := string(typeName) + "/" + id
+	entry := bundleEntry(cpb.HTTPVerbCode_DELETE, nil /*resource*/, url)
+	return applyOptions(entry, opts)
+}
+
+// NewCollectionEntry takes in a FHIR Resource and creates a BundleEntry
+// for the resource.
+func NewCollectionEntry(res fhir.Resource) *bcrpb.Bundle_Entry {
+	return &bcrpb.Bundle_Entry{
+		Resource: containedresource.Wrap(res),
+		FullUrl:  resource.URI(res),
+	}
+}
+
+type patchOps []struct {
+	Op    string      `json:"op"`
+	Path  string      `json:"path"`
+	Value interface{} `json:"value,omitempty"`
+}
+
+// validatePatch performs basic validation of the patch payload, disallowing
+// unknown fields. It doesn't check existence of required fields and their
+// values.
+func validatePatch(payload []byte) error {
+	if !json.Valid(payload) {
+		return ErrInvalidPayload
+	}
+	var patch patchOps
+	dec := json.NewDecoder(bytes.NewReader(payload))
+	dec.DisallowUnknownFields()
+	return dec.Decode(&patch)
+}
+
+// PatchEntryFromBytes takes in a Resource Identity and a byte array payload,
+// and returns a BundleEntry corresponding to a JSON Patch for the resource.
+//
+// See https://cloud.google.com/healthcare-api/docs/how-tos/fhir-resources#executing_a_patch_request_in_a_fhir_bundle
+// for the details of how PATCH payload can be generated for known fields.
+//
+// For PATCH operations generation consider using one of the Go jsonpatch libraries from https://jsonpatch.com/#go.
+// See the ./bundle_example_test.go/TestPatchEntry*() for the list of usage examples.
+func PatchEntryFromBytes(identity *resource.Identity, payload []byte) (*bcrpb.Bundle_Entry, error) {
+	if identity == nil || identity.Type() == "" || identity.ID() == "" {
+		return nil, fmt.Errorf("unable to patch resource with identity (%v): %w", identity, ErrInvalidIdentity)
+	}
+	if payload == nil || len(payload) == 0 {
+		return nil, fmt.Errorf("unable to patch resource: %w", ErrMissingPayload)
+	}
+	if err := validatePatch(payload); err != nil {
+		return nil, fmt.Errorf("unable to patch resource, %w: %w", ErrInvalidPayload, err)
+	}
+
+	br := &bcrpb.ContainedResource{
+		OneofResource: &bcrpb.ContainedResource_Binary{
+			Binary: &bpb.Binary{
+				ContentType: &bpb.Binary_ContentTypeCode{Value: "application/json-patch+json"},
+				Data:        &dtpb.Base64Binary{Value: payload},
+			},
+		},
+	}
+	return &bcrpb.Bundle_Entry{
+		Resource: br,
+		Request: &bcrpb.Bundle_Entry_Request{
+			Method: &bcrpb.Bundle_Entry_Request_MethodCode{
+				Value: cpb.HTTPVerbCode_PATCH,
+			},
+			Url: identity.RelativeURI(),
+		},
+	}, nil
+}
+
+// UnwrapEntry unwraps a bundle entry into a FHIR Resource.
+//
+// If the bundle entry is nil, or if the entry does not contain a resource, this
+// function will return nil.
+func UnwrapEntry(entry *bcrpb.Bundle_Entry) fhir.Resource {
+	if entry == nil {
+		return nil
+	}
+	return containedresource.Unwrap(entry.GetResource())
+}
+
+func bundleEntry(method cpb.HTTPVerbCode_Value, resource fhir.Resource, requestURL string) *bcrpb.Bundle_Entry {
+	return &bcrpb.Bundle_Entry{
+		Resource: containedresource.Wrap(resource),
+		Request: &bcrpb.Bundle_Entry_Request{
+			Method: &bcrpb.Bundle_Entry_Request_MethodCode{
+				Value: method,
+			},
+			Url: &dtpb.Uri{
+				Value: requestURL,
+			},
+		},
+	}
+}
+
+func applyOptions(entry *bcrpb.Bundle_Entry, opts []EntryOption) *bcrpb.Bundle_Entry {
+	for _, opt := range opts {
+		opt.updateEntry(entry)
+	}
+	return entry
+}
+
+// EntryReference generates a FHIR Reference proto pointing to the Entry's
+// resource.
+func EntryReference(entry *bcrpb.Bundle_Entry) *dtpb.Reference {
+	if entry.GetResource() == nil {
+		return nil
+	}
+	res := UnwrapEntry(entry)
+	resourceType := resource.TypeOf(res)
+	return reference.Weak(resourceType, entry.GetFullUrl().GetValue())
+}
+
+// SetEntryIfMatch sets the entry.Request.IfMatch based on
+// entry.Resource.Meta.VersionId.
+func SetEntryIfMatch(entry *bcrpb.Bundle_Entry) {
+	req := entry.GetRequest()
+	// No request or a request with If-Match already set
+	if req == nil || req.GetIfMatch() != nil {
+		return
+	}
+
+	// If-Match is only respected for PUT and PATCH, and PATCH uses a specially-
+	// constructed resource so it doesn't make sense to infer anything from it;
+	// some day If-Match on DELETE might be respected, update this code then
+	if req.GetMethod().GetValue() != cpb.HTTPVerbCode_PUT {
+		return
+	}
+
+	// Set If-Match should be based on the entry.Resource
+	version := resource.VersionETag(UnwrapEntry(entry))
+	if version != "" {
+		req.IfMatch = fhir.String(version)
+	}
+}
diff --git a/internal/bundle/bundle_entry_example_test.go b/internal/bundle/bundle_entry_example_test.go
new file mode 100644
index 0000000..a9500ae
--- /dev/null
+++ b/internal/bundle/bundle_entry_example_test.go
@@ -0,0 +1,94 @@
+package bundle_test
+
+import (
+	"encoding/json"
+	"fmt"
+	"log"
+
+	"github.com/google/fhir/go/fhirversion"
+	"github.com/google/fhir/go/jsonformat"
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	r4pb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/mattbaird/jsonpatch"
+	"github.com/verily-src/fhirpath-go/internal/bundle"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"google.golang.org/protobuf/proto"
+)
+
+var resIdentity, _ = resource.NewIdentity("Patient", "123", "")
+
+func ExamplePatchEntryFromBytes_stringPatch() {
+	patch := `[{"op":"add","path":"/active","value":true}]`
+	pEntry, err := bundle.PatchEntryFromBytes(resIdentity, []byte(patch))
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Printf("PatchEntry: %+v", pEntry)
+}
+
+func ExamplePatchEntryFromBytes_mapPatch() {
+	patch := []map[string]interface{}{
+		{
+			"op":    "replace",
+			"path":  "/active",
+			"value": true,
+		},
+	}
+	payload, err := json.Marshal(patch)
+	if err != nil {
+		log.Fatal(err)
+	}
+	pEntry, err := bundle.PatchEntryFromBytes(resIdentity, payload)
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Printf("PatchEntry: %+v", pEntry)
+}
+
+// ExamplePatchEntryFromBytes_diffPatch creates a patch for the diff of two
+// given resources diff.
+func ExamplePatchEntryFromBytes_diffPatch() {
+	res := &r4pb.ContainedResource{
+		OneofResource: &r4pb.ContainedResource_Patient{
+			Patient: &ppb.Patient{
+				Id: &dtpb.Id{
+					Value: "123",
+				},
+				Active: &dtpb.Boolean{
+					Value: false,
+				},
+			},
+		},
+	}
+	newRes := proto.Clone(res).(*r4pb.ContainedResource)
+	newRes.GetPatient().Active = &dtpb.Boolean{Value: true}
+
+	m, err := jsonformat.NewMarshaller(false, "", "", fhirversion.R4)
+	if err != nil {
+		log.Fatal(err)
+	}
+	resB, err := m.Marshal(res)
+	if err != nil {
+		log.Fatal(err)
+	}
+	newResB, err := m.Marshal(newRes)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	patch, err := jsonpatch.CreatePatch(resB, newResB)
+	if err != nil {
+		log.Fatal(err)
+	}
+	pPayload, err := json.Marshal(patch)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	pEntry, err := bundle.PatchEntryFromBytes(resIdentity, pPayload)
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Printf("PatchEntry: %+v", pEntry)
+}
diff --git a/internal/bundle/bundle_entry_test.go b/internal/bundle/bundle_entry_test.go
new file mode 100644
index 0000000..266c51d
--- /dev/null
+++ b/internal/bundle/bundle_entry_test.go
@@ -0,0 +1,477 @@
+package bundle_test
+
+import (
+	"testing"
+
+	cpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/codes_go_proto"
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	bpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/binary_go_proto"
+	bcrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/bundle"
+	"github.com/verily-src/fhirpath-go/internal/containedresource"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+var (
+	iuURI               = fhir.URI("Patient/IU")
+	patient             = &ppb.Patient{Id: fhir.ID("IU")}
+	ifNoneExistFullOpt  = bundle.WithIfNoneExist(&dtpb.Identifier{System: fhir.URI("system"), Value: fhir.String("value")})
+	ifNoneExistSysOpt   = bundle.WithIfNoneExist(&dtpb.Identifier{System: fhir.URI("system")})
+	ifNoneExistValOpt   = bundle.WithIfNoneExist(&dtpb.Identifier{Value: fhir.String("value")})
+	ifNoneExistEmptyOpt = bundle.WithIfNoneExist(&dtpb.Identifier{})
+
+	patientPostEntry           = makePatientEntry(cpb.HTTPVerbCode_POST, patient, nil, "")
+	patientPostEntryFullHeader = makePatientEntry(cpb.HTTPVerbCode_POST, patient, nil, "identifier=system|value")
+	patientPostEntrySysHeader  = makePatientEntry(cpb.HTTPVerbCode_POST, patient, nil, "identifier=system|")
+	patientPostEntryValHeader  = makePatientEntry(cpb.HTTPVerbCode_POST, patient, nil, "identifier=value")
+	uriPostEntry               = makePatientEntry(cpb.HTTPVerbCode_POST, patient, iuURI, "")
+	patientPutEntry            = makePatientEntry(cpb.HTTPVerbCode_PUT, patient, nil, "")
+	uriPutEntry                = makePatientEntry(cpb.HTTPVerbCode_PUT, patient, iuURI, "")
+)
+
+func TestGetEntry(t *testing.T) {
+	// Only a single test case because the impl is a pass-thru to VersionedGetEntry.
+	wantEntry := makeEntryForGet("Patient/1234", "//Full")
+	gotEntry := bundle.NewGetEntry(resource.Patient, "1234",
+		bundle.WithFullURL("//Full"))
+
+	if diff := cmp.Diff(wantEntry, gotEntry, protocmp.Transform()); diff != "" {
+		t.Errorf("GetEntry mismatch (-want, +got):\n%s", diff)
+	}
+}
+
+func TestVersionedGetEntry(t *testing.T) {
+	testCases := []struct {
+		name      string
+		typeName  string
+		id        string
+		version   string
+		opts      []bundle.EntryOption
+		wantEntry *bcrpb.Bundle_Entry
+	}{
+		{"no version", "Patient", "1234", "", nil, makeEntryForGet("Patient/1234", "Patient/1234")},
+		{"with version", "Patient", "1234", "abcd", nil, makeEntryForGet("Patient/1234/_history/abcd", "Patient/1234")},
+		{"with full", "Patient", "1234", "abcd", []bundle.EntryOption{bundle.WithFullURL("//Full")},
+			makeEntryForGet("Patient/1234/_history/abcd", "//Full")},
+		{"ignore if-none-exist", "Patient", "1234", "", []bundle.EntryOption{ifNoneExistFullOpt},
+			makeEntryForGet("Patient/1234", "Patient/1234")},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			entry := bundle.NewVersionedGetEntry(resource.Type(tc.typeName), tc.id, tc.version, tc.opts...)
+
+			got, want := entry, tc.wantEntry
+			if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("VersionedGetEntry(%s) mismatch (-want, +got):\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+func makeEntryForGet(requestUrl string, fullUrl string) *bcrpb.Bundle_Entry {
+	return &bcrpb.Bundle_Entry{
+		FullUrl: fhir.URI(fullUrl),
+		Request: &bcrpb.Bundle_Entry_Request{
+			Method: &bcrpb.Bundle_Entry_Request_MethodCode{
+				Value: cpb.HTTPVerbCode_GET,
+			},
+			Url: fhir.URI(requestUrl),
+		},
+	}
+}
+
+func TestPostEntry(t *testing.T) {
+	testCases := []struct {
+		name      string
+		resource  fhir.Resource
+		opts      []bundle.EntryOption
+		wantEntry *bcrpb.Bundle_Entry
+	}{
+		{"no options", patient, nil, patientPostEntry},
+		{"empty uri", patient, []bundle.EntryOption{bundle.WithFullURL("")}, patientPostEntry},
+		{"uri provided", patient, []bundle.EntryOption{bundle.WithFullURL(iuURI.GetValue())}, uriPostEntry},
+		{"apply if-none-exist", patient, []bundle.EntryOption{ifNoneExistFullOpt}, patientPostEntryFullHeader},
+		{"apply if-none-exist with identifier.system", patient, []bundle.EntryOption{ifNoneExistSysOpt}, patientPostEntrySysHeader},
+		{"apply if-none-exist with identifier.value", patient, []bundle.EntryOption{ifNoneExistValOpt}, patientPostEntryValHeader},
+		{"ignore if-none-exist with empty identifier", patient, []bundle.EntryOption{ifNoneExistEmptyOpt}, patientPostEntry},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			entry := bundle.NewPostEntry(tc.resource, tc.opts...)
+
+			got, want := entry, tc.wantEntry
+			if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("PostEntry(%s) mismatch (-want, +got):\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestPutEntry(t *testing.T) {
+	testCases := []struct {
+		name      string
+		resource  fhir.Resource
+		opts      []bundle.EntryOption
+		wantEntry *bcrpb.Bundle_Entry
+	}{
+		{"no options", patient, nil, patientPutEntry},
+		{"empty uri", patient, []bundle.EntryOption{bundle.WithFullURL("")}, patientPutEntry},
+		{"uri provided", patient, []bundle.EntryOption{bundle.WithFullURL(iuURI.GetValue())}, uriPutEntry},
+		{"ignore if-none-exist", patient, []bundle.EntryOption{ifNoneExistFullOpt}, patientPutEntry},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			entry := bundle.NewPutEntry(tc.resource, tc.opts...)
+
+			got, want := entry, tc.wantEntry
+			if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("PutEntry(%s) mismatch (-want, +got):\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestDeleteEntry(t *testing.T) {
+	testCases := []struct {
+		name      string
+		inType    resource.Type
+		inID      string
+		opts      []bundle.EntryOption
+		wantEntry *bcrpb.Bundle_Entry
+	}{
+		{
+			name:   "no options",
+			inType: resource.Patient,
+			inID:   "1234",
+			opts:   nil,
+			wantEntry: &bcrpb.Bundle_Entry{
+				Request: &bcrpb.Bundle_Entry_Request{
+					Method: &bcrpb.Bundle_Entry_Request_MethodCode{
+						Value: cpb.HTTPVerbCode_DELETE,
+					},
+					Url: &dtpb.Uri{
+						Value: "Patient/1234",
+					},
+				},
+			},
+		},
+		{
+			name:   "with option id",
+			inType: resource.Patient,
+			inID:   "1234",
+			opts:   []bundle.EntryOption{bundle.WithFullURL("test-full-url")},
+			wantEntry: &bcrpb.Bundle_Entry{
+				Request: &bcrpb.Bundle_Entry_Request{
+					Method: &bcrpb.Bundle_Entry_Request_MethodCode{
+						Value: cpb.HTTPVerbCode_DELETE,
+					},
+					Url: &dtpb.Uri{
+						Value: "Patient/1234",
+					},
+				},
+				FullUrl: &dtpb.Uri{
+					Value: "test-full-url",
+				},
+			},
+		},
+		{
+			name:   "ignore if-none-exist",
+			inType: resource.Patient,
+			inID:   "1234",
+			opts:   []bundle.EntryOption{ifNoneExistFullOpt},
+			wantEntry: &bcrpb.Bundle_Entry{
+				Request: &bcrpb.Bundle_Entry_Request{
+					Method: &bcrpb.Bundle_Entry_Request_MethodCode{
+						Value: cpb.HTTPVerbCode_DELETE,
+					},
+					Url: &dtpb.Uri{
+						Value: "Patient/1234",
+					},
+				},
+			},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			entry := bundle.NewDeleteEntry(tc.inType, tc.inID, tc.opts...)
+
+			got, want := entry, tc.wantEntry
+			if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("NewDeleteEntry(%s) mismatch (-want, +got):\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestPatchEntryFromBytes(t *testing.T) {
+
+	patch := []byte(`[{"op":"add","path":"/active","value":true}]`)
+	validIdentity, _ := resource.NewIdentity("Patient", "123", "0")
+
+	testCases := []struct {
+		name      string
+		resID     *resource.Identity
+		payload   []byte
+		wantEntry *bcrpb.Bundle_Entry
+		wantError error
+	}{
+		{
+			name:    "Valid patch",
+			resID:   validIdentity,
+			payload: patch,
+			wantEntry: &bcrpb.Bundle_Entry{
+				Request: &bcrpb.Bundle_Entry_Request{
+					Method: &bcrpb.Bundle_Entry_Request_MethodCode{
+						Value: cpb.HTTPVerbCode_PATCH,
+					},
+					Url: &dtpb.Uri{
+						Value: "Patient/123",
+					},
+				},
+				Resource: &bcrpb.ContainedResource{
+					OneofResource: &bcrpb.ContainedResource_Binary{
+						Binary: &bpb.Binary{
+							ContentType: &bpb.Binary_ContentTypeCode{Value: "application/json-patch+json"},
+							Data:        &dtpb.Base64Binary{Value: patch},
+						},
+					},
+				},
+			},
+		},
+		{
+			name:      "Invalid resource identity",
+			resID:     &resource.Identity{},
+			payload:   patch,
+			wantError: bundle.ErrInvalidIdentity,
+		},
+		{
+			name:      "Nil resource identity",
+			resID:     nil,
+			payload:   patch,
+			wantError: bundle.ErrInvalidIdentity,
+		},
+		{
+			name:      "Nil payload",
+			resID:     validIdentity,
+			payload:   nil,
+			wantError: bundle.ErrMissingPayload,
+		},
+		{
+			name:      "Empty payload",
+			resID:     validIdentity,
+			payload:   []byte{},
+			wantError: bundle.ErrMissingPayload,
+		},
+		{
+			name:      "Invalid payload - single op",
+			resID:     validIdentity,
+			payload:   []byte(`{"op":"add","path":"/active","value":true}`),
+			wantError: bundle.ErrInvalidPayload,
+		},
+		{
+			name:      "Invalid payload - wrong field",
+			resID:     validIdentity,
+			payload:   []byte(`[{"op":"add","url":"/active","value":true}]`),
+			wantError: bundle.ErrInvalidPayload,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			entry, err := bundle.PatchEntryFromBytes(tc.resID, tc.payload)
+
+			if tc.wantError == nil {
+				got, want := entry, tc.wantEntry
+				if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
+					t.Errorf("PatchEntryFromBytes(%s) mismatch (-want, +got):\n%s", tc.name, diff)
+				}
+			} else {
+				got, want := err, tc.wantError
+				if !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+					t.Errorf("PatchEntryFromBytes(%s): unexpected error got %s, want %s", tc.name, got, want)
+				}
+			}
+		})
+	}
+}
+
+func TestCollectionEntry(t *testing.T) {
+	wantEntry := &bcrpb.Bundle_Entry{
+		Resource: containedresource.Wrap(patient),
+		FullUrl:  &dtpb.Uri{Value: "Patient/IU"},
+	}
+	entry := bundle.NewCollectionEntry(patient)
+
+	got, want := entry, wantEntry
+	if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
+		t.Errorf("CollectionEntry mismatch (-want, +got):\n%s", diff)
+	}
+}
+
+// Test helper to create a BundleEntry of a Patient
+func makePatientEntry(method cpb.HTTPVerbCode_Value, res fhir.Resource, uri *dtpb.Uri, header string) *bcrpb.Bundle_Entry {
+	requestURL := "Patient"
+	if method == cpb.HTTPVerbCode_PUT {
+		requestURL = resource.URIString(res)
+	}
+	entry := &bcrpb.Bundle_Entry{
+		Resource: containedresource.Wrap(res),
+		Request: &bcrpb.Bundle_Entry_Request{
+			Method: &bcrpb.Bundle_Entry_Request_MethodCode{
+				Value: method,
+			},
+			Url: &dtpb.Uri{
+				Value: requestURL,
+			},
+		},
+	}
+	if uriString := uri.GetValue(); uriString != "" {
+		entry.FullUrl = uri
+	}
+	if header != "" {
+		entry.Request.IfNoneExist = fhir.String(header)
+	}
+	return entry
+}
+
+func TestEntryReference(t *testing.T) {
+	entry := &bcrpb.Bundle_Entry{
+		FullUrl: &dtpb.Uri{
+			Value: "patient-full-url",
+		},
+		Resource: containedresource.Wrap(resource.New("Patient")),
+	}
+	wantRef := &dtpb.Reference{
+		Type: &dtpb.Uri{
+			Value: "Patient",
+		},
+		Reference: &dtpb.Reference_Uri{
+			Uri: &dtpb.String{
+				Value: "patient-full-url",
+			},
+		},
+	}
+
+	ref := bundle.EntryReference(entry)
+
+	got, want := ref, wantRef
+	if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+		t.Errorf("EntryReference: (-got +want):\n%v", diff)
+	}
+}
+
+func TestSetEntryIfMatch(t *testing.T) {
+	testCases := []struct {
+		name        string
+		entry       *bcrpb.Bundle_Entry
+		wantVersion string
+	}{
+		{
+			"does not override ifMatch with resource-derived value if it's already set",
+			&bcrpb.Bundle_Entry{
+				Request: &bcrpb.Bundle_Entry_Request{
+					Method:  &bcrpb.Bundle_Entry_Request_MethodCode{Value: cpb.HTTPVerbCode_POST},
+					IfMatch: fhir.String("already set"),
+				},
+				Resource: &bcrpb.ContainedResource{
+					OneofResource: &bcrpb.ContainedResource_Patient{Patient: &ppb.Patient{Meta: &dtpb.Meta{VersionId: fhir.ID("derived")}}},
+				},
+			},
+			"already set",
+		},
+		{
+			"does not set ifMatch if there's no resource version",
+			&bcrpb.Bundle_Entry{
+				Request: &bcrpb.Bundle_Entry_Request{Method: &bcrpb.Bundle_Entry_Request_MethodCode{Value: cpb.HTTPVerbCode_POST}},
+				Resource: &bcrpb.ContainedResource{
+					OneofResource: &bcrpb.ContainedResource_Patient{Patient: &ppb.Patient{Meta: &dtpb.Meta{VersionId: fhir.ID("")}}},
+				},
+			},
+			"",
+		},
+		{
+			"does not set ifMatch with resource-derived value if the method is unspecified",
+			&bcrpb.Bundle_Entry{
+				Request: &bcrpb.Bundle_Entry_Request{},
+				Resource: &bcrpb.ContainedResource{
+					OneofResource: &bcrpb.ContainedResource_Patient{Patient: &ppb.Patient{Meta: &dtpb.Meta{VersionId: fhir.ID("derived")}}},
+				},
+			},
+			"",
+		},
+		{
+			"does not set ifMatch with resource-derived value if it's a GET",
+			&bcrpb.Bundle_Entry{
+				Request: &bcrpb.Bundle_Entry_Request{Method: &bcrpb.Bundle_Entry_Request_MethodCode{Value: cpb.HTTPVerbCode_GET}},
+				Resource: &bcrpb.ContainedResource{
+					OneofResource: &bcrpb.ContainedResource_Patient{Patient: &ppb.Patient{Meta: &dtpb.Meta{VersionId: fhir.ID("derived")}}},
+				},
+			},
+			"",
+		},
+		{
+			"does not set ifMatch with resource-derived value if it's a HEAD",
+			&bcrpb.Bundle_Entry{
+				Request: &bcrpb.Bundle_Entry_Request{Method: &bcrpb.Bundle_Entry_Request_MethodCode{Value: cpb.HTTPVerbCode_HEAD}},
+				Resource: &bcrpb.ContainedResource{
+					OneofResource: &bcrpb.ContainedResource_Patient{Patient: &ppb.Patient{Meta: &dtpb.Meta{VersionId: fhir.ID("derived")}}},
+				},
+			},
+			"",
+		},
+		{
+			"does not set ifMatch with resource-derived value if it's a POST",
+			&bcrpb.Bundle_Entry{
+				Request: &bcrpb.Bundle_Entry_Request{Method: &bcrpb.Bundle_Entry_Request_MethodCode{Value: cpb.HTTPVerbCode_POST}},
+				Resource: &bcrpb.ContainedResource{
+					OneofResource: &bcrpb.ContainedResource_Patient{Patient: &ppb.Patient{Meta: &dtpb.Meta{VersionId: fhir.ID("derived")}}},
+				},
+			},
+			"",
+		},
+		{
+			"sets ifMatch with resource-derived value if it's a PUT",
+			&bcrpb.Bundle_Entry{
+				Request: &bcrpb.Bundle_Entry_Request{Method: &bcrpb.Bundle_Entry_Request_MethodCode{Value: cpb.HTTPVerbCode_PUT}},
+				Resource: &bcrpb.ContainedResource{
+					OneofResource: &bcrpb.ContainedResource_Patient{Patient: &ppb.Patient{Meta: &dtpb.Meta{VersionId: fhir.ID("derived")}}},
+				},
+			},
+			`W/"derived"`,
+		},
+		{
+			"does not set ifMatch with resource-derived value if it's a PATCH",
+			&bcrpb.Bundle_Entry{
+				Request: &bcrpb.Bundle_Entry_Request{Method: &bcrpb.Bundle_Entry_Request_MethodCode{Value: cpb.HTTPVerbCode_PATCH}},
+				Resource: &bcrpb.ContainedResource{
+					OneofResource: &bcrpb.ContainedResource_Patient{Patient: &ppb.Patient{Meta: &dtpb.Meta{VersionId: fhir.ID("derived")}}},
+				},
+			},
+			"",
+		},
+		{
+			"does not set ifMatch with resource-derived value if it's a DELETE",
+			&bcrpb.Bundle_Entry{
+				Request: &bcrpb.Bundle_Entry_Request{Method: &bcrpb.Bundle_Entry_Request_MethodCode{Value: cpb.HTTPVerbCode_DELETE}},
+				Resource: &bcrpb.ContainedResource{
+					OneofResource: &bcrpb.ContainedResource_Patient{Patient: &ppb.Patient{Meta: &dtpb.Meta{VersionId: fhir.ID("derived")}}},
+				},
+			},
+			"",
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			bundle.SetEntryIfMatch(tc.entry)
+
+			gotVersion := tc.entry.GetRequest().GetIfMatch().GetValue()
+			if diff := cmp.Diff(gotVersion, tc.wantVersion); diff != "" {
+				t.Errorf("SetEntryIfMatch(%s) version got = %v, want = %v", tc.name, gotVersion, tc.wantVersion)
+			}
+		})
+	}
+}
diff --git a/internal/bundle/bundle_option.go b/internal/bundle/bundle_option.go
new file mode 100644
index 0000000..dd2aa4c
--- /dev/null
+++ b/internal/bundle/bundle_option.go
@@ -0,0 +1,51 @@
+package bundle
+
+import (
+	"time"
+
+	cpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/codes_go_proto"
+	bcrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/bundleopt"
+)
+
+// Option is an option interface for constructing bundles
+// from raw data.
+type Option = bundleopt.BundleOption
+
+// entriesOpt is a bundle option for including bundle entries.
+type entriesOpt []*bcrpb.Bundle_Entry
+
+func (o entriesOpt) updateBundle(bundle *bcrpb.Bundle) {
+	if len(o) > 0 {
+		bundle.Entry = o
+		if bundle.GetType().GetValue() == cpb.BundleTypeCode_SEARCHSET || bundle.GetType().GetValue() == cpb.BundleTypeCode_HISTORY {
+			bundle.Total = fhir.UnsignedInt(uint32(len(o)))
+		}
+	}
+}
+
+// WithEntries adds bundle entries to a bundle.
+func WithEntries(entries ...*bcrpb.Bundle_Entry) Option {
+	entriesopt := entriesOpt(entries)
+	return bundleopt.Transform(entriesopt.updateBundle)
+}
+
+// timeOpt is a bundle option for including a timestamp.
+type timeOpt time.Time
+
+func (o timeOpt) updateBundle(bundle *bcrpb.Bundle) {
+	bundle.Timestamp = fhir.Instant(time.Time(o))
+}
+
+// WithTimestamp adds a given time to the bundle's timestamp.
+func WithTimestamp(t time.Time) Option {
+	timeOpt := timeOpt(t)
+	return bundleopt.Transform(timeOpt.updateBundle)
+}
+
+// WithTimestampNow adds the current time to the bundle's timestamp.
+func WithTimestampNow() Option {
+	timeOpt := timeOpt(time.Now())
+	return bundleopt.Transform(timeOpt.updateBundle)
+}
diff --git a/internal/bundle/bundle_test.go b/internal/bundle/bundle_test.go
new file mode 100644
index 0000000..1f4c11f
--- /dev/null
+++ b/internal/bundle/bundle_test.go
@@ -0,0 +1,216 @@
+package bundle_test
+
+import (
+	"testing"
+	"time"
+
+	cpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/codes_go_proto"
+	bcrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/bundle"
+	"github.com/verily-src/fhirpath-go/internal/fhirtest"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func TestBundle(t *testing.T) {
+	iuTime := time.Date(1993, time.May, 16, 0, 0, 0, 0, time.UTC)
+	iuPatient := &ppb.Patient{Id: fhir.ID("IU")}
+	uaenaPatient := &ppb.Patient{Id: fhir.ID("Uaena")}
+	firstEntry := bundle.NewPostEntry(iuPatient)
+	secondEntry := bundle.NewPostEntry(uaenaPatient)
+	baseBundle := &bcrpb.Bundle{Type: &bcrpb.Bundle_TypeCode{Value: cpb.BundleTypeCode_TRANSACTION}}
+	timeBundle := proto.Clone(baseBundle).(*bcrpb.Bundle)
+	timeBundle.Timestamp = fhir.Instant(iuTime)
+	fullTxnBundle := &bcrpb.Bundle{
+		Type:  &bcrpb.Bundle_TypeCode{Value: cpb.BundleTypeCode_TRANSACTION},
+		Entry: []*bcrpb.Bundle_Entry{firstEntry, secondEntry},
+	}
+
+	fullSearchBundle := &bcrpb.Bundle{
+		Type:  &bcrpb.Bundle_TypeCode{Value: cpb.BundleTypeCode_SEARCHSET},
+		Entry: []*bcrpb.Bundle_Entry{firstEntry, secondEntry},
+		Total: fhir.UnsignedInt(2),
+	}
+
+	testCases := []struct {
+		name           string
+		opts           []bundle.Option
+		bundleTypeCode cpb.BundleTypeCode_Value
+		wantBundle     *bcrpb.Bundle
+	}{
+		{
+			name:           "no options",
+			opts:           nil,
+			bundleTypeCode: cpb.BundleTypeCode_TRANSACTION,
+			wantBundle:     baseBundle,
+		},
+		{
+			name:           "empty entries",
+			opts:           []bundle.Option{bundle.WithEntries()},
+			bundleTypeCode: cpb.BundleTypeCode_TRANSACTION,
+			wantBundle:     baseBundle,
+		},
+		{
+			name:           "multiple entries in a transaction bundle",
+			opts:           []bundle.Option{bundle.WithEntries(firstEntry, secondEntry)},
+			bundleTypeCode: cpb.BundleTypeCode_TRANSACTION,
+			wantBundle:     fullTxnBundle,
+		},
+		{
+			name:           "multiple entries in a search bundle",
+			opts:           []bundle.Option{bundle.WithEntries(firstEntry, secondEntry)},
+			bundleTypeCode: cpb.BundleTypeCode_SEARCHSET,
+			wantBundle:     fullSearchBundle,
+		},
+		{
+			name:           "timestamp",
+			opts:           []bundle.Option{bundle.WithTimestamp(iuTime)},
+			bundleTypeCode: cpb.BundleTypeCode_TRANSACTION,
+			wantBundle:     timeBundle,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			bundle := bundle.New(tc.bundleTypeCode, tc.opts...)
+
+			got, want := bundle, tc.wantBundle
+			if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Bundle(%s) mismatch (-want, +got):\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestTransactionBundle_WithNoArguments_CreatesEmptyTransactionBundle(t *testing.T) {
+	want := cpb.BundleTypeCode_TRANSACTION
+
+	bundle := bundle.NewTransaction()
+
+	got := bundle.GetType().GetValue()
+	if got != want {
+		t.Errorf("TransactionBundle: got %v, want %v", got, want)
+	}
+}
+
+func TestCollectionBundle_WithNoArguments_CreatesEmptyCollectionBundle(t *testing.T) {
+	want := cpb.BundleTypeCode_COLLECTION
+
+	bundle := bundle.NewCollection()
+
+	got := bundle.GetType().GetValue()
+	if got != want {
+		t.Errorf("CollectionBundle: got %v, want %v", got, want)
+	}
+}
+
+func TestBatchBundle_WithNoArguments_CreatesEmptyBatchBundle(t *testing.T) {
+	want := cpb.BundleTypeCode_BATCH
+
+	bundle := bundle.NewBatch()
+
+	got := bundle.GetType().GetValue()
+	if got != want {
+		t.Errorf("BatchBundle: got %v, want %v", got, want)
+	}
+}
+
+func TestHistoryBundle_WithNoArguments_CreatesEmptyHistoryBundle(t *testing.T) {
+	want := cpb.BundleTypeCode_HISTORY
+
+	bundle := bundle.NewHistory()
+
+	got := bundle.GetType().GetValue()
+	if got != want {
+		t.Errorf("HistoryBundle: got %v, want %v", got, want)
+	}
+}
+
+func TestUnwrapBundle(t *testing.T) {
+	testCases := []struct {
+		name string
+		want []fhir.Resource
+	}{
+		{"Empty", []fhir.Resource{}},
+		{"Contains resources", []fhir.Resource{
+			fhirtest.NewResource(t, resource.Patient),
+			fhirtest.NewResource(t, resource.Patient),
+		}},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			put := func(resource fhir.Resource) *bcrpb.Bundle_Entry {
+				return bundle.NewPutEntry(resource)
+			}
+			sut := bundle.NewCollection(
+				bundle.WithEntries(slices.Map(tc.want, put)...),
+			)
+
+			got := bundle.Unwrap(sut)
+
+			want := tc.want
+			if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+				t.Errorf("UnwrapBundle: diff (-got,+want):\n%v", diff)
+			}
+		})
+	}
+}
+
+func TestUnwrapMap(t *testing.T) {
+	patientOne := fhirtest.NewResource(t, resource.Patient)
+	patientTwo := fhirtest.NewResource(t, resource.Patient)
+	taskOne := fhirtest.NewResource(t, resource.Task)
+	taskTwo := fhirtest.NewResource(t, resource.Task)
+	testCases := []struct {
+		name string
+		in   []fhir.Resource
+		want map[resource.Type][]fhir.Resource
+	}{
+		{"Empty", []fhir.Resource{}, map[resource.Type][]fhir.Resource{}},
+		{"Contains unique resource types", []fhir.Resource{
+			patientOne,
+			taskOne,
+		}, map[resource.Type][]fhir.Resource{
+			resource.Patient: {patientOne},
+			resource.Task:    {taskOne},
+		}},
+		{"Contains multiple of a single type", []fhir.Resource{
+			patientOne,
+			patientTwo,
+		}, map[resource.Type][]fhir.Resource{
+			resource.Patient: {patientOne, patientTwo},
+		}},
+		{"Contains multiple of multiple types", []fhir.Resource{
+			patientOne,
+			patientTwo,
+			taskOne,
+			taskTwo,
+		}, map[resource.Type][]fhir.Resource{
+			resource.Task:    {taskOne, taskTwo},
+			resource.Patient: {patientOne, patientTwo},
+		}},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			put := func(resource fhir.Resource) *bcrpb.Bundle_Entry {
+				return bundle.NewPutEntry(resource)
+			}
+			sut := bundle.NewCollection(
+				bundle.WithEntries(slices.Map(tc.in, put)...),
+			)
+
+			got := bundle.UnwrapMap(sut)
+
+			want := tc.want
+			if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+				t.Errorf("UnwrapBundle: diff (-got,+want):\n%v", diff)
+			}
+		})
+	}
+}
diff --git a/internal/bundle/identity.go b/internal/bundle/identity.go
new file mode 100644
index 0000000..703fd04
--- /dev/null
+++ b/internal/bundle/identity.go
@@ -0,0 +1,27 @@
+package bundle
+
+import (
+	"errors"
+
+	bcrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/element/reference"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+)
+
+var (
+	ErrNoLocation = errors.New("bundle entry response missing location")
+)
+
+// IdentityOfResponse returns a complete Identity
+// (Type and ID always set, VersionID set if applicable) representing the
+// location contained in the given bundle entry response.
+func IdentityOfResponse(response *bcrpb.Bundle_Entry_Response) (*resource.Identity, error) {
+	// Per the FHIR spec, location may be a relative or absolute URI, which may
+	// include a _history component.
+	location := response.GetLocation().GetValue()
+	if location == "" {
+		return nil, ErrNoLocation
+	}
+
+	return reference.IdentityFromURL(location)
+}
diff --git a/internal/bundle/identity_test.go b/internal/bundle/identity_test.go
new file mode 100644
index 0000000..20fbc13
--- /dev/null
+++ b/internal/bundle/identity_test.go
@@ -0,0 +1,48 @@
+package bundle_test
+
+import (
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	bcrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/verily-src/fhirpath-go/internal/bundle"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+)
+
+func newIdentity(t *testing.T, typeName, id, version string) *resource.Identity {
+	t.Helper()
+	ident, err := resource.NewIdentity(typeName, id, version)
+	if err != nil {
+		t.Fatalf("NewIdentity: %v", err)
+	}
+	return ident
+}
+
+func TestIdentityOfResponse_NoLocation_ReturnsError(t *testing.T) {
+	response := &bcrpb.Bundle_Entry_Response{}
+	_, err := bundle.IdentityOfResponse(response)
+
+	if !cmp.Equal(err, bundle.ErrNoLocation, cmpopts.EquateErrors()) {
+		t.Errorf("IdentityOfResponse error got %v, want nil", err)
+	}
+}
+
+func TestIdentityOfResponse(t *testing.T) {
+	wantIdentity := newIdentity(t, "Patient", "123", "abc")
+	response := &bcrpb.Bundle_Entry_Response{
+		Location: &dtpb.Uri{
+			Value: "https://healthcare.googleapis.com/v1/projects/123/locations/abc/datasets/def/fhirStores/ghi/fhir/Patient/123/_history/abc",
+		},
+	}
+
+	ident, err := bundle.IdentityOfResponse(response)
+
+	if err != nil {
+		t.Fatalf("IdentityOfResponse error got %v, want nil", err)
+	}
+	if got, want := ident, wantIdentity; !got.Equal(want) {
+		t.Errorf("IdentityOfResponse got %s, want %s", got, want)
+	}
+}
diff --git a/internal/bundleopt/bundleopt.go b/internal/bundleopt/bundleopt.go
new file mode 100644
index 0000000..5299881
--- /dev/null
+++ b/internal/bundleopt/bundleopt.go
@@ -0,0 +1,25 @@
+package bundleopt
+
+import (
+	bcrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+)
+
+// BundleOption is an option interface for constructing bundles
+// from raw data.
+type BundleOption interface {
+	updateBundle(bundle *bcrpb.Bundle)
+}
+
+type Transform func(b *bcrpb.Bundle)
+
+func (t Transform) updateBundle(entry *bcrpb.Bundle) {
+	t(entry)
+}
+
+func Apply(bundle *bcrpb.Bundle, opts ...BundleOption) {
+	for _, opt := range opts {
+		opt.updateBundle(bundle)
+	}
+}
+
+var _ BundleOption = (*Transform)(nil)
diff --git a/internal/containedresource/contained_resource.go b/internal/containedresource/contained_resource.go
new file mode 100644
index 0000000..033fefa
--- /dev/null
+++ b/internal/containedresource/contained_resource.go
@@ -0,0 +1,186 @@
+package containedresource
+
+import (
+	"errors"
+	"fmt"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	bcrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/element/identifier"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"github.com/verily-src/fhirpath-go/internal/protofields"
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+var (
+	ErrGenerateIfNoneExist error = errors.New("GenerateIfNoneExist()")
+)
+
+// Wrap creates a ContainedResource proto based on an existing FHIR proto.
+// Usage:
+//
+//	patient := &Patient{...}
+//	cr := Wrap(patient)
+func Wrap(res fhir.Resource) *bcrpb.ContainedResource {
+	if res == nil {
+		return nil
+	}
+	name := resource.TypeOf(res)
+
+	cr := &bcrpb.ContainedResource{}
+
+	// This field exists for ALL valid FHIR R4 "Resource" types
+	field := protofields.Resources[string(name)].ContainedResource.Resource
+
+	// The only way this can fail is if `resource` was not a valid FHIR resource
+	// to begin with (e.g. a custom mock, the r4 "Resource" proto (which is meant
+	// to only be a baste type), or some other invalid resource). This has been
+	// extensively tested against all valid r4 protos, and thus if this happens, it
+	// is best to fail early and panic, rather than make this API virally return an
+	// error that is impossible to experience in practice, and very difficult to
+	// actually trigger.
+	if field == nil {
+		panic(fmt.Sprintf("Invalid resource with name %v specified in ContainResource", name))
+	}
+
+	cr.ProtoReflect().Set(field, protoreflect.ValueOfMessage(res.ProtoReflect()))
+	return cr
+}
+
+// Unwrap will extract the underlying value contained in this
+// resource, if there is one, and return it. This enables downstream callers to
+// switch off of the resource type, or to perform type-conversions.
+//
+// This function is effectively the inverse of `ContainedResource`, such that the
+// following assertion will always hold:
+// `proto.Equal(fhirutil.Unwrap(fhirutil.ContainedResource(resource)), resource)`
+func Unwrap(cr *bcrpb.ContainedResource) fhir.Resource {
+	field := getContainedResourceOneOfField(cr)
+
+	// If field is nil, it means we have no contained resource value set -- so
+	// the only valid value to return while unwrapping is `nil`.
+	if field == nil {
+		return nil
+	}
+	ref := cr.ProtoReflect()
+	message := ref.Get(field).Message()
+
+	// All ContainedType values valid for the OneOf definition MUST satisfy the
+	// resource interface. This assertion can not fail.
+	return message.Interface().ProtoReflect().Interface().(fhir.Resource)
+}
+
+// TypeOf is a helper for getting the type-name of a contained resource.
+//
+// If the contained resource is nil, or the contained resource does not contain
+// any resource, this function will panic.
+func TypeOf(cr *bcrpb.ContainedResource) resource.Type {
+	return resource.TypeOf(Unwrap(cr))
+}
+
+// ID is a helper for getting the ID of a contained resource.
+//
+// If the contained resource is nil, or the contained resource does not contain
+// any resource, this will return an empty string.
+func ID(cr *bcrpb.ContainedResource) string {
+	return resource.ID(Unwrap(cr))
+}
+
+// VersionID gets the version-ID of the specified contained-resource as a string.
+// If `nil` is provided, this returns an empty string.
+func VersionID(cr *bcrpb.ContainedResource) string {
+	return resource.VersionID(Unwrap(cr))
+}
+
+// URI is a helper for getting the URI of a contained-resource as a FHIR URI object.
+// The URI is returned in the format Type/ID, e.g. Patient/123.
+func URI(cr *bcrpb.ContainedResource) *dtpb.Uri {
+	return resource.URI(Unwrap(cr))
+}
+
+// URIString is a helper for getting the URI of a contained-resource in
+// string form. The URI is returned in the format Type/ID, e.g. Patient/123.
+func URIString(cr *bcrpb.ContainedResource) string {
+	return resource.URIString(Unwrap(cr))
+}
+
+// VersionedURI is a helper for getting the URI of a contained-resource as a
+// FHIR URI object. The URI is returned in the format Type/ID/_history/VERSION.
+func VersionedURI(cr *bcrpb.ContainedResource) *dtpb.Uri {
+	return resource.VersionedURI(Unwrap(cr))
+}
+
+// VersionedURIString is a helper for getting the URI of a contained-resource in
+// string form. The URI is returned in the format Type/ID/_history/VERSION.
+func VersionedURIString(cr *bcrpb.ContainedResource) (string, bool) {
+	return resource.VersionedURIString(Unwrap(cr))
+}
+
+// getContainedResourceOneOfField gets the field for the OneOf entry in the
+// ContainedResource. This function returns nil if either the contained-resource
+// is nil, or the contained-resource does not contain any resource.
+func getContainedResourceOneOfField(cr *bcrpb.ContainedResource) protoreflect.FieldDescriptor {
+	if cr == nil {
+		return nil
+	}
+	if cr.GetOneofResource() == nil {
+		return nil
+	}
+
+	// Get the active OneOf field, and return that value as an interface
+	const oneofField = "oneof_resource"
+
+	reflect := cr.ProtoReflect()
+	descriptor := reflect.Descriptor()
+	oneof := descriptor.Oneofs().ByName(oneofField)
+	return reflect.WhichOneof(oneof)
+}
+
+// GenerateIfNoneExist generates an If-None-Exist header value using a single
+// Identifier from the contained resource. The provided system is used to
+// filter identifiers to only an identifier with a matching system.
+//
+// If no matching Identifier is found, return error if emptyIsErr is true, or
+// return empty string and no error if emptyIsErr is false.
+//
+// The GCP FHIR store only supports atomic conditional operations on a single
+// identifier, so this function returns an error if there are multiple
+// identifiers matching the query.
+//
+// This is used for FHIR conditional create or other conditional methods.
+// Untrusted data in Identifiers is escaped both for FHIR and for URL safety.
+func GenerateIfNoneExist(cr *bcrpb.ContainedResource, system string, emptyIsErr bool) (string, error) {
+	if cr == nil {
+		return "", fmt.Errorf("%w: ContainedResource is nil", ErrGenerateIfNoneExist)
+	}
+
+	res := Unwrap(cr)
+	if res == nil {
+		return "", fmt.Errorf("%w: Unwrap() returned nil / no contained resource", ErrGenerateIfNoneExist)
+	}
+
+	identifiers, err := resource.GetIdentifierList(res)
+	if err != nil {
+		return "", err
+	}
+
+	found := slices.Filter(identifiers, func(id *dtpb.Identifier) bool {
+		return id.GetSystem().GetValue() == system
+	})
+
+	if len(found) == 0 {
+		if emptyIsErr {
+			return "", fmt.Errorf("%w: found no Identifiers with system=%#v", ErrGenerateIfNoneExist, system)
+		} else {
+			return "", nil
+		}
+	}
+
+	if len(found) > 1 {
+		return "", fmt.Errorf("%w: found multiple Identifiers with system=%#v, want just one", ErrGenerateIfNoneExist, system)
+	}
+
+	return identifier.GenerateIfNoneExist(found[0]), nil
+}
diff --git a/internal/containedresource/contained_resource_example_test.go b/internal/containedresource/contained_resource_example_test.go
new file mode 100644
index 0000000..c1f28d4
--- /dev/null
+++ b/internal/containedresource/contained_resource_example_test.go
@@ -0,0 +1,88 @@
+package containedresource_test
+
+import (
+	"fmt"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/containedresource"
+	"google.golang.org/protobuf/proto"
+)
+
+func ExampleWrap() {
+	patient := &patient_go_proto.Patient{
+		Id: fhir.ID("12345"),
+	}
+
+	cr := containedresource.Wrap(patient)
+
+	fmt.Printf("Patient ID = %v", cr.GetPatient().GetId().GetValue())
+	// Output: Patient ID = 12345
+}
+
+func ExampleUnwrap() {
+	patient := &patient_go_proto.Patient{
+		Id: fhir.ID("12345"),
+	}
+	cr := containedresource.Wrap(patient)
+
+	unwrapped := containedresource.Unwrap(cr).(*patient_go_proto.Patient)
+
+	if proto.Equal(patient, unwrapped) {
+		fmt.Printf("Resources match!")
+	}
+	// Output: Resources match!
+}
+
+func ExampleTypeOf() {
+	patient := &patient_go_proto.Patient{
+		Id: fhir.ID("12345"),
+	}
+	cr := containedresource.Wrap(patient)
+
+	crType := containedresource.TypeOf(cr)
+
+	fmt.Printf("Contained Resource type = %v", crType)
+	// Output: Contained Resource type = Patient
+}
+
+func ExampleID() {
+	patient := &patient_go_proto.Patient{
+		Id: fhir.ID("12345"),
+	}
+	cr := containedresource.Wrap(patient)
+
+	id := containedresource.ID(cr)
+
+	fmt.Printf("Contained Resource ID = %v", id)
+	// Output: Contained Resource ID = 12345
+}
+
+func ExampleGenerateIfNoneExist() {
+	patient := &patient_go_proto.Patient{
+		Id: fhir.ID("12345"),
+		Identifier: []*dtpb.Identifier{
+			&dtpb.Identifier{
+				System: &dtpb.Uri{Value: "http://fake.com"},
+				Value:  &dtpb.String{Value: "9efbf82d-7a58-4d14-bec1-63f8fda148a8"},
+			},
+		},
+	}
+
+	cr := containedresource.Wrap(patient)
+
+	value, err := containedresource.GenerateIfNoneExist(cr, "http://fake.com", true)
+	if err != nil {
+		panic(err)
+	}
+
+	headers := map[string]string{}
+
+	if value != "" {
+		headers["If-None-Exist"] = value
+	}
+
+	fmt.Printf("If-None-Exist: %v", headers["If-None-Exist"])
+	// Output: If-None-Exist: identifier=http%3A%2F%2Ffake.com%7C9efbf82d-7a58-4d14-bec1-63f8fda148a8
+}
diff --git a/internal/containedresource/contained_resource_test.go b/internal/containedresource/contained_resource_test.go
new file mode 100644
index 0000000..239470b
--- /dev/null
+++ b/internal/containedresource/contained_resource_test.go
@@ -0,0 +1,291 @@
+package containedresource_test
+
+import (
+	"errors"
+	"net/url"
+	"strings"
+	"testing"
+
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/codes_go_proto"
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	bcrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/containedresource"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/fhirtest"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"google.golang.org/protobuf/proto"
+)
+
+func TestWrap_WithNilContainedResource_ReturnsNil(t *testing.T) {
+	got := containedresource.Wrap(nil)
+
+	if got != nil {
+		t.Errorf("ContainedResource: got %v, want nil", got)
+	}
+}
+
+func TestWrap_WithResource_WrapsResource(t *testing.T) {
+	for name, resource := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+
+			got := containedresource.Wrap(resource)
+			if got == nil {
+				t.Fatalf("ContainedResource(%v): got nil, want value", name)
+			}
+
+			if got, want := containedresource.ID(got), resource.GetId().GetValue(); got != want {
+				t.Errorf("ContainedResource(%v): got %v, want %v", name, got, want)
+			}
+		})
+	}
+}
+
+func TestUnwrap_WithInvalidresource_ReturnsNil(t *testing.T) {
+	testCases := []struct {
+		name     string
+		resource *bcrpb.ContainedResource
+	}{
+		{"NilContainedResource", nil},
+		// Note: ContainedResource is not a real FHIR type, and thus should never
+		// be populated as empty in practice when deserializing or receiving valid
+		// FHIR payloads; so this test should never happen to begin with.
+		{"EmptyContainedResource", &bcrpb.ContainedResource{}},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := containedresource.Unwrap(nil)
+
+			if got != nil {
+				t.Errorf("Unwrap(%v): got %v, want nil", tc.name, got)
+			}
+		})
+	}
+}
+
+func TestUnwrap_WithContainedResource_ReturnsResource(t *testing.T) {
+	for name, resource := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			got := containedresource.Unwrap(containedresource.Wrap(resource))
+
+			if !proto.Equal(got, resource) {
+				t.Errorf("Unwrap(%v): got %v, want %v", name, got, resource)
+			}
+		})
+	}
+}
+
+func TestTypeOf_WithNil_Panics(t *testing.T) {
+	defer func() { _ = recover() }()
+
+	containedresource.TypeOf(nil)
+
+	t.Errorf("TypeOf: expected panic")
+}
+
+func TestTypeOf_WithEmptyContainedResource_Panics(t *testing.T) {
+	defer func() { _ = recover() }()
+	cr := &bcrpb.ContainedResource{}
+
+	containedresource.TypeOf(cr)
+
+	t.Errorf("TypeOf: expected panic")
+}
+
+func TestTypeOf_WithResource_ReturnsName(t *testing.T) {
+	for name, res := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			got := containedresource.TypeOf(containedresource.Wrap(res))
+
+			if got, want := got, resource.Type(name); got != want {
+				t.Errorf("ContainedResourceName(%v): got %v, want %v", name, got, want)
+			}
+		})
+	}
+}
+
+func TestID_WithNil_ReturnsEmptyString(t *testing.T) {
+	got := containedresource.ID(nil)
+
+	if got != "" {
+		t.Errorf("ID: got %v, want empty string", got)
+	}
+}
+
+func TestID_WithEmptyContainedResource_ReturnsEmptyString(t *testing.T) {
+	cr := &bcrpb.ContainedResource{}
+
+	got := containedresource.ID(cr)
+
+	if got != "" {
+		t.Errorf("ContainedResourceID: got %v, want empty string", got)
+	}
+}
+
+func TestID_WithResource_ReturnsID(t *testing.T) {
+	for name, resource := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			got := containedresource.ID(containedresource.Wrap(resource))
+
+			if got, want := got, resource.GetId().GetValue(); got != want {
+				t.Errorf("ContainedResourceID(%v): got %v, want %v", name, got, want)
+			}
+		})
+	}
+}
+
+func TestGenerateIfNoneExist_Errors(t *testing.T) {
+	patient0 := &patient_go_proto.Patient{
+		Id: fhir.ID("12345"),
+	}
+	patient1 := &patient_go_proto.Patient{
+		Id: fhir.ID("12345"),
+		Identifier: []*dtpb.Identifier{
+			&dtpb.Identifier{
+				System: &dtpb.Uri{Value: "http://fake.com"},
+				Value:  &dtpb.String{Value: "9efbf82d-7a58-4d14-bec1-63f8fda148a8"},
+			},
+		},
+	}
+
+	testCases := []struct {
+		name    string
+		input   *bcrpb.ContainedResource
+		wantErr string
+	}{
+		{
+			"empty ContainedResource",
+			&bcrpb.ContainedResource{},
+			"Unwrap() returned nil / no contained resource",
+		},
+		{
+			"nil ContainedResource",
+			nil,
+			"ContainedResource is nil",
+		},
+		{
+			"No identifier, emptyIsErr=true",
+			containedresource.Wrap(patient0),
+			"found no Identifiers",
+		},
+		{
+			"No matching identifier, emptyIsErr=true",
+			containedresource.Wrap(patient1),
+			"found no Identifiers",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			header, err := containedresource.GenerateIfNoneExist(tc.input, "no-such-system", true)
+
+			if !strings.Contains(err.Error(), tc.wantErr) {
+				t.Errorf("got error %#v, want %#v", err.Error(), tc.wantErr)
+			}
+			if !errors.Is(err, containedresource.ErrGenerateIfNoneExist) {
+				t.Errorf("got error %#v, want errors.Is(..., ErrGenerateIfNoneExist)", err)
+			}
+			if header != "" {
+				t.Errorf("got %v, want empty string", header)
+			}
+		})
+	}
+}
+
+func TestGenerateIfNoneExist(t *testing.T) {
+	patient0 := &patient_go_proto.Patient{
+		Id: fhir.ID("12345"),
+	}
+	patient1 := &patient_go_proto.Patient{
+		Id: fhir.ID("12345"),
+		Identifier: []*dtpb.Identifier{
+			&dtpb.Identifier{
+				System: &dtpb.Uri{Value: "http://fake.com"},
+				Value:  &dtpb.String{Value: "9efbf82d-7a58-4d14-bec1-63f8fda148a8"},
+			},
+		},
+	}
+	patient2 := &patient_go_proto.Patient{
+		Id: fhir.ID("12345"),
+		Identifier: []*dtpb.Identifier{
+			&dtpb.Identifier{
+				System: &dtpb.Uri{Value: "http://fake.com"},
+				Value:  &dtpb.String{Value: "9efbf82d-7a58-4d14-bec1-63f8fda148a8"},
+			},
+			&dtpb.Identifier{
+				Use: &dtpb.Identifier_UseCode{
+					Value: codes_go_proto.IdentifierUseCode_USUAL,
+				},
+				System: &dtpb.Uri{Value: "urn:oid:2.16.840.1.113883.2.4.6.3"},
+				Value:  &dtpb.String{Value: "12345"},
+			},
+		},
+	}
+
+	patient3 := fhirtest.NewResource(t, "Patient", fhirtest.WithGeneratedIdentifier("http://example.com/fake-id")).(*patient_go_proto.Patient)
+
+	patient4 := &patient_go_proto.Patient{
+		Id: fhir.ID("12345"),
+		Identifier: []*dtpb.Identifier{
+			&dtpb.Identifier{
+				System: &dtpb.Uri{Value: "http://fake.com"},
+				Value:  &dtpb.String{Value: "foo,bar,baz|omg"},
+			},
+		},
+	}
+
+	patient5 := &patient_go_proto.Patient{
+		Id: fhir.ID("12345"),
+		Identifier: []*dtpb.Identifier{
+			&dtpb.Identifier{
+				System: &dtpb.Uri{Value: "http://fake.com"},
+				Value:  &dtpb.String{Value: "9efbf82d-7a58-4d14-bec1-63f8fda148a8"},
+			},
+			&dtpb.Identifier{
+				System: &dtpb.Uri{Value: "http://fake.com"},
+				Value:  &dtpb.String{Value: "7d541708-b068-4347-a8dc-cce1dcdb5314"},
+			},
+		},
+	}
+
+	testCases := []struct {
+		name    string
+		patient *patient_go_proto.Patient
+		system  string
+		want    string
+		wantErr string
+	}{
+		{"Patient with no Identifier, emptyIsErr false", patient0, "system", "", ""},
+		{"Patient with single Identifier", patient1, "http://fake.com", "identifier=" + url.QueryEscape("http://fake.com|9efbf82d-7a58-4d14-bec1-63f8fda148a8"), ""},
+		{"Patient with two Identifiers but one matching", patient2, "http://fake.com", "identifier=" + url.QueryEscape("http://fake.com|9efbf82d-7a58-4d14-bec1-63f8fda148a8"), ""},
+		{"Patient with two Identifiers and both matching", patient5, "http://fake.com", "", "found multiple Identifiers"},
+		{"Patient with generated ID", patient3, "http://example.com/fake-id", "identifier=" + url.QueryEscape("http://example.com/fake-id|"+patient3.Identifier[0].Value.Value), ""},
+		{"Special chars in Identifier", patient4, "http://fake.com", "identifier=" + url.QueryEscape(`http://fake.com|foo\,bar\,baz\|omg`), ""},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			cr := containedresource.Wrap(tc.patient)
+
+			got, err := containedresource.GenerateIfNoneExist(cr, tc.system, false)
+
+			if tc.wantErr == "" {
+				if err != nil {
+					t.Errorf("%#v: Bad If-None-Exist:\n  got err %#v\n  want nil", tc.name, err)
+				}
+			} else {
+				if !strings.Contains(err.Error(), tc.wantErr) {
+					t.Errorf("%#v: got error %#v, want %#v", tc.name, err.Error(), tc.wantErr)
+				}
+				if !errors.Is(err, containedresource.ErrGenerateIfNoneExist) {
+					t.Errorf("%#v: got error %#v, want errors.Is(..., ErrGenerateIfNoneExist)", tc.name, err)
+				}
+			}
+
+			if got != tc.want {
+				t.Errorf("%#v: Bad If-None-Exist:\n  got  %#v\n  want %#v", tc.name, got, tc.want)
+			}
+		})
+	}
+}
diff --git a/internal/element/canonical/canonical.go b/internal/element/canonical/canonical.go
new file mode 100644
index 0000000..f278c76
--- /dev/null
+++ b/internal/element/canonical/canonical.go
@@ -0,0 +1,151 @@
+package canonical
+
+import (
+	"errors"
+	"fmt"
+	"regexp"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+)
+
+var (
+	// ErrNoCanonicalURL is an error returned when an API receives a canonical that
+	// does not have a specified URL field.
+	ErrNoCanonicalURL = errors.New("canonical does not contain url")
+
+	// very basic regex for URL matching.
+	// Fragment portion is from https://build.fhir.org/references.html#literal
+	canonicalRegExp = regexp.MustCompile(`^(?P<url>[^|#]+)(\|(?P<version>[A-z0-9-_\.]+))?(#(?P<fragment>[A-z0-9-_\.]{1,64}))?`)
+)
+
+// canonicalConfig is an internal struct for holding canonical information that
+// can be updated from a CanonicalOpt.
+type canonicalConfig struct {
+	fragment, version string
+}
+
+// Option is an option interface for constructing canonicals from raw
+// data.
+type Option interface {
+	updateCanonical(data *canonicalConfig)
+}
+
+// WithFragment adds a "fragment" portion to Canonical references.
+func WithFragment(frag string) Option {
+	return canonicalFragOpt(frag)
+}
+
+// canonicalFragOpt is a simple canonical option for fragment strings.
+type canonicalFragOpt string
+
+func (o canonicalFragOpt) updateCanonical(data *canonicalConfig) {
+	data.fragment = string(o)
+}
+
+// WithVersion adds a "version" portion to Canonical references.
+func WithVersion(version string) Option {
+	return canonicalVersionOpt(version)
+}
+
+// canonicalVersionOpt is a simple canonical option for version strings.
+type canonicalVersionOpt string
+
+func (o canonicalVersionOpt) updateCanonical(data *canonicalConfig) {
+	data.version = string(o)
+}
+
+// New constructs an R4 FHIR New element from the specified
+// url string and canonical options.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#canonical
+func New(url string, opts ...Option) *dtpb.Canonical {
+	data := &canonicalConfig{}
+	for _, opt := range opts {
+		opt.updateCanonical(data)
+	}
+	if data.version != "" {
+		url = fmt.Sprintf("%v|%v", url, data.version)
+	}
+	if data.fragment != "" {
+		url = fmt.Sprintf("%v#%v", url, data.fragment)
+	}
+	return &dtpb.Canonical{
+		Value: url,
+	}
+}
+
+// FromResource creates an R4 FHIR FromResource element from a
+// resource that has a URL, such as a Questionnaire, Device, etc.
+//
+// If the input resource is nil, or if the resource does not have a URL
+// field assigned, this function will return the error `ErrNoCanonicalURL`.
+//
+// See: https://hl7.org/fhir/R4/datatypes.html#canonical and
+// https://hl7.org/fhir/R4/references.html#canonical
+func FromResource(resource fhir.CanonicalResource) (*dtpb.Canonical, error) {
+	if resource == nil || resource.GetUrl() == nil {
+		return nil, ErrNoCanonicalURL
+	}
+	return New(resource.GetUrl().GetValue()), nil
+}
+
+// FragmentFromResource creates an R4 FHIR Canonical element from a resource that
+// has a URL, such as a Questionnaire, Device, etc., and will mark it as a
+// fragment-reference.
+//
+// If the input resource is nil, or if the resource does not have a URL
+// field assigned, this function will return the error `ErrNoCanonicalURL`.
+//
+// See: https://hl7.org/fhir/R4/datatypes.html#canonical and
+// https://hl7.org/fhir/R4/references.html#canonical
+func FragmentFromResource(resource fhir.CanonicalResource) (*dtpb.Canonical, error) {
+	if resource == nil || resource.GetUrl() == nil {
+		return nil, ErrNoCanonicalURL
+	}
+	return New(resource.GetUrl().GetValue(), WithFragment(resource.GetId().GetValue())), nil
+}
+
+// VersionedFromResource creates an R4 FHIR Canonical element from a resource that
+// has a URL, such as a Questionnaire, Device, etc, along with a version string.
+//
+// If the input resource is nil, or if the resource does not have a URL
+// field assigned, this function will return the error `ErrNoCanonicalURL`.
+//
+// See: https://hl7.org/fhir/R4/datatypes.html#canonical and
+// https://hl7.org/fhir/R4/references.html#canonical
+func VersionedFromResource(resource fhir.CanonicalResource) (*dtpb.Canonical, error) {
+	if resource == nil || resource.GetUrl() == nil {
+		return nil, ErrNoCanonicalURL
+	}
+	url := resource.GetUrl()
+	version := resource.GetVersion()
+	if version == nil {
+		return New(url.GetValue()), nil
+	}
+	return New(resource.GetUrl().GetValue(), WithVersion(version.GetValue())), nil
+}
+
+// IdentityFromReference returns an Identity object from a given canonical reference
+// Replaces: ph.ParseCanonical
+func IdentityFromReference(c *dtpb.Canonical) (*resource.CanonicalIdentity, error) {
+	value := c.GetValue()
+	match := canonicalRegExp.FindStringSubmatch(value)
+	result := make(map[string]string)
+	for i, name := range canonicalRegExp.SubexpNames() {
+		if i != 0 && name != "" {
+			result[name] = match[i]
+		}
+	}
+	return resource.NewCanonicalIdentity(result["url"], result["version"], result["fragment"])
+}
+
+// IdentityOf returns a canonicalIdentity representing the given canonical resource
+func IdentityOf(res fhir.CanonicalResource) (*resource.CanonicalIdentity, error) {
+	if res == nil || res.GetUrl() == nil {
+		return nil, ErrNoCanonicalURL
+	}
+
+	return resource.NewCanonicalIdentity(res.GetUrl().GetValue(), res.GetVersion().GetValue(), "")
+}
diff --git a/internal/element/canonical/canonical_test.go b/internal/element/canonical/canonical_test.go
new file mode 100644
index 0000000..2299a40
--- /dev/null
+++ b/internal/element/canonical/canonical_test.go
@@ -0,0 +1,221 @@
+package canonical_test
+
+import (
+	"errors"
+	"fmt"
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	qpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/questionnaire_go_proto"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/element/canonical"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func TestNew(t *testing.T) {
+	const (
+		url      = "https://example.com"
+		version  = "1.2.3"
+		fragment = "frag"
+	)
+	testCases := []struct {
+		name string
+		opts []canonical.Option
+		want string
+	}{
+		{
+			name: "NoOpts",
+			opts: nil,
+			want: url,
+		},
+		{
+			name: "WithVersion",
+			opts: []canonical.Option{canonical.WithVersion(version)},
+			want: fmt.Sprintf("%v|%v", url, version),
+		},
+		{
+			name: "WithFragment",
+			opts: []canonical.Option{canonical.WithFragment(fragment)},
+			want: fmt.Sprintf("%v#%v", url, fragment),
+		},
+		{
+			name: "WithVersionAndFragment",
+			opts: []canonical.Option{canonical.WithVersion(version), canonical.WithFragment(fragment)},
+			want: fmt.Sprintf("%v|%v#%v", url, version, fragment),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := canonical.New(url, tc.opts...)
+
+			if got, want := got.GetValue(), tc.want; got != want {
+				t.Errorf("Canonical(%v): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestCanonicalFromResource_Nil_ReturnsError(t *testing.T) {
+	_, err := canonical.FromResource(nil)
+
+	if got, want := err, canonical.ErrNoCanonicalURL; !errors.Is(got, want) {
+		t.Errorf("CanonicalFromResource: got %v, want %v", got, want)
+	}
+}
+
+func makeCanonicalResource() fhir.CanonicalResource {
+	const (
+		url = "https://example.com"
+	)
+	return &qpb.Questionnaire{
+		Id:      fhir.ID("0xdeadbeef"),
+		Url:     fhir.URI(url),
+		Version: fhir.String("1.0.0"),
+	}
+}
+
+func TestCanonicalFromResource_ReturnsCanonicalWithUrl(t *testing.T) {
+	resource := makeCanonicalResource()
+
+	got, err := canonical.FromResource(resource)
+
+	if err != nil {
+		t.Fatalf("CanonicalFromResource: unexpected error: %v", err)
+	}
+	if got, want := got.GetValue(), resource.GetUrl().GetValue(); got != want {
+		t.Errorf("CanonicalFromResource: got '%v', want '%v'", got, want)
+	}
+}
+
+func TestVersionedFromResource_Nil_ReturnsNil(t *testing.T) {
+	_, err := canonical.VersionedFromResource(nil)
+
+	if got, want := err, canonical.ErrNoCanonicalURL; !errors.Is(got, want) {
+		t.Errorf("VersionedFromResource: got %v, want %v", got, want)
+	}
+}
+
+func TestVersionedCanonical_ReturnsCanonicalWithUrl(t *testing.T) {
+	const (
+		url     = "https://example.com"
+		version = "1.2.3"
+	)
+	testCases := []struct {
+		name     string
+		resource fhir.CanonicalResource
+		want     string
+	}{
+		{
+			name: "NoVersion",
+			resource: &qpb.Questionnaire{
+				Url: fhir.URI(url),
+			},
+			want: url,
+		},
+		{
+			name: "WithVersion",
+			resource: &qpb.Questionnaire{
+				Url:     fhir.URI(url),
+				Version: fhir.String(version),
+			},
+			want: fmt.Sprintf("%v|%v", url, version),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := canonical.VersionedFromResource(tc.resource)
+
+			if err != nil {
+				t.Fatalf("VersionedCanonical(%v): unexpected error: %v", tc.name, err)
+			}
+			if got, want := got.GetValue(), tc.want; got != want {
+				t.Errorf("VersionedCanonical(%v): got '%v', want '%v'", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestFragmentFromResourceFromResourceNil_ReturnsNil(t *testing.T) {
+	_, err := canonical.FragmentFromResource(nil)
+
+	if got, want := err, canonical.ErrNoCanonicalURL; !errors.Is(got, want) {
+		t.Errorf("CanonicalFragmentFromResource: got %v, want %v", got, want)
+	}
+}
+
+func TestFragmentFromResource(t *testing.T) {
+	resource := makeCanonicalResource()
+	want := fmt.Sprintf("%v#%v", resource.GetUrl().GetValue(), resource.GetId().GetValue())
+
+	got, err := canonical.FragmentFromResource(resource)
+
+	if err != nil {
+		t.Fatalf("FragmentFromResource: unexpected error: %v", err)
+	}
+	if got := got.GetValue(); got != want {
+		t.Errorf("FragmentFromResource: got '%v', want '%v'", got, want)
+	}
+}
+
+func TestIdentityFromCanonical(t *testing.T) {
+	testCases := []struct {
+		name, reference, url, version, fragment string
+	}{
+		{
+			name:      "basic",
+			url:       "http://someurl/test-value",
+			reference: "http://someurl/test-value",
+		},
+		{
+			name:      "long url",
+			url:       "https://fhir.acme.com/Questionnaire/example",
+			reference: "https://fhir.acme.com/Questionnaire/example",
+		},
+		{
+			name:      "with version",
+			url:       "https://fhir.acme.com/Questionnaire/example",
+			version:   "1.0.0",
+			reference: "https://fhir.acme.com/Questionnaire/example|1.0.0",
+		},
+		{
+			name:      "with fragment",
+			url:       "http://hl7.org/fhir/ValueSet/my-valueset",
+			fragment:  "vs1",
+			reference: "http://hl7.org/fhir/ValueSet/my-valueset#vs1",
+		},
+		{
+			name:      "with version and fragment",
+			url:       "http://fhir.acme.com/Questionnaire/example",
+			version:   "1.0",
+			fragment:  "vs1",
+			reference: "http://fhir.acme.com/Questionnaire/example|1.0#vs1",
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			c := &dtpb.Canonical{
+				Value: tc.reference,
+			}
+			got, err := canonical.IdentityFromReference(c)
+			if err != nil {
+				t.Fatalf("IdentityFromReference(%s): unexpected error: %v", tc.name, err)
+			}
+			want := &resource.CanonicalIdentity{
+				Url:      tc.url,
+				Version:  tc.version,
+				Fragment: tc.fragment,
+			}
+			if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+				t.Errorf("IdentityFromReference(%s): %v", tc.name, diff)
+			}
+			if diff := cmp.Diff(tc.reference, got.String()); diff != "" {
+				t.Errorf("IdentityFromReference(%s).String: %v", tc.name, diff)
+			}
+		})
+	}
+}
diff --git a/internal/element/codeableconcept/codeableconcept.go b/internal/element/codeableconcept/codeableconcept.go
new file mode 100644
index 0000000..3628fcc
--- /dev/null
+++ b/internal/element/codeableconcept/codeableconcept.go
@@ -0,0 +1,17 @@
+package codeableconcept
+
+import (
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+)
+
+// FindBySystem searches the slice of Codings within a CodeableConcept for the
+// first Coding that contains the given system.
+func FindCodingBySystem(codeableconcept *dtpb.CodeableConcept, system string) *dtpb.Coding {
+	for _, coding := range codeableconcept.GetCoding() {
+		codingSystem := coding.GetSystem()
+		if codingSystem.GetValue() == system {
+			return coding
+		}
+	}
+	return nil
+}
diff --git a/internal/element/coding/coding.go b/internal/element/coding/coding.go
new file mode 100644
index 0000000..88345d6
--- /dev/null
+++ b/internal/element/coding/coding.go
@@ -0,0 +1,20 @@
+package coding
+
+import (
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+)
+
+// FindBySystem searches a slice of codings for the first coding that
+// contains the specified system.
+func FindBySystem(codings []*dtpb.Coding, system string) *dtpb.Coding {
+	for _, coding := range codings {
+		codingSystem := coding.GetSystem()
+		if codingSystem == nil {
+			continue
+		}
+		if codingSystem.GetValue() == system {
+			return coding
+		}
+	}
+	return nil
+}
diff --git a/internal/element/element.go b/internal/element/element.go
new file mode 100644
index 0000000..5bd5545
--- /dev/null
+++ b/internal/element/element.go
@@ -0,0 +1,6 @@
+/*
+Package element defines subpackages focused on FHIR R4 elements. Each subpackage
+is named after the FHIR data type (e.g. identifier) and provides capabilities
+related to that type.
+*/
+package element
diff --git a/internal/element/etag/etag.go b/internal/element/etag/etag.go
new file mode 100644
index 0000000..856da2f
--- /dev/null
+++ b/internal/element/etag/etag.go
@@ -0,0 +1,25 @@
+package etag
+
+import (
+	"errors"
+	"fmt"
+	"regexp"
+)
+
+var (
+	// FHIR version ID follows the [A-Za-z0-9\-\.]{1,64} pattern
+	// https://build.fhir.org/resource-definitions.html#Meta.versionId
+	versionIdInEtagRegexp   = regexp.MustCompile(`W\/"([A-Za-z0-9\-\.]{1,64})"`)
+	ErrInvalidEtagVersionID = errors.New("invalid version ID in etag")
+)
+
+// Returns the version ID from an ETag string.
+func VersionIDFromEtag(etag string) (string, error) {
+	matches := versionIdInEtagRegexp.FindStringSubmatch(etag)
+	// leftmost match of the regular expression in string itself and then the matches, if any.
+	// We expect the second match to be the version ID.
+	if len(matches) != 2 {
+		return "", fmt.Errorf("%w: got version ID: %s", ErrInvalidEtagVersionID, etag)
+	}
+	return matches[1], nil
+}
diff --git a/internal/element/etag/etag_test.go b/internal/element/etag/etag_test.go
new file mode 100644
index 0000000..a4e0b0a
--- /dev/null
+++ b/internal/element/etag/etag_test.go
@@ -0,0 +1,58 @@
+package etag_test
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/verily-src/fhirpath-go/internal/element/etag"
+)
+
+func TestVersionIDFromEtag(t *testing.T) {
+	testCases := []struct {
+		name          string
+		etag          string
+		wantVersionID string
+		wantErr       error
+	}{
+		{
+			name:          "valid etag",
+			etag:          `W/"foo"`,
+			wantVersionID: "foo",
+			wantErr:       nil,
+		},
+		{
+			name:    "empty etag",
+			etag:    "",
+			wantErr: etag.ErrInvalidEtagVersionID,
+		},
+		{
+			name:    "etag without version ID",
+			etag:    `W/""`,
+			wantErr: etag.ErrInvalidEtagVersionID,
+		},
+		{
+			name:    "etag with version ID with non-ASCII letters",
+			etag:    `W/"?()"`,
+			wantErr: etag.ErrInvalidEtagVersionID,
+		},
+		{
+			name:    "etag with long version ID",
+			etag:    `W/"12345678901234567890123456789012345678901234567890123456789012345"`,
+			wantErr: etag.ErrInvalidEtagVersionID,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			gotVersionID, gotErr := etag.VersionIDFromEtag(tc.etag)
+			if !cmp.Equal(gotErr, tc.wantErr, cmpopts.EquateErrors()) {
+				t.Fatalf("VersionIDFromEtag(%s) error mismatch: got [%v], want [%v]", tc.name, gotErr, tc.wantErr)
+			}
+			if gotErr == nil && gotVersionID != tc.wantVersionID {
+				t.Errorf("VersionIDFromEtag(%s) versionID mismatch: got [%s], want [%s]",
+					tc.name, gotVersionID, tc.wantVersionID)
+			}
+		})
+	}
+}
diff --git a/internal/element/extension/extension.go b/internal/element/extension/extension.go
new file mode 100644
index 0000000..62863a5
--- /dev/null
+++ b/internal/element/extension/extension.go
@@ -0,0 +1,232 @@
+package extension
+
+import (
+	"errors"
+	"fmt"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/protofields"
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+// ErrInvalidValueX is an error reported if trying to create an
+// Extension from an invalid data-type. Most FHIR Elements are supported, with
+// only a few notable exceptions. See the definition of the ValueX constraint to
+// see which elements are valid inputs.
+var ErrInvalidValueX = errors.New("invalid extension ValueX type")
+
+// ValueX is a constraint that enumerates all the valid 'DataType'
+// objects that can be used for extensions. This is used to constrain the
+// `Extension` function so that it cannot fail.
+//
+// This type is exported so that consumers may also expose the same generic
+// requirements to their clients.
+//
+// For more information on these types, see the definition of Extension's
+// `value` fields here: https://www.hl7.org/fhir/r4/extensibility.html#Extension
+type ValueX interface {
+	fhir.Element
+	*dtpb.Base64Binary |
+		*dtpb.Boolean |
+		*dtpb.Canonical |
+		*dtpb.Code |
+		*dtpb.Date |
+		*dtpb.DateTime |
+		*dtpb.Decimal |
+		*dtpb.Id |
+		*dtpb.Instant |
+		*dtpb.Integer |
+		*dtpb.Markdown |
+		*dtpb.Oid |
+		*dtpb.PositiveInt |
+		*dtpb.String |
+		*dtpb.Time |
+		*dtpb.UnsignedInt |
+		*dtpb.Uri |
+		*dtpb.Url |
+		*dtpb.Uuid |
+		*dtpb.Address |
+		*dtpb.Age |
+		*dtpb.Annotation |
+		*dtpb.Attachment |
+		*dtpb.CodeableConcept |
+		*dtpb.Coding |
+		*dtpb.ContactPoint |
+		*dtpb.Count |
+		*dtpb.Distance |
+		*dtpb.Duration |
+		*dtpb.HumanName |
+		*dtpb.Identifier |
+		*dtpb.Money |
+		*dtpb.Period |
+		*dtpb.Quantity |
+		*dtpb.Range |
+		*dtpb.Ratio |
+		*dtpb.Reference |
+		*dtpb.SampledData |
+		*dtpb.Signature |
+		*dtpb.Timing |
+		*dtpb.ContactDetail |
+		*dtpb.Contributor |
+		*dtpb.DataRequirement |
+		*dtpb.Expression |
+		*dtpb.ParameterDefinition |
+		*dtpb.RelatedArtifact |
+		*dtpb.TriggerDefinition |
+		*dtpb.UsageContext |
+		*dtpb.Dosage
+}
+
+// New creates an New extension object from a concrete, and legal,
+// extension type. Unlike `FromElement`, this function cannot fail since the
+// type has been checked ot be valid extension type with Go-1.18 constraints.
+func New[T ValueX](uri string, element T) *dtpb.Extension {
+	ext, err := FromElement(uri, element)
+	if err != nil {
+		// This branch is unreachable due to the type constraint.
+		// Tested in extension_test.go
+		panic(err)
+	}
+	return ext
+}
+
+// FromElement creates an extension from the specified datatype resource.
+// If the datatype is not a valid input, this function returns an error.
+//
+// For convenience functions that cannot return an error, see the various
+// `Extension*` functions.
+func FromElement(uri string, element fhir.Element) (*dtpb.Extension, error) {
+	if element == nil {
+		return nil, fmt.Errorf("no value provided to datatype extension")
+	}
+	name := protofields.DescriptorName(element)
+
+	fields, ok := protofields.Elements[name]
+	if !ok || fields.Extension.ValueX == nil {
+		return nil, fmt.Errorf("extension %v: %w", name, ErrInvalidValueX)
+	}
+
+	valueX := &dtpb.Extension_ValueX{}
+	reflect := valueX.ProtoReflect()
+	reflect.Set(fields.Extension.ValueX, protoreflect.ValueOfMessage(element.ProtoReflect()))
+
+	extension := &dtpb.Extension{
+		Url:   fhir.URI(uri),
+		Value: valueX,
+	}
+	return extension, nil
+}
+
+// Unwrap will return the wrapped Element in this extension, if one
+// exists. If there is none, or if extension is nil, this returns nil.
+func Unwrap(extension *dtpb.Extension) fhir.Element {
+	if extension == nil || extension.Value.GetChoice() == nil {
+		return nil
+	}
+	const choiceField = "choice"
+
+	reflect := extension.Value.ProtoReflect()
+	descriptor := reflect.Descriptor()
+	oneof := descriptor.Oneofs().ByName(choiceField)
+	field := reflect.WhichOneof(oneof)
+	message := reflect.Get(field).Message()
+
+	// Extensions can only have a limited number of extension values, all of which
+	// satisfy 'Element'. This assertion cannot fail unless an invalid oneof value
+	// has been provided, which violates protobuf definitions (and could never
+	// legally happen during transport).
+	return message.Interface().(fhir.Element)
+}
+
+// Clear removes all extensions from the specified FHIR Element
+// or Resource.
+func Clear(ext fhir.Extendable) {
+	if ext == nil {
+		return
+	}
+
+	message := ext.ProtoReflect()
+	field := message.Descriptor().Fields().ByName("extension")
+	message.Clear(field)
+}
+
+// Upsert always replaces or inserts the extension by the URL.
+func Upsert(ext fhir.Extendable, extension *dtpb.Extension) {
+	if ext == nil {
+		panic("No extendable object specified for Upsert; ext is nil.")
+	}
+
+	for _, currExt := range ext.GetExtension() {
+		if currExt.GetUrl().GetValue() == extension.GetUrl().GetValue() {
+			currMessage := currExt.ProtoReflect()
+			valueDesc := currMessage.Descriptor().Fields().ByName("value")
+			currMessage.Set(valueDesc, protoreflect.ValueOfMessage(extension.GetValue().ProtoReflect()))
+			return
+		}
+	}
+
+	AppendInto(ext, extension)
+}
+
+// SetByURL always remove all extensions with url,
+// and creates N extensions with url and values...
+func SetByURL[T ValueX](ext fhir.Extendable, url string, values ...T) {
+	if ext == nil {
+		panic("No extendable object specified for SetByURL; ext is nil.")
+	}
+
+	var newExtensionList []*dtpb.Extension
+
+	for _, currExt := range ext.GetExtension() {
+		if currExt.GetUrl().GetValue() != url {
+			newExtensionList = append(newExtensionList, currExt)
+		}
+	}
+
+	for _, val := range values {
+		newExtensionList = append(newExtensionList, New(url, val))
+	}
+
+	Overwrite(ext, newExtensionList...)
+}
+
+// Overwrite modifies a Resource or Element in-place to overwrite all of the
+// extensions in the given object with the provided ones.
+//
+// This function can only be called with extendable resources or data-types, and
+// thus does not have any error-cases to surface back to the caller.
+//
+// This function will panic if ext is nil.
+func Overwrite(ext fhir.Extendable, extensions ...*dtpb.Extension) {
+	updateExtensionsIn(ext, protoreflect.Message.NewField, extensions...)
+}
+
+// AppendInto appends extensions into the specified Resource or Element.
+// Unlike most "append" operations, this mutates the source object rather than
+// copying and returning a new changed object. This is done to prevent expensive
+// copy-operations on large FHIR resources that are being extended.
+//
+// This function can only be called with extendable resources or data-types, and
+// thus does not have any error-cases to surface back to the caller.
+//
+// This function will panic if ext is nil.
+func AppendInto(ext fhir.Extendable, extensions ...*dtpb.Extension) {
+	updateExtensionsIn(ext, protoreflect.Message.Mutable, extensions...)
+}
+
+// updateExtensionsIn updates the state of extensions inside of the extendable
+// resource, using the specified field accessor to help.
+func updateExtensionsIn(ext fhir.Extendable, get protofields.FieldToValueFunc, extensions ...*dtpb.Extension) {
+	if ext == nil {
+		panic("No extendable object specified for updateExtensionsIn; ext is nil.")
+	}
+	message := ext.ProtoReflect()
+	field := message.Descriptor().Fields().ByName("extension")
+	extensionsList := get(message, field).List()
+	for _, ext := range extensions {
+		val := protoreflect.ValueOfMessage(ext.ProtoReflect())
+		extensionsList.Append(val)
+	}
+	message.Set(field, protoreflect.ValueOfList(extensionsList))
+}
diff --git a/internal/element/extension/extension_example_test.go b/internal/element/extension/extension_example_test.go
new file mode 100644
index 0000000..adab42d
--- /dev/null
+++ b/internal/element/extension/extension_example_test.go
@@ -0,0 +1,42 @@
+package extension_test
+
+import (
+	"fmt"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/task_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/element/extension"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+)
+
+func ExampleOverwrite() {
+	const urlBase = "https://verily-src.github.io/vhp-hds-vvs-fhir-ig/StructureDefinitions"
+	task := &task_go_proto.Task{
+		Extension: []*dtpb.Extension{
+			extension.New(fmt.Sprintf("%v/%v", urlBase, "my-int"), fhir.Integer(42)),
+		},
+	}
+
+	extension.Overwrite(task,
+		extension.New(fmt.Sprintf("%v/%v", urlBase, "my-string"), fhir.String("hello world")),
+		extension.New(fmt.Sprintf("%v/%v", urlBase, "my-bool"), fhir.Boolean(true)),
+	)
+	fmt.Printf("%v extensions in Task!", len(task.GetExtension()))
+	// Output: 2 extensions in Task!
+}
+
+func ExampleAppendInto() {
+	const urlBase = "http://example.com/StructureDefinitions"
+	task := &task_go_proto.Task{
+		Extension: []*dtpb.Extension{
+			extension.New(fmt.Sprintf("%v/%v", urlBase, "my-int"), fhir.Integer(42)),
+		},
+	}
+
+	extension.AppendInto(task,
+		extension.New(fmt.Sprintf("%v/%v", urlBase, "my-string"), fhir.String("hello world")),
+		extension.New(fmt.Sprintf("%v/%v", urlBase, "my-bool"), fhir.Boolean(true)),
+	)
+	fmt.Printf("%v extensions in Task!", len(task.GetExtension()))
+	// Output: 3 extensions in Task!
+}
diff --git a/internal/element/extension/extension_test.go b/internal/element/extension/extension_test.go
new file mode 100644
index 0000000..6c0bb4b
--- /dev/null
+++ b/internal/element/extension/extension_test.go
@@ -0,0 +1,446 @@
+package extension_test
+
+import (
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/verily-src/fhirpath-go/internal/element/canonical"
+	"github.com/verily-src/fhirpath-go/internal/element/extension"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/fhirtest"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func isValueXType(element fhir.Element) bool {
+	switch element.(type) {
+	case *dtpb.Base64Binary,
+		*dtpb.Boolean,
+		*dtpb.Canonical,
+		*dtpb.Code,
+		*dtpb.Date,
+		*dtpb.DateTime,
+		*dtpb.Decimal,
+		*dtpb.Id,
+		*dtpb.Instant,
+		*dtpb.Integer,
+		*dtpb.Markdown,
+		*dtpb.Oid,
+		*dtpb.PositiveInt,
+		*dtpb.String,
+		*dtpb.Time,
+		*dtpb.UnsignedInt,
+		*dtpb.Uri,
+		*dtpb.Url,
+		*dtpb.Uuid,
+		*dtpb.Address,
+		*dtpb.Age,
+		*dtpb.Annotation,
+		*dtpb.Attachment,
+		*dtpb.CodeableConcept,
+		*dtpb.Coding,
+		*dtpb.ContactPoint,
+		*dtpb.Count,
+		*dtpb.Distance,
+		*dtpb.Duration,
+		*dtpb.HumanName,
+		*dtpb.Identifier,
+		*dtpb.Money,
+		*dtpb.Period,
+		*dtpb.Quantity,
+		*dtpb.Range,
+		*dtpb.Ratio,
+		*dtpb.Reference,
+		*dtpb.SampledData,
+		*dtpb.Signature,
+		*dtpb.Timing,
+		*dtpb.ContactDetail,
+		*dtpb.Contributor,
+		*dtpb.DataRequirement,
+		*dtpb.Expression,
+		*dtpb.ParameterDefinition,
+		*dtpb.RelatedArtifact,
+		*dtpb.TriggerDefinition,
+		*dtpb.UsageContext,
+		*dtpb.Dosage:
+		return true
+	}
+	return false
+}
+
+func TestRoundTrip(t *testing.T) {
+	for name, element := range fhirtest.Elements {
+		if !isValueXType(element) {
+			t.Skip()
+		}
+
+		t.Run(name, func(t *testing.T) {
+			ext, err := extension.FromElement("foo", element)
+			if err != nil {
+				t.Fatalf("RoundTrip(%v): got unexpected error %v", name, err)
+			}
+
+			got := extension.Unwrap(ext)
+
+			if got, want := got, element; got != want {
+				t.Errorf("RoundTrip(%v): got %v, want %v", name, got, want)
+			}
+		})
+	}
+}
+
+func TestNew_Base64Binary_ReturnsExtension(t *testing.T) {
+	const url = "http://example.com"
+	input := fhir.Base64Binary([]byte{0xde, 0xad, 0xbe, 0xef})
+	want := &dtpb.Extension{
+		Url: fhir.URI(url),
+		Value: &dtpb.Extension_ValueX{
+			Choice: &dtpb.Extension_ValueX_Base64Binary{
+				Base64Binary: input,
+			},
+		},
+	}
+
+	got := extension.New(url, input)
+
+	if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+		t.Errorf("Extension(Base64Binary): (-got,+want):\n%v", diff)
+	}
+}
+
+func TestNew_Boolean_ReturnsExtension(t *testing.T) {
+	const url = "http://example.com"
+	input := fhir.Boolean(true)
+	want := &dtpb.Extension{
+		Url: fhir.URI(url),
+		Value: &dtpb.Extension_ValueX{
+			Choice: &dtpb.Extension_ValueX_Boolean{
+				Boolean: input,
+			},
+		},
+	}
+
+	got := extension.New(url, input)
+
+	if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+		t.Errorf("Extension(Boolean): (-got,+want):\n%v", diff)
+	}
+}
+
+func TestNew_Canonical_ReturnsExtension(t *testing.T) {
+	const url = "http://example.com"
+	input := canonical.New(url)
+	want := &dtpb.Extension{
+		Url: fhir.URI(url),
+		Value: &dtpb.Extension_ValueX{
+			Choice: &dtpb.Extension_ValueX_Canonical{
+				Canonical: input,
+			},
+		},
+	}
+
+	got := extension.New(url, input)
+
+	if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+		t.Errorf("Extension(Boolean): (-got,+want):\n%v", diff)
+	}
+}
+func TestNew_Address_ReturnsExtension(t *testing.T) {
+	const url = "http://example.com"
+	input := &dtpb.Address{
+		Text: fhir.String("hello world"),
+	}
+	want := &dtpb.Extension{
+		Url: fhir.URI(url),
+		Value: &dtpb.Extension_ValueX{
+			Choice: &dtpb.Extension_ValueX_Address{
+				Address: input,
+			},
+		},
+	}
+
+	got := extension.New(url, input)
+
+	if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+		t.Errorf("Extension(Address): (-got,+want):\n%v", diff)
+	}
+}
+
+func TestNew_Id_ReturnsExtension(t *testing.T) {
+	const url = "http://example.com"
+	input := fhir.String("hello world")
+	want := &dtpb.Extension{
+		Url: fhir.URI(url),
+		Value: &dtpb.Extension_ValueX{
+			Choice: &dtpb.Extension_ValueX_StringValue{
+				StringValue: input,
+			},
+		},
+	}
+
+	got := extension.New(url, input)
+
+	if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+		t.Errorf("Extension(String): (-got,+want):\n%v", diff)
+	}
+}
+
+func TestUnwrap_NilInput_ReturnsNil(t *testing.T) {
+	got := extension.Unwrap(nil)
+
+	if got != nil {
+		t.Errorf("Unwrap: got %v, want nil", got)
+	}
+}
+
+func TestUnwrap(t *testing.T) {
+	for name, element := range fhirtest.Elements {
+		t.Run(name, func(t *testing.T) {
+			ext, err := extension.FromElement("url", element)
+			if err != nil {
+				// Only test using elements that are valid for extensions.
+				t.Skip()
+			}
+
+			got := extension.Unwrap(ext)
+
+			want := element
+			if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+				t.Errorf("Unwrap(%v): (-got,+want):\n%v", name, diff)
+			}
+		})
+	}
+}
+
+func TestFromElement_NilInput_ReturnsError(t *testing.T) {
+	_, err := extension.FromElement("url", nil)
+
+	if err == nil {
+		t.Errorf("Extension: got nil, want err")
+	}
+}
+
+func TestFromElement(t *testing.T) {
+	const url = "http://example.com"
+	testCases := []struct {
+		name    string
+		input   fhir.Element
+		want    *dtpb.Extension
+		wantErr error
+	}{
+		{
+			name:    "Nil",
+			input:   nil,
+			want:    nil,
+			wantErr: cmpopts.AnyError,
+		}, {
+			name:  "String",
+			input: fhir.String("hello world"),
+			want: &dtpb.Extension{
+				Url: fhir.URI(url),
+				Value: &dtpb.Extension_ValueX{
+					Choice: &dtpb.Extension_ValueX_StringValue{
+						StringValue: fhir.String("hello world"),
+					},
+				},
+			},
+			wantErr: nil,
+		}, {
+			name:    "Extension",
+			input:   &dtpb.Extension{},
+			want:    nil,
+			wantErr: extension.ErrInvalidValueX,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := extension.FromElement(url, tc.input)
+
+			if got, want := err, tc.wantErr; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+				t.Errorf("FromElement(%v): got err %v, want %v", tc.name, got, want)
+			}
+			if got, want := got, tc.want; !cmp.Equal(got, want, protocmp.Transform()) {
+				t.Errorf("FromElement(%v): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestOverwrite_WithNil_Panics(t *testing.T) {
+	defer func() { _ = recover() }()
+
+	extension.Overwrite(nil)
+
+	t.Errorf("Overwrite: expected panic")
+}
+
+func TestOverwrite_WithExtendableResources_ExtendsResource(t *testing.T) {
+	const url = "url"
+	ext := extension.New(url, fhir.Boolean(true))
+
+	for name, object := range fhirtest.DomainResources {
+		t.Run(name, func(t *testing.T) {
+			object := proto.Clone(object).(fhir.DomainResource)
+			extension.Overwrite(object, ext)
+
+			if len(object.GetExtension()) != 1 {
+				t.Fatalf("Overwrite: got len %v, want 1", len(object.GetExtension()))
+			}
+			got := object.GetExtension()[0]
+			if !proto.Equal(got, ext) {
+				t.Errorf("Overwrite: got extension %v, want %v", got, ext)
+			}
+		})
+	}
+}
+
+func TestAppendInto_WithNil_Panics(t *testing.T) {
+	defer func() { _ = recover() }()
+
+	extension.AppendInto(nil)
+
+	t.Errorf("AppendInto: expected panic")
+}
+
+func TestAppendInto_WithResources_ExtendsResource(t *testing.T) {
+	const url = "url"
+	ext := extension.New(url, fhir.Boolean(true))
+
+	for name, object := range fhirtest.DomainResources {
+		t.Run(name, func(t *testing.T) {
+			object := proto.Clone(object).(fhir.DomainResource)
+			extension.Overwrite(object, extension.New(url, fhir.String("Some other value")))
+
+			extension.AppendInto(object, ext)
+
+			if len(object.GetExtension()) != 2 {
+				t.Fatalf("AppendInto: got len %v, want 1", len(object.GetExtension()))
+			}
+			got := object.GetExtension()[1]
+			if !proto.Equal(got, ext) {
+				t.Errorf("AppendInto: got extension %v, want %v", got, ext)
+			}
+		})
+	}
+}
+
+func TestUpsert_WithNil_Panics(t *testing.T) {
+	defer func() { _ = recover() }()
+
+	extension.AppendInto(nil)
+
+	t.Errorf("AppendInto: expected panic")
+}
+
+func TestUpsert_WithResources_ExtendsResource(t *testing.T) {
+	const (
+		urlA = "urlA"
+		urlB = "urlB"
+	)
+	ext := extension.New(urlA, fhir.Boolean(true))
+
+	for name, object := range fhirtest.DomainResources {
+		t.Run(name, func(t *testing.T) {
+			object := proto.Clone(object).(fhir.DomainResource)
+			extension.Overwrite(object, extension.New(urlB, fhir.String("Some other value")))
+
+			extension.Upsert(object, ext)
+
+			if len(object.GetExtension()) != 2 {
+				t.Fatalf("Upsert: got len %v, want 2", len(object.GetExtension()))
+			}
+			got := object.GetExtension()[1]
+			if !proto.Equal(got, ext) {
+				t.Errorf("AppendInto: got extension %v, want %v", got, ext)
+			}
+		})
+	}
+}
+
+func TestUpsert_WithResources_ModifiesResource(t *testing.T) {
+	const urlA = "urlA"
+
+	ext := extension.New(urlA, fhir.Boolean(true))
+
+	for name, object := range fhirtest.DomainResources {
+		t.Run(name, func(t *testing.T) {
+			object := proto.Clone(object).(fhir.DomainResource)
+			extension.Overwrite(object, extension.New(urlA, fhir.String("Some other value")))
+
+			extension.Upsert(object, ext)
+
+			if len(object.GetExtension()) != 1 {
+				t.Fatalf("Upsert: got len %v, want 1", len(object.GetExtension()))
+			}
+			got := object.GetExtension()[0]
+			if !proto.Equal(got, ext) {
+				t.Errorf("AppendInto: got extension %v, want %v", got, ext)
+			}
+		})
+	}
+}
+
+func TestSetByURL_WithNil_Panics(t *testing.T) {
+	defer func() { _ = recover() }()
+
+	extension.SetByURL(nil, "some-url", fhir.Boolean(true))
+
+	t.Errorf("AppendInto: expected panic")
+}
+
+func TestSetByURL_WithResources_ModifiesResource(t *testing.T) {
+	const (
+		urlA = "urlA"
+		urlB = "urlB"
+	)
+	extB := extension.New(urlB, fhir.String("Some B value"))
+	extA := extension.New(urlA, fhir.String("Some A value"))
+	wantExtensions := []*dtpb.Extension{extB, extension.New(urlA, fhir.Boolean(true)), extension.New(urlA, fhir.Boolean(false))}
+	for name, object := range fhirtest.DomainResources {
+		t.Run(name, func(t *testing.T) {
+			object := proto.Clone(object).(fhir.DomainResource)
+			extension.Overwrite(object, extA)
+			extension.AppendInto(object, extB)
+
+			extension.SetByURL(object, urlA, fhir.Boolean(true), fhir.Boolean(false))
+
+			if len(object.GetExtension()) != 3 {
+				t.Fatalf("Upsert: got len %v, want 3", len(object.GetExtension()))
+			}
+			gotExtensions := object.GetExtension()
+			if diff := cmp.Diff(gotExtensions, wantExtensions, protocmp.Transform()); diff != "" {
+				t.Errorf("AppendInto: (-got, +want) %v", diff)
+			}
+		})
+	}
+}
+
+func TestClear_DoesNothing(t *testing.T) {
+	extension.Clear(nil)
+
+	// There really isn't anything to assert on here; there is no input or reaction.
+	// There is no crash either.
+}
+
+func TestClear_ResourceHasExtensions_RemovesExtensions(t *testing.T) {
+	const url = "http://example.com"
+	for name, resource := range fhirtest.DomainResources {
+		t.Run(name, func(t *testing.T) {
+			got := proto.Clone(resource).(fhir.DomainResource)
+			want := proto.Clone(resource).(fhir.DomainResource)
+			extension.Overwrite(got,
+				extension.New(url, fhir.String("hello world")),
+				extension.New(url, fhir.Boolean(true)),
+			)
+
+			extension.Clear(got)
+
+			if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+				t.Errorf("Clear(%v): (-got,+want):\n%v", name, diff)
+			}
+		})
+	}
+}
diff --git a/internal/element/extract.go b/internal/element/extract.go
new file mode 100644
index 0000000..b85a0fb
--- /dev/null
+++ b/internal/element/extract.go
@@ -0,0 +1,208 @@
+package element
+
+import (
+	"errors"
+	"fmt"
+	"sort"
+	"strings"
+
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/reflect/protopath"
+	"google.golang.org/protobuf/reflect/protorange"
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+var (
+	ErrFhirPathNotImplemented error = errors.New("FHIRPath labeling not yet implemented")
+)
+
+// A ElementWithPath holds a FHIR element as a proto message and its corresponding
+// FHIRPath. All FHIR elements (including scalar-ish elements like string
+// and URI) are proto messages.
+// See http://hl7.org/fhir/R4/fhirpath.html.
+type ElementWithPath[elementT proto.Message] struct {
+	Element  elementT
+	FHIRPath string
+}
+
+// SortSliceOfElementWithPath sorts the given slice in-place according to the lexical
+// ordering of the FHIR path of each element.
+func SortSliceOfElementWithPath[elementT proto.Message](s []ElementWithPath[elementT]) {
+	sort.SliceStable(s, func(i, j int) bool {
+		return s[i].FHIRPath < s[j].FHIRPath
+	})
+}
+
+// ExtractAll returns all the elements of type elementT within the resource.
+// The returned elements are not copies: mutations of the returned elements
+// will mutate the resource. The order of the returned list is unspecified.
+func ExtractAll[elementT proto.Message](resource fhir.Resource) ([]elementT, error) {
+	elements, err := extractAllImpl[elementT](resource, false)
+	if err != nil {
+		return nil, err
+	}
+	return slices.Map(elements, func(e ElementWithPath[elementT]) elementT {
+		return e.Element
+	}), nil
+}
+
+// ExtractAllWithPath returns all the elements of typeT within the resource,
+// along with the FHIR path of each such element. The returned elements
+// are not copies: mutations of the returned elements will mutate the resource.
+// The returned list is sorted by the lexical order of the FHIR path of each
+// element.
+func ExtractAllWithPath[elementT proto.Message](resource fhir.Resource) ([]ElementWithPath[elementT], error) {
+	elements, err := extractAllImpl[elementT](resource, true)
+	if err != nil {
+		return nil, err
+	}
+	SortSliceOfElementWithPath(elements)
+	return elements, err
+}
+
+// extractAllImpl extracts all the instances of elementT from resource.
+// If addPaths is true, all returns elements include the FHIR path
+// of that element; otherwise each element's FHIR path is an empty string.
+// The FHIR path labelling is optional because it may trigger error
+// conditions that otherwise would not occur. (Also it is slower).
+//
+// Once FHIR path labelling is proven robust consider always doing it
+// because it yields a nice determinstic sort.
+func extractAllImpl[elementT proto.Message](resource fhir.Resource, addPaths bool) ([]ElementWithPath[elementT], error) {
+	elements := []ElementWithPath[elementT]{}
+	err := protorange.Range(resource.ProtoReflect(), func(pv protopath.Values) error {
+		element, found := getElementOfProtoPath[elementT](pv)
+		if found {
+			var fhirpath string
+			if addPaths {
+				var err error
+				fhirpath, err = computeFHIRPathOfProtoPath(pv.Path)
+				if err != nil {
+					return err
+				}
+			}
+			elementWithPath := ElementWithPath[elementT]{Element: element, FHIRPath: fhirpath}
+			elements = append(elements, elementWithPath)
+		}
+		return nil
+	})
+	return elements, err
+}
+
+// getElementOfProtoPath returns FHIR element referenced by pv.
+//
+// Returns (element, true) if pv references an element of type elementT;
+// else returns (typed-nil, false).
+func getElementOfProtoPath[elementT proto.Message](pv protopath.Values) (elementT, bool) {
+	currStep := pv.Path.Index(-1)
+	currV := pv.Values[pv.Len()-1]
+	var prm protoreflect.Message
+	switch currStep.Kind() {
+	case protopath.FieldAccessStep:
+		// FieldAccess describes access of a field within a message.
+		// The type of the current step value is determined by the field descriptor.
+		fd := currStep.FieldDescriptor()
+		if fd.Kind() == protoreflect.MessageKind && fd.Cardinality() != protoreflect.Repeated {
+			prm = currV.Message()
+		}
+	case protopath.ListIndexStep:
+		// ListIndex describes index of an element within a list.
+		// The previous step value is always a list.
+		// The previous step type is a field access for lists with message kind.
+		prevStep := pv.Path.Index(-2)
+		prevV := pv.Values[pv.Len()-2]
+		if prevStep.Kind() != protopath.FieldAccessStep {
+			break
+		}
+		if prevStep.FieldDescriptor().Kind() == protoreflect.MessageKind {
+			prm = prevV.List().Get(currStep.ListIndex()).Message()
+		}
+	case protopath.MapIndexStep:
+		// MapIndex describes index of an entry within a map.
+		// The previous step value is always a map.
+		// The previous step type is a field access for maps with message kind.
+		prevStep := pv.Path.Index(-2)
+		prevV := pv.Values[pv.Len()-2]
+		if prevStep.Kind() != protopath.FieldAccessStep {
+			break
+		}
+		if prevStep.FieldDescriptor().Kind() == protoreflect.MessageKind {
+			prm = prevV.Map().Get(currStep.MapIndex()).Message()
+		}
+	}
+	if prm != nil {
+		if element, ok := prm.Interface().(elementT); ok {
+			return element, true
+		}
+	}
+	var nilElement elementT
+	return nilElement, false
+}
+
+// leafElementsByMsgFullName is set of Descriptor full names of elements
+// that have proto implementation with subfields that don't matter
+// at the FHIR element level. When computing the FHIR path of a proto path,
+// proto fields of these message types are ignored.
+var leafElementsByMsgFullName = map[string]struct{}{
+	"google.fhir.r4.core.Date":     {},
+	"google.fhir.r4.core.DateTime": {},
+	"google.fhir.r4.core.Instant":  {},
+	"google.fhir.r4.core.Time":     {},
+}
+
+// computeFHIRPathOfProtoPath returns the FHIR path of p.
+//
+// The following cases are supported:
+//   - Typical single and repeated elements.
+//   - extensions. Extensions are expessed as "Resource.extension[k]" instead
+//     of as "Resource.extension('<extension-url>')" because the former is unique
+//     even with repeated extensions of the same URL. (And because
+//     it is simpler to implement here.)
+//   - "choice" fields.
+//   - Special date fields: see above leafElementsByMsgFullName.
+//
+// The following cases will return an error:
+//   - Any element inside a ContainedResource. Typically this happens
+//     for a Bundle.
+//
+// WATCHOUT: There are almost certainly cases that do not return an error
+// but return an incorrect FHIRPath.
+func computeFHIRPathOfProtoPath(p protopath.Path) (string, error) {
+	fhirpath := []string{}
+	for _, step := range p {
+		switch step.Kind() {
+		case protopath.RootStep:
+			fhirpath = append(fhirpath, string(step.MessageDescriptor().Name()))
+
+		case protopath.FieldAccessStep:
+			fd := step.FieldDescriptor()
+			cfn := string(fd.ContainingMessage().FullName())
+			if cfn == "google.fhir.r4.core.ContainedResource" {
+				// Only elements of Resource have well defined FHIRpath within
+				// Bundle.entry.resource. See PHP-8862. Punt on this for now.
+				return "", fmt.Errorf("%w: for %s", ErrFhirPathNotImplemented, cfn)
+			}
+			elementName := fd.JSONName()
+			if cof := fd.ContainingOneof(); cof != nil && cof.Name() == "choice" {
+				cappedName := strings.ToUpper(elementName[0:1]) + elementName[1:]
+				fhirpath[len(fhirpath)-1] += cappedName
+			} else {
+				fhirpath = append(fhirpath, elementName)
+			}
+			if _, found := leafElementsByMsgFullName[cfn]; found {
+				// The fhirpath component above is suffient. The remaining protopath
+				// steps are implementation fields that do not appear in the spec.
+				break
+			}
+
+		case protopath.ListIndexStep:
+			fhirpath[len(fhirpath)-1] += fmt.Sprintf("[%d]", step.ListIndex())
+
+		default:
+			return "", fmt.Errorf("%w: for step %v", ErrFhirPathNotImplemented, step)
+		}
+	}
+	return strings.Join(fhirpath, "."), nil
+}
diff --git a/internal/element/extract_test.go b/internal/element/extract_test.go
new file mode 100644
index 0000000..36dde22
--- /dev/null
+++ b/internal/element/extract_test.go
@@ -0,0 +1,315 @@
+package element_test
+
+// For extraction, we test Reference and CodeableConcept elements.
+// There shouldn't be any need to exhastively test all possible element types,
+// since they they are all proto messages (even scalar-ish elements like
+// strings and URIs).
+
+import (
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	acpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/account_go_proto"
+	appb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/appointment_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/bundle"
+	"github.com/verily-src/fhirpath-go/internal/element"
+	"github.com/verily-src/fhirpath-go/internal/element/extension"
+	"github.com/verily-src/fhirpath-go/internal/fhirtest"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func Test_ExtractAll_OfReference_ValuesCorrect(t *testing.T) {
+	refUri := &dtpb.Reference{
+		Reference: &dtpb.Reference_Uri{
+			Uri: fhir.String("uri-ref"),
+		},
+	}
+	refRelatedPersonId := &dtpb.Reference{
+		Reference: &dtpb.Reference_RelatedPersonId{
+			RelatedPersonId: &dtpb.ReferenceId{
+				Id: fhir.String("related-ref"),
+			},
+		},
+	}
+	testCases := []struct {
+		name      string
+		resource  fhir.Resource
+		wantRefs  []*dtpb.Reference
+		wantPaths []string
+	}{
+		{
+			name:     "No reference",
+			resource: fhirtest.NewResource(t, "Patient"),
+			wantRefs: []*dtpb.Reference{},
+		},
+		{
+			name: "Single reference",
+			resource: fhirtest.NewResource(t, "Patient", fhirtest.WithResourceModification(func(p *ppb.Patient) {
+				p.ManagingOrganization = refUri
+			})),
+			wantRefs:  []*dtpb.Reference{refUri},
+			wantPaths: []string{"Patient.managingOrganization"},
+		},
+		{
+			name: "Multiple references",
+			resource: fhirtest.NewResource(t, "Appointment", fhirtest.WithResourceModification(func(a *appb.Appointment) {
+				a.Participant = []*appb.Appointment_Participant{
+					{
+						Actor: refRelatedPersonId,
+					},
+					{
+						Type:  []*dtpb.CodeableConcept{fhir.CodeableConcept("", fhir.Coding("systest", "code"))},
+						Actor: refUri,
+					},
+				}
+			})),
+			wantRefs:  []*dtpb.Reference{refRelatedPersonId, refUri},
+			wantPaths: []string{"Appointment.participant[0].actor", "Appointment.participant[1].actor"},
+		},
+		{
+			name: "Repeated field references",
+			resource: fhirtest.NewResource(t, "Account", fhirtest.WithResourceModification(func(a *acpb.Account) {
+				a.Subject = []*dtpb.Reference{refRelatedPersonId, refUri}
+			})),
+			wantRefs:  []*dtpb.Reference{refRelatedPersonId, refUri},
+			wantPaths: []string{"Account.subject[0]", "Account.subject[1]"},
+		},
+		{
+			name: "Repeated identical references",
+			resource: fhirtest.NewResource(t, "Account", fhirtest.WithResourceModification(func(a *acpb.Account) {
+				a.Subject = []*dtpb.Reference{refRelatedPersonId, refRelatedPersonId}
+			})),
+			wantRefs:  []*dtpb.Reference{refRelatedPersonId, refRelatedPersonId},
+			wantPaths: []string{"Account.subject[0]", "Account.subject[1]"},
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			gotReferences, _ := element.ExtractAll[*dtpb.Reference](testCase.resource)
+
+			opts := []cmp.Option{
+				protocmp.Transform(),
+				cmpopts.SortSlices(func(a *dtpb.Reference, b *dtpb.Reference) bool { return a.String() < b.String() }),
+			}
+			if !cmp.Equal(testCase.wantRefs, gotReferences, opts...) {
+				t.Errorf("ExtractAll(): got '%v', want '%v'", gotReferences, testCase.wantRefs)
+			}
+
+			gotRefsWithPath, err := element.ExtractAllWithPath[*dtpb.Reference](testCase.resource)
+			if err != nil {
+				t.Fatalf("ExtractAllElementWithPath(%s): got error %v", testCase.name, err)
+			}
+			wantRefsWithPath := zipElementsAndPaths(testCase.wantRefs, testCase.wantPaths)
+			if !cmp.Equal(gotRefsWithPath, wantRefsWithPath, protocmp.Transform(), cmpopts.EquateEmpty()) {
+				t.Errorf("ExtractAllElementWithPath(%s): got '%v', want '%v'", testCase.name, gotRefsWithPath, wantRefsWithPath)
+			}
+		})
+	}
+}
+
+func Test_ExtractAll_OfCodeableConcept_ValuesCorrect(t *testing.T) {
+	concept1 := fhir.CodeableConcept("thing1")
+	concept2 := fhir.CodeableConcept("thing2")
+	concept3 := fhir.CodeableConcept("thing2")
+	testCases := []struct {
+		name         string
+		resource     fhir.Resource
+		wantConcepts []*dtpb.CodeableConcept
+		wantPaths    []string
+		wantError    error
+	}{
+		{
+			name: "single",
+			resource: fhirtest.NewResource(t, "Patient", fhirtest.WithResourceModification(func(p *ppb.Patient) {
+				p.MaritalStatus = concept1
+			})),
+			wantConcepts: []*dtpb.CodeableConcept{concept1},
+			wantPaths:    []string{"Patient.maritalStatus"},
+		},
+		{
+			name: "repeated nested repeated with dup",
+			resource: fhirtest.NewResource(t, "Patient", fhirtest.WithResourceModification(func(p *ppb.Patient) {
+				p.Contact = []*ppb.Patient_Contact{
+					{Relationship: []*dtpb.CodeableConcept{concept1, concept2}},
+					{Relationship: []*dtpb.CodeableConcept{concept2, concept3}},
+				}
+			})),
+			wantConcepts: []*dtpb.CodeableConcept{concept1, concept2, concept2, concept3},
+			wantPaths: []string{
+				"Patient.contact[0].relationship[0]",
+				"Patient.contact[0].relationship[1]",
+				"Patient.contact[1].relationship[0]",
+				"Patient.contact[1].relationship[1]",
+			},
+		},
+		{
+			name: "repeated extension",
+			resource: fhirtest.NewResource(t, "Patient", fhirtest.WithResourceModification(func(p *ppb.Patient) {
+				p.Extension = []*dtpb.Extension{
+					extension.New("my-extension-url", concept1),
+					extension.New("my-extension-url", concept2),
+				}
+			})),
+			wantConcepts: []*dtpb.CodeableConcept{concept1, concept2},
+			wantPaths: []string{
+				"Patient.extension[0].valueCodeableConcept",
+				"Patient.extension[1].valueCodeableConcept",
+			},
+		},
+		{
+			name: "inside ContainedResource inside Bundle",
+			resource: bundle.NewCollection(bundle.WithEntries(bundle.NewCollectionEntry(
+				fhirtest.NewResource(t, "Patient", fhirtest.WithResourceModification(func(p *ppb.Patient) {
+					p.MaritalStatus = concept1
+				}))))),
+			wantError: element.ErrFhirPathNotImplemented,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			gotElementsWithPath, gotErr := element.ExtractAllWithPath[*dtpb.CodeableConcept](tc.resource)
+			if !cmp.Equal(gotErr, tc.wantError, cmpopts.EquateErrors()) {
+				t.Fatalf("ExtractAllWithPath(%s) error got '%v', want '%v'", tc.name, gotErr, tc.wantError)
+			}
+			wantElementsWithPath := zipElementsAndPaths(tc.wantConcepts, tc.wantPaths)
+			if !cmp.Equal(gotElementsWithPath, wantElementsWithPath, protocmp.Transform(), cmpopts.EquateEmpty()) {
+				t.Errorf("ExtractAllElementWithPath(%s): got '%v', want '%v'",
+					tc.name, gotElementsWithPath, wantElementsWithPath)
+			}
+		})
+	}
+}
+
+func Test_ExtractAll_OfSpecial(t *testing.T) {
+	date1 := fhir.DateNow()
+	datetime1 := fhir.DateTimeNow()
+	instant1 := fhir.InstantNow()
+	instant2 := fhir.InstantNow()
+	instant3 := fhir.InstantNow()
+	time1 := fhir.TimeNow()
+
+	testCases := []struct {
+		name         string
+		resource     fhir.Resource
+		extractFunc  func(fhir.Resource) ([]element.ElementWithPath[fhir.Element], error)
+		wantElements []fhir.Element
+		wantPaths    []string
+	}{
+		{name: "simple Date",
+			resource: fhirtest.NewResource(t, "Patient", fhirtest.WithResourceModification(func(p *ppb.Patient) {
+				p.BirthDate = date1
+			})),
+			extractFunc: func(res fhir.Resource) ([]element.ElementWithPath[fhir.Element], error) {
+				asDates, err := element.ExtractAllWithPath[*dtpb.Date](res)
+				return asElements(asDates), err
+			},
+			wantElements: []fhir.Element{date1},
+			wantPaths:    []string{"Patient.birthDate"},
+		},
+		{name: "DateTime inside choice",
+			resource: fhirtest.NewResource(t, "Patient", fhirtest.WithResourceModification(func(p *ppb.Patient) {
+				p.Deceased = &ppb.Patient_DeceasedX{
+					Choice: &ppb.Patient_DeceasedX_DateTime{DateTime: datetime1},
+				}
+			})),
+			extractFunc: func(res fhir.Resource) ([]element.ElementWithPath[fhir.Element], error) {
+				asDateTimes, err := element.ExtractAllWithPath[*dtpb.DateTime](res)
+				return asElements(asDateTimes), err
+			},
+			wantElements: []fhir.Element{datetime1},
+			wantPaths:    []string{"Patient.deceasedDateTime"},
+		},
+		{
+			name: "Instant",
+			resource: fhirtest.NewResource(t, "Appointment", fhirtest.WithResourceModification(func(a *appb.Appointment) {
+				a.Meta.LastUpdated = instant1
+				a.Start = instant2
+				a.End = instant3
+			})),
+			extractFunc: func(res fhir.Resource) ([]element.ElementWithPath[fhir.Element], error) {
+				asInstants, err := element.ExtractAllWithPath[*dtpb.Instant](res)
+				return asElements(asInstants), err
+			},
+			wantElements: []fhir.Element{instant3, instant1, instant2},
+			wantPaths:    []string{"Appointment.end", "Appointment.meta.lastUpdated", "Appointment.start"},
+		},
+		{name: "Time inside extension",
+			resource: fhirtest.NewResource(t, "Patient", fhirtest.WithResourceModification(func(p *ppb.Patient) {
+				p.Extension = []*dtpb.Extension{
+					extension.New("my-extension-url", time1),
+				}
+			})),
+			extractFunc: func(res fhir.Resource) ([]element.ElementWithPath[fhir.Element], error) {
+				asTimes, err := element.ExtractAllWithPath[*dtpb.Time](res)
+				return asElements(asTimes), err
+			},
+			wantElements: []fhir.Element{time1},
+			wantPaths:    []string{"Patient.extension[0].valueTime"},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			gotElementsWithPath, _ := tc.extractFunc(tc.resource)
+			wantElementsWithPath := zipElementsAndPaths(tc.wantElements, tc.wantPaths)
+			if !cmp.Equal(gotElementsWithPath, wantElementsWithPath, protocmp.Transform(), cmpopts.EquateEmpty()) {
+				t.Errorf("ExtractAllElementWithPath(%s): got '%v', want '%v'",
+					tc.name, gotElementsWithPath, wantElementsWithPath)
+			}
+		})
+	}
+}
+
+// asElements maps a slice of ElementWithPath[elementT]
+// to a new slice of [ElementWithPath[fhir.Element].
+func asElements[elementT fhir.Element](elements []element.ElementWithPath[elementT]) []element.ElementWithPath[fhir.Element] {
+	return slices.Map(elements,
+		func(e element.ElementWithPath[elementT]) element.ElementWithPath[fhir.Element] {
+			return element.ElementWithPath[fhir.Element]{Element: e.Element, FHIRPath: e.FHIRPath}
+		},
+	)
+}
+
+func zipElementsAndPaths[elementT proto.Message](elements []elementT, paths []string) []element.ElementWithPath[elementT] {
+	zipped := []element.ElementWithPath[elementT]{}
+	for idx, ele := range elements {
+		var path string
+		if idx < len(paths) {
+			path = paths[idx]
+		}
+		zipped = append(zipped, element.ElementWithPath[elementT]{Element: ele, FHIRPath: path})
+	}
+	return zipped
+}
+
+func Test_ExtractAll_Modifiable(t *testing.T) {
+	resource := fhirtest.NewResource(t, "Patient", fhirtest.WithResourceModification(func(p *ppb.Patient) {
+		p.ManagingOrganization = &dtpb.Reference{
+			Reference: &dtpb.Reference_Uri{
+				Uri: fhir.String("old-ref"),
+			},
+		}
+	})).(*ppb.Patient)
+
+	references, _ := element.ExtractAll[*dtpb.Reference](resource)
+
+	if len(references) != 1 {
+		t.Fatalf("Expected single reference")
+	}
+
+	references[0].Reference = &dtpb.Reference_Uri{
+		Uri: fhir.String("new-ref"),
+	}
+
+	if got, want := resource.ManagingOrganization, references[0]; got != want {
+		t.Errorf("ExtractAll() reference update failed, got '%v', want '%v'", got, want)
+	}
+}
diff --git a/internal/element/identifier/docs.go b/internal/element/identifier/docs.go
new file mode 100644
index 0000000..6ff17c7
--- /dev/null
+++ b/internal/element/identifier/docs.go
@@ -0,0 +1,7 @@
+/*
+Package identifier provides utilities for constructing and working with
+FHIR R4 Identifier elements.
+
+See: http://hl7.org/fhir/R4/datatypes.html#Identifier
+*/
+package identifier
diff --git a/internal/element/identifier/identifier.go b/internal/element/identifier/identifier.go
new file mode 100644
index 0000000..5bc161f
--- /dev/null
+++ b/internal/element/identifier/identifier.go
@@ -0,0 +1,174 @@
+package identifier
+
+import (
+	"fmt"
+	"net/url"
+
+	cpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/codes_go_proto"
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+)
+
+// Use is an alias of the Identifier Use-codes for easier access and readability.
+type Use = cpb.IdentifierUseCode_Value
+
+const (
+	// UseUsual is an alias of the USUAL Identifier use-code for easier access and
+	// readability.
+	UseUsual Use = cpb.IdentifierUseCode_USUAL
+
+	// UseOfficial is an alias of the OFFICIAL Identifier use-code for easier access
+	// and readability.
+	UseOfficial Use = cpb.IdentifierUseCode_OFFICIAL
+
+	// UseTemp is an alias of the TEMP Identifier use-code for easier access and
+	// readability.
+	UseTemp Use = cpb.IdentifierUseCode_TEMP
+
+	// UseSecondary is an alias of the SECONDARY Identifier use-code for easier
+	// access and readability.
+	UseSecondary Use = cpb.IdentifierUseCode_SECONDARY
+
+	// UseOld is an alias of the OLD Identifier use-code for easier access and
+	// readability.
+	UseOld Use = cpb.IdentifierUseCode_OLD
+)
+
+// New constructs a new Identifier object from the given options.
+func New(value, system string, opts ...Option) *dtpb.Identifier {
+	identifier := &dtpb.Identifier{
+		System: fhir.URI(system),
+		Value:  fhir.String(value),
+	}
+	return Update(identifier, opts...)
+}
+
+// Usual is a convenience constructor for forming an identifier with a "Usual"
+// Use-code assigned.
+func Usual(value, system string, opts ...Option) *dtpb.Identifier {
+	return newWithUse(UseUsual, value, system, opts...)
+}
+
+// Official is a convenience constructor for forming an identifier with an
+// "Official" Use-code assigned.
+func Official(value, system string, opts ...Option) *dtpb.Identifier {
+	return newWithUse(UseOfficial, value, system, opts...)
+}
+
+// Temp is a convenience constructor for forming an identifier with a "Temp"
+// Use-code assigned.
+func Temp(value, system string, opts ...Option) *dtpb.Identifier {
+	return newWithUse(UseTemp, value, system, opts...)
+}
+
+// Secondary is a convenience constructor for forming an identifier with a
+// "Secondary" Use-code assigned.
+func Secondary(value, system string, opts ...Option) *dtpb.Identifier {
+	return newWithUse(UseSecondary, value, system, opts...)
+}
+
+// Old is a convenience constructor for forming an identifier with an "Old"
+// Use-code assigned.
+func Old(value, system string, opts ...Option) *dtpb.Identifier {
+	return newWithUse(UseOld, value, system, opts...)
+}
+
+func newWithUse(use Use, value, system string, opts ...Option) *dtpb.Identifier {
+	identifier := &dtpb.Identifier{
+		System: fhir.URI(system),
+		Value:  fhir.String(value),
+		Use: &dtpb.Identifier_UseCode{
+			Value: use,
+		},
+	}
+	return Update(identifier, opts...)
+}
+
+// Update modifies an identifier in-place with the given identifier options.
+//
+// This function returns the input identifier to allow for functional chaining.
+func Update(identifier *dtpb.Identifier, opts ...Option) *dtpb.Identifier {
+	for _, opt := range opts {
+		opt.update(identifier)
+	}
+	return identifier
+}
+
+// Equivalent checks if two identifiers are equivalent by comparing the
+// system and values of the identifier.
+func Equivalent(lhs, rhs *dtpb.Identifier) bool {
+	lsystem, rsystem := lhs.GetSystem(), rhs.GetSystem()
+	lvalue, rvalue := lhs.GetValue(), rhs.GetValue()
+
+	if lsystem.GetValue() != rsystem.GetValue() {
+		return false
+	}
+	if lvalue.GetValue() != rvalue.GetValue() {
+		return false
+	}
+	return true
+}
+
+// FindBySystem searches a slice of identifiers for the first identifier that
+// contains the specified system.
+func FindBySystem(identifiers []*dtpb.Identifier, system string) *dtpb.Identifier {
+	for _, identifier := range identifiers {
+		identifierSystem := identifier.GetSystem()
+		if identifierSystem == nil {
+			continue
+		}
+		if identifierSystem.GetValue() == system {
+			return identifier
+		}
+	}
+	return nil
+}
+
+// QueryString formats a system and value for use in a Search query,
+// escaping FHIR special characters `,|$\` in the input.
+// Use this in a query param as the value with key `identifier`.
+func QueryString(system string, value string) string {
+	// escape special characters like `|` from identifier
+	escapedSystem := fhir.EscapeSearchParam(system)
+	escapedValue := fhir.EscapeSearchParam(value)
+	return fmt.Sprintf("%s|%s", escapedSystem, escapedValue)
+}
+
+// QueryIdentifier formats an Identifier proto for use in a Search query,
+// escaping FHIR special characters `,|$\` in the input.
+// Use this in a query param as the value with key `identifier`.
+func QueryIdentifier(id *dtpb.Identifier) string {
+	return QueryString(id.System.Value, id.Value.Value)
+}
+
+// GenerateIfNoneExist takes an Identifier and generates a query appropriate for use in an If-None-Exist header.
+// This is used for FHIR conditional create or other conditional methods.
+//
+// Untrusted data in Identifiers is escaped both for FHIR and for URL safety.
+//
+// Returns an empty string if identifier is nil.
+//
+// This function only accepts a single identifier due to limitations of the GCP
+// FHIR store. Important note:
+// The GCP FHIR store only supports conditional queries on a single identifier,
+// with no modifiers (so identifier=foo is OK, while identifier:exact=foo is
+// invalid). Deviating from this in API v1 will result in an HTTP 400 invalid
+// query error. NB: Deviating from this in API v1beta1 results in silent
+// fallback to non-transactional search, meaning the conditional queries will
+// have race conditions.
+func GenerateIfNoneExist(identifier *dtpb.Identifier) string {
+	if identifier == nil {
+		return ""
+	}
+
+	// build up a query string like format for the header
+	// data is URL encoded in case the identifier contains special characters
+	qs := url.Values{}
+
+	search := QueryIdentifier(identifier)
+	// use identifier= rather than identifier:exact=, see limitation above
+	qs.Add("identifier", search)
+
+	// URL encode the result
+	return qs.Encode()
+}
diff --git a/internal/element/identifier/identifier_example_test.go b/internal/element/identifier/identifier_example_test.go
new file mode 100644
index 0000000..a5a16d9
--- /dev/null
+++ b/internal/element/identifier/identifier_example_test.go
@@ -0,0 +1,52 @@
+package identifier_test
+
+import (
+	"fmt"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/element/identifier"
+)
+
+func ExampleQueryIdentifier() {
+	ident := &dtpb.Identifier{
+		System: &dtpb.Uri{Value: "http://fake.com"},
+		Value:  &dtpb.String{Value: "b0459744-b74b-441a-aee4-9dd97c80c642"},
+	}
+
+	search := identifier.QueryIdentifier(ident)
+	fmt.Printf("identifier:exact=%s", search)
+	// Output: identifier:exact=http://fake.com|b0459744-b74b-441a-aee4-9dd97c80c642
+}
+
+func ExampleQueryIdentifier_escape() {
+	ident := &dtpb.Identifier{
+		System: &dtpb.Uri{Value: "http://fake.com"},
+		Value:  &dtpb.String{Value: "foo,bar|baz"},
+	}
+
+	search := identifier.QueryIdentifier(ident)
+	fmt.Printf("identifier:exact=%s", search)
+	// Output: identifier:exact=http://fake.com|foo\,bar\|baz
+}
+
+func ExampleQueryString() {
+	search := identifier.QueryString("http://fake.com", "1234")
+	fmt.Printf("identifier:exact=%s", search)
+	// Output: identifier:exact=http://fake.com|1234
+}
+func ExampleQueryString_escape() {
+	search := identifier.QueryString("http://fake.com", `$foo|bar\baz`)
+	fmt.Printf("identifier:exact=%s", search)
+	// Output: identifier:exact=http://fake.com|\$foo\|bar\\baz
+}
+
+func ExampleGenerateIfNoneExist() {
+	id := &dtpb.Identifier{
+		System: &dtpb.Uri{Value: "http://fake.com"},
+		Value:  &dtpb.String{Value: "9efbf82d-7a58-4d14-bec1-63f8fda148a8"},
+	}
+
+	header := identifier.GenerateIfNoneExist(id)
+	fmt.Printf("If-None-Exist: %v", header)
+	// Output: If-None-Exist: identifier=http%3A%2F%2Ffake.com%7C9efbf82d-7a58-4d14-bec1-63f8fda148a8
+}
diff --git a/internal/element/identifier/identifier_test.go b/internal/element/identifier/identifier_test.go
new file mode 100644
index 0000000..c2c2578
--- /dev/null
+++ b/internal/element/identifier/identifier_test.go
@@ -0,0 +1,329 @@
+package identifier_test
+
+import (
+	"net/url"
+	"testing"
+
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/codes_go_proto"
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/uuid"
+	"github.com/verily-src/fhirpath-go/internal/element/identifier"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"google.golang.org/protobuf/proto"
+)
+
+// New also tests the behaviors of various opts.
+func TestNew(t *testing.T) {
+	const system = "system"
+	const value = "value"
+	period := &dtpb.Period{
+		Start: fhir.DateTimeNow(),
+	}
+	ref := &dtpb.Reference{
+		Reference: &dtpb.Reference_Uri{
+			Uri: fhir.String("uri"),
+		},
+	}
+	cc := &dtpb.CodeableConcept{
+		Text: fhir.String("example"),
+	}
+	ext := &dtpb.Extension{
+		Url: fhir.URI("url"),
+	}
+	testCases := []struct {
+		name string
+		opts []identifier.Option
+		want *dtpb.Identifier
+	}{
+		{
+			name: "No options",
+			want: &dtpb.Identifier{
+				System: fhir.URI(system),
+				Value:  fhir.String(value),
+			},
+		}, {
+			name: "WithUse",
+			opts: []identifier.Option{identifier.WithUse(identifier.UseOfficial)},
+			want: &dtpb.Identifier{
+				System: fhir.URI(system),
+				Value:  fhir.String(value),
+				Use: &dtpb.Identifier_UseCode{
+					Value: identifier.UseOfficial,
+				},
+			},
+		}, {
+			name: "WithAssigner",
+			opts: []identifier.Option{identifier.WithAssigner(ref)},
+			want: &dtpb.Identifier{
+				System:   fhir.URI(system),
+				Value:    fhir.String(value),
+				Assigner: ref,
+			},
+		}, {
+			name: "WithPeriod",
+			opts: []identifier.Option{identifier.WithPeriod(period)},
+			want: &dtpb.Identifier{
+				System: fhir.URI(system),
+				Value:  fhir.String(value),
+				Period: period,
+			},
+		}, {
+			name: "WithType",
+			opts: []identifier.Option{identifier.WithType(cc)},
+			want: &dtpb.Identifier{
+				System: fhir.URI(system),
+				Value:  fhir.String(value),
+				Type:   cc,
+			},
+		}, {
+			name: "WithExtensions",
+			opts: []identifier.Option{identifier.WithExtensions(ext)},
+			want: &dtpb.Identifier{
+				System:    fhir.URI(system),
+				Value:     fhir.String(value),
+				Extension: []*dtpb.Extension{ext},
+			},
+		}, {
+			name: "IncludeExtensions",
+			opts: []identifier.Option{identifier.IncludeExtensions(ext)},
+			want: &dtpb.Identifier{
+				System:    fhir.URI(system),
+				Value:     fhir.String(value),
+				Extension: []*dtpb.Extension{ext},
+			},
+		}, {
+			name: "WithID",
+			opts: []identifier.Option{identifier.WithID("id")},
+			want: &dtpb.Identifier{
+				System: fhir.URI(system),
+				Value:  fhir.String(value),
+				Id:     fhir.String("id"),
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		got := identifier.New(value, system, tc.opts...)
+
+		if want := tc.want; !proto.Equal(got, want) {
+			t.Errorf("New(%v): got %v, want %v", tc.name, got, want)
+		}
+	}
+}
+
+func TestUpdate(t *testing.T) {
+	const system = "system"
+	const value = "value"
+
+	ext := &dtpb.Extension{
+		Url: fhir.URI("url"),
+	}
+	testCases := []struct {
+		name string
+		opts []identifier.Option
+		want *dtpb.Identifier
+	}{
+		{
+			name: "IncludeExtensions",
+			opts: []identifier.Option{identifier.IncludeExtensions(ext)},
+			want: &dtpb.Identifier{
+				Extension: []*dtpb.Extension{ext},
+			},
+		}, {
+			name: "WithSystemString",
+			opts: []identifier.Option{identifier.WithSystemString(system)},
+			want: &dtpb.Identifier{
+				System: fhir.URI(system),
+			},
+		}, {
+			name: "WithValue",
+			opts: []identifier.Option{identifier.WithValue(value)},
+			want: &dtpb.Identifier{
+				Value: fhir.String(value),
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+
+		got := identifier.Update(&dtpb.Identifier{}, tc.opts...)
+
+		if want := tc.want; !proto.Equal(got, want) {
+			t.Errorf("Update(%v): got %v, want %v", tc.name, got, want)
+		}
+	}
+}
+
+func TestNewWithUse(t *testing.T) {
+	const system = "system"
+	const value = "value"
+
+	testCases := []struct {
+		name string
+		fn   func(string, string, ...identifier.Option) *dtpb.Identifier
+		use  identifier.Use
+	}{
+		{"Usual", identifier.Usual, identifier.UseUsual},
+		{"Official", identifier.Official, identifier.UseOfficial},
+		{"Temp", identifier.Temp, identifier.UseTemp},
+		{"Secondary", identifier.Secondary, identifier.UseSecondary},
+		{"Old", identifier.Old, identifier.UseOld},
+	}
+
+	for _, tc := range testCases {
+		got := tc.fn(value, system)
+
+		want := &dtpb.Identifier{
+			System: fhir.URI(system),
+			Value:  fhir.String(value),
+			Use: &dtpb.Identifier_UseCode{
+				Value: tc.use,
+			},
+		}
+		if !proto.Equal(got, want) {
+			t.Errorf("%v: got %v, want %v", tc.name, got, want)
+		}
+	}
+}
+
+func TestEquivalent(t *testing.T) {
+	const value = "value"
+	const system = "system"
+	testCases := []struct {
+		name string
+		lhs  *dtpb.Identifier
+		rhs  *dtpb.Identifier
+		want bool
+	}{
+		{
+			name: "Systems don't match",
+			lhs:  identifier.New(value, system),
+			rhs:  identifier.New(value, system+"1"),
+			want: false,
+		}, {
+			name: "Values don't match",
+			lhs:  identifier.New(value, system),
+			rhs:  identifier.New(value+"1", system),
+			want: false,
+		}, {
+			name: "Both system and values don't match",
+			lhs:  identifier.New(value, system),
+			rhs:  identifier.New(value+"1", system+"1"),
+			want: false,
+		}, {
+			name: "Both systems and values match",
+			lhs:  identifier.New(value, system),
+			rhs:  identifier.New(value, system),
+			want: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		got := identifier.Equivalent(tc.lhs, tc.rhs)
+
+		if got != tc.want {
+			t.Errorf("Equivalent(%v): got %v, want %v", tc.name, got, tc.want)
+		}
+	}
+}
+
+func TestFindBySystem(t *testing.T) {
+	want := identifier.New("value", "system")
+
+	testCases := []struct {
+		name     string
+		haystack []*dtpb.Identifier
+		want     *dtpb.Identifier
+	}{
+		{
+			name:     "Input empty",
+			haystack: nil,
+			want:     nil,
+		}, {
+			name: "Input not found",
+			haystack: []*dtpb.Identifier{
+				identifier.New("a", "a-system"),
+				identifier.New("b", "b-system"),
+			},
+			want: nil,
+		}, {
+			name: "Input is first value",
+			haystack: []*dtpb.Identifier{
+				want,
+				identifier.New("a", "a-system"),
+				identifier.New("b", "b-system"),
+			},
+			want: want,
+		}, {
+			name: "Input is last value",
+			haystack: []*dtpb.Identifier{
+				identifier.New("a", "a-system"),
+				identifier.New("b", "b-system"),
+				want,
+			},
+			want: want,
+		}, {
+			name: "Input is middle entry",
+			haystack: []*dtpb.Identifier{
+				identifier.New("a", "a-system"),
+				want,
+				identifier.New("b", "b-system"),
+			},
+			want: want,
+		},
+	}
+
+	for _, tc := range testCases {
+		got := identifier.FindBySystem(tc.haystack, want.System.Value)
+
+		if got != tc.want {
+			t.Errorf("FindBySystem(%v): got %v, want %v", tc.name, got, tc.want)
+		}
+	}
+}
+
+func TestGenerateIfNoneExist(t *testing.T) {
+
+	id1 := &dtpb.Identifier{
+		System: &dtpb.Uri{Value: "http://fake.com"},
+		Value:  &dtpb.String{Value: "9efbf82d-7a58-4d14-bec1-63f8fda148a8"},
+	}
+	id2 := &dtpb.Identifier{
+		Use: &dtpb.Identifier_UseCode{
+			Value: codes_go_proto.IdentifierUseCode_USUAL,
+		},
+		System: &dtpb.Uri{Value: "urn:oid:2.16.840.1.113883.2.4.6.3"},
+		Value:  &dtpb.String{Value: "12345"},
+	}
+
+	id3 := &dtpb.Identifier{
+		System: &dtpb.Uri{Value: "http://example.com/fake-id"},
+		Value:  &dtpb.String{Value: uuid.NewString()},
+	}
+
+	id4 := &dtpb.Identifier{
+		System: &dtpb.Uri{Value: "http://fake.com"},
+		Value:  &dtpb.String{Value: "foo,bar,baz|omg"},
+	}
+
+	testCases := []struct {
+		name  string
+		input *dtpb.Identifier
+		want  string
+	}{
+		{"nil Identifier", nil, ""},
+		{"single Identifier", id1, "identifier=" + url.QueryEscape("http://fake.com|9efbf82d-7a58-4d14-bec1-63f8fda148a8")},
+		{"Identifier with use code", id2, "identifier=" + url.QueryEscape("urn:oid:2.16.840.1.113883.2.4.6.3|12345")},
+		{"generated ID", id3, "identifier=" + url.QueryEscape("http://example.com/fake-id|"+id3.Value.Value)},
+		{"Special chars in Identifier", id4, "identifier=" + url.QueryEscape(`http://fake.com|foo\,bar\,baz\|omg`)},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := identifier.GenerateIfNoneExist(tc.input)
+			if got != tc.want {
+				t.Errorf("%#v: Bad If-None-Exist:\n  got  %#v\n  want %#v", tc.name, got, tc.want)
+			}
+		})
+	}
+}
diff --git a/internal/element/identifier/opts.go b/internal/element/identifier/opts.go
new file mode 100644
index 0000000..9a5b854
--- /dev/null
+++ b/internal/element/identifier/opts.go
@@ -0,0 +1,103 @@
+package identifier
+
+import (
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+)
+
+// Option is an abstraction for options to construct or modify Identifier elements.
+type Option interface {
+	update(*dtpb.Identifier)
+}
+
+// WithUse returns an Identifier Option that sets the Identifier.Use to the
+// specified use.
+func WithUse(use Use) Option {
+	return withCallback(func(i *dtpb.Identifier) {
+		i.Use = &dtpb.Identifier_UseCode{
+			Value: use,
+		}
+	})
+}
+
+// WithExtensions return an Identifier Option that sets the Identifier.Extension
+// field to the specified extensions.
+func WithExtensions(ext ...*dtpb.Extension) Option {
+	return withCallback(func(i *dtpb.Identifier) {
+		i.Extension = ext
+	})
+}
+
+// IncludeExtensions return an Identifier Option that appends the specified
+// extensions to the Identifier.Extension field.
+func IncludeExtensions(ext ...*dtpb.Extension) Option {
+	return withCallback(func(i *dtpb.Identifier) {
+		i.Extension = append(i.Extension, ext...)
+	})
+}
+
+// WithType returns an Identifier Option that sets the Identifier.Type to the
+// specified type.
+func WithType(ty *dtpb.CodeableConcept) Option {
+	return withCallback(func(i *dtpb.Identifier) {
+		i.Type = ty
+	})
+}
+
+// WithSystem returns an Identifier Option that sets the Identifier.System to the
+// specified system.
+func WithSystem(system *dtpb.Uri) Option {
+	return withCallback(func(i *dtpb.Identifier) {
+		i.System = system
+	})
+}
+
+// WithSystemString returns an Identifier Option that sets the Identifier.System
+// to the specified system string.
+func WithSystemString(system string) Option {
+	return WithSystem(fhir.URI(system))
+}
+
+// WithValue returns an Identifier Option that sets the Identifier.Value to the
+// specified value.
+func WithValue(value string) Option {
+	return withCallback(func(i *dtpb.Identifier) {
+		i.Value = fhir.String(value)
+	})
+}
+
+// WithPeriod returns an Identifier Option that sets the Identifier.Period to the
+// specified period.
+func WithPeriod(period *dtpb.Period) Option {
+	return withCallback(func(i *dtpb.Identifier) {
+		i.Period = period
+	})
+}
+
+// WithAssigner returns an Identifier Option that sets the Identifier.Assigner to the
+// specified assigner reference.
+func WithAssigner(assigner *dtpb.Reference) Option {
+	return withCallback(func(i *dtpb.Identifier) {
+		i.Assigner = assigner
+	})
+}
+
+// WithID returns an Identifier Option that sets the Identifier.Id to the
+// specified ID.
+func WithID(id string) Option {
+	return withCallback(func(i *dtpb.Identifier) {
+		i.Id = fhir.String(id)
+	})
+}
+
+type callbackOpt struct {
+	callback func(*dtpb.Identifier)
+}
+
+func (o callbackOpt) update(i *dtpb.Identifier) {
+	o.callback(i)
+}
+
+func withCallback(callback func(*dtpb.Identifier)) Option {
+	return callbackOpt{callback}
+}
diff --git a/internal/element/meta/meta.go b/internal/element/meta/meta.go
new file mode 100644
index 0000000..726217b
--- /dev/null
+++ b/internal/element/meta/meta.go
@@ -0,0 +1,95 @@
+package meta
+
+import (
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+// Option is an option interface for modifying meta in place.
+type Option interface {
+	updateMeta(meta *dtpb.Meta)
+}
+
+// Update updates meta in place with given opts.
+func Update(meta *dtpb.Meta, opts ...Option) *dtpb.Meta {
+	for _, opt := range opts {
+		opt.updateMeta(meta)
+	}
+	return meta
+}
+
+// WithTags replaces meta.tag.
+func WithTags(tags ...*dtpb.Coding) Option {
+	return withCodingOpt(tags)
+}
+
+type withCodingOpt []*dtpb.Coding
+
+func (wco withCodingOpt) updateMeta(meta *dtpb.Meta) {
+	meta.Tag = wco
+}
+
+// WithExtensions replaces meta.extension.
+func WithExtensions(exts ...*dtpb.Extension) Option {
+	return withExtensionOpt(exts)
+}
+
+type withExtensionOpt []*dtpb.Extension
+
+func (weo withExtensionOpt) updateMeta(meta *dtpb.Meta) {
+	meta.Extension = weo
+}
+
+// IncludeTags appends to meta.tag.
+func IncludeTags(tags ...*dtpb.Coding) Option {
+	return includeCodingOpt(tags)
+}
+
+type includeCodingOpt []*dtpb.Coding
+
+func (ico includeCodingOpt) updateMeta(meta *dtpb.Meta) {
+	meta.Tag = append(meta.Tag, ico...)
+}
+
+// WithProfiles replaces meta.profile.
+func WithProfiles(profiles ...*dtpb.Canonical) Option {
+	return withCanonicalOpt(profiles)
+}
+
+type withCanonicalOpt []*dtpb.Canonical
+
+func (wco withCanonicalOpt) updateMeta(meta *dtpb.Meta) {
+	meta.Profile = wco
+}
+
+// IncludeProfiles appends to meta.profile.
+func IncludeProfiles(profiles ...*dtpb.Canonical) Option {
+	return includeCanonicalOpt(profiles)
+}
+
+type includeCanonicalOpt []*dtpb.Canonical
+
+func (ico includeCanonicalOpt) updateMeta(meta *dtpb.Meta) {
+	meta.Profile = append(meta.Profile, ico...)
+}
+
+// ReplaceInResource replaces the resource meta field with the provided meta
+// object.
+func ReplaceInResource(resource fhir.Resource, meta *dtpb.Meta) {
+	reflect := resource.ProtoReflect()
+	metaField := getMetaField(reflect)
+
+	reflect.Set(metaField, protoreflect.ValueOfMessage(meta.ProtoReflect()))
+}
+
+// EnsureInResource ensures that the resource meta field exists.
+func EnsureInResource(resource fhir.Resource) {
+	if resource.GetMeta() == nil {
+		ReplaceInResource(resource, &dtpb.Meta{})
+	}
+}
+
+func getMetaField(reflect protoreflect.Message) protoreflect.FieldDescriptor {
+	return reflect.Descriptor().Fields().ByName("meta")
+}
diff --git a/internal/element/meta/meta_example_test.go b/internal/element/meta/meta_example_test.go
new file mode 100644
index 0000000..b00acad
--- /dev/null
+++ b/internal/element/meta/meta_example_test.go
@@ -0,0 +1,25 @@
+package meta_test
+
+import (
+	"fmt"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/element/canonical"
+	"github.com/verily-src/fhirpath-go/internal/element/meta"
+)
+
+func ExampleUpdate() {
+	m := &dtpb.Meta{}
+
+	meta.Update(m,
+		meta.WithTags(fhir.Coding("urn:oid:verily/sample-tag-system", "sample-tag-value")),
+		meta.WithProfiles(canonical.New("urn:oid:verily/sample-profile")),
+	)
+
+	fmt.Printf("meta.profile: %q\n", m.Profile[0].Value)
+	fmt.Printf("meta.tag: {%q, %q}", m.Tag[0].System.Value, m.Tag[0].Code.Value)
+	// Output:
+	// meta.profile: "urn:oid:verily/sample-profile"
+	// meta.tag: {"urn:oid:verily/sample-tag-system", "sample-tag-value"}
+}
diff --git a/internal/element/meta/meta_test.go b/internal/element/meta/meta_test.go
new file mode 100644
index 0000000..bb5fbb1
--- /dev/null
+++ b/internal/element/meta/meta_test.go
@@ -0,0 +1,233 @@
+package meta_test
+
+import (
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/element/canonical"
+	"github.com/verily-src/fhirpath-go/internal/element/extension"
+	"github.com/verily-src/fhirpath-go/internal/element/meta"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func TestWithTags(t *testing.T) {
+	at := fhir.Coding("at-s", "at-v")
+	bt := fhir.Coding("b-s", "b-v")
+	testCases := []struct {
+		name     string
+		inMeta   *dtpb.Meta
+		inTags   []*dtpb.Coding
+		wantTags []*dtpb.Coding
+	}{
+		{
+			name: "Clears Tags",
+			inMeta: &dtpb.Meta{
+				Tag: []*dtpb.Coding{at},
+			},
+			inTags:   nil,
+			wantTags: nil,
+		},
+		{
+			name: "Replaces Tags",
+			inMeta: &dtpb.Meta{
+				Tag: []*dtpb.Coding{at},
+			},
+			inTags:   []*dtpb.Coding{bt},
+			wantTags: []*dtpb.Coding{bt},
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			inMeta := proto.Clone(testCase.inMeta).(*dtpb.Meta)
+
+			meta.Update(inMeta, meta.WithTags(testCase.inTags...))
+
+			if diff := cmp.Diff(inMeta.GetTag(), testCase.wantTags, protocmp.Transform()); diff != "" {
+				t.Errorf("WithTags(): (-want, +got)\n%v", diff)
+			}
+		})
+	}
+}
+
+func TestIncludesTags(t *testing.T) {
+	at := fhir.Coding("at-s", "at-v")
+	bt := fhir.Coding("b-s", "b-v")
+	testCases := []struct {
+		name     string
+		inMeta   *dtpb.Meta
+		inTags   []*dtpb.Coding
+		wantTags []*dtpb.Coding
+	}{
+		{
+			name: "Maintains Tags",
+			inMeta: &dtpb.Meta{
+				Tag: []*dtpb.Coding{at},
+			},
+			inTags:   nil,
+			wantTags: []*dtpb.Coding{at},
+		},
+		{
+			name: "Appends Tags",
+			inMeta: &dtpb.Meta{
+				Tag: []*dtpb.Coding{at},
+			},
+			inTags:   []*dtpb.Coding{bt},
+			wantTags: []*dtpb.Coding{at, bt},
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			inMeta := proto.Clone(testCase.inMeta).(*dtpb.Meta)
+
+			meta.Update(inMeta, meta.IncludeTags(testCase.inTags...))
+
+			if diff := cmp.Diff(inMeta.GetTag(), testCase.wantTags, protocmp.Transform()); diff != "" {
+				t.Errorf("IncludeTags(): (-want, +got)\n%v", diff)
+			}
+		})
+	}
+}
+
+func TestWithMetaProfiles(t *testing.T) {
+	ap := canonical.New("ap")
+	bp := canonical.New("bp")
+	testCases := []struct {
+		name         string
+		inMeta       *dtpb.Meta
+		inProfiles   []*dtpb.Canonical
+		wantProfiles []*dtpb.Canonical
+	}{
+		{
+			name: "Clears Profiles",
+			inMeta: &dtpb.Meta{
+				Profile: []*dtpb.Canonical{ap},
+			},
+			inProfiles:   nil,
+			wantProfiles: nil,
+		},
+		{
+			name: "Replaces Profiles",
+			inMeta: &dtpb.Meta{
+				Profile: []*dtpb.Canonical{ap},
+			},
+			inProfiles:   []*dtpb.Canonical{bp},
+			wantProfiles: []*dtpb.Canonical{bp},
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			inMeta := proto.Clone(testCase.inMeta).(*dtpb.Meta)
+
+			meta.Update(inMeta, meta.WithProfiles(testCase.inProfiles...))
+
+			if diff := cmp.Diff(inMeta.GetProfile(), testCase.wantProfiles, protocmp.Transform()); diff != "" {
+				t.Errorf("WithProfiles(): (-want, +got)\n%v", diff)
+			}
+		})
+	}
+}
+
+func TestIncludeMetaProfiles(t *testing.T) {
+	ap := canonical.New("ap")
+	bp := canonical.New("bp")
+	testCases := []struct {
+		name         string
+		inMeta       *dtpb.Meta
+		inProfiles   []*dtpb.Canonical
+		wantProfiles []*dtpb.Canonical
+	}{
+		{
+			name: "Maintains Profiles",
+			inMeta: &dtpb.Meta{
+				Profile: []*dtpb.Canonical{ap},
+			},
+			inProfiles:   nil,
+			wantProfiles: []*dtpb.Canonical{ap},
+		},
+		{
+			name: "Appends Profiles",
+			inMeta: &dtpb.Meta{
+				Profile: []*dtpb.Canonical{ap},
+			},
+			inProfiles:   []*dtpb.Canonical{bp},
+			wantProfiles: []*dtpb.Canonical{ap, bp},
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			inMeta := proto.Clone(testCase.inMeta).(*dtpb.Meta)
+
+			meta.Update(inMeta, meta.IncludeProfiles(testCase.inProfiles...))
+
+			if diff := cmp.Diff(inMeta.GetProfile(), testCase.wantProfiles, protocmp.Transform()); diff != "" {
+				t.Errorf("IncludeProfiles(): (-want, +got)\n%v", diff)
+			}
+		})
+	}
+}
+
+func TestReplaceMeta(t *testing.T) {
+	patient := &ppb.Patient{Meta: &dtpb.Meta{}}
+	wantMeta := &dtpb.Meta{VersionId: fhir.ID("apple")}
+
+	t.Run("ReplaceMeta", func(t *testing.T) {
+		meta.ReplaceInResource(patient, &dtpb.Meta{VersionId: fhir.ID("apple")})
+		if diff := cmp.Diff(patient.GetMeta(), wantMeta, protocmp.Transform()); diff != "" {
+			t.Errorf("ReplaceMeta(): (-want, +got)\n%v", diff)
+		}
+	})
+}
+
+func TestEnsureMeta(t *testing.T) {
+	patient := &ppb.Patient{}
+	wantMeta := &dtpb.Meta{}
+
+	t.Run("EnsureMeta", func(t *testing.T) {
+		meta.EnsureInResource(patient)
+		if diff := cmp.Diff(patient.GetMeta(), wantMeta, protocmp.Transform()); diff != "" {
+			t.Errorf("EnsureMeta(): (-want, +got)\n%v", diff)
+		}
+	})
+}
+
+func TestWithExtension(t *testing.T) {
+	oldExtension := extension.New("extension-url-old", fhir.String("extension-old"))
+	newExtension := extension.New("extension-url-new", fhir.String("extension-new"))
+	testCases := []struct {
+		name          string
+		inMeta        *dtpb.Meta
+		inExtension   []*dtpb.Extension
+		wantExtension []*dtpb.Extension
+	}{
+		{
+			name: "Clears Extension",
+			inMeta: &dtpb.Meta{
+				Extension: []*dtpb.Extension{oldExtension},
+			},
+			inExtension:   nil,
+			wantExtension: nil,
+		},
+		{
+			name: "Replaces Extension",
+			inMeta: &dtpb.Meta{
+				Extension: []*dtpb.Extension{oldExtension},
+			},
+			inExtension:   []*dtpb.Extension{newExtension},
+			wantExtension: []*dtpb.Extension{newExtension},
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			inMeta := proto.Clone(testCase.inMeta).(*dtpb.Meta)
+			meta.Update(inMeta, meta.WithExtensions(testCase.inExtension...))
+			if diff := cmp.Diff(inMeta.GetExtension(), testCase.wantExtension, protocmp.Transform()); diff != "" {
+				t.Errorf("WithExtensions(): (-want, +got)\n%v", diff)
+			}
+		})
+	}
+}
diff --git a/internal/element/reference/identity.go b/internal/element/reference/identity.go
new file mode 100644
index 0000000..ef4043a
--- /dev/null
+++ b/internal/element/reference/identity.go
@@ -0,0 +1,131 @@
+package reference
+
+import (
+	"errors"
+	"fmt"
+	"regexp"
+	"strings"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/iancoleman/strcase"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+// Source: https://hl7.org/fhir/r4/references.html#literal
+// WATCHOUT: See PHP-9300 about anchored matches.
+var restFHIRServiceBaseURLRegex = regexp.MustCompile(`^(http|https):\/\/([A-Za-z0-9\-\\\.\:\%\$]*\/)+$`)
+var restFHIRServiceResourceURLRegex = regexp.MustCompile(`^((http|https):\/\/([A-Za-z0-9\-\\\.\:\%\$\_]*\/)+)?(Account|ActivityDefinition|AdverseEvent|AllergyIntolerance|Appointment|AppointmentResponse|AuditEvent|Basic|Binary|BiologicallyDerivedProduct|BodyStructure|Bundle|CapabilityStatement|CarePlan|CareTeam|CatalogEntry|ChargeItem|ChargeItemDefinition|Claim|ClaimResponse|ClinicalImpression|CodeSystem|Communication|CommunicationRequest|CompartmentDefinition|Composition|ConceptMap|Condition|Consent|Contract|Coverage|CoverageEligibilityRequest|CoverageEligibilityResponse|DetectedIssue|Device|DeviceDefinition|DeviceMetric|DeviceRequest|DeviceUseStatement|DiagnosticReport|DocumentManifest|DocumentReference|EffectEvidenceSynthesis|Encounter|Endpoint|EnrollmentRequest|EnrollmentResponse|EpisodeOfCare|EventDefinition|Evidence|EvidenceVariable|ExampleScenario|ExplanationOfBenefit|FamilyMemberHistory|Flag|Goal|GraphDefinition|Group|GuidanceResponse|HealthcareService|ImagingStudy|Immunization|ImmunizationEvaluation|ImmunizationRecommendation|ImplementationGuide|InsurancePlan|Invoice|Library|Linkage|List|Location|Measure|MeasureReport|Media|Medication|MedicationAdministration|MedicationDispense|MedicationKnowledge|MedicationRequest|MedicationStatement|MedicinalProduct|MedicinalProductAuthorization|MedicinalProductContraindication|MedicinalProductIndication|MedicinalProductIngredient|MedicinalProductInteraction|MedicinalProductManufactured|MedicinalProductPackaged|MedicinalProductPharmaceutical|MedicinalProductUndesirableEffect|MessageDefinition|MessageHeader|MolecularSequence|NamingSystem|NutritionOrder|Observation|ObservationDefinition|OperationDefinition|OperationOutcome|Organization|OrganizationAffiliation|Patient|PaymentNotice|PaymentReconciliation|Person|PlanDefinition|Practitioner|PractitionerRole|Procedure|Provenance|Questionnaire|QuestionnaireResponse|RelatedPerson|RequestGroup|ResearchDefinition|ResearchElementDefinition|ResearchStudy|ResearchSubject|RiskAssessment|RiskEvidenceSynthesis|Schedule|SearchParameter|ServiceRequest|Slot|Specimen|SpecimenDefinition|StructureDefinition|StructureMap|Subscription|Substance|SubstanceNucleicAcid|SubstancePolymer|SubstanceProtein|SubstanceReferenceInformation|SubstanceSourceMaterial|SubstanceSpecification|SupplyDelivery|SupplyRequest|Task|TerminologyCapabilities|TestReport|TestScript|ValueSet|VerificationResult|VisionPrescription)\/[A-Za-z0-9\-\.]{1,64}(\/_history\/[A-Za-z0-9\-\.]{1,64})?$`)
+
+var (
+	ErrInvalidAbsoluteURL           = errors.New("invalid absolute uri")
+	ErrInvalidRelativeURI           = errors.New("invalid relative uri")
+	ErrInvalidURL                   = errors.New("invalid url")
+	ErrInvalidURI                   = errors.New("invalid reference uri")
+	ErrFragmentMissingType          = errors.New("fragment reference missing type")
+	ErrReferenceOneOfResourceNotSet = errors.New("reference.oneof_resource was not set")
+)
+
+// oneofReferenceDescriptor returns the FieldDescriptor for the oneof option that has been set
+// within the Reference. An error is returned if no option is set.
+func oneofReferenceDescriptor(x *dtpb.Reference) (protoreflect.FieldDescriptor, error) {
+	msg := x.ProtoReflect()
+	oneofDescriptor := msg.Descriptor().Oneofs().ByName("reference")
+	fd := msg.WhichOneof(oneofDescriptor)
+	if fd == nil {
+		return nil, ErrReferenceOneOfResourceNotSet
+	}
+	return fd, nil
+}
+
+// IdentityOf returns a complete Identity (Type and ID always set,
+// VersionID set if applicable) representing the given reference.
+func IdentityOf(ref *dtpb.Reference) (*resource.Identity, error) {
+	// Fragment
+	if ref.GetFragment() != nil {
+		if refType := ref.GetType(); refType != nil {
+			return resource.NewIdentity(refType.GetValue(), ref.GetFragment().GetValue(), "")
+		}
+		return nil, ErrFragmentMissingType
+	}
+
+	// Absolute and Relative URIs
+	if uri := ref.GetUri(); uri != nil {
+		return IdentityFromURL(uri.GetValue())
+	}
+	return identityOfStrong(ref)
+}
+
+func identityOfStrong(ref *dtpb.Reference) (*resource.Identity, error) {
+	fd, err := oneofReferenceDescriptor(ref)
+	if err != nil {
+		return nil, err
+	}
+
+	// All "reference" fields are named "[resource_type]_id" in the FHIR protos.
+	// If we chop off "_id" and convert to camel-case, it's the resource type
+	// name.
+	resType := string(fd.Name())
+	resType, _ = strings.CutSuffix(resType, "_id")
+	resType = strcase.ToCamel(resType)
+
+	m := ref.ProtoReflect().Get(fd).Message().Interface()
+	refID, ok := m.(*dtpb.ReferenceId)
+	if !ok {
+		return nil, fmt.Errorf("unable to extract refID")
+	}
+	identID := refID.GetValue()
+	identVersion := refID.GetHistory().GetValue()
+
+	return resource.NewIdentity(resType, identID, identVersion)
+}
+
+// IdentityFromURL returns a complete Identity (Type and ID always
+// set, VersionID set if applicable) representing the resource at the given
+// relative or absolute URL, which may optionally include a _history component.
+func IdentityFromURL(url string) (*resource.Identity, error) {
+	lit, err := LiteralInfoFromURI(url)
+	if err != nil {
+		return nil, err
+	}
+	if lit.identity == nil {
+		return nil, ErrInvalidURL
+	}
+	return lit.identity, nil
+}
+
+// IdentityFromAbsoluteURL returns a complete Identity (Type and ID always
+// set, VersionID set if applicable) representing the resource at the given
+// absolute URL, which may optionally include a _history component. It
+// validates the url against FHIR-provided regex for FHIR REST servers.
+// Example absolute: "https://healthcare.googleapis.com/v1/projects/${project}/locations/${location}/datasets/${dataset}/fhirStores/${fhirStore}/fhir/Patient/123/_history/abc"
+// Example relative: "Patient/123/_history/abc"
+func IdentityFromAbsoluteURL(url string) (*resource.Identity, error) {
+	lit, err := LiteralInfoFromURI(url)
+	if err != nil {
+		return nil, err
+	}
+	if lit.identity == nil || lit.serviceBaseURL == "" {
+		return nil, ErrInvalidAbsoluteURL
+	}
+	return lit.identity, nil
+}
+
+// IdentityFromRelativeURI returns a complete Identity (Type and ID always
+// set, VersionID set if applicable) representing the resource at the given
+// relative URI, which may optionally include a _history component.
+func IdentityFromRelativeURI(uri string) (*resource.Identity, error) {
+	uriParts := strings.Split(uri, "/")
+	switch len(uriParts) {
+	case 2:
+		// e.g. Patient/123
+		return resource.NewIdentity(uriParts[0], uriParts[1], "")
+	case 4:
+		// e.g. Patient/123/_history/abc
+		if uriParts[2] != "_history" {
+			break
+		}
+		return resource.NewIdentity(uriParts[0], uriParts[1], uriParts[3])
+	}
+	return nil, fmt.Errorf("%w: %s", ErrInvalidRelativeURI, uri)
+}
diff --git a/internal/element/reference/identity_test.go b/internal/element/reference/identity_test.go
new file mode 100644
index 0000000..778ad85
--- /dev/null
+++ b/internal/element/reference/identity_test.go
@@ -0,0 +1,306 @@
+package reference_test
+
+import (
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/verily-src/fhirpath-go/internal/element/reference"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+)
+
+func newIdentity(t *testing.T, typeName, id, version string) *resource.Identity {
+	t.Helper()
+	ident, err := resource.NewIdentity(typeName, id, version)
+	if err != nil {
+		t.Fatalf("NewIdentity: %v", err)
+	}
+	return ident
+}
+
+func TestIdentityOf_BadReference_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name      string
+		reference *dtpb.Reference
+		wantErr   error
+	}{
+		{
+			"invalid absolute uri",
+			&dtpb.Reference{
+				Reference: &dtpb.Reference_Uri{
+					Uri: &dtpb.String{
+						Value: "https://example.com",
+					},
+				},
+			},
+			reference.ErrInvalidURI,
+		},
+		{
+			"fragment without type",
+			&dtpb.Reference{
+				Reference: &dtpb.Reference_Fragment{
+					Fragment: &dtpb.String{
+						Value: "https://example.com",
+					},
+				},
+			},
+			reference.ErrFragmentMissingType,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := reference.IdentityOf(tc.reference)
+
+			got, want := err, tc.wantErr
+			if !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+				t.Errorf("IdentityOf(%s) error got '%v', want '%v'", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestIdentityOf(t *testing.T) {
+	testCases := []struct {
+		name      string
+		reference *dtpb.Reference
+		want      *resource.Identity
+	}{
+		{
+			"Reference",
+			&dtpb.Reference{
+				Reference: &dtpb.Reference_AccountId{
+					AccountId: &dtpb.ReferenceId{Value: "123"},
+				},
+			},
+			newIdentity(t, "Account", "123", ""),
+		},
+		{
+			"Reference with history",
+			&dtpb.Reference{
+				Reference: &dtpb.Reference_PatientId{
+					PatientId: &dtpb.ReferenceId{Value: "123", History: &dtpb.Id{Value: "abc"}},
+				},
+			},
+			newIdentity(t, "Patient", "123", "abc"),
+		},
+		{
+			"Relative uri reference",
+			&dtpb.Reference{
+				Reference: &dtpb.Reference_Uri{
+					Uri: &dtpb.String{
+						Value: "Patient/123",
+					},
+				},
+			},
+			newIdentity(t, "Patient", "123", ""),
+		},
+		{
+			"Relative uri reference with history",
+			&dtpb.Reference{
+				Reference: &dtpb.Reference_Uri{
+					Uri: &dtpb.String{
+						Value: "Patient/123/_history/abc",
+					},
+				},
+			},
+			newIdentity(t, "Patient", "123", "abc"),
+		},
+		{
+			"Absolute uri reference",
+			&dtpb.Reference{
+				Reference: &dtpb.Reference_Uri{
+					Uri: &dtpb.String{
+						Value: "https://healthcare.googleapis.com/v1/projects/123/locations/abc/datasets/def/fhirStores/ghi/fhir/Patient/123",
+					},
+				},
+			},
+			newIdentity(t, "Patient", "123", ""),
+		},
+		{
+			"Absolute uri reference with history",
+			&dtpb.Reference{
+				Reference: &dtpb.Reference_Uri{
+					Uri: &dtpb.String{
+						Value: "https://healthcare.googleapis.com/v1/projects/123/locations/abc/datasets/def/fhirStores/ghi/fhir/Patient/123/_history/abc",
+					},
+				},
+			},
+			newIdentity(t, "Patient", "123", "abc"),
+		},
+		{
+			"Fragment reference",
+			&dtpb.Reference{
+				Reference: &dtpb.Reference_Fragment{
+					Fragment: &dtpb.String{
+						Value: "123",
+					},
+				},
+				Type: &dtpb.Uri{
+					Value: "Patient",
+				},
+			},
+			newIdentity(t, "Patient", "123", ""),
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ident, err := reference.IdentityOf(tc.reference)
+
+			if err != nil {
+				t.Fatalf("IdentityOf(%s) error got %v, want nil", tc.name, err)
+			}
+			if got, want := ident, tc.want; !got.Equal(want) {
+				t.Errorf("IdentityOf(%s) got %s, want %s", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestIdentityFromAbsoluteURL_BadInput_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name    string
+		url     string
+		wantErr error
+	}{
+		{
+			"url with fragment",
+			"https://healthcare.googleapis.com/v1/projects/123/locations/abc/datasets/def/fhirStores/ghi/fhir/Patient/123#crID",
+			reference.ErrInvalidURI,
+		},
+		{
+			"canonical url",
+			"https://healthcare.googleapis.com/v1/projects/123/locations/abc/datasets/def/fhirStores/ghi/fhir/Patient/123|1",
+			reference.ErrInvalidURI,
+		},
+		{
+			"invalid server url",
+			"https://not/a/fhir/server/url",
+			cmpopts.AnyError,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := reference.IdentityFromAbsoluteURL(tc.url)
+
+			got, want := err, tc.wantErr
+			if !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+				t.Errorf("IdentityFromAbsoluteURL(%s) error got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestIdentityFromAbsoluteURL(t *testing.T) {
+	testCases := []struct {
+		name string
+		url  string
+		want *resource.Identity
+	}{
+		{
+			"no version",
+			"https://healthcare.googleapis.com/v1/projects/123/locations/abc/datasets/def/fhirStores/ghi/fhir/Patient/123",
+			newIdentity(t, "Patient", "123", ""),
+		},
+		{
+			"versioned",
+			"https://healthcare.googleapis.com/v1/projects/123/locations/abc/datasets/def/fhirStores/ghi/fhir/Patient/123/_history/abc",
+			newIdentity(t, "Patient", "123", "abc"),
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ident, err := reference.IdentityFromAbsoluteURL(tc.url)
+
+			if err != nil {
+				t.Fatalf("IdentityFromAbsoluteURL(%s) error got %v, want nil", tc.name, err)
+			}
+			if got, want := ident, tc.want; !cmp.Equal(got, want) {
+				t.Errorf("IdentityFromAbsoluteURL(%s) got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestIdentityFromRelativeURI_BadURL_ReturnsError(t *testing.T) {
+	_, err := reference.IdentityFromRelativeURI("Patient/123/_history")
+
+	if got, want := err, reference.ErrInvalidRelativeURI; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+		t.Fatalf("IdentityFromRelativeURI error got %v, want %v", got, want)
+	}
+}
+
+func TestIdentityFromRelativeURI(t *testing.T) {
+	testCases := []struct {
+		name string
+		url  string
+		want *resource.Identity
+	}{
+		{
+			"no version",
+			"Patient/123",
+			newIdentity(t, "Patient", "123", ""),
+		},
+		{
+			"versioned",
+			"Patient/123/_history/abc",
+			newIdentity(t, "Patient", "123", "abc"),
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ident, err := reference.IdentityFromRelativeURI(tc.url)
+
+			if err != nil {
+				t.Fatalf("IdentityFromRelativeURI(%s) error got %v, want nil", tc.name, err)
+			}
+			if got, want := ident, tc.want; !cmp.Equal(got, want) {
+				t.Errorf("IdentityFromRelativeURI(%s) got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestIdentityFromURL_BadURL_ReturnsError(t *testing.T) {
+	_, err := reference.IdentityFromURL("Patient/123/_history")
+
+	if got, want := err, reference.ErrInvalidURI; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+		t.Fatalf("IdentityFromURL error got %v, want %v", got, want)
+	}
+}
+
+func TestIdentityFromURL(t *testing.T) {
+	testCases := []struct {
+		name string
+		url  string
+		want *resource.Identity
+	}{
+		{
+			"absolute url",
+			"https://healthcare.googleapis.com/v1/projects/123/locations/abc/datasets/def/fhirStores/ghi/fhir/Patient/123",
+			newIdentity(t, "Patient", "123", ""),
+		},
+		{
+			"relative uri",
+			"Patient/123/_history/abc",
+			newIdentity(t, "Patient", "123", "abc"),
+		},
+		{
+			"relative uri using RequestGroup resource type",
+			"RequestGroup/123/_history/abc",
+			newIdentity(t, "RequestGroup", "123", "abc"),
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ident, err := reference.IdentityFromURL(tc.url)
+
+			if err != nil {
+				t.Fatalf("IdentityFromURL(%s) error got %v, want nil", tc.name, err)
+			}
+			if got, want := ident, tc.want; !cmp.Equal(got, want) {
+				t.Errorf("IdentityFromURL(%s) got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
diff --git a/internal/element/reference/literal.go b/internal/element/reference/literal.go
new file mode 100644
index 0000000..b9980bd
--- /dev/null
+++ b/internal/element/reference/literal.go
@@ -0,0 +1,346 @@
+package reference
+
+import (
+	"errors"
+	"fmt"
+	"net/url"
+	"strings"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+)
+
+var (
+	ErrNotLiteral              error = errors.New("Reference is not a literal")
+	ErrTypeInvalid             error = errors.New("Reference.type is invalid")
+	ErrTypeInconsistent        error = errors.New("Reference.reference and Reference.type are inconsistent")
+	ErrWeakInvalid             error = errors.New("Reference.reference is invalid weak URI")
+	ErrStrongInvalid           error = errors.New("Reference.reference is invalid strong URI")
+	ErrExplicitFragmentInvalid error = errors.New("Reference.reference is invalid explicit fragment")
+	ErrServiceBaseURLInvalid   error = errors.New("ServiceBaseURL is invalid")
+)
+
+// A LiteralInfo is the result of parsing a FHIR Reference when that reference
+// contains a literal reference. It is immutable.
+//
+// LiteralInfo supports all valid forms a literal reference:
+//   - REST URI: absolute and relative, versioned and unversioned,
+//     strong and weak.
+//   - FragmentID: a reference to a contained resource within an implied
+//     containing resource.
+//   - non-REST URI: URNs (eg., UUID and OID) and versionless canonicals.
+//     These occur most commonly in request bundles (vs response bundles).
+//
+// A key goal of LiteralInfo is to canonicalize between the "weak" and "strong"
+// representations of a literal reference. Note that this distinction is
+// specific to the GCP proto mapping of FHIR and does not appear in the standard.
+//
+// The fields fragmentID, identity, and nonRESTURI are mutually exclusive: exactly
+// one will be non-nil.
+//
+// See FHIR spec: http://hl7.org/fhir/R4/references.html#literal.
+type LiteralInfo struct {
+	// If non-nil, the type of the referenced resource.
+	// If non-nil and identity also present, both guaranteed to have the same type.
+	resType *resource.Type
+
+	// If non-nil, the fragment ID that is relative to an implied containing resource.
+	// A fragment ID may be an empty string, which indicates the implied
+	// containing resource itself (as opposed to a contained resource within
+	// the containing resource).
+	fragmentID *string
+
+	// The identity (type, ID and optional version) of the referenced resource.
+	// Applies to REST URLs
+	identity *resource.Identity
+
+	// The service base URL of the FHIR store that holds the referenced resource.
+	// Only non-empty if identity is non-nil and the reference is absolute.
+	// Generally references to resources in the same store (as the reference itself)
+	// are relative, and the service base URL will be empty.
+	// See http://hl7.org/fhir/R4/http.html#general
+	serviceBaseURL string
+
+	// If non-nil, the non-REST URI of the referenced resource.
+	// Typically an URN (UUID or OID). See NonRESTURI() accessor for details.
+	nonRESTURI *string
+}
+
+// clone returns a shallow copy.
+//
+// All fields have immutable types, so a shallow copy is safe.
+// LiteralInfo itself is immutable; thus, clone() is only used internally
+// by the With*() mutators.
+func (lit *LiteralInfo) clone() *LiteralInfo {
+	return &LiteralInfo{
+		resType:        lit.resType,
+		fragmentID:     lit.fragmentID,
+		identity:       lit.identity,
+		serviceBaseURL: lit.serviceBaseURL,
+		nonRESTURI:     lit.nonRESTURI,
+	}
+}
+
+// Type returns the resource type of the reference resource, if known.
+func (lit *LiteralInfo) Type() (resource.Type, bool) {
+	if lit.resType == nil {
+		var emptyType resource.Type
+		return emptyType, false
+	}
+	return *lit.resType, true
+}
+
+// FragmentID returns the fragment ID of the reference fragment,
+// if the literal reference is to fragment.
+func (lit *LiteralInfo) FragmentID() (string, bool) {
+	if lit.fragmentID == nil {
+		return "", false
+	}
+	return *lit.fragmentID, true
+}
+
+// Identity returns the identity of the reference resource,
+// if the referenced resource has REST identity.
+func (lit *LiteralInfo) Identity() (*resource.Identity, bool) {
+	return lit.identity, lit.identity != nil
+}
+
+// Identity returns the identity of the reference resource.
+// Only non-empty if the reference resource has a REST identity,
+// and that REST identity was absolute (not relative).
+func (lit *LiteralInfo) ServiceBaseURL() string {
+	return lit.serviceBaseURL
+}
+
+// WithServiceBaseURL returns a LiteralInfo with the given serviceBaseURL.
+// The given serviceBaseURL must be empty or a valid serviceBaseURL.
+func (lit *LiteralInfo) WithServiceBaseURL(serviceBaseURL string) (*LiteralInfo, error) {
+	// WATCHOUT: Our regex wants a trailing slash, but our convention is to omit it.
+	// We add the slash here rather than change the regex to keep traceability to the spec.
+	if serviceBaseURL != "" && !restFHIRServiceBaseURLRegex.MatchString(serviceBaseURL+"/") {
+		return nil, fmt.Errorf("%w: '%s'", ErrServiceBaseURLInvalid, serviceBaseURL)
+	}
+	if serviceBaseURL == lit.serviceBaseURL {
+		return lit, nil
+	}
+	newLit := lit.clone()
+	newLit.serviceBaseURL = serviceBaseURL
+	return newLit, nil
+}
+
+// NonRESTURI returns the non-REST URI of the referenced resource,
+// if the referenced resource has a non-REST URI. A non-REST URI
+// is typically an URN (UUID or OID) or a versionless canonical URL.
+// Th URN form is typically only used in bundle transaction requests
+// when the REST URI is not yet known.
+//
+// See http://hl7.org/fhir/R4/references.html#literal, and specifically:
+//   - "... in a bundle during a transaction, reference URLs may actually
+//     contain logical URIs (e.g. OIDs or UUIDs) that resolve within the
+//     transaction."
+//   - "The URL may contain a reference to a canonical URL and applications can
+//     use the canonical URL resolution methods they support ..."
+func (lit *LiteralInfo) NonRESTURI() (string, bool) {
+	if lit.nonRESTURI == nil {
+		return "", false
+	}
+	return *lit.nonRESTURI, true
+}
+
+// URI returns the LiteralInfo's URI equivalent as a *dtpb.Uri.
+func (lit *LiteralInfo) URI() *dtpb.Uri {
+	s := lit.URIString()
+	if s == "" {
+		return nil
+	}
+	return fhir.URI(s)
+}
+
+// URIString returns the LiteralInfo's URI equivalent as a string.
+// The returned URI is suitable for use in Reference.reference.
+// This function is the inverse of LiteralInfoFromURI().
+func (lit *LiteralInfo) URIString() string {
+	if lit == nil {
+		return ""
+	}
+	if lit.fragmentID != nil {
+		return "#" + *lit.fragmentID
+	}
+	if lit.identity != nil {
+		uri := lit.identity.PreferRelativeVersionedURIString()
+		// Could use net/url.JoinPath but it does validation on serviceBaseURL
+		// that we don't want.
+		if lit.serviceBaseURL != "" {
+			uri = lit.serviceBaseURL + "/" + uri
+		}
+		return uri
+	}
+	if lit.nonRESTURI != nil {
+		return *lit.nonRESTURI
+	}
+	// UNREACHED
+	return ""
+}
+
+// PreferRelativeVersionedURIString returns the relative URI with version if
+// available, otherwise just relative URI without version.
+func (lit *LiteralInfo) PreferRelativeVersionedURIString() string {
+	identity, ok := lit.Identity()
+	if !ok {
+		return ""
+	}
+	return identity.PreferRelativeVersionedURIString()
+}
+
+// LiteralInfoOf parses the given literal reference.
+//
+// Returns an error if the given reference is not a valid literal reference.
+func LiteralInfoOf(ref *dtpb.Reference) (*LiteralInfo, error) {
+	var explicitType *resource.Type
+	if resTypeStr := ref.GetType(); resTypeStr != nil {
+		t, err := resource.NewType(resTypeStr.GetValue())
+		if err != nil {
+			// Returned err includes the bad type, so no need to duplicate here.
+			return nil, fmt.Errorf("%w: %v", ErrTypeInvalid, err)
+		}
+		explicitType = &t
+	}
+
+	// Fragment
+	if frag := ref.GetFragment(); frag != nil {
+		fragStr := frag.GetValue()
+		// WATCHOUT: an empty fragStr is a valid fragID but not a valid ID.
+		if fragStr != "" && !fhir.IsID(fragStr) {
+			return nil, fmt.Errorf("%w: invalid fragment ID", ErrExplicitFragmentInvalid)
+		}
+		return &LiteralInfo{
+			resType:    explicitType,
+			fragmentID: &fragStr,
+		}, nil
+	}
+
+	// An absolute or relative weak URI, a weak fragment URI, or a non-REST URI.
+	if uri := ref.GetUri(); uri != nil {
+		litUri, err := LiteralInfoFromURI(uri.GetValue())
+		if err != nil {
+			return nil, fmt.Errorf("%w: uri='%v': %v", ErrWeakInvalid, uri.GetValue(), err)
+		}
+		if litUri.resType == nil {
+			// This will occur for a fragment or URN.
+			litUri.resType = explicitType
+		} else {
+			if explicitType != nil && *litUri.resType != *explicitType {
+				return nil, fmt.Errorf("%w: weak='%v' vs type='%v'",
+					ErrTypeInconsistent, *litUri.resType, explicitType)
+			}
+		}
+		return litUri, nil
+	}
+
+	// Strong relative URIs
+	identity, err := identityOfStrong(ref)
+	if err != nil {
+		if errors.Is(err, ErrReferenceOneOfResourceNotSet) {
+			return nil, ErrNotLiteral
+		}
+		return nil, fmt.Errorf("%w: %v", ErrStrongInvalid, err)
+	}
+	strongType := identity.Type()
+	if explicitType != nil && *explicitType != strongType {
+		return nil, fmt.Errorf("%w: strong='%v' vs type='%v'",
+			ErrTypeInconsistent, strongType, explicitType)
+	}
+	return &LiteralInfo{
+		resType:  &strongType,
+		identity: identity,
+	}, nil
+}
+
+// LiteralInfoFromURI parses a Reference.reference URI string,
+// where Reference is the FHIR element. Within the GCP proto mapping
+// (where Reference.reference is a oneof), this function parses
+// the Reference.uri member of that oneof.
+//
+// Parsing a Reference URI is easier than parsing general resource URL:
+//   - We don't need to worry about canonical URLs. (AFIAK). Canonical URLs
+//     are there own datatype, not a Reference element.
+//   - Fragments within Reference URI are always relative to the containing
+//     resource. That is, Reference.reference cannot specify a containing
+//     resource (relative or absolute) reference that is suffixed by a fragment.
+//     (Canonical references do allow this).
+//
+// This supports the special case of an fragment without an ID. This is used
+// by a contained resource to reference its containing resource.
+//
+// Any returned error does NOT include the uri. That is left to the caller.
+func LiteralInfoFromURI(uri string) (*LiteralInfo, error) {
+	if uri[0] == '#' {
+		// Note that "#" alone is a valid fragment.
+		fragStr := uri[1:]
+		if fragStr != "" && !fhir.IsID(fragStr) {
+			return nil, fmt.Errorf("%w: invalid fragment id", ErrInvalidURI)
+		}
+		return &LiteralInfo{fragmentID: &fragStr}, nil
+	}
+	if strings.Contains(uri, "#") {
+		return nil, fmt.Errorf("%w: non-relative fragment found", ErrInvalidURI)
+	}
+	if strings.Contains(uri, "|") {
+		return nil, fmt.Errorf("%w: canonical version found", ErrInvalidURI)
+	}
+
+	// This regexp matches both relative and absolute REST URIs.
+	uriIndexes := restFHIRServiceResourceURLRegex.FindStringSubmatchIndex(uri)
+
+	if uriIndexes == nil {
+		// Try as a non-REST URI (typically an URN or a versionless canonical).
+		parsedUrl, err := url.Parse(uri)
+		if err != nil {
+			return nil, fmt.Errorf("%w: unparsable: %v", ErrInvalidURI, err)
+		}
+		if parsedUrl.Scheme == "" {
+			return nil, fmt.Errorf("%w: non-REST and missing scheme component", ErrInvalidURI)
+		}
+		if parsedUrl.Opaque != "" || strings.TrimLeft(parsedUrl.Path, "/") != "" {
+			// An URN is parsed as Opaque and a canonical is parsed as a Path.
+			return &LiteralInfo{
+				nonRESTURI: &uri,
+			}, nil
+		}
+		return nil, fmt.Errorf("%w: non-REST and missing both Opaque and Path", ErrInvalidURI)
+	}
+
+	// The relative portion of the URI is the 4th submatch in the regexp.
+	relStartIdx := uriIndexes[8]
+	baseUrl := strings.TrimRight(uri[0:relStartIdx], "/")
+	relUrl := uri[relStartIdx:]
+
+	// The REST regexp could be used to identify all the parts of the relative URI,
+	// but easier just to split.
+	relParts := strings.Split(relUrl, "/")
+	var identity *resource.Identity
+	if !resource.IsType(relParts[0]) {
+		return nil, fmt.Errorf("%w: resource type is invalid", ErrInvalidURI)
+	}
+	if !fhir.IsID(relParts[1]) {
+		return nil, fmt.Errorf("%w: resource id is invalid", ErrInvalidURI)
+	}
+	if len(relParts) == 2 {
+		identity, _ = resource.NewIdentity(relParts[0], relParts[1], "")
+	} else if len(relParts) == 4 && relParts[2] == "_history" {
+		if !fhir.IsID(relParts[3]) {
+			return nil, fmt.Errorf("%w: version id is invalid", ErrInvalidURI)
+		}
+		identity, _ = resource.NewIdentity(relParts[0], relParts[1], relParts[3])
+	} else {
+		return nil, fmt.Errorf("%w: invalid relative component", ErrInvalidURI)
+	}
+
+	identityType := identity.Type()
+	return &LiteralInfo{
+		resType:        &identityType,
+		identity:       identity,
+		serviceBaseURL: baseUrl,
+	}, nil
+}
diff --git a/internal/element/reference/literal_test.go b/internal/element/reference/literal_test.go
new file mode 100644
index 0000000..d5cd712
--- /dev/null
+++ b/internal/element/reference/literal_test.go
@@ -0,0 +1,361 @@
+package reference
+
+import (
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+)
+
+var patientType, _ = resource.NewType("Patient")
+
+func TestLiteralInfoGetters_WhenEmpty(t *testing.T) {
+	// WATCHOUT: This empty literal isn't realistic.
+	lit := &LiteralInfo{}
+	_, ok := lit.Type()
+	if ok {
+		t.Errorf("Expected Type not ok")
+	}
+	_, ok = lit.FragmentID()
+	if ok {
+		t.Errorf("Expected FragmentID not ok")
+	}
+	_, ok = lit.Identity()
+	if ok {
+		t.Errorf("Expected Identity not ok")
+	}
+	url := lit.ServiceBaseURL()
+	if url != "" {
+		t.Errorf("Expected ServiceBaseURL empty")
+	}
+	_, ok = lit.NonRESTURI()
+	if ok {
+		t.Errorf("Expected NonRESTURI not ok")
+	}
+}
+
+func TestLiteralInfoGetters_WhenFull(t *testing.T) {
+	wantFrag := "frag1"
+	wantIdent := mustNewIdentity("Patient", "123", "")
+	wantBaseURL := "https://my.site/fhir"
+	wantNonRESTURI := "nonrest"
+	// WATCHOUT: This full literal isn't realistic.
+	lit := &LiteralInfo{resType: &patientType, fragmentID: &wantFrag,
+		identity: wantIdent, serviceBaseURL: wantBaseURL,
+		nonRESTURI: &wantNonRESTURI}
+
+	gotType, ok := lit.Type()
+	if !ok {
+		t.Errorf("Expected Type ok")
+	}
+	if gotType != patientType {
+		t.Errorf("Expected Patient type: %s", gotType)
+	}
+
+	gotFrag, ok := lit.FragmentID()
+	if !ok {
+		t.Errorf("Expected FragmentID ok")
+	}
+	if gotFrag != wantFrag {
+		t.Errorf("FragmentID(): got %s, want %s", gotFrag, wantFrag)
+	}
+
+	gotIdent, ok := lit.Identity()
+	if !ok {
+		t.Errorf("Expected Identity ok")
+	}
+	if gotIdent != wantIdent {
+		t.Errorf("Identity(): got %s, want %s", gotIdent, wantIdent)
+	}
+
+	gotBaseURL := lit.ServiceBaseURL()
+	if gotBaseURL != wantBaseURL {
+		t.Errorf("Wrong ServiceBaseURL got %s, want %s", gotBaseURL, wantBaseURL)
+	}
+
+	gotNonRESTURI, ok := lit.NonRESTURI()
+	if !ok {
+		t.Errorf("Expected NonRESTURI ok")
+	}
+	if gotNonRESTURI != wantNonRESTURI {
+		t.Errorf("Wrong NonRESTURI got %s, want %s", gotNonRESTURI, wantNonRESTURI)
+	}
+}
+
+func TestLiteralInfoSetters_WithServiceBaseURL(t *testing.T) {
+	baseUrl := "http://fhir.my.com/scope/teststore"
+
+	testCases := []struct {
+		name       string
+		startLit   *LiteralInfo
+		setBaseUrl string
+		wantLit    *LiteralInfo
+		wantErr    error
+	}{
+		{
+			name:       "clear serviceBaseURL",
+			startLit:   &LiteralInfo{serviceBaseURL: baseUrl},
+			setBaseUrl: "",
+			wantLit:    &LiteralInfo{serviceBaseURL: ""},
+		},
+		{
+			name:       "set serviceBaseURL",
+			startLit:   &LiteralInfo{},
+			setBaseUrl: baseUrl,
+			wantLit:    &LiteralInfo{serviceBaseURL: baseUrl},
+		},
+		{
+			name:       "bad serviceBaseURL",
+			startLit:   &LiteralInfo{},
+			setBaseUrl: "this is not valid URL",
+			wantErr:    ErrServiceBaseURLInvalid,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			gotLit, gotErr := tc.startLit.WithServiceBaseURL(tc.setBaseUrl)
+			if !cmp.Equal(gotErr, tc.wantErr, cmpopts.EquateErrors()) {
+				t.Fatalf("SetServiceBaseURL(%s) error mismatch: got '%v', want '%v'",
+					tc.name, gotErr, tc.wantErr)
+			}
+			if diff := cmp.Diff(gotLit, tc.wantLit, cmp.AllowUnexported(LiteralInfo{})); diff != "" {
+				t.Errorf("SetServiceBaseURL(%s) literal (-got, +want)\n%s\n", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestLiteralInfo_PreferRelativeVersionedURIString(t *testing.T) {
+	baseUrl := "http://fhir.my.com/scope/teststore"
+
+	testCases := []struct {
+		name            string
+		startLit        *LiteralInfo
+		wantRelativeURI string
+	}{
+		{
+			name:            "Get relativeURI with version",
+			startLit:        &LiteralInfo{serviceBaseURL: baseUrl, identity: mustNewIdentity("Patient", "1234", "abc")},
+			wantRelativeURI: "Patient/1234/_history/abc",
+		},
+		{
+			name:            "Get relativeURI without version",
+			startLit:        &LiteralInfo{serviceBaseURL: baseUrl, identity: mustNewIdentity("Patient", "1234", "")},
+			wantRelativeURI: "Patient/1234",
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			gotRelativeURI := tc.startLit.PreferRelativeVersionedURIString()
+			if diff := cmp.Diff(gotRelativeURI, tc.wantRelativeURI); diff != "" {
+				t.Errorf("relativeURI(%s) of literal (-got, +want)\n%s\n", tc.name, diff)
+			}
+		})
+	}
+}
+
+// Tests converting a Reference -> Literal -> URI.
+// This isn't strictly a round trip, but close enough.
+func TestLiteralInfo_Reference_RoundTrip(t *testing.T) {
+	strongRef, _ := Typed("Patient", "123")
+	strongVersionedRef := &dtpb.Reference{
+		Type: fhir.URI("Patient"),
+		Reference: &dtpb.Reference_PatientId{
+			PatientId: &dtpb.ReferenceId{Value: "123", History: fhir.ID("abc")},
+		},
+	}
+	inconsistentStrongRef, _ := Typed("Patient", "123")
+	inconsistentStrongRef.Type = fhir.URI("Person")
+	testCases := []struct {
+		name     string
+		inputRef *dtpb.Reference
+		wantLit  *LiteralInfo // vs result of calling LiteralInfoOf(inputRef)
+		wantErr  error
+		wantUri  string // vs result of calling gotLit.URI()
+	}{
+		// equivalent weak and strong relative unversioned
+		{
+			name:     "weak basic",
+			inputRef: Weak("Patient", "Patient/123"),
+			wantLit:  &LiteralInfo{resType: &patientType, identity: mustNewIdentity("Patient", "123", "")},
+			wantUri:  "Patient/123",
+		},
+		{
+			name:     "weak basic no type",
+			inputRef: &dtpb.Reference{Reference: &dtpb.Reference_Uri{Uri: fhir.String("Patient/123")}},
+			wantLit:  &LiteralInfo{resType: &patientType, identity: mustNewIdentity("Patient", "123", "")},
+			wantUri:  "Patient/123",
+		},
+		{
+			name:     "strong basic",
+			inputRef: strongRef,
+			wantLit:  &LiteralInfo{resType: &patientType, identity: mustNewIdentity("Patient", "123", "")},
+			wantUri:  "Patient/123",
+		},
+
+		// equivalent weak and strong relative versioned
+		{
+			name:     "weak versioned",
+			inputRef: Weak("Patient", "Patient/123/_history/abc"),
+			wantLit:  &LiteralInfo{resType: &patientType, identity: mustNewIdentity("Patient", "123", "abc")},
+			wantUri:  "Patient/123/_history/abc",
+		},
+		{
+			name:     "strong versioned",
+			inputRef: strongVersionedRef,
+			wantLit:  &LiteralInfo{resType: &patientType, identity: mustNewIdentity("Patient", "123", "abc")},
+			wantUri:  "Patient/123/_history/abc",
+		},
+
+		// equivalent good uri (weak-like) and explicit fragments
+		{
+			name:     "uri fragment",
+			inputRef: &dtpb.Reference{Type: fhir.URI("Patient"), Reference: &dtpb.Reference_Uri{Uri: fhir.String("#hello")}},
+			wantLit:  &LiteralInfo{resType: &patientType, fragmentID: ptrString("hello")},
+			wantUri:  "#hello",
+		},
+		{
+			name: "explicit fragment",
+			inputRef: &dtpb.Reference{Type: fhir.URI("Patient"),
+				Reference: &dtpb.Reference_Fragment{Fragment: fhir.String("hello")}},
+			wantLit: &LiteralInfo{resType: &patientType, fragmentID: ptrString("hello")},
+			wantUri: "#hello",
+		},
+
+		// various valid edge cases of fragments
+		{
+			name:     "empty uri fragment",
+			inputRef: &dtpb.Reference{Type: fhir.URI("Patient"), Reference: &dtpb.Reference_Uri{Uri: fhir.String("#")}},
+			wantLit:  &LiteralInfo{resType: &patientType, fragmentID: ptrString("")},
+			wantUri:  "#",
+		},
+		{
+			name: "empty explicit fragment",
+			inputRef: &dtpb.Reference{Type: fhir.URI("Patient"),
+				Reference: &dtpb.Reference_Fragment{Fragment: fhir.String("")}},
+			wantLit: &LiteralInfo{resType: &patientType, fragmentID: ptrString("")},
+			wantUri: "#",
+		},
+		{
+			name:     "uri fragment no type",
+			inputRef: &dtpb.Reference{Reference: &dtpb.Reference_Uri{Uri: fhir.String("#hello")}},
+			wantLit:  &LiteralInfo{fragmentID: ptrString("hello")},
+			wantUri:  "#hello",
+		},
+
+		// equivalent invalid uri and explicit fragments
+		{
+			name:     "bad uri fragment",
+			inputRef: &dtpb.Reference{Type: fhir.URI("Patient"), Reference: &dtpb.Reference_Uri{Uri: fhir.String("#@@@@")}},
+			wantErr:  ErrWeakInvalid,
+		},
+		{
+			name: "bad explicit fragment",
+			inputRef: &dtpb.Reference{Type: fhir.URI("Patient"),
+				Reference: &dtpb.Reference_Fragment{Fragment: fhir.String("@@@@")}},
+			wantErr: ErrExplicitFragmentInvalid,
+		},
+
+		{"nil ref", nil, nil, ErrNotLiteral, ""},
+		{"empty", &dtpb.Reference{}, nil, ErrNotLiteral, ""},
+		{"type only", &dtpb.Reference{Type: fhir.URI("Patient")}, nil, ErrNotLiteral, ""},
+		{"bad type", &dtpb.Reference{Type: fhir.URI("Blah")}, nil, ErrTypeInvalid, ""},
+		{"bad weak", Weak("Patient", "bogus-uri"), nil, ErrWeakInvalid, ""},
+		{"weak w/frag", Weak("Patient", "Patient/124#blah"), nil, ErrWeakInvalid, ""},
+		{"weak w/other type", Weak("Person", "Patient/124"), nil, ErrTypeInconsistent, ""},
+		{"strong w/other type", inconsistentStrongRef, nil, ErrTypeInconsistent, ""},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			gotLit, gotErr := LiteralInfoOf(tc.inputRef)
+			if !cmp.Equal(gotErr, tc.wantErr, cmpopts.EquateErrors()) {
+				t.Fatalf("RoundTrip(%s) LiteralInfoOf() error got '%v', want '%v'",
+					tc.name, gotErr, tc.wantErr)
+			}
+			if diff := cmp.Diff(gotLit, tc.wantLit, cmp.AllowUnexported(LiteralInfo{})); diff != "" {
+				t.Errorf("RoundTrip(%s) LiteralInfoOf() mismatch (-got, +want)\n%s\n", tc.name, diff)
+			}
+			gotUri := gotLit.URI().GetValue()
+			if gotUri != tc.wantUri {
+				t.Errorf("RoundTrip(%s) URI() mismatch: got '%s', want '%s'", tc.name, gotUri, tc.wantUri)
+			}
+		})
+	}
+}
+
+func TestLiteralInfo_URI_RoundTrip(t *testing.T) {
+	baseUrl := "http://fhir.my.com/scope/teststore"
+	testCases := []struct {
+		name     string
+		inputUri string
+		wantLit  *LiteralInfo
+		wantErr  error
+	}{
+		{"rel-uri", "Patient/1234",
+			&LiteralInfo{resType: &patientType, identity: mustNewIdentity("Patient", "1234", "")}, nil},
+		{"rel-vers-uri", "Patient/1234/_history/abc",
+			&LiteralInfo{resType: &patientType, identity: mustNewIdentity("Patient", "1234", "abc")}, nil},
+		{"abs-uri", baseUrl + "/Patient/1234",
+			&LiteralInfo{resType: &patientType, serviceBaseURL: baseUrl, identity: mustNewIdentity("Patient", "1234", "")}, nil},
+		{"abs-vers-uri", baseUrl + "/Patient/1234/_history/abc",
+			&LiteralInfo{resType: &patientType, serviceBaseURL: baseUrl, identity: mustNewIdentity("Patient", "1234", "abc")}, nil},
+
+		{"rel-missing-vid", "Patient/1234/_history", nil, ErrInvalidURI},
+		{"rel-bad-type", "NotAPatient/1234", nil, ErrInvalidURI},
+		{"rel-bad-resource-id", "Patient/@@@@", nil, ErrInvalidURI},
+		{"rel-bad-version-id", "Patient/1234/_history/@@@@", nil, ErrInvalidURI},
+
+		{"frag-normal", "#1234", &LiteralInfo{fragmentID: ptrString("1234")}, nil},
+		{"frag-bad-id", "#@@@@", nil, ErrInvalidURI},
+
+		// This is special case. Note that the frag string is empty but present (not nil).
+		{"frag-container", "#", &LiteralInfo{fragmentID: ptrString("")}, nil},
+
+		{"bad-nonlocal-frag", "Patient/1234#frag1", nil, ErrInvalidURI},
+		{"bad-canonical-version", "Patient/1234|v12", nil, ErrInvalidURI},
+		{"bad-history-token", "Patient/1234/nothistory/abc", nil, ErrInvalidURI},
+		{"bad-abs-uri", "my.site.com/Patient/1234", nil, ErrInvalidURI},
+
+		{"urn-uuid", "urn:uuid:1234",
+			&LiteralInfo{nonRESTURI: ptrString("urn:uuid:1234")}, nil},
+		{"urn-bad", "urn:", nil, ErrInvalidURI},
+		{"canonical-http", "http://example.com/my-thing",
+			&LiteralInfo{nonRESTURI: ptrString("http://example.com/my-thing")}, nil},
+		{"canonical-bad", "http://example.com/", nil, ErrInvalidURI},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			gotLit, gotErr := LiteralInfoFromURI(tc.inputUri)
+			if !cmp.Equal(gotErr, tc.wantErr, cmpopts.EquateErrors()) {
+				t.Fatalf("RoundTrip(%s): LiteralInfoFromURI() error got '%v', want '%v'. Got LiteralInfo='%v'",
+					tc.name, gotErr, tc.wantErr, gotLit)
+			}
+			if diff := cmp.Diff(gotLit, tc.wantLit, cmp.AllowUnexported(LiteralInfo{})); diff != "" {
+				t.Errorf("RoundTrip(%s): LiteralInfoFromURI() literal (-got, +want)\n%s\n", tc.name, diff)
+			}
+			gotUri := gotLit.URI().GetValue()
+			wantUri := tc.inputUri
+			if tc.wantErr != nil {
+				wantUri = ""
+			}
+			if gotUri != wantUri {
+				t.Errorf("RoundTrip(%s): URI() got '%s', want '%s'", tc.name, gotUri, wantUri)
+			}
+		})
+	}
+}
+
+func mustNewIdentity(resType, id, version string) *resource.Identity {
+	identity, err := resource.NewIdentity(resType, id, version)
+	if err != nil {
+		panic(err)
+	}
+	return identity
+}
+
+func ptrString(s string) *string {
+	return &s
+}
diff --git a/internal/element/reference/reference.go b/internal/element/reference/reference.go
new file mode 100644
index 0000000..1fb6aff
--- /dev/null
+++ b/internal/element/reference/reference.go
@@ -0,0 +1,184 @@
+package reference
+
+import (
+	"errors"
+	"fmt"
+	"path"
+
+	"github.com/google/fhir/go/jsonformat"
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/element"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"github.com/verily-src/fhirpath-go/internal/protofields"
+	"google.golang.org/protobuf/proto"
+)
+
+var (
+	ErrNotCanonicalResource = errors.New("resource type is not a Canonical resource")
+	ErrNotResource          = errors.New("not a resource type")
+	ErrNoResourceID         = errors.New("no resource ID")
+	ErrNoResourceVersion    = errors.New("no resource version")
+	ErrStrongConversion     = errors.New("error converting to strong reference from weak reference")
+)
+
+// ExtractAll finds all references contained in a resource.
+func ExtractAll(resource fhir.Resource) ([]*dtpb.Reference, error) {
+	return element.ExtractAll[*dtpb.Reference](resource)
+}
+
+// Canonical creates a Canonical reference for the given resource type and
+// raw reference. This will error if the provided resource type is not
+// a Canonical FHIR resource.
+func Canonical(resourceType resource.Type, reference string) (*dtpb.Reference, error) {
+	res, ok := protofields.Resources[resourceType.String()]
+	if !ok {
+		return nil, fmt.Errorf("%w: %T", ErrNotResource, resourceType)
+	}
+	testResource := res.New()
+	_, isCanonical := testResource.(fhir.CanonicalResource)
+	if !isCanonical {
+		return nil, fmt.Errorf("%w: %T", ErrNotCanonicalResource, resourceType)
+	}
+
+	return Weak(resourceType, reference), nil
+}
+
+// Weak creates an weak reference for the given resource type and
+// raw reference. The resource type must be a valid FHIR resource type.
+// Generally, it is preferred to use Canonoical() for Canonical references.
+// Examples:
+//
+//	{
+//	  "type": "Questionnaire",
+//	  "reference": "https://example.com/questionnaire"
+//	}
+//
+//	{
+//	  "type": "GuidanceResponse",
+//	  "reference": "urn:uuid:5a17b7c2-e01c-4bc7-b973-31d4156b11d7"
+//	}
+//
+// For more info on references see:
+// - https://www.hl7.org/fhir/references.html#canonical
+// - https://www.hl7.org/fhir/bundle.html#references
+func Weak(resourceType resource.Type, reference string) *dtpb.Reference {
+	return &dtpb.Reference{
+		Type: fhir.URI(resourceType.String()),
+		Reference: &dtpb.Reference_Uri{
+			Uri: fhir.String(reference),
+		},
+	}
+}
+
+// Typed creates a typed, literal FHIR reference for the given resource type and id.
+// Returns an error if resourceId is invalid or resourceType is outside
+// of the known R4 types (which should be impossible).
+func Typed(resourceType resource.Type, resourceId string) (*dtpb.Reference, error) {
+	return typedFromURIString(resourceType, path.Join(resourceType.String(), resourceId))
+}
+
+func typedFromURIString(resourceType resource.Type, uri string) (*dtpb.Reference, error) {
+	weakReference := Weak(resourceType, uri)
+	message := proto.Clone(weakReference)
+	err := jsonformat.NormalizeReference(message)
+	if err != nil {
+		return nil, fmt.Errorf("error normalizing reference: %w", err)
+	}
+	typedReference := message.(*dtpb.Reference)
+	// If conversion to a typed reference succeeded then the untyped URI reference
+	// will be removed and replaced with a different option in the oneof.
+	// https://github.com/google/fhir/blob/master/proto/google/fhir/proto/r4/core/datatypes.proto#L3303-L3304
+	if typedReference.GetUri().GetValue() != "" {
+		return nil, fmt.Errorf("%w: %v", ErrStrongConversion, typedReference)
+	}
+	return message.(*dtpb.Reference), nil
+}
+
+// Logical creates a logical FHIR reference for the given resource type, identifier
+// system, and identifier value.
+// Replaces ph.LogicalReference
+func Logical(resourceType resource.Type, identifierSystem, identifierValue string) *dtpb.Reference {
+	return &dtpb.Reference{
+		Type:       fhir.URI(resourceType.String()),
+		Identifier: fhir.Identifier(identifierSystem, identifierValue),
+	}
+}
+
+// LogicalReferenceIdentifier creates a logical FHIR reference for a given resource type and
+// Indentifier.
+// Replaces: ph.LogicalReferenceIdentifier
+func LogicalFromIdentifier(resourceType resource.Type, identifier *dtpb.Identifier) *dtpb.Reference {
+	return &dtpb.Reference{
+		Type:       fhir.URI(resourceType.String()),
+		Identifier: identifier,
+	}
+}
+
+// TypedFromResource returns a reference to the given resource that
+// is strongly typed and without a version.
+func TypedFromResource(res fhir.Resource) (*dtpb.Reference, error) {
+	return Typed(resource.TypeOf(res), resource.ID(res))
+}
+
+// TypedFromIdentity returns a reference to the given identity that
+// is always relative, always strongly typed, and will include a version ID
+// if-and-only-if the given identity does.
+func TypedFromIdentity(identity *resource.Identity) *dtpb.Reference {
+	ref, err := typedFromURIString(identity.Type(), identity.PreferRelativeVersionedURIString())
+	if err != nil {
+		// Impossible to trigger this error (or at least I couldn't find a way).
+		panic(err)
+	}
+	return ref
+}
+
+// WeakRelativeVersioned returns a reference to the given resource
+// is is weakly typed, relative (to the FHIR service base URL), and versioned.
+// It returns an error if either the resource's ID or version is missing.
+func WeakRelativeVersioned(res fhir.Resource) (*dtpb.Reference, error) {
+	identity, ok := resource.IdentityOf(res)
+	if !ok {
+		return nil, ErrNoResourceID // Missing ID seems most likely cause...
+	}
+	uri, ok := identity.RelativeVersionedURIString()
+	if !ok {
+		return nil, ErrNoResourceVersion
+	}
+	return Weak(identity.Type(), uri), nil
+}
+
+// Is compares two references for referencial equality.
+// If lhs can be determined to refer to the same resource as rhs, then this
+// returns true -- otherwise this returns false. If the underlying resource
+// cannot be determined, then the result is false.
+func Is(lhs, rhs *dtpb.Reference) bool {
+	// If the two references have the same value layout, we don't need to do
+	// more complicated extraction.
+	if proto.Equal(lhs, rhs) {
+		return true
+	}
+
+	// Check for logical referencial equality
+	if li, ri := lhs.GetIdentifier(), rhs.GetIdentifier(); !proto.Equal(li, ri) {
+		return false
+	}
+
+	// Check for literal referencial equality. This requires determining the literal
+	// identity of the referenced resource, since protos represent references as
+	// either a URI _or_ a strongly-typed reference ID in a oneof field. This will
+	// extract the relevant parts to allow for proper equality.
+	if lhs.Reference != nil || rhs.Reference != nil {
+		left, err := IdentityOf(lhs)
+		if err != nil {
+			return false
+		}
+		right, err := IdentityOf(rhs)
+		if err != nil {
+			return false
+		}
+
+		return left.Equal(right)
+	}
+	return true
+}
diff --git a/internal/element/reference/reference_test.go b/internal/element/reference/reference_test.go
new file mode 100644
index 0000000..cb3c0fc
--- /dev/null
+++ b/internal/element/reference/reference_test.go
@@ -0,0 +1,455 @@
+package reference_test
+
+import (
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	acpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/account_go_proto"
+	appb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/appointment_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/verily-src/fhirpath-go/internal/element/reference"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/fhirtest"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func Test_ExtractAll_ValuesCorrect(t *testing.T) {
+	refUri := &dtpb.Reference{
+		Reference: &dtpb.Reference_Uri{
+			Uri: fhir.String("uri-ref"),
+		},
+	}
+	refRelatedPersonId := &dtpb.Reference{
+		Reference: &dtpb.Reference_RelatedPersonId{
+			RelatedPersonId: &dtpb.ReferenceId{
+				Id: fhir.String("related-ref"),
+			},
+		},
+	}
+	testCases := []struct {
+		name       string
+		resource   fhir.Resource
+		references []*dtpb.Reference
+	}{
+		{
+			name:     "No reference",
+			resource: fhirtest.NewResource(t, "Patient"),
+		},
+		{
+			name: "Single reference",
+			resource: fhirtest.NewResource(t, "Patient", fhirtest.WithResourceModification(func(p *ppb.Patient) {
+				p.ManagingOrganization = refUri
+			})),
+			references: []*dtpb.Reference{refUri},
+		},
+		{
+			name: "Multiple references",
+			resource: fhirtest.NewResource(t, "Appointment", fhirtest.WithResourceModification(func(a *appb.Appointment) {
+				a.Participant = []*appb.Appointment_Participant{
+					{
+						Type:  []*dtpb.CodeableConcept{fhir.CodeableConcept("", fhir.Coding("systest", "code"))},
+						Actor: refUri,
+					},
+					{
+						Actor: refRelatedPersonId,
+					},
+				}
+			})),
+			references: []*dtpb.Reference{refRelatedPersonId, refUri},
+		},
+		{
+			name: "Repeated field references",
+			resource: fhirtest.NewResource(t, "Account", fhirtest.WithResourceModification(func(a *acpb.Account) {
+				a.Subject = []*dtpb.Reference{refRelatedPersonId, refUri}
+			})),
+			references: []*dtpb.Reference{refRelatedPersonId, refUri},
+		},
+		{
+			name: "Repeated identical references",
+			resource: fhirtest.NewResource(t, "Account", fhirtest.WithResourceModification(func(a *acpb.Account) {
+				a.Subject = []*dtpb.Reference{refRelatedPersonId, refRelatedPersonId}
+			})),
+			references: []*dtpb.Reference{refRelatedPersonId, refRelatedPersonId},
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			gotReferences, _ := reference.ExtractAll(testCase.resource)
+
+			opts := []cmp.Option{
+				protocmp.Transform(),
+				cmpopts.SortSlices(func(a *dtpb.Reference, b *dtpb.Reference) bool { return a.String() < b.String() }),
+				cmpopts.EquateEmpty(),
+			}
+			if !cmp.Equal(testCase.references, gotReferences, opts...) {
+				t.Errorf("ExtractAll(): got '%v', want '%v'", gotReferences, testCase.references)
+			}
+		})
+	}
+}
+
+func Test_ExtractAll_Modifiable(t *testing.T) {
+	resource := fhirtest.NewResource(t, "Patient", fhirtest.WithResourceModification(func(p *ppb.Patient) {
+		p.ManagingOrganization = &dtpb.Reference{
+			Reference: &dtpb.Reference_Uri{
+				Uri: fhir.String("old-ref"),
+			},
+		}
+	})).(*ppb.Patient)
+
+	references, _ := reference.ExtractAll(resource)
+
+	if len(references) != 1 {
+		t.Fatalf("Expected single reference")
+	}
+
+	references[0].Reference = &dtpb.Reference_Uri{
+		Uri: fhir.String("new-ref"),
+	}
+
+	if got, want := resource.ManagingOrganization, references[0]; got != want {
+		t.Errorf("ExtractAll() reference update failed, got '%v', want '%v'", got, want)
+	}
+}
+
+func canonicalReference(t resource.Type, ref string) *dtpb.Reference {
+	return &dtpb.Reference{
+		Type: fhir.URI(t.String()),
+		Reference: &dtpb.Reference_Uri{
+			Uri: fhir.String(ref),
+		},
+	}
+}
+
+func TestCanonical(t *testing.T) {
+	testCases := []struct {
+		name         string
+		resourceType resource.Type
+		reference    string
+		wantError    error
+	}{
+		{"canonical reference", "Questionnaire", "https://example.com/questionnaire", nil},
+		{"invalid resource type", "NotAResource", "https://example.com/questionnaire", reference.ErrNotResource},
+		{"resource reference errors", "Patient", "Patient/123", reference.ErrNotCanonicalResource},
+		{"bundle reference errors", "GuidanceResponse", "urn:uuid:5a17b7c2-e01c-4bc7-b973-31d4156b11d7", reference.ErrNotCanonicalResource},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ref, err := reference.Canonical(tc.resourceType, tc.reference)
+
+			if got, want := err, tc.wantError; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+				t.Fatalf("Canonical(%s) error got %v, want %v", tc.name, got, want)
+			}
+			if tc.wantError == nil {
+				wantRef := canonicalReference(tc.resourceType, tc.reference)
+				got, want := ref, wantRef
+				if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+					t.Errorf("Canonical(%s) (-got, +want)\n%s\n", tc.name, diff)
+				}
+			}
+		})
+	}
+}
+
+func TestWeak(t *testing.T) {
+	testCases := []struct {
+		name         string
+		resourceType resource.Type
+		reference    string
+	}{
+		{"canonical reference", "Questionnaire", "https://example.com/questionnaire"},
+		{"resource reference", "Patient", "Patient/123"},
+		{"bundle reference", "GuidanceResponse", "urn:uuid:5a17b7c2-e01c-4bc7-b973-31d4156b11d7"},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			wantRef := canonicalReference(tc.resourceType, tc.reference)
+
+			ref := reference.Weak(tc.resourceType, tc.reference)
+
+			got, want := ref, wantRef
+			if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+				t.Errorf("Weak(%s) (-got, +want)\n%s\n", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestTyped(t *testing.T) {
+	testCases := []struct {
+		name         string
+		resourceType resource.Type
+		resourceId   string
+		wantError    error
+		want         *dtpb.Reference
+	}{
+		{"resource reference", "Patient", "test-patient-id", nil, &dtpb.Reference{
+			Reference: &dtpb.Reference_PatientId{PatientId: &dtpb.ReferenceId{Value: "test-patient-id"}},
+			Type:      &dtpb.Uri{Value: "Patient"},
+		}},
+		{"invalid ref type", "InvalidResource", "5a17b7c2-e01c-4bc7-b973-31d4156b11d7", reference.ErrStrongConversion, nil},
+		{"missing ref id", "InvalidResource", "", reference.ErrStrongConversion, nil},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ref, err := reference.Typed(tc.resourceType, tc.resourceId)
+
+			if !cmp.Equal(err, tc.wantError, cmpopts.EquateErrors()) {
+				t.Fatalf("Typed(%s) error got %v, want %v", tc.name, err, tc.wantError)
+			}
+
+			if tc.wantError == nil {
+				got, want := ref, tc.want
+				if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+					t.Errorf("Typed(%s) (-got, +want)\n%s\n", tc.name, diff)
+				}
+			}
+		})
+	}
+}
+
+func TestLogical(t *testing.T) {
+	resourceType := resource.Patient
+	identifierSystem := "IdentifierSystem"
+	identifierValue := "IdentifierValue"
+	reference := reference.Logical(resourceType, identifierSystem, identifierValue)
+
+	if diff := cmp.Diff(reference.GetType().GetValue(), resourceType.String()); diff != "" {
+		t.Errorf("logicalReference.type (-got, +want)\n%s\n", diff)
+	}
+	if diff := cmp.Diff(reference.GetIdentifier().GetSystem().GetValue(), identifierSystem); diff != "" {
+		t.Errorf("logicalReference.identifier.system (-got, +want)\n%s\n", diff)
+	}
+	if diff := cmp.Diff(reference.GetIdentifier().GetValue().GetValue(), identifierValue); diff != "" {
+		t.Errorf("logicalReference.identifier.value (-got, +want)\n%s\n", diff)
+	}
+}
+
+func TestLogicalFromIdentifier(t *testing.T) {
+	resourceType := resource.Patient
+	identifierSystem := "IdentifierSystem"
+	identifierValue := "IdentifierValue"
+	identifier := fhir.Identifier(identifierSystem, identifierValue)
+	identifierCc := fhir.CodeableConcept("", fhir.Coding("system", "code"))
+	identifier.Type = identifierCc
+
+	reference := reference.LogicalFromIdentifier(resourceType, identifier)
+	if diff := cmp.Diff(reference.GetType().GetValue(), resourceType.String()); diff != "" {
+		t.Errorf("logicalReference.type (-got, +want)\n%s\n", diff)
+	}
+	if diff := cmp.Diff(reference.GetIdentifier().GetSystem().GetValue(), identifierSystem); diff != "" {
+		t.Errorf("logicalReference.identifier.system (-got, +want)\n%s\n", diff)
+	}
+	if diff := cmp.Diff(reference.GetIdentifier().GetValue().GetValue(), identifierValue); diff != "" {
+		t.Errorf("logicalReference.identifier.value (-got, +want)\n%s\n", diff)
+	}
+
+	actualCc := reference.GetIdentifier().GetType()
+	if diff := cmp.Diff(identifierCc, actualCc, protocmp.Transform()); diff != "" {
+		t.Errorf("unexpected proto diff:\n%v", diff)
+	}
+}
+
+func TestTypedFromResource(t *testing.T) {
+	patient := &ppb.Patient{Id: fhir.ID("1234"),
+		Meta: &dtpb.Meta{VersionId: fhir.ID("abcd")}, // ignored
+	}
+	got, err := reference.TypedFromResource(patient)
+	if err != nil {
+		t.Fatalf("TypedFromResource failed: %v", err)
+	}
+	want := &dtpb.Reference{Type: fhir.URI("Patient"), Reference: &dtpb.Reference_PatientId{
+		PatientId: &dtpb.ReferenceId{
+			Value: "1234",
+		},
+	}}
+	if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+		t.Errorf("TypedFromResource (-got, +want)\n%s\n", diff)
+	}
+}
+
+func TestTypedFromResource_WhenMissingId(t *testing.T) {
+	patient := &ppb.Patient{} // no ID
+	_, err := reference.TypedFromResource(patient)
+	wantErr := reference.ErrStrongConversion
+	if !cmp.Equal(err, wantErr, cmpopts.EquateErrors()) {
+		t.Errorf("TypedFromResource error got [%v], want [%v]", err, wantErr)
+	}
+}
+
+func TestTypedFromIdentity(t *testing.T) {
+	testCases := []struct {
+		name          string
+		inputIdentity *resource.Identity
+		wantRef       *dtpb.Reference
+	}{
+		{
+			name:          "unversioned",
+			inputIdentity: mustNewIdentity("Patient", "1234", ""),
+			wantRef: &dtpb.Reference{
+				Type:      fhir.URI("Patient"),
+				Reference: &dtpb.Reference_PatientId{PatientId: &dtpb.ReferenceId{Value: "1234"}},
+			},
+		},
+		{
+			name:          "versioned",
+			inputIdentity: mustNewIdentity("Patient", "1234", "abc"),
+			wantRef: &dtpb.Reference{
+				Type: fhir.URI("Patient"),
+				Reference: &dtpb.Reference_PatientId{
+					PatientId: &dtpb.ReferenceId{Value: "1234", History: fhir.ID("abc")}},
+			},
+		},
+		{
+			name:          "missing-id",
+			inputIdentity: mustNewIdentity("Patient", "", ""),
+			wantRef: &dtpb.Reference{
+				Type:      fhir.URI("Patient"),
+				Reference: &dtpb.Reference_PatientId{PatientId: &dtpb.ReferenceId{Value: ""}},
+			},
+		},
+		{
+			name:          "missing-id with version",
+			inputIdentity: mustNewIdentity("Patient", "", "abc"),
+			wantRef: &dtpb.Reference{
+				Type: fhir.URI("Patient"),
+				Reference: &dtpb.Reference_PatientId{
+					PatientId: &dtpb.ReferenceId{Value: "", History: fhir.ID("abc")}},
+			},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			gotRef := reference.TypedFromIdentity(tc.inputIdentity)
+			if diff := cmp.Diff(gotRef, tc.wantRef, protocmp.Transform()); diff != "" {
+				t.Errorf("TypedFromIdentity(%s) (-got, +want)\n%s\n", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestWeakRelativeVersioned(t *testing.T) {
+	testCases := []struct {
+		name      string
+		inputRes  fhir.Resource
+		wantRef   *dtpb.Reference
+		wantError error
+	}{
+		{"normal",
+			&ppb.Patient{Id: fhir.ID("1234"), Meta: &dtpb.Meta{VersionId: fhir.ID("abcd")}},
+			&dtpb.Reference{Type: fhir.URI("Patient"),
+				Reference: &dtpb.Reference_Uri{Uri: fhir.String("Patient/1234/_history/abcd")},
+			},
+			nil},
+		{"missing-resource-id", &ppb.Patient{}, nil, reference.ErrNoResourceID},
+		{"missing-version-id", &ppb.Patient{Id: fhir.ID("1234")}, nil, reference.ErrNoResourceVersion},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := reference.WeakRelativeVersioned(tc.inputRes)
+			if !cmp.Equal(err, tc.wantError, cmpopts.EquateErrors()) {
+				t.Errorf("WeakRelativeVersioned(%s) error got [%v], want [%v]", tc.name, err, tc.wantError)
+			}
+			if diff := cmp.Diff(got, tc.wantRef, protocmp.Transform()); diff != "" {
+				t.Errorf("WeakRelativeVersioned(%s) (-got, +want)\n%s\n", tc.name, diff)
+			}
+		})
+	}
+}
+
+func mustNewIdentity(resourceType, id, versionID string) *resource.Identity {
+	identity, err := resource.NewIdentity(resourceType, id, versionID)
+	if err != nil {
+		panic(err)
+	}
+	return identity
+}
+
+func TestIs(t *testing.T) {
+	const (
+		system      = "FooSystem"
+		systemValue = "value"
+		id          = "3591dd62-fdcc-438f-882d-50540d3f5c18"
+		url         = "https://example.com"
+	)
+	testCases := []struct {
+		name  string
+		left  *dtpb.Reference
+		right *dtpb.Reference
+		want  bool
+	}{
+		{
+			name:  "Two identical logical references",
+			left:  reference.Logical(resource.Account, system, systemValue),
+			right: reference.Logical(resource.Account, system, systemValue),
+			want:  true,
+		}, {
+			name:  "Two different logical references",
+			left:  reference.Logical(resource.Account, system, systemValue),
+			right: reference.Logical(resource.Account, system, systemValue+"-different"),
+			want:  false,
+		}, {
+			name:  "Two identical literal references",
+			left:  mustNewLiteral(resource.Patient, id),
+			right: mustNewLiteral(resource.Patient, id),
+			want:  true,
+		}, {
+			name:  "Two different literal references",
+			left:  mustNewLiteral(resource.Patient, id),
+			right: mustNewLiteral(resource.Patient, id+"1"),
+			want:  false,
+		}, {
+			name:  "Two identical literal URLs",
+			left:  reference.Weak(resource.Account, url),
+			right: reference.Weak(resource.Account, url),
+			want:  true,
+		}, {
+			name:  "Two different literal URLs",
+			left:  reference.Weak(resource.Account, url),
+			right: reference.Weak(resource.Account, url+"/something-else"),
+			want:  false,
+		}, {
+			name:  "Left unknown reference type",
+			left:  reference.Weak(resource.Account, url),
+			right: mustNewLiteral(resource.Patient, id),
+			want:  false,
+		}, {
+			name:  "Right unknown reference type",
+			left:  mustNewLiteral(resource.Patient, id),
+			right: reference.Weak(resource.Account, url),
+			want:  false,
+		}, {
+			name:  "Left logical, right literal reference",
+			left:  mustNewLiteral(resource.Patient, id),
+			right: reference.Logical(resource.Account, system, systemValue),
+			want:  false,
+		}, {
+			name:  "Left literal, right logical reference",
+			left:  reference.Logical(resource.Account, system, systemValue),
+			right: mustNewLiteral(resource.Patient, id),
+			want:  false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := reference.Is(tc.left, tc.right)
+
+			if got != tc.want {
+				t.Errorf("Is(%v): got %v, want %v", tc.name, got, tc.want)
+			}
+		})
+	}
+}
+
+func mustNewLiteral(res resource.Type, id string) *dtpb.Reference {
+	got, err := reference.Typed(res, id)
+	if err != nil {
+		panic(err)
+	}
+	return got
+}
diff --git a/internal/fhir/constraints.go b/internal/fhir/constraints.go
new file mode 100644
index 0000000..e900bd0
--- /dev/null
+++ b/internal/fhir/constraints.go
@@ -0,0 +1,129 @@
+package fhir
+
+import (
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+)
+
+type primitiveDataType interface {
+	*dtpb.Base64Binary |
+		*dtpb.Boolean |
+		*dtpb.Canonical |
+		*dtpb.Code |
+		*dtpb.Date |
+		*dtpb.DateTime |
+		*dtpb.Decimal |
+		*dtpb.Id |
+		*dtpb.Instant |
+		*dtpb.Integer |
+		*dtpb.Markdown |
+		*dtpb.Oid |
+		*dtpb.PositiveInt |
+		*dtpb.String |
+		*dtpb.Time |
+		*dtpb.UnsignedInt |
+		*dtpb.Uri |
+		*dtpb.Url |
+		*dtpb.Uuid
+}
+
+type complexDataType interface {
+	*dtpb.Address |
+		*dtpb.Age |
+		*dtpb.Attachment |
+		*dtpb.CodeableConcept |
+		*dtpb.Coding |
+		*dtpb.ContactPoint |
+		*dtpb.Count |
+		*dtpb.Distance |
+		*dtpb.Duration |
+		*dtpb.HumanName |
+		*dtpb.Identifier |
+		*dtpb.Money |
+		*dtpb.MoneyQuantity |
+		*dtpb.Period |
+		*dtpb.Quantity |
+		*dtpb.Range |
+		*dtpb.Ratio |
+		*dtpb.SampledData |
+		*dtpb.Signature |
+		*dtpb.SimpleQuantity |
+		*dtpb.Timing
+}
+
+type metaDataType interface {
+	*dtpb.ContactDetail |
+		*dtpb.Contributor |
+		*dtpb.DataRequirement |
+		*dtpb.Expression |
+		*dtpb.ParameterDefinition |
+		*dtpb.RelatedArtifact |
+		*dtpb.TriggerDefinition |
+		*dtpb.UsageContext
+}
+
+type specialPurposeDataType interface {
+	*dtpb.Dosage |
+		*dtpb.ElementDefinition |
+		*dtpb.Extension |
+		*dtpb.MarketingStatus |
+		*dtpb.Meta |
+		*dtpb.Narrative |
+		*dtpb.ProductShelfLife |
+		*dtpb.Reference
+}
+
+// DataType is an constraint-definition of FHIR datatypes, which all support ID
+// and Extension fields, in addition to their base values.
+//
+// Note: "DataType" is also an "Element", so these interfaces are logically
+// equivalent -- and so this is represented as a constraint of valid datatypes.
+//
+// The R4 spec doesn't explicitly refer to "DataType" as a distinction from
+// "Element", but the R5 spec does, and its definition is compatible with R4.
+// This is retained here so that we can have a proper vernacular and mechanism
+// for referring to these types in generic ways through constraints.
+//
+// See https://www.hl7.org/fhir/r5/types.html#DataType for more details.
+type DataType interface {
+	Element
+	primitiveDataType | complexDataType | metaDataType | specialPurposeDataType
+}
+
+// PrimitiveType is a constraint-definition of FHIR datatypes, which all support ID
+// and Extension fields, in addition to their base values.
+//
+// Note: "DataType" is also an "Element", so these interfaces are logically
+// equivalent -- and so this is represented as a constraint of valid datatypes.
+//
+// The R4 spec doesn't explicitly refer to "PrimitiveType" as a distinction from
+// "Element", but the R5 spec does, and its definition is compatible with R4.
+// This is retained here so that we can have a proper vernacular and mechanism
+// for referring to these types in generic ways through constraints.
+//
+// See https://www.hl7.org/fhir/types.html#PrimitiveType for more details.
+type PrimitiveType interface {
+	Element
+	primitiveDataType
+}
+
+// BackboneType is a constraint-definition of FHIR backbone element, which all
+// support ID, Extension, and modifier-extension fields, in addition to their
+// base values.
+//
+// Note: "BackboneType" is also an "BackboneElement", so these interfaces are logically
+// equivalent -- and so this is represented as a constraint of valid datatypes.
+//
+// The R4 spec doesn't explicitly refer to "BackboneType" as a distinction from
+// "BackboneElement", but the R5 spec does, and its definition is compatible with R4.
+// This is retained here so that we can have a proper vernacular and mechanism
+// for referring to these types in generic ways through constraints.
+//
+// See https://www.hl7.org/fhir/r5/types.html#BackboneType for more details.
+type BackboneType interface {
+	BackboneElement
+	*dtpb.Timing |
+		*dtpb.ElementDefinition |
+		*dtpb.MarketingStatus |
+		*dtpb.ProductShelfLife |
+		*dtpb.Dosage
+}
diff --git a/internal/fhir/doc.go b/internal/fhir/doc.go
new file mode 100644
index 0000000..19fe986
--- /dev/null
+++ b/internal/fhir/doc.go
@@ -0,0 +1,13 @@
+/*
+Package fhir provides a library for working with R4 Google FHIR protos:
+https://github.com/google/fhir.
+
+This provides various quality-of-life utilities over the base FHIR definitions,
+such as:
+
+- Defining abstract base resources as their respective interfaces
+- Creation functions for Elements
+- Access utilities for ContainedResources and References
+- etc.
+*/
+package fhir
diff --git a/internal/fhir/duration.go b/internal/fhir/duration.go
new file mode 100644
index 0000000..3748134
--- /dev/null
+++ b/internal/fhir/duration.go
@@ -0,0 +1,110 @@
+package fhir
+
+import (
+	"time"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/units"
+)
+
+// DurationFromTime converts an R4 FHIR Time Element into an R4 FHIR Duration
+// value.
+//
+// If the underlying time has Second-based precision, the returned Duration will
+// also have seconds precision; otherwise this will fallback into nanosecond
+// precision.
+func DurationFromTime(t *dtpb.Time) *dtpb.Duration {
+	// Constrain the time to 24 hours
+	duration := time.Microsecond * time.Duration(t.GetValueUs()) % (time.Hour * 24)
+
+	switch t.GetPrecision() {
+	case dtpb.Time_SECOND:
+		return Seconds(duration)
+	case dtpb.Time_MILLISECOND:
+		return Milliseconds(duration)
+	case dtpb.Time_MICROSECOND:
+		fallthrough
+	default:
+		return Microseconds(duration)
+	}
+}
+
+// Duration creates a Duration proto with the provided value, computing the
+// largest whole-unit of time that can be used to represent the time.
+func Duration(d time.Duration) *dtpb.Duration {
+	value := d.Nanoseconds()
+	if value == 0 {
+		return durationValue(float64(value), units.Days)
+	}
+	unitConversions := []struct {
+		unit     units.Time
+		duration time.Duration
+	}{
+		{units.Days, 24 * time.Hour},
+		{units.Hours, time.Hour},
+		{units.Minutes, time.Minute},
+		{units.Seconds, time.Second},
+		{units.Milliseconds, time.Millisecond},
+		{units.Microseconds, time.Microsecond},
+		{units.Nanoseconds, time.Nanosecond},
+	}
+	for _, conversion := range unitConversions {
+		if d >= conversion.duration && d == d.Round(conversion.duration) {
+			numUnits := d / conversion.duration
+			return durationValue(float64(numUnits), conversion.unit)
+		}
+	}
+	return durationValue(float64(value), units.Nanoseconds)
+}
+
+// Nanoseconds creates a Duration proto with the specified time value, rounded
+// to nanosecond accuracy.
+func Nanoseconds(value time.Duration) *dtpb.Duration {
+	return durationValue(float64(value.Nanoseconds()), units.Nanoseconds)
+}
+
+// Milliseconds creates a Duration proto with the specified time value, rounded
+// to millisecond accuracy.
+func Milliseconds(value time.Duration) *dtpb.Duration {
+	millis := float64(value.Nanoseconds()) / float64(time.Millisecond.Nanoseconds())
+	return durationValue(millis, units.Milliseconds)
+}
+
+// Microseconds creates a Duration proto with the specified time value, rounded
+// to microsecond accuracy.
+func Microseconds(value time.Duration) *dtpb.Duration {
+	micros := float64(value.Nanoseconds()) / float64(time.Microsecond.Nanoseconds())
+	return durationValue(micros, units.Microseconds)
+}
+
+// Seconds creates a Duration proto with the specified time value, rounded
+// to second accuracy.
+func Seconds(value time.Duration) *dtpb.Duration {
+	return durationValue(value.Seconds(), units.Seconds)
+}
+
+// Minutes creates a Duration proto with the specified time value, rounded
+// to minute accuracy.
+func Minutes(value time.Duration) *dtpb.Duration {
+	return durationValue(value.Minutes(), units.Minutes)
+}
+
+// Hours creates a Duration proto with the specified time value, rounded
+// to hour-accuracy.
+func Hours(value time.Duration) *dtpb.Duration {
+	return durationValue(value.Hours(), units.Hours)
+}
+
+// Days creates a Duration proto with the specified time value, rounded
+// to day-accuracy.
+func Days(value time.Duration) *dtpb.Duration {
+	return durationValue(value.Hours()/24, units.Days)
+}
+
+func durationValue(value float64, unit units.Time) *dtpb.Duration {
+	return &dtpb.Duration{
+		Value:  Decimal(value),
+		Code:   Code(unit.Symbol()),
+		System: URI(unit.System()),
+	}
+}
diff --git a/internal/fhir/duration_test.go b/internal/fhir/duration_test.go
new file mode 100644
index 0000000..60922c8
--- /dev/null
+++ b/internal/fhir/duration_test.go
@@ -0,0 +1,161 @@
+package fhir_test
+
+import (
+	"testing"
+	"time"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/units"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func newDuration(value int64, unit units.Time) *dtpb.Duration {
+	return &dtpb.Duration{
+		Value:  fhir.Decimal(float64(value)),
+		Code:   fhir.Code(unit.Symbol()),
+		System: fhir.URI(unit.System()),
+	}
+}
+
+func TestDurationFromTime(t *testing.T) {
+	testCases := []struct {
+		name       string
+		time       string
+		value      int64
+		multiplier int64
+		unit       units.Time
+	}{
+		{"Seconds", "00:01:00", 1, int64(time.Minute) / int64(time.Second), units.Seconds},
+		{"Milliseconds", "00:01:00.000", 1, int64(time.Minute) / int64(time.Millisecond), units.Milliseconds},
+		{"Microseconds", "00:01:00.000000", 1, int64(time.Minute) / int64(time.Microsecond), units.Microseconds},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			input := fhir.MustParseTime(tc.time)
+			value := tc.value * tc.multiplier
+			want := newDuration(value, tc.unit)
+
+			got := fhir.DurationFromTime(input)
+
+			if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+				t.Errorf("DurationFromTime(%v): (-got +want):\n%v", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestDuration(t *testing.T) {
+	testCases := []struct {
+		name       string
+		value      int64
+		multiplier int64
+		unit       units.Time
+	}{
+		{"Zero", 0, 1, units.Days},
+		{"Nanoseconds", 805, 1, units.Nanoseconds},
+		{"Microseconds", 15, int64(time.Microsecond), units.Microseconds},
+		{"Milliseconds", 32, int64(time.Millisecond), units.Milliseconds},
+		{"Seconds", 42, int64(time.Second), units.Seconds},
+		{"Minutes", 1, int64(time.Minute), units.Minutes},
+		{"Hours", 12, int64(time.Hour), units.Hours},
+		{"Days", 3, 24 * int64(time.Hour), units.Days},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			value := tc.value * tc.multiplier
+			timeDuration := time.Duration(value)
+			want := newDuration(tc.value, tc.unit)
+
+			got := fhir.Duration(timeDuration)
+
+			if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+				t.Errorf("Duration(%v): (-got +want):\n%v", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestNanoseconds(t *testing.T) {
+	value := time.Duration(400)
+	want := newDuration(int64(value), units.Nanoseconds)
+
+	got := fhir.Nanoseconds(value)
+
+	if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+		t.Errorf("Nanoseconds: (-got +want):\n%v", diff)
+	}
+}
+
+func TestMicroseconds(t *testing.T) {
+	value := time.Duration(400)
+	duration := time.Microsecond * value
+	want := newDuration(int64(value), units.Microseconds)
+
+	got := fhir.Microseconds(duration)
+
+	if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+		t.Errorf("Microseconds: (-got +want):\n%v", diff)
+	}
+}
+
+func TestMilliseconds(t *testing.T) {
+	value := 400
+	duration := time.Millisecond * 400
+	want := newDuration(int64(value), units.Milliseconds)
+
+	got := fhir.Milliseconds(duration)
+
+	if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+		t.Errorf("Milliseconds: (-got +want):\n%v", diff)
+	}
+}
+
+func TestSeconds(t *testing.T) {
+	value := 400
+	duration := time.Second * 400
+	want := newDuration(int64(value), units.Seconds)
+
+	got := fhir.Seconds(duration)
+
+	if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+		t.Errorf("Seconds: (-got +want):\n%v", diff)
+	}
+}
+
+func TestMinutes(t *testing.T) {
+	value := 400
+	duration := time.Minute * 400
+	want := newDuration(int64(value), units.Minutes)
+
+	got := fhir.Minutes(duration)
+
+	if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+		t.Errorf("Minutes: (-got +want):\n%v", diff)
+	}
+}
+
+func TestHours(t *testing.T) {
+	value := 400
+	duration := time.Hour * 400
+	want := newDuration(int64(value), units.Hours)
+
+	got := fhir.Hours(duration)
+
+	if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+		t.Errorf("Hours: (-got +want):\n%v", diff)
+	}
+}
+
+func TestDays(t *testing.T) {
+	value := 400
+	duration := time.Hour * 24 * 400
+	want := newDuration(int64(value), units.Days)
+
+	got := fhir.Days(duration)
+
+	if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+		t.Errorf("Days: (-got +want):\n%v", diff)
+	}
+}
diff --git a/internal/fhir/elements_general.go b/internal/fhir/elements_general.go
new file mode 100644
index 0000000..b7e9290
--- /dev/null
+++ b/internal/fhir/elements_general.go
@@ -0,0 +1,307 @@
+package fhir
+
+import (
+	"time"
+
+	cpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/codes_go_proto"
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/slices"
+)
+
+const ucumUnitSystem = "http://unitsofmeasure.org"
+
+// General-Purpose Data Types:
+//
+// The section below defines types from the "Data Types" heading in
+// http://hl7.org/fhir/R4/datatypes.html#open
+
+// Annotation creates an R4 FHIR Annotation element with the specified text,
+// author, and time of creation.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#annotation
+func Annotation(text, author string, when time.Time) *dtpb.Annotation {
+	return &dtpb.Annotation{
+		Text: Markdown(text),
+		Author: &dtpb.Annotation_AuthorX{
+			Choice: &dtpb.Annotation_AuthorX_StringValue{
+				StringValue: String(author),
+			},
+		},
+		Time: DateTime(when),
+	}
+}
+
+// AnnotationReference creates an R4 FHIR Annotation element with the specified
+// text, a reference to the author, and the time of creation.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#annotation
+func AnnotationReference(text string, author *dtpb.Reference, when time.Time) *dtpb.Annotation {
+	return &dtpb.Annotation{
+		Text: Markdown(text),
+		Author: &dtpb.Annotation_AuthorX{
+			Choice: &dtpb.Annotation_AuthorX_Reference{
+				Reference: author,
+			},
+		},
+		Time: DateTime(when),
+	}
+}
+
+// Coding creates an R4 FHIR Coding element with the provided system and code.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#coding
+func Coding(system, code string) *dtpb.Coding {
+	return &dtpb.Coding{
+		System: URI(system),
+		Code:   Code(code),
+	}
+}
+
+// CodingWithVersion creates an R4 FHIR Coding element with the provided system,
+// code, and version.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#coding
+func CodingWithVersion(system, code, version string) *dtpb.Coding {
+	return &dtpb.Coding{
+		System:  URI(system),
+		Code:    Code(code),
+		Version: String(version),
+	}
+}
+
+// CodeableConcept creates an R4 FHIR CodeableConcept with the specified codings,
+// and with the Text element if the given text argument is non-empty.
+//
+// Providing a non-empty Text element is good practice but not required.
+// See: http://hl7.org/fhir/R4/datatypes.html#codeableconcept
+func CodeableConcept(text string, coding ...*dtpb.Coding) *dtpb.CodeableConcept {
+	concept := &dtpb.CodeableConcept{
+		Coding: coding,
+	}
+	if text != "" {
+		concept.Text = String(text)
+	}
+	return concept
+}
+
+// ContactPoint creates an R4 FHIR ContactPoint element from the system and value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#contactpoint
+func ContactPoint(system cpb.ContactPointSystemCode_Value, value string) *dtpb.ContactPoint {
+	return &dtpb.ContactPoint{
+		System: &dtpb.ContactPoint_SystemCode{
+			Value: system,
+		},
+		Value: String(value),
+	}
+}
+
+// EmailContactPoint creates an R4 FHIR ContactPoint element for the Email
+// system given a value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#contactpoint
+func EmailContactPoint(value string) *dtpb.ContactPoint {
+	return ContactPoint(cpb.ContactPointSystemCode_EMAIL, value)
+}
+
+// PhoneContactPoint creates an R4 FHIR ContactPoint element for the Phone
+// system given a value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#contactpoint
+func PhoneContactPoint(value string) *dtpb.ContactPoint {
+	return ContactPoint(cpb.ContactPointSystemCode_PHONE, value)
+}
+
+// SmsContactPoint creates an R4 FHIR ContactPoint element for the SMS system
+// given a value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#contactpoint
+func SmsContactPoint(value string) *dtpb.ContactPoint {
+	return ContactPoint(cpb.ContactPointSystemCode_SMS, value)
+}
+
+// PagerContactPoint creates an R4 FHIR ContactPoint element for the Pager
+// system given a value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#contactpoint
+func PagerContactPoint(value string) *dtpb.ContactPoint {
+	return ContactPoint(cpb.ContactPointSystemCode_PAGER, value)
+}
+
+// FaxContactPoint creates an R4 FHIR ContactPoint element for the Fax system
+// given a value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#contactpoint
+func FaxContactPoint(value string) *dtpb.ContactPoint {
+	return ContactPoint(cpb.ContactPointSystemCode_FAX, value)
+}
+
+// OtherContactPoint creates an R4 FHIR ContactPoint element for the Other
+// system given a value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#contactpoint
+func OtherContactPoint(value string) *dtpb.ContactPoint {
+	return ContactPoint(cpb.ContactPointSystemCode_OTHER, value)
+}
+
+// Identifier creates an R4 FHIR Identifier element with the provided system
+// and value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#identifier
+func Identifier(system, value string) *dtpb.Identifier {
+	return &dtpb.Identifier{
+		System: URI(system),
+		Value:  String(value),
+	}
+}
+
+// Money creates an R4 FHIR Money element from the money value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#Money
+func Money(value float64) *dtpb.Money {
+	return &dtpb.Money{
+		Value: Decimal(value),
+	}
+}
+
+// MoneyQuantity creates an R4 FHIR MoneyQuantity element from the value and units.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#MoneyQuantity
+func MoneyQuantity(value float64, unit string) *dtpb.MoneyQuantity {
+	return &dtpb.MoneyQuantity{
+		Value: Decimal(value),
+		Unit:  String(unit),
+	}
+}
+
+// Period creates an R4 FHIR Period element with the provided start and end times.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#period
+func Period(start, end time.Time) *dtpb.Period {
+	return &dtpb.Period{
+		Start: DateTime(start),
+		End:   DateTime(end),
+	}
+}
+
+// Quantity creates an R4 FHIR Quantity element from the given value and units.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#quantity
+func Quantity(value float64, unit string) *dtpb.Quantity {
+	return &dtpb.Quantity{
+		Value: Decimal(value),
+		Unit:  String(unit),
+	}
+}
+
+// UCUMQuantity creates an R4 FHIR Quantity element representing a
+// value and UCUM unit.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#quantity
+// TODO(PHP-9521): Add a unit package to validate against UCUM units.
+func UCUMQuantity(value float64, unit string) *dtpb.Quantity {
+	return &dtpb.Quantity{
+		Value:  Decimal(value),
+		Unit:   String(unit),
+		Code:   Code(unit),
+		System: URI(ucumUnitSystem),
+	}
+}
+
+// QuantityFromSimpleQuantity is a convenience utility for converting a
+// SimpleQuantity to its base-class definition of Quantity.
+// If the input is nil, this returns nil.
+//
+// For more information, see the diagram for Primitive Types here:
+// https://www.hl7.org/fhir/datatypes.html
+func QuantityFromSimpleQuantity(value *dtpb.SimpleQuantity) *dtpb.Quantity {
+	return quantityFrom(value, value == nil)
+}
+
+// QuantityFromDuration is a convenience utility for converting a
+// Duration to its base-class definition of Quantity.
+// If the input is nil, this returns nil.
+//
+// For more information, see the diagram for Primitive Types here:
+// https://www.hl7.org/fhir/datatypes.html
+func QuantityFromDuration(value *dtpb.Duration) *dtpb.Quantity {
+	return quantityFrom(value, value == nil)
+}
+
+// QuantityFromMoneyQuantity is a convenience utility for converting a
+// MoneyQuantity to its base-class definition of Quantity.
+// If the input is nil, this returns nil.
+//
+// For more information, see the diagram for Primitive Types here:
+// https://www.hl7.org/fhir/datatypes.html
+func QuantityFromMoneyQuantity(value *dtpb.MoneyQuantity) *dtpb.Quantity {
+	return quantityFrom(value, value == nil)
+}
+
+// quantityLike is a helper interface for implementing the QuantityFrom*
+// functions. This defines the common interface for all Quantity objects.
+type quantityLike interface {
+	GetValue() *dtpb.Decimal
+	GetUnit() *dtpb.String
+	GetSystem() *dtpb.Uri
+	GetCode() *dtpb.Code
+}
+
+// quantityFrom is a helper function for implementing the QuantityFrom* functions
+// which all have the same implementation.
+//
+// This function takes 'isNil' as a boolean argument to work around the fact that
+// a nil pointer passed to an interface forms a non-nil interface in Go, and
+// reflection is more costly than a bool check.
+func quantityFrom(value quantityLike, isNil bool) *dtpb.Quantity {
+	if isNil {
+		return nil
+	}
+	return &dtpb.Quantity{
+		Value:  value.GetValue(),
+		Unit:   value.GetUnit(),
+		System: value.GetSystem(),
+		Code:   value.GetCode(),
+	}
+}
+
+// Range creates an R4 FHIR Range element with the given low and high end of the
+// range, using the specified units.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#range
+func Range(low, high float64, unit string) *dtpb.Range {
+	return &dtpb.Range{
+		Low:  SimpleQuantity(low, unit),
+		High: SimpleQuantity(high, unit),
+	}
+}
+
+// Ratio creates an R4 FHIR Ratio element with the given numerator and denominator.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#ratio
+func Ratio(numerator, denominator float64) *dtpb.Ratio {
+	return &dtpb.Ratio{
+		Numerator:   &dtpb.Quantity{Value: Decimal(numerator)},
+		Denominator: &dtpb.Quantity{Value: Decimal(denominator)},
+	}
+}
+
+// SimpleQuantity creates an R4 FHIR SimpleQuantity element from the given value
+// and units.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#SimpleQuantity
+func SimpleQuantity(value float64, unit string) *dtpb.SimpleQuantity {
+	return &dtpb.SimpleQuantity{
+		Value: Decimal(value),
+		Unit:  String(unit),
+	}
+}
+
+// Timing creates an R4 FHIR Timing element observing the events specified in `times`.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#timing
+func Timing(times ...time.Time) *dtpb.Timing {
+	return &dtpb.Timing{
+		Event: slices.Map(times, DateTime),
+	}
+}
diff --git a/internal/fhir/elements_general_test.go b/internal/fhir/elements_general_test.go
new file mode 100644
index 0000000..8dd8f4e
--- /dev/null
+++ b/internal/fhir/elements_general_test.go
@@ -0,0 +1,45 @@
+package fhir_test
+
+import (
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func TestCodeableConcept_WithoutText(t *testing.T) {
+	myCoding := fhir.Coding("my-system", "my-code")
+	yourCoding := fhir.Coding("your-system", "your-code")
+	testCases := []struct {
+		name    string
+		text    string
+		codings []*dtpb.Coding
+		want    *dtpb.CodeableConcept
+	}{
+		{"empty", "", nil, &dtpb.CodeableConcept{}},
+		{"full", "my-text", []*dtpb.Coding{myCoding, yourCoding},
+			&dtpb.CodeableConcept{
+				Coding: []*dtpb.Coding{myCoding, yourCoding},
+				Text:   fhir.String("my-text"),
+			},
+		},
+		{"without text", "", []*dtpb.Coding{myCoding},
+			&dtpb.CodeableConcept{
+				Coding: []*dtpb.Coding{myCoding},
+				// The key behavior is the absence of the Text element.
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			sut := fhir.CodeableConcept(tc.text, tc.codings...)
+			if diff := cmp.Diff(tc.want, sut, protocmp.Transform()); diff != "" {
+				t.Errorf("CodeableConcept mismatch (-want, +got):\n%s", diff)
+			}
+		})
+	}
+
+}
diff --git a/internal/fhir/elements_metadata.go b/internal/fhir/elements_metadata.go
new file mode 100644
index 0000000..bb02072
--- /dev/null
+++ b/internal/fhir/elements_metadata.go
@@ -0,0 +1,19 @@
+package fhir
+
+import dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+
+// Metadata Types:
+//
+// The section below defines types from the "MetaDataTypes" heading in
+// http://hl7.org/fhir/R4/datatypes.html#open
+
+// ContactDetail creates an R4 FHIR ContactDetail element from a string value
+// and the specified contact-points.
+//
+// See: http://hl7.org/fhir/R4/metadatatypes.html#ContactDetail
+func ContactDetail(name string, telecom ...*dtpb.ContactPoint) *dtpb.ContactDetail {
+	return &dtpb.ContactDetail{
+		Name:    String(name),
+		Telecom: telecom,
+	}
+}
diff --git a/internal/fhir/elements_primitive.go b/internal/fhir/elements_primitive.go
new file mode 100644
index 0000000..37fa5bf
--- /dev/null
+++ b/internal/fhir/elements_primitive.go
@@ -0,0 +1,343 @@
+package fhir
+
+import (
+	"errors"
+	"fmt"
+	"math"
+	"regexp"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/uuid"
+	"github.com/verily-src/fhirpath-go/internal/slices"
+)
+
+var (
+	// ErrIntegerDataLoss is an error raised in APIs that might unintentionally
+	// truncate integral values that the user wouldn't expect.
+	ErrIntegerDataLoss = errors.New("data-loss occurred during integer conversion")
+)
+
+// Primitive Types:
+//
+// The section below defines types from the "Primitive Types" heading in
+// http://hl7.org/fhir/R4/datatypes.html#open
+
+// Base64Binary creates an R4 FHIR Base64Binary element the specified bytes.
+//
+// See: https://hl7.org/fhir/R4/datatypes.html#base64Binary
+func Base64Binary(value []byte) *dtpb.Base64Binary {
+	return &dtpb.Base64Binary{
+		Value: value,
+	}
+}
+
+// Boolean creates a Boolean proto from a primitive value.
+//
+// See: https://hl7.org/fhir/R4/datatypes.html#boolean
+func Boolean(value bool) *dtpb.Boolean {
+	return &dtpb.Boolean{
+		Value: value,
+	}
+}
+
+// Canonical defined in canonical.go
+
+// Code creates an R4 FHIR Code element from a string value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#code
+func Code(value string) *dtpb.Code {
+	return &dtpb.Code{
+		Value: value,
+	}
+}
+
+// Date and DateTime in time.go
+
+// Decimal creates an R4 FHIR Decimal element from a float64 (double) value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#decimal
+func Decimal(value float64) *dtpb.Decimal {
+	return &dtpb.Decimal{
+		Value: fmt.Sprint(value),
+	}
+}
+
+// ID creates an R4 FHIR ID element from a string value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#id
+func ID(value string) *dtpb.Id {
+	return &dtpb.Id{
+		Value: value,
+	}
+}
+
+var idRegexp *regexp.Regexp = regexp.MustCompile(`^[A-Za-z0-9\-\.]{1,64}$`)
+
+// IsID returns true if the given string a valid FHIR ID.
+// See http://hl7.org/fhir/R4/datatypes.html#id.
+func IsID(id string) bool {
+	return idRegexp.MatchString(id)
+}
+
+// RandomID generates a new random R4 FHIR ID element.
+func RandomID() *dtpb.Id {
+	return ID(uuid.NewString())
+}
+
+// Instant in time.go
+
+// Integer creates an R4 FHIR Integer element from an int32 value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#integer
+func Integer(value int32) *dtpb.Integer {
+	return &dtpb.Integer{
+		Value: value,
+	}
+}
+
+// IntegerFromInt creates an R4 FHIR Integer element from an int value.
+//
+// Go's int type is architecture dependent, values greater than int32 will be
+// truncated, as described in the go tour: https://tour.golang.org/basics/11
+// If this occurs, this function returns an error.
+func IntegerFromInt(value int) (*dtpb.Integer, error) {
+	if val, ok := tryNarrowInt32(value); ok {
+		return &dtpb.Integer{
+			Value: val,
+		}, nil
+	}
+	return nil, fmt.Errorf("integer(%v): %w", value, ErrIntegerDataLoss)
+}
+
+// tryNarrowInt32 is an implementation function used in the `IntegerFromInt`
+// that narrows an int to an in32 and tests if there was any truncation.
+func tryNarrowInt32(v int) (int32, bool) {
+	v32 := int32(v)
+	if int(v32) == v {
+		return v32, true
+	}
+	return 0, false
+}
+
+// IntegerFromPositiveInt attempts to create an R4 FHIR Integer element from a
+// PositiveInt value. This function may fail of the value stored in the PositiveInt
+// exceeds the cardinality of Integer, which may cause a signed integer overflow.
+//
+// For more information, see the diagram for Primitive Types here:
+// https://www.hl7.org/fhir/datatypes.html
+func IntegerFromPositiveInt(value *dtpb.PositiveInt) (*dtpb.Integer, error) {
+	return integerFrom(value)
+}
+
+// IntegerFromUnsignedInt attempts to create an R4 FHIR Integer element from an
+// UnsignedInt value. This function may fail of the value stored in the UnsignedInt
+// exceeds the cardinality of Integer, which may cause a signed integer overflow.
+//
+// For more information, see the diagram for Primitive Types here:
+// https://www.hl7.org/fhir/datatypes.html
+func IntegerFromUnsignedInt(value *dtpb.UnsignedInt) (*dtpb.Integer, error) {
+	return integerFrom(value)
+}
+
+// integerFrom is an implementation function used in the `IntegerFrom*`
+// functions to minimize code duplication.
+func integerFrom(value interface{ GetValue() uint32 }) (*dtpb.Integer, error) {
+	if isLossyConversionToInt32(value.GetValue()) {
+		return nil, fmt.Errorf("integer(%v): %w", value, ErrIntegerDataLoss)
+	}
+	return &dtpb.Integer{
+		Value: int32(value.GetValue()),
+	}, nil
+}
+
+// isLossyConversionToInt32 checks if a uint32 value can be converted to an int32
+// value without losing its original value, which can happen if the sign-bit is
+// set in the uint32.
+func isLossyConversionToInt32(value uint32) bool {
+	// This works by testing that casting a uint32 to an int32 and checking that
+	// the value is still positive. If it changes to negative, the cast was never
+	// valid.
+	return value > math.MaxInt32
+}
+
+// Markdown creates an R4 FHIR Markdown element from a string value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#markdown
+func Markdown(value string) *dtpb.Markdown {
+	return &dtpb.Markdown{
+		Value: value,
+	}
+}
+
+// OID creates an R4 FHIR OID element from a OID-string value, prepending the
+// necessary "urn:oid:" to the value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#oid
+func OID(value string) *dtpb.Oid {
+	return &dtpb.Oid{
+		Value: fmt.Sprintf("urn:oid:%v", value),
+	}
+}
+
+// PositiveInt creates an R4 FHIR PositiveInt element from a uint32 value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#positiveInt
+func PositiveInt(value uint32) *dtpb.PositiveInt {
+	return &dtpb.PositiveInt{
+		Value: value,
+	}
+}
+
+// String creates an R4 FHIR String element from a string value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#string
+func String(value string) *dtpb.String {
+	return &dtpb.String{
+		Value: value,
+	}
+}
+
+// Strings creates an array of R4 FHIR String elements from a string value. This
+// is offered as a convenience function, since many FHIR protos have arrays of
+// FHIR string types, and converting between Go strings and FHIR strings is a
+// common and repetitive process for some types.
+func Strings(values ...string) []*dtpb.String {
+	return slices.Map(values, String)
+}
+
+// StringFromCode is a convenience utility for converting a Code to its
+// base-class definition of String. If the input is nil, this returns nil.
+//
+// For more information, see the diagram for Primitive Types here:
+// https://www.hl7.org/fhir/datatypes.html
+func StringFromCode(code *dtpb.Code) *dtpb.String {
+	return stringFrom(code, code == nil)
+}
+
+// StringFromMarkdown is a convenience utility for converting Markdown to its
+// base-class definition of String. If the input is nil, this returns nil.
+//
+// For more information, see the diagram for Primitive Types here:
+// https://www.hl7.org/fhir/datatypes.html
+func StringFromMarkdown(markdown *dtpb.Markdown) *dtpb.String {
+	return stringFrom(markdown, markdown == nil)
+}
+
+// StringFromID is a convenience utility for converting an Id to its
+// base-class definition of String. If the input is nil, this returns nil.
+//
+// For more information, see the diagram for Primitive Types here:
+// https://www.hl7.org/fhir/datatypes.html
+func StringFromID(id *dtpb.Id) *dtpb.String {
+	return stringFrom(id, id == nil)
+}
+
+// stringFrom is an implementation of the `StringFrom*` series of functions to
+// cut down on repetition.
+//
+// This function takes 'isNil' as a boolean argument to work around the fact that
+// a nil pointer passed to an interface forms a non-nil interface in Go, and
+// reflection is more costly than a bool check.
+func stringFrom(value interface{ GetValue() string }, isNil bool) *dtpb.String {
+	if isNil {
+		return nil
+	}
+	return &dtpb.String{
+		Value: value.GetValue(),
+	}
+}
+
+// Time in time.go
+
+// UnsignedInt creates an R4 FHIR UnsignedInt element from a uint32 value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#unsignedInt
+func UnsignedInt(value uint32) *dtpb.UnsignedInt {
+	return &dtpb.UnsignedInt{
+		Value: value,
+	}
+}
+
+// URI creates an R4 FHIR URI element from a string value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#uri
+func URI(value string) *dtpb.Uri {
+	return &dtpb.Uri{
+		Value: value,
+	}
+}
+
+// URIFromCanonical is a convenience utility for converting a canonical to its
+// base-class definition of URI. If the input is nil, this returns nil.
+//
+// For more information, see the diagram for Primitive Types here:
+// https://www.hl7.org/fhir/datatypes.html
+func URIFromCanonical(canonical *dtpb.Canonical) *dtpb.Uri {
+	return uriFrom(canonical, canonical == nil)
+}
+
+// URIFromOID is a convenience utility for converting an OID to its
+// base-class definition of URI. If the input is nil, this returns nil.
+//
+// For more information, see the diagram for Primitive Types here:
+// https://www.hl7.org/fhir/datatypes.html
+func URIFromOID(oid *dtpb.Oid) *dtpb.Uri {
+	return uriFrom(oid, oid == nil)
+}
+
+// URIFromURL is a convenience utility for converting a URL to its
+// base-class definition of URI. If the input is nil, this returns nil.
+//
+// For more information, see the diagram for Primitive Types here:
+// https://www.hl7.org/fhir/datatypes.html
+func URIFromURL(url *dtpb.Url) *dtpb.Uri {
+	return uriFrom(url, url == nil)
+}
+
+// URIFromUUID is a convenience utility for converting a UUID to its
+// base-class definition of URI. If the input is nil, this returns nil.
+//
+// For more information, see the diagram for Primitive Types here:
+// https://www.hl7.org/fhir/datatypes.html
+func URIFromUUID(uuid *dtpb.Uuid) *dtpb.Uri {
+	return uriFrom(uuid, uuid == nil)
+}
+
+// uriFrom is a convenience helper for implementing the various `UriFrom*`
+// functions which are all the same repetative logic.
+//
+// This function takes 'isNil' as a boolean argument to work around the fact that
+// a nil pointer passed to an interface forms a non-nil interface in Go, and
+// reflection is more costly than a bool check.
+func uriFrom(other interface{ GetValue() string }, isNil bool) *dtpb.Uri {
+	if isNil {
+		return nil
+	}
+	return URI(other.GetValue())
+}
+
+// URL creates an R4 FHIR URL element from a string value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#url
+func URL(value string) *dtpb.Url {
+	return &dtpb.Url{
+		Value: value,
+	}
+}
+
+// UUID creates an R4 FHIR UUID element from a uuid-string value, prepending the
+// necessary "urn:uuid:" to the value.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#uuid
+func UUID(value string) *dtpb.Uuid {
+	return &dtpb.Uuid{
+		Value: fmt.Sprintf("urn:uuid:%v", value),
+	}
+}
+
+// RandomUUID generates a random new UUID.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#uuid
+func RandomUUID() *dtpb.Uuid {
+	return UUID(uuid.NewString())
+}
diff --git a/internal/fhir/elements_primitive_test.go b/internal/fhir/elements_primitive_test.go
new file mode 100644
index 0000000..0a76bd4
--- /dev/null
+++ b/internal/fhir/elements_primitive_test.go
@@ -0,0 +1,390 @@
+package fhir_test
+
+import (
+	"math"
+	"strings"
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/element/canonical"
+)
+
+func TestBase64Binary(t *testing.T) {
+	want := []byte{0xde, 0xad, 0xbe, 0xef}
+
+	sut := fhir.Base64Binary(want)
+
+	if got := sut.GetValue(); !cmp.Equal(got, want) {
+		t.Errorf("Base64Binary: got %v, want %v", got, want)
+	}
+}
+
+func TestBoolean(t *testing.T) {
+	want := true
+
+	sut := fhir.Boolean(want)
+
+	if got := sut.GetValue(); !cmp.Equal(got, want) {
+		t.Errorf("Boolean: got %v, want %v", got, want)
+	}
+}
+
+func TestCode(t *testing.T) {
+	want := "value"
+
+	sut := fhir.Code(want)
+
+	if got := sut.GetValue(); !cmp.Equal(got, want) {
+		t.Errorf("Code: got %v, want %v", got, want)
+	}
+}
+
+func TestID(t *testing.T) {
+	want := "id"
+
+	sut := fhir.ID(want)
+
+	if got := sut.GetValue(); !cmp.Equal(got, want) {
+		t.Errorf("ID: got %v, want %v", got, want)
+	}
+}
+
+func TestIsID(t *testing.T) {
+	testCases := []struct {
+		name    string
+		inputId string
+		wantOk  bool
+	}{
+		{"empty", "", false},
+		{"typical", "NormalId-.123", true},
+		{"valid inside bogus", "&&&hello", false},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			gotOk := fhir.IsID(tc.inputId)
+			if gotOk != tc.wantOk {
+				t.Errorf("IsID(%s) ok mismatch: got %v, want %v", tc.name, gotOk, tc.wantOk)
+			}
+		})
+	}
+}
+
+func TestInteger(t *testing.T) {
+	want := int32(42)
+
+	sut := fhir.Integer(want)
+
+	if got := sut.GetValue(); !cmp.Equal(got, want) {
+		t.Errorf("Integer: got %v, want %v", got, want)
+	}
+}
+
+func TestIntegerFromInt_Truncates_ReturnsError(t *testing.T) {
+	input := math.MaxInt64
+
+	_, err := fhir.IntegerFromInt(input)
+
+	if got, want := err, fhir.ErrIntegerDataLoss; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+		t.Errorf("IntegerFromInt: got %v, err %v", got, want)
+	}
+}
+
+func TestIntegerFromInt_ValidValue_ReturnsInteger(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value int
+	}{
+		{"Zero", 0},
+		{"MaxInt32", math.MaxInt32},
+		{"MinInt32", math.MinInt32},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			sut, err := fhir.IntegerFromInt(tc.value)
+			if err != nil {
+				t.Fatalf("IntegerFromInt(%v): unexpected error '%v'", tc.name, err)
+			}
+
+			if got, want := sut.GetValue(), tc.value; got != int32(want) {
+				t.Errorf("IntegerFromInt(%v): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestIntegerFromPositiveInt_Truncates_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value uint32
+	}{
+		{"OneOverInt32Max", math.MaxInt32 + 1},
+		{"UInt32Max", math.MaxUint32},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := fhir.IntegerFromPositiveInt(fhir.PositiveInt(tc.value))
+
+			if got, want := err, fhir.ErrIntegerDataLoss; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+				t.Errorf("IntegerFromPositiveInt(%v): got %v, err %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestIntegerFromPositiveInt_ValidValue_ReturnsInteger(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value uint32
+	}{
+		{"Zero", 0},
+		{"MaxInt32", math.MaxInt32},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			sut, err := fhir.IntegerFromPositiveInt(fhir.PositiveInt(tc.value))
+			if err != nil {
+				t.Fatalf("IntegerFromPositiveInt(%v): unexpected error '%v'", tc.name, err)
+			}
+
+			if got, want := sut.GetValue(), tc.value; got != int32(want) {
+				t.Errorf("IntegerFromPositiveInt(%v): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestIntegerFromUnsignedInt_Truncates_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value uint32
+	}{
+		{"OneOverInt32Max", math.MaxInt32 + 1},
+		{"UInt32Max", math.MaxUint32},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := fhir.IntegerFromUnsignedInt(fhir.UnsignedInt(tc.value))
+
+			if got, want := err, fhir.ErrIntegerDataLoss; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+				t.Errorf("IntegerFromUnsignedInt(%v): got %v, err %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestIntegerFromUnsignedInt_ValidValue_ReturnsInteger(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value uint32
+	}{
+		{"Zero", 0},
+		{"MaxInt32", math.MaxInt32},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			sut, err := fhir.IntegerFromUnsignedInt(fhir.UnsignedInt(tc.value))
+			if err != nil {
+				t.Fatalf("IntegerFromUnsignedInt(%v): unexpected error '%v'", tc.name, err)
+			}
+
+			if got, want := sut.GetValue(), tc.value; got != int32(want) {
+				t.Errorf("IntegerFromUnsignedInt(%v): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestMarkdown(t *testing.T) {
+	want := "This is **basically** just for code _coverage_; let's be honest."
+
+	sut := fhir.Markdown(want)
+
+	if got := sut.GetValue(); !cmp.Equal(got, want) {
+		t.Errorf("Markdown: got %v, want %v", got, want)
+	}
+}
+
+func TestOID(t *testing.T) {
+	const wantPrefix = "urn:oid:"
+	want := "foobar"
+
+	sut := fhir.OID(want)
+
+	if got := sut.GetValue(); !strings.HasPrefix(got, wantPrefix) {
+		t.Errorf("OID: got value '%v', want prefix '%v'", wantPrefix, got)
+	}
+	if got := sut.GetValue(); !strings.HasSuffix(got, want) {
+		t.Errorf("OID: got value '%v', want suffix '%v'", want, got)
+	}
+}
+
+func TestString(t *testing.T) {
+	want := "Lorem ipsum"
+
+	sut := fhir.String(want)
+
+	if got := sut.GetValue(); !cmp.Equal(got, want) {
+		t.Errorf("String: got %v, want %v", got, want)
+	}
+}
+
+func TestStrings(t *testing.T) {
+	want := []string{"Lorem", "ipsum", "dalor", "sit", "amet"}
+	toString := func(s *dtpb.String) string { return s.GetValue() }
+
+	got := fhir.Strings(want...)
+
+	if got := slices.Map(got, toString); !cmp.Equal(got, want) {
+		t.Errorf("String: got %v, want %v", got, want)
+	}
+}
+
+func TestStringFromCode(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value *dtpb.Code
+	}{
+		{"Nil", nil},
+		{"WithValue", fhir.Code("foobar")},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := fhir.StringFromCode(tc.value)
+
+			if got, want := got.GetValue(), tc.value.GetValue(); got != want {
+				t.Errorf("StringFromCode(%v): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestStringFromMarkdown(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value *dtpb.Markdown
+	}{
+		{"Nil", nil},
+		{"WithValue", fhir.Markdown("foobar")},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := fhir.StringFromMarkdown(tc.value)
+
+			if got, want := got.GetValue(), tc.value.GetValue(); got != want {
+				t.Errorf("StringFromMarkdown(%v): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestStringFromID(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value *dtpb.Id
+	}{
+		{"Nil", nil},
+		{"WithValue", fhir.ID("foobar")},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := fhir.StringFromID(tc.value)
+
+			if got, want := got.GetValue(), tc.value.GetValue(); got != want {
+				t.Errorf("StringFromID(%v): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestURIFromCanonical(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value *dtpb.Canonical
+	}{
+		{"Nil", nil},
+		{"WithValue", canonical.New("https://some-uri")},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := fhir.URIFromCanonical(tc.value)
+
+			if got, want := got.GetValue(), tc.value.GetValue(); got != want {
+				t.Errorf("URIFromCanonical(%v): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestURIFromOID(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value *dtpb.Oid
+	}{
+		{"Nil", nil},
+		{"WithValue", fhir.OID("https://some-uri")},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := fhir.URIFromOID(tc.value)
+
+			if got, want := got.GetValue(), tc.value.GetValue(); got != want {
+				t.Errorf("URIFromOID(%v): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestURIFromURL(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value *dtpb.Url
+	}{
+		{"Nil", nil},
+		{"WithValue", fhir.URL("https://some-uri.com")},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := fhir.URIFromURL(tc.value)
+
+			if got, want := got.GetValue(), tc.value.GetValue(); got != want {
+				t.Errorf("URIFromURL(%v): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestURIFromUUID(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value *dtpb.Uuid
+	}{
+		{"Nil", nil},
+		{"WithValue", fhir.RandomUUID()},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := fhir.URIFromUUID(tc.value)
+
+			if got, want := got.GetValue(), tc.value.GetValue(); got != want {
+				t.Errorf("URIFromUUID(%v): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
diff --git a/internal/fhir/elements_special.go b/internal/fhir/elements_special.go
new file mode 100644
index 0000000..629148f
--- /dev/null
+++ b/internal/fhir/elements_special.go
@@ -0,0 +1,26 @@
+package fhir
+
+import dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+
+// Special Types:
+//
+// The section below defines types from the "Special Types" heading in
+// http://hl7.org/fhir/R4/datatypes.html#open
+
+// Narrative creates a R4 FHIR Narrative element from a string value.
+//
+// See: http://hl7.org/fhir/R4/narrative.html
+func Narrative(value string) *dtpb.Narrative {
+	return &dtpb.Narrative{
+		Div: XHTML(value),
+	}
+}
+
+// XHTML creates an R4 FHIR XHTML element from a string value.
+//
+// See: http://hl7.org/fhir/R4/narrative.html#xhtml
+func XHTML(value string) *dtpb.Xhtml {
+	return &dtpb.Xhtml{
+		Value: value,
+	}
+}
diff --git a/internal/fhir/elements_special_test.go b/internal/fhir/elements_special_test.go
new file mode 100644
index 0000000..5c30b6e
--- /dev/null
+++ b/internal/fhir/elements_special_test.go
@@ -0,0 +1,28 @@
+package fhir_test
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+)
+
+func TestNarrative(t *testing.T) {
+	want := "<blah></blah>"
+
+	sut := fhir.Narrative(want)
+
+	if got := sut.GetDiv().GetValue(); !cmp.Equal(got, want) {
+		t.Errorf("Narrative: got %v, want %v", got, want)
+	}
+}
+
+func TestXHTML(t *testing.T) {
+	want := "<blah></blah>"
+
+	sut := fhir.XHTML(want)
+
+	if got := sut.GetValue(); !cmp.Equal(got, want) {
+		t.Errorf("XHTML: got %v, want %v", got, want)
+	}
+}
diff --git a/internal/fhir/encoding.go b/internal/fhir/encoding.go
new file mode 100644
index 0000000..2297dbe
--- /dev/null
+++ b/internal/fhir/encoding.go
@@ -0,0 +1,26 @@
+package fhir
+
+import "strings"
+
+// These characters have special meaning in FHIR Search queries
+const SearchSpecialChars = `\,$|`
+
+// Escape values intended for use as a parameter in a FHIR Search.
+//
+// These characters have special meaning in Search queries and must be backslash escaped:
+//
+//	`\`, `|`, `,`, `$`
+//
+// This function assumes that URL-encoding is performed later. (Percent
+// encoding is automatically handled by the healthcare client library when
+// query params are passed as a map.)
+//
+// For example, `foo,bar` becomes `foo\,bar`
+func EscapeSearchParam(value string) string {
+	out := value
+	for _, crune := range SearchSpecialChars {
+		c := string(crune)
+		out = strings.ReplaceAll(out, c, `\`+c)
+	}
+	return out
+}
diff --git a/internal/fhir/encoding_test.go b/internal/fhir/encoding_test.go
new file mode 100644
index 0000000..d8f3815
--- /dev/null
+++ b/internal/fhir/encoding_test.go
@@ -0,0 +1,33 @@
+package fhir_test
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+)
+
+func TestEscapeSearchParam(t *testing.T) {
+
+	testCases := []struct {
+		input string
+		want  string
+	}{
+		{``, ``},
+		{`\`, `\\`},
+		{`$`, `\$`},
+		{`,`, `\,`},
+		{`|`, `\|`},
+		{`C:\bin\go foo, bar, baz | omg $500!`, `C:\\bin\\go foo\, bar\, baz \| omg \$500!`},
+	}
+
+	for i, tc := range testCases {
+		t.Run(fmt.Sprintf("testCases[%d]", i), func(t *testing.T) {
+			got := fhir.EscapeSearchParam(tc.input)
+
+			if got != tc.want {
+				t.Errorf("got %#v, want %#v", got, tc.want)
+			}
+		})
+	}
+}
diff --git a/internal/fhir/iface.go b/internal/fhir/iface.go
new file mode 100644
index 0000000..4dc2919
--- /dev/null
+++ b/internal/fhir/iface.go
@@ -0,0 +1,146 @@
+package fhir
+
+import (
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/types/known/anypb"
+)
+
+// Base is the interface-definition of the FHIR abstract base type which is the
+// ancestor of all FHIR objects (both resources and elements).
+//
+// Represented in Go, this simply embeds the proto.Message interface, since this
+// is a utility for the google/fhir proto definitions.
+type Base interface {
+	proto.Message
+}
+
+// Resource is the interface-definition of the FHIR Abstract base type which is
+// the ancestor of all FHIR resources.
+// See https://www.hl7.org/fhir/r4/resource.html#Resource for more details.
+//
+// This interface is defined by embedding the proto.Message interface, since all
+// FHIR resources in this library must also be proto.Message types
+type Resource interface {
+	GetId() *dtpb.Id
+	GetImplicitRules() *dtpb.Uri
+	GetMeta() *dtpb.Meta
+	GetLanguage() *dtpb.Code
+	Base
+}
+
+// Extendable is an interface for abstraction resources or data-types that have
+// extension properties.
+//
+// This is not an official FHIR abstract class; this is something simply named
+// here for the general convenience, since not all FHIR types are extendable.
+//
+// This embeds the proto.Message interface into this interface to help distinguish
+// that this still refers to protos in the process.
+type Extendable interface {
+	GetExtension() []*dtpb.Extension
+	Base
+}
+
+// DomainResource is the interface-definition of the FHIR Abstract base type
+// which is the ancestor of all FHIR domain resource objects (effectively
+// everything that is not a datatype or bundle/contained-resource).
+// See https://www.hl7.org/fhir/r4/domainresource.html for more details.
+//
+// This interface extends from the `Resource` interface by embedding it. Any
+// `DomainResource` is also a `Resource`.
+type DomainResource interface {
+	GetText() *dtpb.Narrative
+	GetContained() []*anypb.Any
+	GetModifierExtension() []*dtpb.Extension
+	Extendable
+	Resource
+}
+
+// CanonicalResource represents resources that have a canonical URL:
+//
+//   - They have a canonical URL (note: all resources with a canonical URL are
+//     specializations of this type)
+//   - They have version, status, and data properties to help manage their publication
+//   - They carry some additional metadata about their use, including copyright information
+//
+// CanonicalResource objects may be the logical target of Canonical references.
+//
+// Note: This is technically an "R5" interface type that is not officially part
+// of the R4 spec, however its definition is still applicable and applies to "R4"
+// resource types. Using this still provides us with a proper vernacular for
+// referring to these resources.
+//
+// See https://www.hl7.org/fhir/r5/canonicalresource.html for more details.
+type CanonicalResource interface {
+	GetUrl() *dtpb.Uri
+	GetIdentifier() []*dtpb.Identifier
+	GetVersion() *dtpb.String
+	GetName() *dtpb.String
+	GetTitle() *dtpb.String
+	GetExperimental() *dtpb.Boolean
+	GetDate() *dtpb.DateTime
+	GetPublisher() *dtpb.String
+	GetContact() []*dtpb.ContactDetail
+	GetDescription() *dtpb.Markdown
+	GetUseContext() []*dtpb.UsageContext
+	GetJurisdiction() []*dtpb.CodeableConcept
+	GetPurpose() *dtpb.Markdown
+	GetCopyright() *dtpb.Markdown
+	// This interface should technically also have 'GetStatus()', however the
+	// return type differs based on resource type in the proto definitions -- and
+	// so this can't be referred to in a homogeneous way.
+	// GetStatus() interface{}
+
+	DomainResource
+}
+
+// MetadataResource represents resources that carry additional publication
+// metadata over other CanonicalResources, describing their review and use in
+// more details.
+//
+// As an interface, this type is never created directly.
+//
+// Note: This is technically an "R5" interface type that is not officially part
+// of the R4 spec, however its definition is still applicable and applies to "R4"
+// resource types. Using this still provides us with a proper vernacular for
+// referring to these resources.
+//
+// See https://www.hl7.org/fhir/r5/metadataresource.html for more details.
+type MetadataResource interface {
+	GetApprovalDate() *dtpb.Date
+	GetLastReviewDate() *dtpb.Date
+	GetEffectivePeriod() *dtpb.Period
+	GetTopic() []*dtpb.CodeableConcept
+	GetAuthor() []*dtpb.ContactDetail
+	GetEditor() []*dtpb.ContactDetail
+	GetReviewer() []*dtpb.ContactDetail
+	GetEndorser() []*dtpb.ContactDetail
+	GetRelatedArtifact() []*dtpb.RelatedArtifact
+
+	CanonicalResource
+}
+
+// Element is the base definition for all elements in a resource.
+//
+// See https://www.hl7.org/fhir/r4/element.html for more details.
+//
+// This interface is defined by embedding the proto.Message interface, since all
+// FHIR elements in this library must also be proto.Message types.
+type Element interface {
+	GetId() *dtpb.String
+	Extendable
+	Base
+}
+
+// BackboneElement is the base definition for all elements that are defined
+// inside a resource - but not those in a data type.
+//
+// See https://www.hl7.org/fhir/r4/backboneelement.html for more details.
+//
+// This interface is defined by embedding the proto.Message interface, since all
+// FHIR backbone elements in this library must also be proto.Message types.
+type BackboneElement interface {
+	GetModifierExtension() []*dtpb.Extension
+	Element
+}
diff --git a/internal/fhir/iface_test.go b/internal/fhir/iface_test.go
new file mode 100644
index 0000000..d201521
--- /dev/null
+++ b/internal/fhir/iface_test.go
@@ -0,0 +1,483 @@
+package fhir_test
+
+import (
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/account_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/activity_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/adverse_event_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/allergy_intolerance_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/appointment_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/appointment_response_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/audit_event_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/basic_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/binary_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/biologically_derived_product_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/body_structure_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/capability_statement_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/care_plan_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/care_team_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/catalog_entry_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/charge_item_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/charge_item_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/claim_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/claim_response_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/clinical_impression_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/code_system_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/communication_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/communication_request_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/compartment_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/composition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/concept_map_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/condition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/consent_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/contract_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/coverage_eligibility_request_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/coverage_eligibility_response_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/coverage_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/detected_issue_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_metric_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_request_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_use_statement_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/diagnostic_report_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/document_manifest_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/document_reference_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/effect_evidence_synthesis_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/encounter_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/endpoint_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/enrollment_request_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/enrollment_response_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/episode_of_care_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/event_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/evidence_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/evidence_variable_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/example_scenario_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/explanation_of_benefit_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/family_member_history_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/flag_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/goal_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/graph_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/group_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/guidance_response_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/healthcare_service_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/imaging_study_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/immunization_evaluation_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/immunization_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/immunization_recommendation_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/implementation_guide_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/insurance_plan_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/invoice_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/library_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/linkage_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/list_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/location_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/measure_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/measure_report_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/media_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_administration_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_dispense_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_knowledge_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_request_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_statement_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_authorization_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_contraindication_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_indication_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_ingredient_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_interaction_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_manufactured_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_packaged_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_pharmaceutical_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_undesirable_effect_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/message_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/message_header_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/molecular_sequence_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/naming_system_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/nutrition_order_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/observation_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/observation_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/operation_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/operation_outcome_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/organization_affiliation_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/organization_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/parameters_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/payment_notice_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/payment_reconciliation_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/person_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/plan_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/practitioner_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/practitioner_role_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/procedure_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/provenance_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/questionnaire_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/questionnaire_response_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/related_person_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/request_group_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/research_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/research_element_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/research_study_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/research_subject_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/risk_assessment_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/risk_evidence_synthesis_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/schedule_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/search_parameter_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/service_request_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/slot_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/specimen_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/specimen_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/structure_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/structure_map_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/subscription_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/substance_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/substance_nucleic_acid_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/substance_polymer_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/substance_protein_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/substance_reference_information_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/substance_source_material_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/substance_specification_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/supply_delivery_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/supply_request_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/task_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/terminology_capabilities_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/test_report_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/test_script_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/value_set_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/verification_result_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/vision_prescription_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+)
+
+// Note: The tests in this file are all statically validated by the interface
+// type assigned to the anonymous variables.
+
+// Resource types (https://www.hl7.org/fhir/resource.html#Resource).
+// This may exclude certain Trial-Use types that google/fhir doesn't implement.
+
+var _ fhir.Resource = (*account_go_proto.Account)(nil)
+var _ fhir.Resource = (*activity_definition_go_proto.ActivityDefinition)(nil)
+var _ fhir.Resource = (*adverse_event_go_proto.AdverseEvent)(nil)
+var _ fhir.Resource = (*allergy_intolerance_go_proto.AllergyIntolerance)(nil)
+var _ fhir.Resource = (*appointment_go_proto.Appointment)(nil)
+var _ fhir.Resource = (*appointment_response_go_proto.AppointmentResponse)(nil)
+var _ fhir.Resource = (*audit_event_go_proto.AuditEvent)(nil)
+var _ fhir.Resource = (*basic_go_proto.Basic)(nil)
+var _ fhir.Resource = (*biologically_derived_product_go_proto.BiologicallyDerivedProduct)(nil)
+var _ fhir.Resource = (*body_structure_go_proto.BodyStructure)(nil)
+var _ fhir.Resource = (*capability_statement_go_proto.CapabilityStatement)(nil)
+var _ fhir.Resource = (*care_plan_go_proto.CarePlan)(nil)
+var _ fhir.Resource = (*care_team_go_proto.CareTeam)(nil)
+var _ fhir.Resource = (*catalog_entry_go_proto.CatalogEntry)(nil)
+var _ fhir.Resource = (*charge_item_go_proto.ChargeItem)(nil)
+var _ fhir.Resource = (*charge_item_definition_go_proto.ChargeItemDefinition)(nil)
+var _ fhir.Resource = (*claim_go_proto.Claim)(nil)
+var _ fhir.Resource = (*claim_response_go_proto.ClaimResponse)(nil)
+var _ fhir.Resource = (*clinical_impression_go_proto.ClinicalImpression)(nil)
+var _ fhir.Resource = (*code_system_go_proto.CodeSystem)(nil)
+var _ fhir.Resource = (*communication_go_proto.Communication)(nil)
+var _ fhir.Resource = (*communication_request_go_proto.CommunicationRequest)(nil)
+var _ fhir.Resource = (*compartment_definition_go_proto.CompartmentDefinition)(nil)
+var _ fhir.Resource = (*composition_go_proto.Composition)(nil)
+var _ fhir.Resource = (*concept_map_go_proto.ConceptMap)(nil)
+var _ fhir.Resource = (*condition_go_proto.Condition)(nil)
+var _ fhir.Resource = (*consent_go_proto.Consent)(nil)
+var _ fhir.Resource = (*contract_go_proto.Contract)(nil)
+var _ fhir.Resource = (*coverage_go_proto.Coverage)(nil)
+var _ fhir.Resource = (*coverage_eligibility_request_go_proto.CoverageEligibilityRequest)(nil)
+var _ fhir.Resource = (*coverage_eligibility_response_go_proto.CoverageEligibilityResponse)(nil)
+var _ fhir.Resource = (*detected_issue_go_proto.DetectedIssue)(nil)
+var _ fhir.Resource = (*device_go_proto.Device)(nil)
+var _ fhir.Resource = (*device_definition_go_proto.DeviceDefinition)(nil)
+var _ fhir.Resource = (*device_metric_go_proto.DeviceMetric)(nil)
+var _ fhir.Resource = (*device_request_go_proto.DeviceRequest)(nil)
+var _ fhir.Resource = (*device_use_statement_go_proto.DeviceUseStatement)(nil)
+var _ fhir.Resource = (*diagnostic_report_go_proto.DiagnosticReport)(nil)
+var _ fhir.Resource = (*document_manifest_go_proto.DocumentManifest)(nil)
+var _ fhir.Resource = (*document_reference_go_proto.DocumentReference)(nil)
+var _ fhir.Resource = (*effect_evidence_synthesis_go_proto.EffectEvidenceSynthesis)(nil)
+var _ fhir.Resource = (*encounter_go_proto.Encounter)(nil)
+var _ fhir.Resource = (*endpoint_go_proto.Endpoint)(nil)
+var _ fhir.Resource = (*enrollment_request_go_proto.EnrollmentRequest)(nil)
+var _ fhir.Resource = (*enrollment_response_go_proto.EnrollmentResponse)(nil)
+var _ fhir.Resource = (*episode_of_care_go_proto.EpisodeOfCare)(nil)
+var _ fhir.Resource = (*event_definition_go_proto.EventDefinition)(nil)
+var _ fhir.Resource = (*evidence_go_proto.Evidence)(nil)
+var _ fhir.Resource = (*evidence_variable_go_proto.EvidenceVariable)(nil)
+var _ fhir.Resource = (*example_scenario_go_proto.ExampleScenario)(nil)
+var _ fhir.Resource = (*explanation_of_benefit_go_proto.ExplanationOfBenefit)(nil)
+var _ fhir.Resource = (*family_member_history_go_proto.FamilyMemberHistory)(nil)
+var _ fhir.Resource = (*flag_go_proto.Flag)(nil)
+var _ fhir.Resource = (*goal_go_proto.Goal)(nil)
+var _ fhir.Resource = (*graph_definition_go_proto.GraphDefinition)(nil)
+var _ fhir.Resource = (*group_go_proto.Group)(nil)
+var _ fhir.Resource = (*guidance_response_go_proto.GuidanceResponse)(nil)
+var _ fhir.Resource = (*healthcare_service_go_proto.HealthcareService)(nil)
+var _ fhir.Resource = (*imaging_study_go_proto.ImagingStudy)(nil)
+var _ fhir.Resource = (*immunization_go_proto.Immunization)(nil)
+var _ fhir.Resource = (*immunization_evaluation_go_proto.ImmunizationEvaluation)(nil)
+var _ fhir.Resource = (*immunization_recommendation_go_proto.ImmunizationRecommendation)(nil)
+var _ fhir.Resource = (*implementation_guide_go_proto.ImplementationGuide)(nil)
+var _ fhir.Resource = (*insurance_plan_go_proto.InsurancePlan)(nil)
+var _ fhir.Resource = (*invoice_go_proto.Invoice)(nil)
+var _ fhir.Resource = (*library_go_proto.Library)(nil)
+var _ fhir.Resource = (*linkage_go_proto.Linkage)(nil)
+var _ fhir.Resource = (*list_go_proto.List)(nil)
+var _ fhir.Resource = (*location_go_proto.Location)(nil)
+var _ fhir.Resource = (*measure_go_proto.Measure)(nil)
+var _ fhir.Resource = (*measure_report_go_proto.MeasureReport)(nil)
+var _ fhir.Resource = (*media_go_proto.Media)(nil)
+var _ fhir.Resource = (*medication_go_proto.Medication)(nil)
+var _ fhir.Resource = (*medication_administration_go_proto.MedicationAdministration)(nil)
+var _ fhir.Resource = (*medication_dispense_go_proto.MedicationDispense)(nil)
+var _ fhir.Resource = (*medication_knowledge_go_proto.MedicationKnowledge)(nil)
+var _ fhir.Resource = (*medication_request_go_proto.MedicationRequest)(nil)
+var _ fhir.Resource = (*medication_statement_go_proto.MedicationStatement)(nil)
+var _ fhir.Resource = (*medicinal_product_go_proto.MedicinalProduct)(nil)
+var _ fhir.Resource = (*medicinal_product_authorization_go_proto.MedicinalProductAuthorization)(nil)
+var _ fhir.Resource = (*medicinal_product_contraindication_go_proto.MedicinalProductContraindication)(nil)
+var _ fhir.Resource = (*medicinal_product_indication_go_proto.MedicinalProductIndication)(nil)
+var _ fhir.Resource = (*medicinal_product_ingredient_go_proto.MedicinalProductIngredient)(nil)
+var _ fhir.Resource = (*medicinal_product_interaction_go_proto.MedicinalProductInteraction)(nil)
+var _ fhir.Resource = (*medicinal_product_manufactured_go_proto.MedicinalProductManufactured)(nil)
+var _ fhir.Resource = (*medicinal_product_packaged_go_proto.MedicinalProductPackaged)(nil)
+var _ fhir.Resource = (*medicinal_product_pharmaceutical_go_proto.MedicinalProductPharmaceutical)(nil)
+var _ fhir.Resource = (*medicinal_product_undesirable_effect_go_proto.MedicinalProductUndesirableEffect)(nil)
+var _ fhir.Resource = (*message_definition_go_proto.MessageDefinition)(nil)
+var _ fhir.Resource = (*message_header_go_proto.MessageHeader)(nil)
+var _ fhir.Resource = (*molecular_sequence_go_proto.MolecularSequence)(nil)
+var _ fhir.Resource = (*naming_system_go_proto.NamingSystem)(nil)
+var _ fhir.Resource = (*nutrition_order_go_proto.NutritionOrder)(nil)
+var _ fhir.Resource = (*observation_go_proto.Observation)(nil)
+var _ fhir.Resource = (*observation_definition_go_proto.ObservationDefinition)(nil)
+var _ fhir.Resource = (*operation_definition_go_proto.OperationDefinition)(nil)
+var _ fhir.Resource = (*operation_outcome_go_proto.OperationOutcome)(nil)
+var _ fhir.Resource = (*organization_go_proto.Organization)(nil)
+var _ fhir.Resource = (*organization_affiliation_go_proto.OrganizationAffiliation)(nil)
+var _ fhir.Resource = (*patient_go_proto.Patient)(nil)
+var _ fhir.Resource = (*payment_notice_go_proto.PaymentNotice)(nil)
+var _ fhir.Resource = (*payment_reconciliation_go_proto.PaymentReconciliation)(nil)
+var _ fhir.Resource = (*person_go_proto.Person)(nil)
+var _ fhir.Resource = (*plan_definition_go_proto.PlanDefinition)(nil)
+var _ fhir.Resource = (*practitioner_go_proto.Practitioner)(nil)
+var _ fhir.Resource = (*practitioner_role_go_proto.PractitionerRole)(nil)
+var _ fhir.Resource = (*procedure_go_proto.Procedure)(nil)
+var _ fhir.Resource = (*provenance_go_proto.Provenance)(nil)
+var _ fhir.Resource = (*questionnaire_go_proto.Questionnaire)(nil)
+var _ fhir.Resource = (*questionnaire_response_go_proto.QuestionnaireResponse)(nil)
+var _ fhir.Resource = (*related_person_go_proto.RelatedPerson)(nil)
+var _ fhir.Resource = (*request_group_go_proto.RequestGroup)(nil)
+var _ fhir.Resource = (*research_definition_go_proto.ResearchDefinition)(nil)
+var _ fhir.Resource = (*research_element_definition_go_proto.ResearchElementDefinition)(nil)
+var _ fhir.Resource = (*research_study_go_proto.ResearchStudy)(nil)
+var _ fhir.Resource = (*research_subject_go_proto.ResearchSubject)(nil)
+var _ fhir.Resource = (*risk_assessment_go_proto.RiskAssessment)(nil)
+var _ fhir.Resource = (*risk_evidence_synthesis_go_proto.RiskEvidenceSynthesis)(nil)
+var _ fhir.Resource = (*schedule_go_proto.Schedule)(nil)
+var _ fhir.Resource = (*search_parameter_go_proto.SearchParameter)(nil)
+var _ fhir.Resource = (*service_request_go_proto.ServiceRequest)(nil)
+var _ fhir.Resource = (*slot_go_proto.Slot)(nil)
+var _ fhir.Resource = (*specimen_go_proto.Specimen)(nil)
+var _ fhir.Resource = (*specimen_definition_go_proto.SpecimenDefinition)(nil)
+var _ fhir.Resource = (*structure_definition_go_proto.StructureDefinition)(nil)
+var _ fhir.Resource = (*structure_map_go_proto.StructureMap)(nil)
+var _ fhir.Resource = (*subscription_go_proto.Subscription)(nil)
+var _ fhir.Resource = (*substance_go_proto.Substance)(nil)
+var _ fhir.Resource = (*substance_nucleic_acid_go_proto.SubstanceNucleicAcid)(nil)
+var _ fhir.Resource = (*substance_polymer_go_proto.SubstancePolymer)(nil)
+var _ fhir.Resource = (*substance_protein_go_proto.SubstanceProtein)(nil)
+var _ fhir.Resource = (*substance_reference_information_go_proto.SubstanceReferenceInformation)(nil)
+var _ fhir.Resource = (*substance_source_material_go_proto.SubstanceSourceMaterial)(nil)
+var _ fhir.Resource = (*substance_specification_go_proto.SubstanceSpecification)(nil)
+var _ fhir.Resource = (*supply_delivery_go_proto.SupplyDelivery)(nil)
+var _ fhir.Resource = (*supply_request_go_proto.SupplyRequest)(nil)
+var _ fhir.Resource = (*task_go_proto.Task)(nil)
+var _ fhir.Resource = (*terminology_capabilities_go_proto.TerminologyCapabilities)(nil)
+var _ fhir.Resource = (*test_report_go_proto.TestReport)(nil)
+var _ fhir.Resource = (*test_script_go_proto.TestScript)(nil)
+var _ fhir.Resource = (*value_set_go_proto.ValueSet)(nil)
+var _ fhir.Resource = (*verification_result_go_proto.VerificationResult)(nil)
+var _ fhir.Resource = (*vision_prescription_go_proto.VisionPrescription)(nil)
+var _ fhir.Resource = (*parameters_go_proto.Parameters)(nil)
+var _ fhir.Resource = (*binary_go_proto.Binary)(nil)
+var _ fhir.Resource = (*bundle_and_contained_resource_go_proto.Bundle)(nil)
+
+// DomainResource types (https://www.hl7.org/fhir/resource.html#DomainResource).
+// This may exclude certain Trial-Use types that google/fhir doesn't implement.
+
+var _ fhir.DomainResource = (*account_go_proto.Account)(nil)
+var _ fhir.DomainResource = (*activity_definition_go_proto.ActivityDefinition)(nil)
+var _ fhir.DomainResource = (*adverse_event_go_proto.AdverseEvent)(nil)
+var _ fhir.DomainResource = (*allergy_intolerance_go_proto.AllergyIntolerance)(nil)
+var _ fhir.DomainResource = (*appointment_go_proto.Appointment)(nil)
+var _ fhir.DomainResource = (*appointment_response_go_proto.AppointmentResponse)(nil)
+var _ fhir.DomainResource = (*audit_event_go_proto.AuditEvent)(nil)
+var _ fhir.DomainResource = (*basic_go_proto.Basic)(nil)
+var _ fhir.DomainResource = (*biologically_derived_product_go_proto.BiologicallyDerivedProduct)(nil)
+var _ fhir.DomainResource = (*body_structure_go_proto.BodyStructure)(nil)
+var _ fhir.DomainResource = (*capability_statement_go_proto.CapabilityStatement)(nil)
+var _ fhir.DomainResource = (*care_plan_go_proto.CarePlan)(nil)
+var _ fhir.DomainResource = (*care_team_go_proto.CareTeam)(nil)
+var _ fhir.DomainResource = (*catalog_entry_go_proto.CatalogEntry)(nil)
+var _ fhir.DomainResource = (*charge_item_go_proto.ChargeItem)(nil)
+var _ fhir.DomainResource = (*charge_item_definition_go_proto.ChargeItemDefinition)(nil)
+var _ fhir.DomainResource = (*claim_go_proto.Claim)(nil)
+var _ fhir.DomainResource = (*claim_response_go_proto.ClaimResponse)(nil)
+var _ fhir.DomainResource = (*clinical_impression_go_proto.ClinicalImpression)(nil)
+var _ fhir.DomainResource = (*code_system_go_proto.CodeSystem)(nil)
+var _ fhir.DomainResource = (*communication_go_proto.Communication)(nil)
+var _ fhir.DomainResource = (*communication_request_go_proto.CommunicationRequest)(nil)
+var _ fhir.DomainResource = (*compartment_definition_go_proto.CompartmentDefinition)(nil)
+var _ fhir.DomainResource = (*composition_go_proto.Composition)(nil)
+var _ fhir.DomainResource = (*concept_map_go_proto.ConceptMap)(nil)
+var _ fhir.DomainResource = (*condition_go_proto.Condition)(nil)
+var _ fhir.DomainResource = (*consent_go_proto.Consent)(nil)
+var _ fhir.DomainResource = (*contract_go_proto.Contract)(nil)
+var _ fhir.DomainResource = (*coverage_go_proto.Coverage)(nil)
+var _ fhir.DomainResource = (*coverage_eligibility_request_go_proto.CoverageEligibilityRequest)(nil)
+var _ fhir.DomainResource = (*coverage_eligibility_response_go_proto.CoverageEligibilityResponse)(nil)
+var _ fhir.DomainResource = (*detected_issue_go_proto.DetectedIssue)(nil)
+var _ fhir.DomainResource = (*device_go_proto.Device)(nil)
+var _ fhir.DomainResource = (*device_definition_go_proto.DeviceDefinition)(nil)
+var _ fhir.DomainResource = (*device_metric_go_proto.DeviceMetric)(nil)
+var _ fhir.DomainResource = (*device_request_go_proto.DeviceRequest)(nil)
+var _ fhir.DomainResource = (*device_use_statement_go_proto.DeviceUseStatement)(nil)
+var _ fhir.DomainResource = (*diagnostic_report_go_proto.DiagnosticReport)(nil)
+var _ fhir.DomainResource = (*document_manifest_go_proto.DocumentManifest)(nil)
+var _ fhir.DomainResource = (*document_reference_go_proto.DocumentReference)(nil)
+var _ fhir.DomainResource = (*effect_evidence_synthesis_go_proto.EffectEvidenceSynthesis)(nil)
+var _ fhir.DomainResource = (*encounter_go_proto.Encounter)(nil)
+var _ fhir.DomainResource = (*endpoint_go_proto.Endpoint)(nil)
+var _ fhir.DomainResource = (*enrollment_request_go_proto.EnrollmentRequest)(nil)
+var _ fhir.DomainResource = (*enrollment_response_go_proto.EnrollmentResponse)(nil)
+var _ fhir.DomainResource = (*episode_of_care_go_proto.EpisodeOfCare)(nil)
+var _ fhir.DomainResource = (*event_definition_go_proto.EventDefinition)(nil)
+var _ fhir.DomainResource = (*evidence_go_proto.Evidence)(nil)
+var _ fhir.DomainResource = (*evidence_variable_go_proto.EvidenceVariable)(nil)
+var _ fhir.DomainResource = (*example_scenario_go_proto.ExampleScenario)(nil)
+var _ fhir.DomainResource = (*explanation_of_benefit_go_proto.ExplanationOfBenefit)(nil)
+var _ fhir.DomainResource = (*family_member_history_go_proto.FamilyMemberHistory)(nil)
+var _ fhir.DomainResource = (*flag_go_proto.Flag)(nil)
+var _ fhir.DomainResource = (*goal_go_proto.Goal)(nil)
+var _ fhir.DomainResource = (*graph_definition_go_proto.GraphDefinition)(nil)
+var _ fhir.DomainResource = (*group_go_proto.Group)(nil)
+var _ fhir.DomainResource = (*guidance_response_go_proto.GuidanceResponse)(nil)
+var _ fhir.DomainResource = (*healthcare_service_go_proto.HealthcareService)(nil)
+var _ fhir.DomainResource = (*imaging_study_go_proto.ImagingStudy)(nil)
+var _ fhir.DomainResource = (*immunization_go_proto.Immunization)(nil)
+var _ fhir.DomainResource = (*immunization_evaluation_go_proto.ImmunizationEvaluation)(nil)
+var _ fhir.DomainResource = (*immunization_recommendation_go_proto.ImmunizationRecommendation)(nil)
+var _ fhir.DomainResource = (*implementation_guide_go_proto.ImplementationGuide)(nil)
+var _ fhir.DomainResource = (*insurance_plan_go_proto.InsurancePlan)(nil)
+var _ fhir.DomainResource = (*invoice_go_proto.Invoice)(nil)
+var _ fhir.DomainResource = (*library_go_proto.Library)(nil)
+var _ fhir.DomainResource = (*linkage_go_proto.Linkage)(nil)
+var _ fhir.DomainResource = (*list_go_proto.List)(nil)
+var _ fhir.DomainResource = (*location_go_proto.Location)(nil)
+var _ fhir.DomainResource = (*measure_go_proto.Measure)(nil)
+var _ fhir.DomainResource = (*measure_report_go_proto.MeasureReport)(nil)
+var _ fhir.DomainResource = (*media_go_proto.Media)(nil)
+var _ fhir.DomainResource = (*medication_go_proto.Medication)(nil)
+var _ fhir.DomainResource = (*medication_administration_go_proto.MedicationAdministration)(nil)
+var _ fhir.DomainResource = (*medication_dispense_go_proto.MedicationDispense)(nil)
+var _ fhir.DomainResource = (*medication_knowledge_go_proto.MedicationKnowledge)(nil)
+var _ fhir.DomainResource = (*medication_request_go_proto.MedicationRequest)(nil)
+var _ fhir.DomainResource = (*medication_statement_go_proto.MedicationStatement)(nil)
+var _ fhir.DomainResource = (*medicinal_product_go_proto.MedicinalProduct)(nil)
+var _ fhir.DomainResource = (*medicinal_product_authorization_go_proto.MedicinalProductAuthorization)(nil)
+var _ fhir.DomainResource = (*medicinal_product_contraindication_go_proto.MedicinalProductContraindication)(nil)
+var _ fhir.DomainResource = (*medicinal_product_indication_go_proto.MedicinalProductIndication)(nil)
+var _ fhir.DomainResource = (*medicinal_product_ingredient_go_proto.MedicinalProductIngredient)(nil)
+var _ fhir.DomainResource = (*medicinal_product_interaction_go_proto.MedicinalProductInteraction)(nil)
+var _ fhir.DomainResource = (*medicinal_product_manufactured_go_proto.MedicinalProductManufactured)(nil)
+var _ fhir.DomainResource = (*medicinal_product_packaged_go_proto.MedicinalProductPackaged)(nil)
+var _ fhir.DomainResource = (*medicinal_product_pharmaceutical_go_proto.MedicinalProductPharmaceutical)(nil)
+var _ fhir.DomainResource = (*medicinal_product_undesirable_effect_go_proto.MedicinalProductUndesirableEffect)(nil)
+var _ fhir.DomainResource = (*message_definition_go_proto.MessageDefinition)(nil)
+var _ fhir.DomainResource = (*message_header_go_proto.MessageHeader)(nil)
+var _ fhir.DomainResource = (*molecular_sequence_go_proto.MolecularSequence)(nil)
+var _ fhir.DomainResource = (*naming_system_go_proto.NamingSystem)(nil)
+var _ fhir.DomainResource = (*nutrition_order_go_proto.NutritionOrder)(nil)
+var _ fhir.DomainResource = (*observation_go_proto.Observation)(nil)
+var _ fhir.DomainResource = (*observation_definition_go_proto.ObservationDefinition)(nil)
+var _ fhir.DomainResource = (*operation_definition_go_proto.OperationDefinition)(nil)
+var _ fhir.DomainResource = (*operation_outcome_go_proto.OperationOutcome)(nil)
+var _ fhir.DomainResource = (*organization_go_proto.Organization)(nil)
+var _ fhir.DomainResource = (*organization_affiliation_go_proto.OrganizationAffiliation)(nil)
+var _ fhir.DomainResource = (*patient_go_proto.Patient)(nil)
+var _ fhir.DomainResource = (*payment_notice_go_proto.PaymentNotice)(nil)
+var _ fhir.DomainResource = (*payment_reconciliation_go_proto.PaymentReconciliation)(nil)
+var _ fhir.DomainResource = (*person_go_proto.Person)(nil)
+var _ fhir.DomainResource = (*plan_definition_go_proto.PlanDefinition)(nil)
+var _ fhir.DomainResource = (*practitioner_go_proto.Practitioner)(nil)
+var _ fhir.DomainResource = (*practitioner_role_go_proto.PractitionerRole)(nil)
+var _ fhir.DomainResource = (*procedure_go_proto.Procedure)(nil)
+var _ fhir.DomainResource = (*provenance_go_proto.Provenance)(nil)
+var _ fhir.DomainResource = (*questionnaire_go_proto.Questionnaire)(nil)
+var _ fhir.DomainResource = (*questionnaire_response_go_proto.QuestionnaireResponse)(nil)
+var _ fhir.DomainResource = (*related_person_go_proto.RelatedPerson)(nil)
+var _ fhir.DomainResource = (*request_group_go_proto.RequestGroup)(nil)
+var _ fhir.DomainResource = (*research_definition_go_proto.ResearchDefinition)(nil)
+var _ fhir.DomainResource = (*research_element_definition_go_proto.ResearchElementDefinition)(nil)
+var _ fhir.DomainResource = (*research_study_go_proto.ResearchStudy)(nil)
+var _ fhir.DomainResource = (*research_subject_go_proto.ResearchSubject)(nil)
+var _ fhir.DomainResource = (*risk_assessment_go_proto.RiskAssessment)(nil)
+var _ fhir.DomainResource = (*risk_evidence_synthesis_go_proto.RiskEvidenceSynthesis)(nil)
+var _ fhir.DomainResource = (*schedule_go_proto.Schedule)(nil)
+var _ fhir.DomainResource = (*search_parameter_go_proto.SearchParameter)(nil)
+var _ fhir.DomainResource = (*service_request_go_proto.ServiceRequest)(nil)
+var _ fhir.DomainResource = (*slot_go_proto.Slot)(nil)
+var _ fhir.DomainResource = (*specimen_go_proto.Specimen)(nil)
+var _ fhir.DomainResource = (*specimen_definition_go_proto.SpecimenDefinition)(nil)
+var _ fhir.DomainResource = (*structure_definition_go_proto.StructureDefinition)(nil)
+var _ fhir.DomainResource = (*structure_map_go_proto.StructureMap)(nil)
+var _ fhir.DomainResource = (*subscription_go_proto.Subscription)(nil)
+var _ fhir.DomainResource = (*substance_go_proto.Substance)(nil)
+var _ fhir.DomainResource = (*substance_nucleic_acid_go_proto.SubstanceNucleicAcid)(nil)
+var _ fhir.DomainResource = (*substance_polymer_go_proto.SubstancePolymer)(nil)
+var _ fhir.DomainResource = (*substance_protein_go_proto.SubstanceProtein)(nil)
+var _ fhir.DomainResource = (*substance_reference_information_go_proto.SubstanceReferenceInformation)(nil)
+var _ fhir.DomainResource = (*substance_source_material_go_proto.SubstanceSourceMaterial)(nil)
+var _ fhir.DomainResource = (*substance_specification_go_proto.SubstanceSpecification)(nil)
+var _ fhir.DomainResource = (*supply_delivery_go_proto.SupplyDelivery)(nil)
+var _ fhir.DomainResource = (*supply_request_go_proto.SupplyRequest)(nil)
+var _ fhir.DomainResource = (*task_go_proto.Task)(nil)
+var _ fhir.DomainResource = (*terminology_capabilities_go_proto.TerminologyCapabilities)(nil)
+var _ fhir.DomainResource = (*test_report_go_proto.TestReport)(nil)
+var _ fhir.DomainResource = (*test_script_go_proto.TestScript)(nil)
+var _ fhir.DomainResource = (*value_set_go_proto.ValueSet)(nil)
+var _ fhir.DomainResource = (*verification_result_go_proto.VerificationResult)(nil)
+var _ fhir.DomainResource = (*vision_prescription_go_proto.VisionPrescription)(nil)
+
+// CanonicalResource types from https://www.hl7.org/fhir/canonicalresource.html#bnr.
+// The list of CanonicalResources here is slightly smaller than on HL7's
+// site since it includes trial-use types, and objects with trial-use fields
+// which are not modeled in the google/fhir protos.
+
+var _ fhir.CanonicalResource = (*activity_definition_go_proto.ActivityDefinition)(nil)
+var _ fhir.CanonicalResource = (*code_system_go_proto.CodeSystem)(nil)
+var _ fhir.CanonicalResource = (*event_definition_go_proto.EventDefinition)(nil)
+var _ fhir.CanonicalResource = (*library_go_proto.Library)(nil)
+var _ fhir.CanonicalResource = (*measure_go_proto.Measure)(nil)
+var _ fhir.CanonicalResource = (*message_definition_go_proto.MessageDefinition)(nil)
+var _ fhir.CanonicalResource = (*plan_definition_go_proto.PlanDefinition)(nil)
+var _ fhir.CanonicalResource = (*questionnaire_go_proto.Questionnaire)(nil)
+var _ fhir.CanonicalResource = (*research_definition_go_proto.ResearchDefinition)(nil)
+var _ fhir.CanonicalResource = (*research_element_definition_go_proto.ResearchElementDefinition)(nil)
+var _ fhir.CanonicalResource = (*structure_definition_go_proto.StructureDefinition)(nil)
+var _ fhir.CanonicalResource = (*structure_map_go_proto.StructureMap)(nil)
+var _ fhir.CanonicalResource = (*value_set_go_proto.ValueSet)(nil)
+
+// MetadataResource types from https://www.hl7.org/fhir/metadataresource.html#bnr.
+// The list of MetadataResources here is slightly smaller than on HL7's
+// site since it includes trial-use types, and objects with trial-use fields
+// which are not modeled in the google/fhir protos.
+
+var _ fhir.MetadataResource = (*activity_definition_go_proto.ActivityDefinition)(nil)
+var _ fhir.MetadataResource = (*event_definition_go_proto.EventDefinition)(nil)
+var _ fhir.MetadataResource = (*library_go_proto.Library)(nil)
+var _ fhir.MetadataResource = (*measure_go_proto.Measure)(nil)
+var _ fhir.MetadataResource = (*plan_definition_go_proto.PlanDefinition)(nil)
+var _ fhir.MetadataResource = (*research_definition_go_proto.ResearchDefinition)(nil)
+var _ fhir.MetadataResource = (*research_element_definition_go_proto.ResearchElementDefinition)(nil)
diff --git a/internal/fhir/protofields.go b/internal/fhir/protofields.go
new file mode 100644
index 0000000..c594c51
--- /dev/null
+++ b/internal/fhir/protofields.go
@@ -0,0 +1,13 @@
+package fhir
+
+import (
+	"github.com/verily-src/fhirpath-go/internal/protofields"
+)
+
+// UnwrapValueX obtains the underlying Message for oneof ValueX
+// elements, which use a nested Choice field. Returns nil if the input message
+// doesn't have a Choice field, or if the Oneof descriptor is unpopulated.
+// See wrapped implementation for more information.
+func UnwrapValueX(element Base) Base {
+	return protofields.UnwrapOneofField(element, "choice")
+}
diff --git a/internal/fhir/time.go b/internal/fhir/time.go
new file mode 100644
index 0000000..40aac7e
--- /dev/null
+++ b/internal/fhir/time.go
@@ -0,0 +1,358 @@
+package fhir
+
+import (
+	"fmt"
+	"time"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+)
+
+// extractTimezone gets the string representation of a UTC offset.
+//
+// This source is taken from:
+// https://github.com/google/fhir/blob/1d1e7189749fcdbbececc1c70e00dd498bfb33d1/go/jsonformat/internal/jsonpbhelper/fhirutil.go#L453-L463
+func extractTimezone(t time.Time) string {
+	_, offset := t.Zone()
+	sign := "+"
+	if offset < 0 {
+		sign = "-"
+		offset = -offset
+	}
+	hour := offset / 3600
+	minute := offset % 3600 / 60
+	return fmt.Sprintf("%s%02d:%02d", sign, hour, minute)
+}
+
+// Date creates an R4 FHIR Date element from a Time value, accurate to a given
+// day.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#date
+func Date(t time.Time) *dtpb.Date {
+	return &dtpb.Date{
+		ValueUs:   t.UnixMicro(),
+		Precision: dtpb.Date_DAY,
+		Timezone:  extractTimezone(t),
+	}
+}
+
+// DateNow creates an R4 FHIR Date element at the current time using the
+// highest available precision.
+func DateNow() *dtpb.Date {
+	return Date(time.Now())
+}
+
+// DateTime creates an R4 FHIR DateTime element from a Time value, accurate
+// to the microsecond.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#datetime
+func DateTime(t time.Time) *dtpb.DateTime {
+	return &dtpb.DateTime{
+		ValueUs:   t.UnixMicro(),
+		Precision: dtpb.DateTime_MICROSECOND,
+		Timezone:  extractTimezone(t),
+	}
+}
+
+// DateTimeNow creates an R4 FHIR DateTime element at the current time using the
+// highest available precision.
+func DateTimeNow() *dtpb.DateTime {
+	return DateTime(time.Now())
+}
+
+// Instant creates an R4 FHIR Instant element from a Time value, accurate to the
+// microsecond.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#instant
+func Instant(t time.Time) *dtpb.Instant {
+	return &dtpb.Instant{
+		ValueUs:   t.UnixMicro(),
+		Precision: dtpb.Instant_MICROSECOND,
+		Timezone:  extractTimezone(t),
+	}
+}
+
+// InstantNow creates an R4 FHIR Instant element at the current time using the
+// highest available precision.
+func InstantNow() *dtpb.Instant {
+	return Instant(time.Now())
+}
+
+// Time creates an R4 FHIR Time element from a Time value.
+//
+// FHIR Time elements represent a time of day, disconnected from any date.
+// As a result, the value stored in this proto will be modulo 24-hours to keep
+// it within that 1 day time. Put differently, this will only ever be populated
+// with the number of microseconds since the start of the unix epoch, modulo one
+// day in microseconds.
+//
+// See: http://hl7.org/fhir/R4/datatypes.html#time
+func Time(t time.Time) *dtpb.Time {
+	return &dtpb.Time{
+		ValueUs:   t.UnixMicro() % (time.Hour * 24).Microseconds(),
+		Precision: dtpb.Time_MICROSECOND,
+	}
+}
+
+// TimeNow creates an R4 FHIR Time element at the current time using the
+// highest available precision.
+func TimeNow() *dtpb.Time {
+	return Time(time.Now())
+}
+
+// TimeOfDay creates a Time proto at the specified time.
+//
+// This function will return an error if any of the values exceed the valid
+// range for their time unit (e.g. if 'hour' exceeds 24, or minute exceeds 60, etc).
+//
+// The precision is determine by the value set for the micro second parameter;
+// if the value is 0, the precision is set to seconds. If the value is
+// a multiple of 1000, the value is set to millisecond precision; otherwise, its
+// set to microsecond precision.
+func TimeOfDay(hour, minute, second, micros int64) (*dtpb.Time, error) {
+	const (
+		maxHour   = 24
+		maxMinute = 60
+		maxSecond = 60
+		maxMicros = 1_000_000
+	)
+	if hour >= maxHour || hour < 0 {
+		return nil, fmt.Errorf("invalid hour '%v'; expected range is [0, %v)", hour, maxHour)
+	}
+	if minute >= maxMinute || minute < 0 {
+		return nil, fmt.Errorf("invalid minute '%v'; expected range is [0, %v)", minute, maxMinute)
+	}
+	if second >= maxSecond || second < 0 {
+		return nil, fmt.Errorf("invalid second '%v'; expected range is [0, %v)", second, maxSecond)
+	}
+	if micros >= maxMicros || micros < 0 {
+		return nil, fmt.Errorf("invalid micros '%v'; expected range is [0, %v)", micros, maxMicros)
+	}
+
+	precision := dtpb.Time_MICROSECOND
+	if micros == 0 {
+		precision = dtpb.Time_SECOND
+	} else if (micros % 1_000) == 0 {
+		precision = dtpb.Time_MILLISECOND
+	}
+
+	return &dtpb.Time{
+		ValueUs: hour*time.Hour.Microseconds() +
+			minute*time.Minute.Microseconds() +
+			second*time.Second.Microseconds() +
+			micros,
+		Precision: precision,
+	}, nil
+}
+
+// ParseDate converts the input string into a FHIR Date element.
+// The format of the input string must follow the FHIR Date format as defined
+// in http://hl7.org/fhir/R4/datatypes.html#date, e.g.
+//
+//   - YYYY,
+//   - YYYY-MM, or
+//   - YYYY-MM-DD
+//
+// The returned Date will have a precision equal to what was specified in the
+// input string.
+func ParseDate(value string) (*dtpb.Date, error) {
+	dateFormats := []struct {
+		format    string
+		precision dtpb.Date_Precision
+	}{
+		{"2006-01-02", dtpb.Date_DAY},
+		{"2006-01", dtpb.Date_MONTH},
+		{"2006", dtpb.Date_YEAR},
+	}
+
+	var t time.Time
+	var err error
+	for _, format := range dateFormats {
+		t, err = time.Parse(format.format, value)
+		if err == nil {
+			return &dtpb.Date{
+				ValueUs:   t.UnixMicro(),
+				Timezone:  extractTimezone(t),
+				Precision: format.precision,
+			}, nil
+		}
+	}
+	return nil, fmt.Errorf("unable to parse date '%v': %w", value, err)
+}
+
+// MustParseDate parses a date as according to ParseDate, but panics if the date
+// is invalid.
+func MustParseDate(value string) *dtpb.Date {
+	result, err := ParseDate(value)
+	if err != nil {
+		panic(err)
+	}
+	return result
+}
+
+// ParseDateTime converts the input string into a FHIR DateTime element.
+// The format of the input string must follow the FHIR DateTime format as defined
+// in http://hl7.org/fhir/R4/datatypes.html#datetime, e.g.
+//
+//   - YYYY,
+//   - YYYY-MM,
+//   - YYYY-MM-DD, or
+//   - YYYY-MM-DDThh:mm:ss+zz:zz (with optional milli/micro precision)
+//
+// The returned DateTime will have a precision equal to what was specified in the
+// input string.
+func ParseDateTime(value string) (*dtpb.DateTime, error) {
+	dateFormats := []struct {
+		format    string
+		precision dtpb.DateTime_Precision
+	}{
+		{"2006-01-02T15:04:05.000000-07:00", dtpb.DateTime_MICROSECOND},
+		{"2006-01-02T15:04:05.000000Z", dtpb.DateTime_MICROSECOND},
+		{"2006-01-02T15:04:05.000-07:00", dtpb.DateTime_MILLISECOND},
+		{"2006-01-02T15:04:05.000Z", dtpb.DateTime_MILLISECOND},
+		{"2006-01-02T15:04:05-07:00", dtpb.DateTime_SECOND},
+		{"2006-01-02T15:04:05Z", dtpb.DateTime_SECOND},
+		{"2006-01-02", dtpb.DateTime_DAY},
+		{"2006-01", dtpb.DateTime_MONTH},
+		{"2006", dtpb.DateTime_YEAR},
+	}
+
+	var t time.Time
+	var err error
+	for _, format := range dateFormats {
+		t, err = time.Parse(format.format, value)
+		if err == nil {
+			return &dtpb.DateTime{
+				ValueUs:   t.UnixMicro(),
+				Timezone:  extractTimezone(t),
+				Precision: format.precision,
+			}, nil
+		}
+	}
+	return nil, fmt.Errorf("unable to parse datetime '%v': %w", value, err)
+}
+
+// MustParseDateTime parses a date as according to ParseDateTime, but panics if
+// the date is invalid.
+func MustParseDateTime(value string) *dtpb.DateTime {
+	result, err := ParseDateTime(value)
+	if err != nil {
+		panic(err)
+	}
+	return result
+}
+
+// ParseInstant converts the input string into a FHIR Instant element.
+// The format of the input string must follow the FHIR Instant format as defined
+// in http://hl7.org/fhir/R4/datatypes.html#instant, e.g.
+//
+//   - yyyy-mm-ddThh:mm:ss+zz:zz,
+//   - yyyy-mm-ddThh:mm:ss.000+zz:zz, or
+//   - yyyy-mm-ddThh:mm:ss.000000+zz:zz,
+//
+// The returned Instant will have a precision equal to what was specified in the
+// input string.
+func ParseInstant(value string) (*dtpb.Instant, error) {
+	dateFormats := []struct {
+		format    string
+		precision dtpb.Instant_Precision
+	}{
+		{"2006-01-02T15:04:05.000000-07:00", dtpb.Instant_MICROSECOND},
+		{"2006-01-02T15:04:05.000000Z", dtpb.Instant_MICROSECOND},
+		{"2006-01-02T15:04:05.000-07:00", dtpb.Instant_MILLISECOND},
+		{"2006-01-02T15:04:05.000Z", dtpb.Instant_MILLISECOND},
+		{"2006-01-02T15:04:05-07:00", dtpb.Instant_SECOND},
+		{"2006-01-02T15:04:05Z", dtpb.Instant_SECOND},
+	}
+
+	var t time.Time
+	var err error
+	for _, format := range dateFormats {
+		t, err = time.Parse(format.format, value)
+		if err == nil {
+			return &dtpb.Instant{
+				ValueUs:   t.UnixMicro(),
+				Timezone:  extractTimezone(t),
+				Precision: format.precision,
+			}, nil
+		}
+	}
+	return nil, fmt.Errorf("unable to parse instant '%v': %w", value, err)
+}
+
+// MustParseInstant parses a date as according to ParseInstant, but panics if
+// the time is invalid.
+func MustParseInstant(value string) *dtpb.Instant {
+	result, err := ParseInstant(value)
+	if err != nil {
+		panic(err)
+	}
+	return result
+}
+
+// yearZeroBase is the time set for 0000-01-01.
+//
+// The time.Parse has the opinionated choice that any parsed time without any
+// year associated to it _must_ be offset from this date. Thus, we store this
+// so that parsed times will be offset from the unix timestamp instead.
+var yearZeroBase time.Time
+
+func init() {
+	tm, err := time.Parse("2006-01-02", "0000-01-01")
+	if err != nil {
+		panic(fmt.Sprintf("Unexpected error while creating base time: %v", err))
+	}
+	yearZeroBase = tm
+}
+
+// ParseTime converts the input string into a FHIR Time element.
+// The format of the input string must follow the FHIR Time format as defined
+// in http://hl7.org/fhir/R4/datatypes.html#time, e.g.
+//
+//   - hh:mm:ss,
+//   - hh:mm:ss.000, or
+//   - hh:mm:ss.000000,
+//
+// The returned Time will have a precision equal to what was specified in the
+// input string.
+func ParseTime(value string) (*dtpb.Time, error) {
+	dateFormats := []struct {
+		format    string
+		precision dtpb.Time_Precision
+	}{
+		{"15:04:05.000000", dtpb.Time_MICROSECOND},
+		{"15:04:05.000", dtpb.Time_MILLISECOND},
+		{"15:04:05", dtpb.Time_SECOND},
+	}
+
+	var t time.Time
+	var err error
+	for _, format := range dateFormats {
+		t, err = time.Parse(format.format, value)
+		if err != nil {
+			continue
+		}
+
+		// time.Parse without any date context forms a time offset from `0000-01-01`
+		// for some strange reason. This causes a large negative unix timestamp
+		// which is hard to make work for FHIR Time types. To compensate for this,
+		// we subtract the 0000-01-01 timestamp from our time, so that 00:00:00 will
+		// form a time of '0', e.g. it forces all times to be relative to the
+		// unix epoch.
+		value := t.UnixMicro() - yearZeroBase.UnixMicro()
+		return &dtpb.Time{
+			ValueUs:   value,
+			Precision: format.precision,
+		}, nil
+	}
+	return nil, fmt.Errorf("unable to parse time '%v': %w", value, err)
+}
+
+// MustParseTime parses a date as according to ParseTime, but panics if
+// the time is invalid.
+func MustParseTime(time string) *dtpb.Time {
+	result, err := ParseTime(time)
+	if err != nil {
+		panic(err)
+	}
+	return result
+}
diff --git a/internal/fhir/time_test.go b/internal/fhir/time_test.go
new file mode 100644
index 0000000..a91858b
--- /dev/null
+++ b/internal/fhir/time_test.go
@@ -0,0 +1,352 @@
+package fhir_test
+
+import (
+	"testing"
+	"time"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+)
+
+func TestTimeOfDay_BadInput_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name         string
+		hour         int64
+		minute       int64
+		second       int64
+		microseconds int64
+	}{
+		{"BigHour", 24, 10, 0, 0},
+		{"NegativeHour", -1, 10, 0, 0},
+		{"BigMinute", 12, 60, 0, 0},
+		{"NegativeMinute", 12, -1, 0, 0},
+		{"BigSecond", 12, 10, 60, 0},
+		{"NegativeSecond", 12, 10, -1, 0},
+		{"BigMicrosecond", 12, 10, 0, 1_000_000},
+		{"NegativeMicrosecond", 12, 10, 0, -1},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := fhir.TimeOfDay(tc.hour, tc.minute, tc.second, tc.microseconds)
+
+			if err == nil {
+				t.Errorf("TimeOfDay(%v): want err, got nil", tc.name)
+			}
+		})
+	}
+}
+
+func TestTimeOfDay_GoodInput_ReturnsTimeBelow24Hours(t *testing.T) {
+	testCases := []struct {
+		name         string
+		hour         int64
+		minute       int64
+		second       int64
+		microseconds int64
+	}{
+		{"NormalValue", 4, 20, 6, 9},
+		{"MinHour", 0, 20, 6, 9},
+		{"MaxHour", 23, 20, 6, 9},
+		{"MinMinute", 4, 0, 6, 9},
+		{"MaxMinute", 4, 59, 6, 9},
+		{"MinSecond", 4, 20, 0, 9},
+		{"MaxSecond", 4, 20, 59, 9},
+		{"MinMicros", 4, 20, 6, 0},
+		{"MaxMicros", 4, 20, 6, 999_999},
+		{"AlmostMidnight", 23, 59, 59, 999_999},
+		{"Midnight", 0, 0, 0, 0},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			day := (time.Hour * 24).Microseconds()
+			got, err := fhir.TimeOfDay(tc.hour, tc.minute, tc.second, tc.microseconds)
+			if err != nil {
+				t.Fatalf("TimeOfDay(%v): unexpected error: %v", tc.name, got)
+			}
+
+			if got, want := got.ValueUs, day; got >= want {
+				t.Errorf("TimeOfDay(%v): got us %v, want below %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestParseTime_GoodInput_ReturnsTime(t *testing.T) {
+	testCases := []struct {
+		name          string
+		value         string
+		wantPrecision dtpb.Time_Precision
+	}{
+		{"Second", "01:02:03", dtpb.Time_SECOND},
+		{"Milliseconds", "01:02:03.456", dtpb.Time_MILLISECOND},
+		{"Microseconds", "01:02:03.456789", dtpb.Time_MICROSECOND},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := fhir.ParseTime(tc.value)
+			if err != nil {
+				t.Fatalf("ParseTime(%v): got err: %v", tc.name, err)
+			}
+
+			t.Run("HasExpectedPrecision", func(t *testing.T) {
+				if got, want := got.Precision, tc.wantPrecision; got != want {
+					t.Errorf("ParseTime(%v): got %v, want %v", tc.name, got, want)
+				}
+			})
+		})
+	}
+}
+
+func TestParseTime_BadInput_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value string
+	}{
+		{"BadHour", "25:02:03"},
+		{"BadMinute", "01:61:03"},
+		{"BadSecond", "01:02:61"},
+		{"WrongFormat", "01:02:61:72"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := fhir.ParseTime(tc.value)
+
+			if err == nil {
+				t.Errorf("ParseTime(%v): want err, got nil", tc.name)
+			}
+		})
+	}
+}
+
+func TestMustParseTime_GoodInput_ReturnsTime(t *testing.T) {
+	got := fhir.MustParseTime("01:02:03.456789")
+
+	if got, want := got.Precision, dtpb.Time_MICROSECOND; got != want {
+		t.Errorf("MustParseTime: got %v, want %v", got, want)
+	}
+}
+
+func TestMustParseTime_BadInput_Panics(t *testing.T) {
+	defer func() { _ = recover() }()
+
+	fhir.MustParseTime("March 26, 1993")
+
+	// If code reaches here, it means we didn't panic
+	t.Errorf("MustParseTime: expected panic")
+}
+
+func TestParseDate_GoodInput_ReturnsDate(t *testing.T) {
+	testCases := []struct {
+		name          string
+		value         string
+		wantPrecision dtpb.Date_Precision
+	}{
+		{"Year", "2012", dtpb.Date_YEAR},
+		{"Month", "2012-10", dtpb.Date_MONTH},
+		{"Day", "2012-10-15", dtpb.Date_DAY},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := fhir.ParseDate(tc.value)
+			if err != nil {
+				t.Fatalf("ParseDate(%v): got err: %v", tc.name, err)
+			}
+
+			t.Run("HasExpectedPrecision", func(t *testing.T) {
+				if got, want := got.Precision, tc.wantPrecision; got != want {
+					t.Errorf("ParseDate(%v): got %v, want %v", tc.name, got, want)
+				}
+			})
+		})
+	}
+}
+
+func TestParseDate_BadInput_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value string
+	}{
+		{"BadMonth", "2012-13"},
+		{"BadDay", "2012-10-32"},
+		{"WrongFormat", "2012-10-32T10:32:00"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := fhir.ParseDate(tc.value)
+
+			if err == nil {
+				t.Fatalf("ParseDate(%v): want err, got nil", tc.name)
+			}
+		})
+	}
+}
+
+func TestMustParseDate_GoodInput_ReturnsTime(t *testing.T) {
+	got := fhir.MustParseDate("2012")
+
+	if got, want := got.Precision, dtpb.Date_YEAR; got != want {
+		t.Errorf("MustParseDate: got %v, want %v", got, want)
+	}
+}
+
+func TestMustParseDate_BadInput_Panics(t *testing.T) {
+	defer func() { _ = recover() }()
+
+	fhir.MustParseDate("March 26, 1993")
+
+	// If code reaches here, it means we didn't panic
+	t.Errorf("MustParseDate: expected panic")
+}
+
+func TestParseDateTime_GoodInput_ReturnsDateTime(t *testing.T) {
+	testCases := []struct {
+		name          string
+		value         string
+		wantPrecision dtpb.DateTime_Precision
+	}{
+		{"Year", "2012", dtpb.DateTime_YEAR},
+		{"Month", "2012-10", dtpb.DateTime_MONTH},
+		{"Day", "2012-10-15", dtpb.DateTime_DAY},
+		{"Second", "2012-10-15T01:02:03-04:00", dtpb.DateTime_SECOND},
+		{"Millisecond", "2012-10-15T01:02:03.123-04:00", dtpb.DateTime_MILLISECOND},
+		{"Microsecond", "2012-10-15T01:02:03.123456-04:00", dtpb.DateTime_MICROSECOND},
+		{"SecondZ", "2012-10-15T01:02:03Z", dtpb.DateTime_SECOND},
+		{"MillisecondZ", "2012-10-15T01:02:03.123Z", dtpb.DateTime_MILLISECOND},
+		{"MicrosecondZ", "2012-10-15T01:02:03.123456Z", dtpb.DateTime_MICROSECOND},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := fhir.ParseDateTime(tc.value)
+			if err != nil {
+				t.Fatalf("ParseDateTime(%v): got err: %v", tc.name, err)
+			}
+
+			t.Run("HasExpectedPrecision", func(t *testing.T) {
+				if got, want := got.Precision, tc.wantPrecision; got != want {
+					t.Errorf("ParseDateTime(%v): got %v, want %v", tc.name, got, want)
+				}
+			})
+		})
+	}
+}
+
+func TestParseDateTime_BadInput_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value string
+	}{
+		{"BadMonth", "2012-13"},
+		{"BadDay", "2012-10-32"},
+		{"BadHour", "2012-10-15T24:02:03-04:00"},
+		{"BadMinute", "2012-10-15T01:61:03-04:00"},
+		{"BadSecond", "2012-10-15T01:02:61-04:00"},
+		{"BadTimezone", "2012-10-15T01:02:61-61:00"},
+		{"WrongFormat", "January 02, 2015"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := fhir.ParseDateTime(tc.value)
+
+			if err == nil {
+				t.Errorf("ParseDateTime(%v): want err, got nil", tc.name)
+			}
+		})
+	}
+}
+
+func TestMustParseDateTime_GoodInput_ReturnsTime(t *testing.T) {
+	got := fhir.MustParseDateTime("2012")
+
+	if got, want := got.Precision, dtpb.DateTime_YEAR; got != want {
+		t.Errorf("MustParseDate: got %v, want %v", got, want)
+	}
+}
+
+func TestMustParseDateTime_BadInput_Panics(t *testing.T) {
+	defer func() { _ = recover() }()
+
+	fhir.MustParseDateTime("March 26, 1993")
+
+	// If code reaches here, it means we didn't panic
+	t.Errorf("MustParseDateTime: expected panic")
+}
+
+func TestParseInstant_GoodInput_ReturnsInstant(t *testing.T) {
+	testCases := []struct {
+		name          string
+		value         string
+		wantPrecision dtpb.Instant_Precision
+	}{
+		{"Second", "2019-01-02T01:02:03-04:00", dtpb.Instant_SECOND},
+		{"Millisecond", "2019-01-02T01:02:03.123-04:00", dtpb.Instant_MILLISECOND},
+		{"Microsecond", "2019-01-02T01:02:03.123456-04:00", dtpb.Instant_MICROSECOND},
+		{"SecondZ", "2019-01-02T01:02:03Z", dtpb.Instant_SECOND},
+		{"MillisecondZ", "2019-01-02T01:02:03.123Z", dtpb.Instant_MILLISECOND},
+		{"MicrosecondZ", "2019-01-02T01:02:03.123456Z", dtpb.Instant_MICROSECOND},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := fhir.ParseInstant(tc.value)
+			if err != nil {
+				t.Fatalf("ParseInstant(%v): got err: %v", tc.name, err)
+			}
+
+			t.Run("HasExpectedPrecision", func(t *testing.T) {
+				if got, want := got.Precision, tc.wantPrecision; got != want {
+					t.Errorf("ParseInstant(%v): got %v, want %v", tc.name, got, want)
+				}
+			})
+		})
+	}
+}
+
+func TestParseInstant_BadInput_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value string
+	}{
+		{"BadMonth", "2019-20-02T10:02:03-04:00"},
+		{"BadDay", "2019-01-40T10:02:03-04:00"},
+		{"BadHour", "2019-01-02T24:02:03-04:00"},
+		{"BadMinute", "2019-01-02T01:60:03-04:00"},
+		{"BadSecond", "2019-01-02T01:02:60-04:00"},
+		{"BadTimeZone", "2019-01-02T01:02:60-64:00"},
+		{"WrongFormat", "January 02, 2015"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := fhir.ParseInstant(tc.value)
+
+			if err == nil {
+				t.Errorf("ParseInstant(%v): want err, got nil", tc.name)
+			}
+		})
+	}
+}
+
+func TestMustParseInstant_GoodInput_ReturnsTime(t *testing.T) {
+	got := fhir.MustParseInstant("2019-10-02T01:02:03-04:00")
+
+	if got, want := got.Precision, dtpb.Instant_SECOND; got != want {
+		t.Errorf("MustParseInstant: got %v, want %v", got, want)
+	}
+}
+
+func TestMustParseInstant_BadInput_Panics(t *testing.T) {
+	defer func() { _ = recover() }()
+
+	fhir.MustParseInstant("March 26, 1993")
+
+	// If code reaches here, it means we didn't panic
+	t.Errorf("MustParseInstant: expected panic")
+}
diff --git a/internal/fhirconv/doc.go b/internal/fhirconv/doc.go
new file mode 100644
index 0000000..6dae525
--- /dev/null
+++ b/internal/fhirconv/doc.go
@@ -0,0 +1,5 @@
+/*
+Package fhirconv provides conversion utilities to Go-native types from FHIR R4
+Elements.
+*/
+package fhirconv
diff --git a/internal/fhirconv/integer.go b/internal/fhirconv/integer.go
new file mode 100644
index 0000000..5d09906
--- /dev/null
+++ b/internal/fhirconv/integer.go
@@ -0,0 +1,112 @@
+package fhirconv
+
+import (
+	"errors"
+	"fmt"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/narrow"
+	"golang.org/x/exp/constraints"
+)
+
+var (
+	// ErrIntegerTruncated is an error raised when an integer truncation occurs
+	// during integer conversion
+	ErrIntegerTruncated = errors.New("integer truncation")
+)
+
+// integerType is a constraint for FHIR integer types.
+type integerType interface {
+	*dtpb.Integer | *dtpb.UnsignedInt | *dtpb.PositiveInt
+}
+
+// ToInt8 converts a FHIR Integer type into a Go native int8.
+func ToInt8[From integerType](v From) (int8, error) {
+	return ToInteger[int8](v)
+}
+
+// ToInt16 converts a FHIR Integer type into a Go native int16.
+func ToInt16[From integerType](v From) (int16, error) {
+	return ToInteger[int16](v)
+}
+
+// ToInt32 converts a FHIR Integer type into a Go native int32.
+func ToInt32[From integerType](v From) (int32, error) {
+	return ToInteger[int32](v)
+}
+
+// ToInt64 converts a FHIR Integer type into a Go native int64.
+func ToInt64[From integerType](v From) (int64, error) {
+	return ToInteger[int64](v)
+}
+
+// ToInt converts a FHIR Integer type into a Go native int.
+func ToInt[From integerType](v From) (int, error) {
+	return ToInteger[int](v)
+}
+
+// ToUint8 converts a FHIR Integer type into a Go native uint8.
+func ToUint8[From integerType](v From) (uint8, error) {
+	return ToInteger[uint8](v)
+}
+
+// ToUint16 converts a FHIR Integer type into a Go native uint16.
+func ToUint16[From integerType](v From) (uint16, error) {
+	return ToInteger[uint16](v)
+}
+
+// ToUint32 converts a FHIR Integer type into a Go native uint32.
+func ToUint32[From integerType](v From) (uint32, error) {
+	return ToInteger[uint32](v)
+}
+
+// ToUint64 converts a FHIR Integer type into a Go native uint64.
+func ToUint64[From integerType](v From) (uint64, error) {
+	return ToInteger[uint64](v)
+}
+
+// ToUint converts a FHIR Integer type into a Go native uint.
+func ToUint[From integerType](v From) (uint, error) {
+	return ToInteger[uint](v)
+}
+
+// ToInteger converts a FHIR Integer type into a Go native integer type.
+//
+// If the value of the integer does not fit into the receiver integer type,
+// this function will return an ErrIntegerTruncated.
+func ToInteger[To constraints.Integer, From integerType](v From) (To, error) {
+	var result To
+	if val, ok := any(v).(interface{ GetValue() uint32 }); ok {
+		if result, ok := narrow.ToInteger[To](uint64(val.GetValue())); ok {
+			return result, nil
+		}
+		return 0, truncationError[To](val.GetValue())
+	} else if val, ok := any(v).(interface{ GetValue() int32 }); ok {
+
+		if result, ok := narrow.ToInteger[To](int64(val.GetValue())); ok {
+			return result, nil
+		}
+		return 0, truncationError[To](val.GetValue())
+	}
+	// This cannot be reached because this function is constrained to only
+	// take FHIR Elements that fit one of the above two types.
+	return result, ErrIntegerTruncated
+}
+
+// MustConvertToInteger converts a FHIR Integer type into a Go native integer type.
+//
+// If the value stored in the integer type cannot fit into the receiver type,
+// this function will panic.
+func MustConvertToInteger[To constraints.Integer, From integerType](v From) To {
+	result, err := ToInteger[To](v)
+	if err != nil {
+		panic(err)
+	}
+	return result
+}
+
+// truncationError forms an Error type for truncation errors.
+func truncationError[To constraints.Integer, From constraints.Integer](value From) error {
+	var result To
+	return fmt.Errorf("%w: type %T with value %v does not fit into receiver %T", ErrIntegerTruncated, value, value, result)
+}
diff --git a/internal/fhirconv/integer_test.go b/internal/fhirconv/integer_test.go
new file mode 100644
index 0000000..95bcc07
--- /dev/null
+++ b/internal/fhirconv/integer_test.go
@@ -0,0 +1,182 @@
+package fhirconv_test
+
+import (
+	"errors"
+	"math"
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/fhirconv"
+	"golang.org/x/exp/constraints"
+)
+
+type integerType interface {
+	*dtpb.Integer | *dtpb.UnsignedInt | *dtpb.PositiveInt
+}
+
+func wantTruncation[To constraints.Integer, From integerType](value From) func(*testing.T) {
+	return func(t *testing.T) {
+		t.Helper()
+		var to To
+
+		_, err := fhirconv.ToInteger[To](value)
+
+		if got, want := err, fhirconv.ErrIntegerTruncated; !errors.Is(got, want) {
+			t.Errorf("ToInteger[%T]: got err %v, want %v", to, got, want)
+		}
+	}
+}
+
+func wantConversion[To constraints.Integer, From integerType, Input constraints.Integer](conv func(Input) From, input Input) func(*testing.T) {
+	return func(t *testing.T) {
+		t.Helper()
+		var to To
+		value := conv(input)
+
+		got, err := fhirconv.ToInteger[To](value)
+		if err != nil {
+			t.Fatalf("ToInteger[%T]: unexpected error '%v'", to, err)
+		}
+
+		if got, want := got, To(input); got != want {
+			t.Errorf("ToInteger[%T]: got err %v, want %v", to, got, want)
+		}
+	}
+}
+
+func TestToInteger_ValueExceedsReceiver_ReturnsError(t *testing.T) {
+	// Note: nested sub-tests are being done both for organization and test naming.
+	// We also can't use table-driven tests here, since the input is a type -- so
+	// this forces duplication.
+	t.Run("FromPositiveInt", func(t *testing.T) {
+		t.Run("ToInt8", wantTruncation[int8](fhir.PositiveInt(math.MaxUint32)))
+		t.Run("ToInt16", wantTruncation[int16](fhir.PositiveInt(math.MaxUint32)))
+		t.Run("ToInt32", wantTruncation[int32](fhir.PositiveInt(math.MaxUint32)))
+		t.Run("ToUint8", wantTruncation[uint8](fhir.PositiveInt(math.MaxUint32)))
+		t.Run("ToUint16", wantTruncation[uint16](fhir.PositiveInt(math.MaxUint32)))
+	})
+
+	t.Run("FromUnsignedInt", func(t *testing.T) {
+		t.Run("ToInt8", wantTruncation[int8](fhir.UnsignedInt(math.MaxUint32)))
+		t.Run("ToInt16", wantTruncation[int16](fhir.UnsignedInt(math.MaxUint32)))
+		t.Run("ToInt32", wantTruncation[int32](fhir.UnsignedInt(math.MaxUint32)))
+		t.Run("ToUint8", wantTruncation[uint8](fhir.UnsignedInt(math.MaxUint32)))
+		t.Run("ToUint16", wantTruncation[uint16](fhir.UnsignedInt(math.MaxUint32)))
+	})
+
+	t.Run("FromInteger", func(t *testing.T) {
+		t.Run("ToInt8", wantTruncation[int8](fhir.Integer(math.MaxInt32)))
+		t.Run("ToInt16", wantTruncation[int16](fhir.Integer(math.MaxInt32)))
+		// int32, int64, and int can't truncate
+		t.Run("ToUint8", wantTruncation[uint8](fhir.Integer(-1)))
+		t.Run("ToUint16", wantTruncation[uint16](fhir.Integer(-1)))
+		t.Run("ToUint32", wantTruncation[uint32](fhir.Integer(-1)))
+		t.Run("ToUint32", wantTruncation[uint64](fhir.Integer(-1)))
+		t.Run("ToUintptr", wantTruncation[uintptr](fhir.Integer(-1)))
+		t.Run("ToUint", wantTruncation[uint](fhir.Integer(-1)))
+	})
+}
+
+func TestToInteger_ValueWithinRange_ReturnsValue(t *testing.T) {
+	// Note: nested sub-tests are being done both for organization and test naming.
+	// We also can't use table-driven tests here, since the input is a type -- so
+	// this forces duplication.
+	t.Run("FromPositiveInt", func(t *testing.T) {
+		t.Run("ToInt8", wantConversion[int8](fhir.PositiveInt, 0))
+		t.Run("ToInt8Max", wantConversion[int8](fhir.PositiveInt, math.MaxInt8))
+		t.Run("ToInt16", wantConversion[int16](fhir.PositiveInt, 0))
+		t.Run("ToInt16Max", wantConversion[int16](fhir.PositiveInt, math.MaxInt16))
+		t.Run("ToInt32", wantConversion[int32](fhir.PositiveInt, 0))
+		t.Run("ToInt32Max", wantConversion[int32](fhir.PositiveInt, math.MaxInt32))
+		t.Run("ToInt64", wantConversion[int64](fhir.PositiveInt, 0))
+		t.Run("ToInt64MaxUint32", wantConversion[int64](fhir.PositiveInt, math.MaxUint32))
+		t.Run("ToInt", wantConversion[int](fhir.PositiveInt, 0))
+		t.Run("ToIntMaxInt32", wantConversion[int](fhir.PositiveInt, math.MaxInt32))
+		t.Run("ToUint8", wantConversion[uint8](fhir.PositiveInt, 0))
+		t.Run("ToUint8Max", wantConversion[uint8](fhir.PositiveInt, math.MaxUint8))
+		t.Run("ToUint16", wantConversion[uint16](fhir.PositiveInt, 0))
+		t.Run("ToUint16Max", wantConversion[uint16](fhir.PositiveInt, math.MaxUint8))
+		t.Run("ToUint32", wantConversion[uint32](fhir.PositiveInt, 0))
+		t.Run("ToUint32Max", wantConversion[uint32](fhir.PositiveInt, math.MaxUint32))
+		t.Run("ToUint64", wantConversion[uint64](fhir.PositiveInt, 0))
+		t.Run("ToUint64MaxUint32", wantConversion[uint64](fhir.PositiveInt, math.MaxUint32))
+		t.Run("ToUint", wantConversion[uint](fhir.PositiveInt, 0))
+		t.Run("ToUintMaxUint32", wantConversion[uint](fhir.PositiveInt, math.MaxUint32))
+		t.Run("ToUintptr", wantConversion[uintptr](fhir.PositiveInt, 0))
+	})
+
+	t.Run("FromUnsignedInt", func(t *testing.T) {
+		t.Run("ToInt8", wantConversion[int8](fhir.UnsignedInt, 0))
+		t.Run("ToInt8Max", wantConversion[int8](fhir.UnsignedInt, math.MaxInt8))
+		t.Run("ToInt16", wantConversion[int16](fhir.UnsignedInt, 0))
+		t.Run("ToInt16Max", wantConversion[int16](fhir.UnsignedInt, math.MaxInt16))
+		t.Run("ToInt32", wantConversion[int32](fhir.UnsignedInt, 0))
+		t.Run("ToInt32Max", wantConversion[int32](fhir.UnsignedInt, math.MaxInt32))
+		t.Run("ToInt64", wantConversion[int64](fhir.UnsignedInt, 0))
+		t.Run("ToInt64MaxUint32", wantConversion[int64](fhir.UnsignedInt, math.MaxUint32))
+		t.Run("ToInt", wantConversion[int](fhir.UnsignedInt, 0))
+		t.Run("ToIntMaxInt32", wantConversion[int](fhir.UnsignedInt, math.MaxInt32))
+		t.Run("ToUint8", wantConversion[uint8](fhir.UnsignedInt, 0))
+		t.Run("ToUint8Max", wantConversion[uint8](fhir.UnsignedInt, math.MaxUint8))
+		t.Run("ToUint16", wantConversion[uint16](fhir.UnsignedInt, 0))
+		t.Run("ToUint16Max", wantConversion[uint16](fhir.UnsignedInt, math.MaxUint8))
+		t.Run("ToUint32", wantConversion[uint32](fhir.UnsignedInt, 0))
+		t.Run("ToUint32Max", wantConversion[uint32](fhir.UnsignedInt, math.MaxUint32))
+		t.Run("ToUint64", wantConversion[uint64](fhir.UnsignedInt, 0))
+		t.Run("ToUint64MaxUint32", wantConversion[uint64](fhir.UnsignedInt, math.MaxUint32))
+		t.Run("ToUint", wantConversion[uint](fhir.UnsignedInt, 0))
+		t.Run("ToUintMaxUint32", wantConversion[uint](fhir.UnsignedInt, math.MaxUint32))
+		t.Run("ToUintptr", wantConversion[uintptr](fhir.UnsignedInt, 0))
+	})
+
+	t.Run("FromInteger", func(t *testing.T) {
+		t.Run("ToInt8", wantConversion[int8](fhir.Integer, 0))
+		t.Run("ToInt8Max", wantConversion[int8](fhir.Integer, math.MaxInt8))
+		t.Run("ToInt8Min", wantConversion[int8](fhir.Integer, math.MinInt8))
+		t.Run("ToInt16", wantConversion[int16](fhir.Integer, 0))
+		t.Run("ToInt16Max", wantConversion[int16](fhir.Integer, math.MaxInt16))
+		t.Run("ToInt16Min", wantConversion[int16](fhir.Integer, math.MinInt16))
+		t.Run("ToInt32", wantConversion[int32](fhir.Integer, 0))
+		t.Run("ToInt32Max", wantConversion[int32](fhir.Integer, math.MaxInt32))
+		t.Run("ToInt32Min", wantConversion[int32](fhir.Integer, math.MinInt32))
+		t.Run("ToInt64", wantConversion[int64](fhir.Integer, 0))
+		t.Run("ToInt64MaxInt32", wantConversion[int64](fhir.Integer, math.MaxInt32))
+		t.Run("ToInt64MinInt32", wantConversion[int64](fhir.Integer, math.MinInt32))
+		t.Run("ToInt", wantConversion[int](fhir.Integer, 0))
+		t.Run("ToIntMaxInt32", wantConversion[int](fhir.Integer, math.MaxInt32))
+		t.Run("ToIntMinInt32", wantConversion[int](fhir.Integer, math.MinInt32))
+		t.Run("ToUint8", wantConversion[uint8](fhir.Integer, 0))
+		t.Run("ToUint8Max", wantConversion[uint8](fhir.Integer, math.MaxUint8))
+		t.Run("ToUint16", wantConversion[uint16](fhir.Integer, 0))
+		t.Run("ToUint16Max", wantConversion[uint16](fhir.Integer, math.MaxUint16))
+		t.Run("ToUint32", wantConversion[uint32](fhir.Integer, 0))
+		t.Run("ToUint32MaxInt32", wantConversion[uint32](fhir.Integer, math.MaxInt32))
+		t.Run("ToUint64", wantConversion[uint64](fhir.Integer, 0))
+		t.Run("ToUint64MaxInt32", wantConversion[uint64](fhir.Integer, math.MaxInt32))
+		t.Run("ToUint", wantConversion[uint](fhir.Integer, 0))
+		t.Run("ToUintMaxInt32", wantConversion[uint](fhir.Integer, math.MaxInt32))
+		t.Run("ToUintptr", wantConversion[uintptr](fhir.Integer, 0))
+	})
+}
+
+func TestMustConvertToInteger_ValueWithinRange_ReturnsValue(t *testing.T) {
+	input := int32(42)
+	value := fhir.Integer(input)
+
+	result := fhirconv.MustConvertToInteger[int32](value)
+
+	if got, want := result, input; got != want {
+		t.Fatalf("MustConvertToInteger[int32]: got %v, want %v", got, want)
+	}
+}
+
+func TestMustConvertToInteger_ValueOutsideRange_Panics(t *testing.T) {
+	defer func() { _ = recover() }()
+	value := fhir.Integer(-1)
+
+	fhirconv.MustConvertToInteger[uint8](value)
+
+	// This can't be reached if a panic occurs
+	t.Fatalf("MustConvertToInt: expected panic")
+}
diff --git a/internal/fhirconv/string.go b/internal/fhirconv/string.go
new file mode 100644
index 0000000..3e4516d
--- /dev/null
+++ b/internal/fhirconv/string.go
@@ -0,0 +1,158 @@
+package fhirconv
+
+import (
+	b64 "encoding/base64"
+	"fmt"
+	"time"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+)
+
+// ToString converts a basic FHIR Primitive into a human-readable string
+// representation.
+func ToString[T fhir.PrimitiveType](val T) string {
+	return toString(val)
+}
+
+// toString is the implementation of the ToString function.
+//
+// This is a separate function written in terms of an interface, rather than
+// being generic, to avoid unnecessary code-bloat -- since generics will generate
+// code for every instantiation with a different type.
+func toString(val fhir.Element) string {
+	switch val := val.(type) {
+	case interface{ GetValue() string }:
+		// URI, URL, OID, UUID, Canonical, String, ID, Markdown, Code, Decimal
+		return val.GetValue()
+	case interface{ GetValue() bool }:
+		// Boolean
+		return fmt.Sprintf("%v", val.GetValue())
+	case interface{ GetValue() uint32 }:
+		// PositiveInteger, UnsignedInt
+		return fmt.Sprintf("%v", val.GetValue())
+	case interface{ GetValue() int32 }:
+		// Integer
+		return fmt.Sprintf("%v", val.GetValue())
+	case interface{ GetValue() []byte }:
+		// Base64Binary
+		return b64.StdEncoding.EncodeToString(val.GetValue())
+	case *dtpb.Instant:
+		return InstantToString(val)
+	case *dtpb.DateTime:
+		return DateTimeToString(val)
+	case *dtpb.Time:
+		return TimeToString(val)
+	case *dtpb.Date:
+		return DateToString(val)
+	}
+	// This can't be reached; the above switch is exhaustive for all possible
+	// inputs, which is restricted by the type constraint.
+	return ""
+}
+
+// InstantToString converts the FHIR Instant element into its string reprsentation
+// as defined in http://hl7.org/fhir/R4/datatypes.html#instant.
+//
+// The level of precision in the output is equivalent to the precision defined
+// in the input Instant proto.
+func InstantToString(val *dtpb.Instant) string {
+	if tm, err := InstantToTime(val); err == nil {
+		switch val.GetPrecision() {
+		case dtpb.Instant_SECOND:
+			return tm.Format("2006-01-02T15:04:05-07:00")
+		case dtpb.Instant_MILLISECOND:
+			return tm.Format("2006-01-02T15:04:05.000-07:00")
+		case dtpb.Instant_MICROSECOND:
+			fallthrough
+		default:
+			return tm.Format("2006-01-02T15:04:05.000000-07:00")
+		}
+	}
+	// Fall-back to a basic representation (this shouldn't happen unless timezone
+	// information is garbage, which is a developer-driven issue).
+	return fmt.Sprintf("Instant(%v)", val.GetValueUs())
+}
+
+// DateTimeToString converts the FHIR DateTime element into its string reprsentation
+// as defined in http://hl7.org/fhir/R4/datatypes.html#datetime.
+//
+// The level of precision in the output is equivalent to the precision defined
+// in the input DateTime proto.
+func DateTimeToString(val *dtpb.DateTime) string {
+	if tm, err := DateTimeToTime(val); err == nil {
+		switch val.GetPrecision() {
+		case dtpb.DateTime_YEAR:
+			return tm.Format("2006")
+		case dtpb.DateTime_MONTH:
+			return tm.Format("2006-01")
+		case dtpb.DateTime_DAY:
+			return tm.Format("2006-01-02")
+		case dtpb.DateTime_SECOND:
+			return tm.Format("2006-01-02T15:04:05-07:00")
+		case dtpb.DateTime_MILLISECOND:
+			return tm.Format("2006-01-02T15:04:05.000-07:00")
+		case dtpb.DateTime_MICROSECOND:
+			fallthrough
+		default:
+			return tm.Format("2006-01-02T15:04:05.000000-07:00")
+		}
+	}
+
+	// Fall-back to a basic representation (this shouldn't happen unless timezone
+	// information is garbage, which is a developer-driven issue).
+	return fmt.Sprintf("DateTime(%v)", val.GetValueUs())
+}
+
+// DateToString converts the FHIR Date element into its string reprsentation
+// as defined in http://hl7.org/fhir/R4/datatypes.html#date.
+//
+// The level of precision in the output is equivalent to the precision defined
+// in the input Date proto.
+func DateToString(val *dtpb.Date) string {
+	if tm, err := DateToTime(val); err == nil {
+		switch val.GetPrecision() {
+		case dtpb.Date_YEAR:
+			return tm.Format("2006")
+		case dtpb.Date_MONTH:
+			return tm.Format("2006-01")
+		case dtpb.Date_DAY:
+			fallthrough
+		default:
+			return tm.Format("2006-01-02")
+		}
+	}
+
+	// Fall-back to a basic representation (this shouldn't happen unless timezone
+	// information is garbage, which is a developer-driven issue).
+	return fmt.Sprintf("Date(%v)", val.GetValueUs())
+}
+
+// TimeToString converts the FHIR Time element into its string reprsentation
+// as defined in http://hl7.org/fhir/R4/datatypes.html#time.
+//
+// The level of precision in the output is equivalent to the precision defined
+// in the input Time proto.
+func TimeToString(val *dtpb.Time) string {
+	duration := TimeToDuration(val)
+
+	hours := (duration / time.Hour) % (time.Hour * 24)
+	duration %= time.Hour
+	minutes := duration / time.Minute
+	duration %= time.Minute
+	seconds := duration / time.Second
+	duration %= time.Second
+	micros := duration / time.Microsecond
+
+	switch val.GetPrecision() {
+	case dtpb.Time_SECOND:
+		return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
+	case dtpb.Time_MILLISECOND:
+		millis := micros / 1_000
+		return fmt.Sprintf("%02d:%02d:%02d.%03d", hours, minutes, seconds, millis)
+	case dtpb.Time_MICROSECOND:
+		fallthrough
+	default:
+		return fmt.Sprintf("%02d:%02d:%02d.%06d", hours, minutes, seconds, micros)
+	}
+}
diff --git a/internal/fhirconv/string_test.go b/internal/fhirconv/string_test.go
new file mode 100644
index 0000000..0d7f550
--- /dev/null
+++ b/internal/fhirconv/string_test.go
@@ -0,0 +1,358 @@
+package fhirconv_test
+
+import (
+	"fmt"
+	"testing"
+	"time"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/fhirconv"
+)
+
+func TestToString_String_ReturnsValue(t *testing.T) {
+	want := "Hello world"
+	str := fhir.String(want)
+
+	got := fhirconv.ToString(str)
+
+	if got != want {
+		t.Errorf("String[String]: got %v, want %v", got, want)
+	}
+}
+
+func TestToString_Code_ReturnsValue(t *testing.T) {
+	want := "Hello world"
+	str := fhir.Code(want)
+
+	got := fhirconv.ToString(str)
+
+	if got != want {
+		t.Errorf("String[Code]: got %v, want %v", got, want)
+	}
+}
+
+func TestToString_ID_ReturnsValue(t *testing.T) {
+	want := "0xdeadbeef"
+	str := fhir.ID(want)
+
+	got := fhirconv.ToString(str)
+
+	if got != want {
+		t.Errorf("String[Id]: got %v, want %v", got, want)
+	}
+}
+
+func TestToString_Markdown_ReturnsValue(t *testing.T) {
+	want := "**This** is markdown"
+	str := fhir.Markdown(want)
+
+	got := fhirconv.ToString(str)
+
+	if got != want {
+		t.Errorf("String[Markdown]: got %v, want %v", got, want)
+	}
+}
+
+func TestToString_URI_ReturnsValue(t *testing.T) {
+	want := "https://example.com"
+	str := fhir.URI(want)
+
+	got := fhirconv.ToString(str)
+
+	if got != want {
+		t.Errorf("String[Uri]: got %v, want %v", got, want)
+	}
+}
+
+func TestToString_URL_ReturnsValue(t *testing.T) {
+	want := "https://example.com"
+	str := fhir.URL(want)
+
+	got := fhirconv.ToString(str)
+
+	if got != want {
+		t.Errorf("String[Url]: got %v, want %v", got, want)
+	}
+}
+
+func TestToString_OID_ReturnsValue(t *testing.T) {
+	want := "123456"
+	str := fhir.OID(want)
+
+	got := fhirconv.ToString(str)
+
+	if want := fmt.Sprintf("urn:oid:%v", want); got != want {
+		t.Errorf("String[Oid]: got %v, want %v", got, want)
+	}
+}
+
+func TestToString_UUID_ReturnsValue(t *testing.T) {
+	want := "25674bb5-6153-4dd7-9d4e-00fecc9058f1"
+	str := fhir.UUID(want)
+
+	got := fhirconv.ToString(str)
+
+	if want := fmt.Sprintf("urn:uuid:%v", want); got != want {
+		t.Errorf("String[Uuid]: got %v, want %v", got, want)
+	}
+}
+
+func TestToString_Boolean_ReturnsValue(t *testing.T) {
+	want := "true"
+	str := fhir.Boolean(true)
+
+	got := fhirconv.ToString(str)
+
+	if got != want {
+		t.Errorf("String[Boolean]: got %v, want %v", got, want)
+	}
+}
+
+func TestToString_PositiveInt_ReturnsValue(t *testing.T) {
+	want := "42"
+	str := fhir.PositiveInt(42)
+
+	got := fhirconv.ToString(str)
+
+	if got != want {
+		t.Errorf("String[PositiveInt]: got %v, want %v", got, want)
+	}
+}
+
+func TestToString_UnsignedInt_ReturnsValue(t *testing.T) {
+	want := "42"
+	str := fhir.UnsignedInt(42)
+
+	got := fhirconv.ToString(str)
+
+	if got != want {
+		t.Errorf("String[UnsignedInt]: got %v, want %v", got, want)
+	}
+}
+
+func TestToString_Integer_ReturnsValue(t *testing.T) {
+	want := "42"
+	str := fhir.Integer(42)
+
+	got := fhirconv.ToString(str)
+
+	if got != want {
+		t.Errorf("String[Integer]: got %v, want %v", got, want)
+	}
+}
+
+func TestToString_Base64Binary_ReturnsValue(t *testing.T) {
+	want := "3q2+7w=="
+	str := fhir.Base64Binary([]byte{0xde, 0xad, 0xbe, 0xef})
+
+	got := fhirconv.ToString(str)
+
+	if got != want {
+		t.Errorf("String[Base64Binary]: got %v, want %v", got, want)
+	}
+}
+
+func TestToString_Instant_ReturnsValue(t *testing.T) {
+	testCases := []struct {
+		name      string
+		want      string
+		precision dtpb.Instant_Precision
+	}{
+		{"UnspecifiedPrecision", "1970-01-01T00:00:32.000000+00:00", dtpb.Instant_PRECISION_UNSPECIFIED},
+		{"MicrosecondPrecision", "1970-01-01T00:00:32.000000+00:00", dtpb.Instant_MICROSECOND},
+		{"MillisecondPrecision", "1970-01-01T00:00:32.000+00:00", dtpb.Instant_MILLISECOND},
+		{"SecondPrecision", "1970-01-01T00:00:32+00:00", dtpb.Instant_SECOND},
+	}
+	timestamp := int64(time.Second * 32 / time.Microsecond)
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			input := fhir.Instant(time.UnixMicro(timestamp).UTC())
+			input.Precision = tc.precision
+
+			got := fhirconv.ToString(input)
+
+			if got, want := got, tc.want; got != want {
+				t.Errorf("ToString[Instant]: got %v, want %v", got, want)
+			}
+		})
+	}
+}
+
+func TestToString_DateTime_ReturnsValue(t *testing.T) {
+	testCases := []struct {
+		name      string
+		want      string
+		precision dtpb.DateTime_Precision
+	}{
+		{"UnspecifiedPrecision", "1970-01-01T00:00:32.000000+00:00", dtpb.DateTime_PRECISION_UNSPECIFIED},
+		{"MicrosecondPrecision", "1970-01-01T00:00:32.000000+00:00", dtpb.DateTime_MICROSECOND},
+		{"MillisecondPrecision", "1970-01-01T00:00:32.000+00:00", dtpb.DateTime_MILLISECOND},
+		{"SecondPrecision", "1970-01-01T00:00:32+00:00", dtpb.DateTime_SECOND},
+		{"DayPrecision", "1970-01-01", dtpb.DateTime_DAY},
+		{"MonthPrecision", "1970-01", dtpb.DateTime_MONTH},
+		{"YearPrecision", "1970", dtpb.DateTime_YEAR},
+	}
+	timestamp := int64(time.Second * 32 / time.Microsecond)
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			input := fhir.DateTime(time.UnixMicro(timestamp).UTC())
+			input.Precision = tc.precision
+
+			got := fhirconv.ToString(input)
+
+			if got, want := got, tc.want; got != want {
+				t.Errorf("ToString[DateTime]: got %v, want %v", got, want)
+			}
+		})
+	}
+}
+
+func TestToString_Date_ReturnsValue(t *testing.T) {
+	testCases := []struct {
+		name      string
+		want      string
+		precision dtpb.Date_Precision
+	}{
+		{"DayPrecision", "1970-01-01", dtpb.Date_DAY},
+		{"MonthPrecision", "1970-01", dtpb.Date_MONTH},
+		{"YearPrecision", "1970", dtpb.Date_YEAR},
+	}
+	timestamp := int64(time.Second * 32 / time.Microsecond)
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			input := fhir.Date(time.UnixMicro(timestamp).UTC())
+			input.Precision = tc.precision
+
+			got := fhirconv.ToString(input)
+
+			if got, want := got, tc.want; got != want {
+				t.Errorf("ToString[Date]: got %v, want %v", got, want)
+			}
+		})
+	}
+}
+
+func TestToString_Time_ReturnsValue(t *testing.T) {
+	testCases := []struct {
+		name                              string
+		hour, minute, second, microsecond int64
+		want                              string
+	}{
+		{"MicrosecondPrecision", 12, 32, 10, 123456, "12:32:10.123456"},
+		{"MillisecondPrecision", 12, 32, 10, 123_000, "12:32:10.123"},
+		{"SecondPrecision", 12, 32, 10, 0, "12:32:10"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			input, err := fhir.TimeOfDay(tc.hour, tc.minute, tc.second, tc.microsecond)
+			if err != nil {
+				t.Fatalf("ToString[Time]: error setting up time-of-day: %v", err)
+			}
+
+			got := fhirconv.ToString(input)
+
+			if got, want := got, tc.want; got != want {
+				t.Errorf("ToString[Date]: got %v, want %v", got, want)
+			}
+		})
+	}
+}
+
+func TestDateToString_RoundTrip_ReturnsInput(t *testing.T) {
+	testCases := []struct {
+		name string
+		want string
+	}{
+		{"YearPrecision", "2019"},
+		{"MonthPrecision", "2019-01"},
+		{"DayPrecision", "2019-01-02"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			value := fhir.MustParseDate(tc.want)
+
+			got := fhirconv.ToString(value)
+
+			if got != tc.want {
+				t.Errorf("RoundTrip(%v): got %v, want %v", tc.name, got, tc.want)
+			}
+		})
+	}
+}
+
+func TestTimeToString_RoundTrip_ReturnsInput(t *testing.T) {
+	testCases := []struct {
+		name string
+		want string
+	}{
+		{"MicrosecondPrecision", "01:02:03.123456"},
+		{"MillisecondPrecision", "01:02:03.123"},
+		{"SecondPrecision", "01:02:03"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			value := fhir.MustParseTime(tc.want)
+
+			got := fhirconv.ToString(value)
+
+			if got != tc.want {
+				t.Errorf("RoundTrip(%v): got %v, want %v", tc.name, got, tc.want)
+			}
+		})
+	}
+}
+
+func TestDateTimeToString_RoundTrip_ReturnsInput(t *testing.T) {
+	testCases := []struct {
+		name string
+		want string
+	}{
+		{"YearPrecision", "2019"},
+		{"MonthPrecision", "2019-01"},
+		{"DayPrecision", "2019-01-02"},
+		{"SecondPrecision", "2019-01-02T01:02:03-04:00"},
+		{"MillisecondPrecision", "2019-01-02T01:02:03.123-04:00"},
+		{"MicrosecondPrecision", "2019-01-02T01:02:03.123456-04:00"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			value := fhir.MustParseDateTime(tc.want)
+
+			got := fhirconv.ToString(value)
+
+			if got != tc.want {
+				t.Errorf("RoundTrip(%v): got %v, want %v", tc.name, got, tc.want)
+			}
+		})
+	}
+}
+func TestInstantToString_RoundTrip_ReturnsInput(t *testing.T) {
+	testCases := []struct {
+		name string
+		want string
+	}{
+		{"MicrosecondPrecision", "2019-01-02T01:02:03.123456-04:00"},
+		{"MillisecondPrecision", "2019-01-02T01:02:03.123-04:00"},
+		{"SecondPrecision", "2019-01-02T01:02:03-04:00"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			value := fhir.MustParseInstant(tc.want)
+
+			got := fhirconv.ToString(value)
+
+			if got != tc.want {
+				t.Errorf("RoundTrip(%v): got %v, want %v", tc.name, got, tc.want)
+			}
+		})
+	}
+}
diff --git a/internal/fhirconv/time.go b/internal/fhirconv/time.go
new file mode 100644
index 0000000..f7e3f83
--- /dev/null
+++ b/internal/fhirconv/time.go
@@ -0,0 +1,138 @@
+package fhirconv
+
+import (
+	"fmt"
+	"strconv"
+	"time"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/units"
+)
+
+// DateTimeToTime converts a FHIR DateTime element into a Go time.Time value.
+//
+// This function will only error if the TimeZone field is invalid.
+//
+// Note: the error that can be returned from this function is unlikely to actually
+// occur in practice. FHIR Timezones are always required to be specified in the
+// form `+zz:zz` or `-zz:zz` -- and the JSON conversion translates this into
+// exactly this form, an empty string, or "UTC" in some cases. These are all
+// valid locations and should never fail as a result.
+func DateTimeToTime(dt *dtpb.DateTime) (time.Time, error) {
+	tz, err := parseLocation(dt.GetTimezone())
+	if err != nil {
+		return time.Time{}, fmt.Errorf("fhirconv.TimeFromDateTime: %w", err)
+	}
+	return time.UnixMicro(dt.GetValueUs()).In(tz), nil
+}
+
+// InstantToTime converts a FHIR Instant element into a Go time.Time value.
+//
+// This function will only error if the TimeZone field is invalid.
+//
+// Note: the error that can be returned from this function is unlikely to actually
+// occur in practice. FHIR Timezones are always required to be specified in the
+// form `+zz:zz` or `-zz:zz` -- and the JSON conversion translates this into
+// exactly this form, an empty string, or "UTC" in some cases. These are all
+// valid locations and should never fail as a result.
+func InstantToTime(dt *dtpb.Instant) (time.Time, error) {
+	tz, err := parseLocation(dt.GetTimezone())
+	if err != nil {
+		return time.Time{}, fmt.Errorf("fhirconv.TimeFromInstant: %w", err)
+	}
+	return time.UnixMicro(dt.GetValueUs()).In(tz), nil
+}
+
+// DateToTime converts a FHIR Date element into a Go time.Time value.
+//
+// This function will only error if the TimeZone field is invalid.
+//
+// Note: the error that can be returned from this function is unlikely to actually
+// occur in practice. FHIR Timezones are always required to be specified in the
+// form `+zz:zz` or `-zz:zz` -- and the JSON conversion translates this into
+// exactly this form, an empty string, or "UTC" in some cases. These are all
+// valid locations and should never fail as a result.
+func DateToTime(dt *dtpb.Date) (time.Time, error) {
+	tz, err := parseLocation(dt.GetTimezone())
+	if err != nil {
+		return time.Time{}, fmt.Errorf("fhirconv.Date: %w", err)
+	}
+	return time.UnixMicro(dt.GetValueUs()).In(tz), nil
+}
+
+// TimeToDuration converts a FHIR Time element into a Go time.Duration value.
+//
+// Despite the name `Time` for the FHIR Element, the time is not actually
+// associated to any real date -- and thus does not correspond to a distinct
+// chronological point, and thus cannot be converted logically into a `time.Time`
+// object.
+func TimeToDuration(dt *dtpb.Time) time.Duration {
+	return time.Microsecond * time.Duration(dt.GetValueUs())
+}
+
+// parseLocation attempts to parse the timezone location from the zone string.
+//
+// Timezones may be specified in one of 3 formats:
+//   - Z
+//   - +zz:zz or -zz:zz
+//   - UTC (or some name)
+//
+// Additionally, this function supports empty strings being translated into
+// UTC.
+func parseLocation(zone string) (*time.Location, error) {
+	if zone == "" {
+		return time.UTC, nil
+	} else if tm, err := time.Parse("MST", zone); err == nil {
+		return tm.Location(), nil
+	} else if tm, err := time.Parse("Z07:00", zone); err == nil {
+		return tm.Location(), nil
+	} else {
+		return nil, fmt.Errorf("unable to parse time-zone from '%v'", zone)
+	}
+}
+
+// DurationToDuration converts a FHIR Duration element into a Go native
+// time.Duration object.
+//
+// This function may return an error in the following conditions:
+//   - The underlying Decimal value is not able to be parsed into a float64
+//   - The unit is not a valid time unit
+func DurationToDuration(d *dtpb.Duration) (time.Duration, error) {
+	value := d.GetValue().GetValue()
+	decimal, err := strconv.ParseFloat(value, 64)
+	if err != nil {
+		return durationToDurationError("bad decimal value '%v': %v", value, err)
+	}
+	code := d.GetCode().GetValue()
+
+	unit, err := units.TimeFromSymbol(code)
+	if err != nil {
+		return durationToDurationError("invalid unit symbol '%v'", code)
+	}
+
+	symbol := unit.Symbol()
+
+	// Special handling is necessary as days are not supported by
+	// time.ParseDuration, as well as the minutes unit being m, not min
+	switch unit {
+	case units.Minutes:
+		symbol = "m"
+	case units.Days:
+		decimal *= 24
+		symbol = units.Hours.Symbol()
+	}
+
+	duration, err := time.ParseDuration(fmt.Sprintf("%v%s", decimal, symbol))
+	if err != nil {
+		// This branch should not be possible to be reached. If we have reached this,
+		// something really bad has happened -- because we form the format string
+		// manually above.
+		return durationToDurationError("%v", err)
+	}
+	return duration, nil
+}
+
+func durationToDurationError(format string, args ...any) (time.Duration, error) {
+	message := fmt.Sprintf(format, args...)
+	return time.Duration(0), fmt.Errorf("fhirconv.DurationToDuration: %v", message)
+}
diff --git a/internal/fhirconv/time_test.go b/internal/fhirconv/time_test.go
new file mode 100644
index 0000000..7c776ed
--- /dev/null
+++ b/internal/fhirconv/time_test.go
@@ -0,0 +1,290 @@
+package fhirconv_test
+
+import (
+	"testing"
+	"time"
+
+	"github.com/google/fhir/go/fhirversion"
+	"github.com/google/fhir/go/jsonformat"
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/fhirconv"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func newMarshaller(t *testing.T) *jsonformat.Marshaller {
+	t.Helper()
+
+	marshaller, err := jsonformat.NewMarshaller(false, "", "", fhirversion.R4)
+	if err != nil {
+		t.Fatalf("Error creating marshaller: %v", err)
+	}
+	return marshaller
+}
+
+func newUnmarshaller(t *testing.T, zone string) *jsonformat.Unmarshaller {
+	t.Helper()
+
+	unmarshaller, err := jsonformat.NewUnmarshaller(zone, fhirversion.R4)
+	if err != nil {
+		t.Fatalf("Error creating unmarshaller: %v", err)
+	}
+	return unmarshaller
+}
+
+func dateTimeExtension(dt *dtpb.DateTime) *dtpb.Extension {
+	return &dtpb.Extension{
+		Value: &dtpb.Extension_ValueX{
+			Choice: &dtpb.Extension_ValueX_DateTime{
+				DateTime: dt,
+			},
+		},
+	}
+}
+
+func loadLocation(t *testing.T, name string) *time.Location {
+	t.Helper()
+
+	tz, err := time.LoadLocation(name)
+	if err != nil {
+		t.Fatalf("Unable to load location: %v", err)
+	}
+	return tz
+}
+
+func TestDateTimeToTime_InvalidInput_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name string
+		zone string
+	}{
+		{"InvalidTimeZoneCode", "XYZBLAH"},
+		{"LargePositiveMinuteOffset", "10:-90"},
+		{"LargeNegativeMinuteOffset", "10:90"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			dt := fhir.DateTimeNow()
+			dt.Timezone = tc.zone
+
+			_, err := fhirconv.DateTimeToTime(dt)
+
+			if err == nil {
+				t.Errorf("DateTimeToTime(%v): got nil, want err", tc.name)
+			}
+		})
+	}
+}
+
+func TestDateTimeToTime_ValidInput_ReturnsTime(t *testing.T) {
+	testCases := []struct {
+		name     string
+		zone     string
+		location *time.Location
+	}{
+		{"EmptyString", "", time.UTC},
+		{"UTC", "UTC", time.UTC},
+		{"EST", "EST", loadLocation(t, "EST")},
+		{"Z", "Z", time.UTC},
+		{"SmallPositiveOffset", "+00:01", time.FixedZone("", 60)},
+		{"SmallNegativeOffset", "-00:02", time.FixedZone("", -120)},
+		{"LargePositiveOffset", "+12:03", time.FixedZone("", 43380)},
+		{"LargeNegativeOffset", "-13:04", time.FixedZone("", -47040)},
+	}
+	const value = 1000
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			dt := fhir.DateTime(time.UnixMicro(value))
+			dt.Timezone = tc.zone
+			want := time.UnixMicro(value).In(tc.location)
+
+			got, err := fhirconv.DateTimeToTime(dt)
+			if err != nil {
+				t.Fatalf("DateTimeToTime(%v): got err '%v', want nil", tc.name, err)
+			}
+
+			if !cmp.Equal(got, want) {
+				t.Errorf("DateTimeToTime(%v): got '%v', want '%v'", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestDateToTime_InvalidInput_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name string
+		zone string
+	}{
+		{"InvalidTimeZoneCode", "XYZBLAH"},
+		{"LargePositiveMinuteOffset", "10:-90"},
+		{"LargeNegativeMinuteOffset", "10:90"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			dt := fhir.DateNow()
+			dt.Timezone = tc.zone
+
+			_, err := fhirconv.DateToTime(dt)
+
+			if err == nil {
+				t.Errorf("DateToTime(%v): got nil, want err", tc.name)
+			}
+		})
+	}
+}
+
+func TestTimeToTime_ValidInput_ReturnsTime(t *testing.T) {
+	testCases := []struct {
+		name     string
+		zone     string
+		location *time.Location
+	}{
+		{"EmptyString", "", time.UTC},
+		{"UTC", "UTC", time.UTC},
+		{"EST", "EST", loadLocation(t, "EST")},
+		{"Z", "Z", time.UTC},
+		{"SmallPositiveOffset", "+00:01", time.FixedZone("", 60)},
+		{"SmallNegativeOffset", "-00:02", time.FixedZone("", -120)},
+		{"LargePositiveOffset", "+12:03", time.FixedZone("", 43380)},
+		{"LargeNegativeOffset", "-13:04", time.FixedZone("", -47040)},
+	}
+	const value = 1000
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			dt := fhir.Date(time.UnixMicro(value))
+			dt.Timezone = tc.zone
+			want := time.UnixMicro(value).In(tc.location)
+
+			got, err := fhirconv.DateToTime(dt)
+			if err != nil {
+				t.Fatalf("DateToTime(%v): got err '%v', want nil", tc.name, err)
+			}
+
+			if !cmp.Equal(got, want) {
+				t.Errorf("DateToTime(%v): got '%v', want '%v'", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestInstantToTime_InvalidInput_ReturnsError(t *testing.T) {
+	testCases := []struct {
+		name string
+		zone string
+	}{
+		{"InvalidTimeZoneCode", "XYZBLAH"},
+		{"LargePositiveMinuteOffset", "10:-90"},
+		{"LargeNegativeMinuteOffset", "10:90"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			dt := fhir.InstantNow()
+			dt.Timezone = tc.zone
+
+			_, err := fhirconv.InstantToTime(dt)
+
+			if err == nil {
+				t.Errorf("InstantToTime(%v): got nil, want err", tc.name)
+			}
+		})
+	}
+}
+
+func TestInstantToTime_ValidInput_ReturnsTime(t *testing.T) {
+	testCases := []struct {
+		name     string
+		zone     string
+		location *time.Location
+	}{
+		{"EmptyString", "", time.UTC},
+		{"UTC", "UTC", time.UTC},
+		{"EST", "EST", loadLocation(t, "EST")},
+		{"Z", "Z", time.UTC},
+		{"SmallPositiveOffset", "+00:01", time.FixedZone("", 60)},
+		{"SmallNegativeOffset", "-00:02", time.FixedZone("", -120)},
+		{"LargePositiveOffset", "+12:03", time.FixedZone("", 43380)},
+		{"LargeNegativeOffset", "-13:04", time.FixedZone("", -47040)},
+	}
+	const value = 1000
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			dt := fhir.Instant(time.UnixMicro(value))
+			dt.Timezone = tc.zone
+			want := time.UnixMicro(value).In(tc.location)
+
+			got, err := fhirconv.InstantToTime(dt)
+			if err != nil {
+				t.Fatalf("InstantToTime(%v): got err '%v', want nil", tc.name, err)
+			}
+
+			if !cmp.Equal(got, want) {
+				t.Errorf("InstantToTime(%v): got '%v', want '%v'", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestDurationToDuration_BadInput_ReturnsError(t *testing.T) {
+	// Creates a base "good" Duration that we can modify for test purposes without
+	// coupling to the units package explicitly.
+	makeDuration := func(setup func(*dtpb.Duration)) *dtpb.Duration {
+		base := fhir.Seconds(time.Second * 10)
+		setup(base)
+		return base
+	}
+
+	testCases := []struct {
+		name     string
+		duration *dtpb.Duration
+	}{
+		{"BadFloatValue", makeDuration(func(d *dtpb.Duration) { d.Value.Value = "4e99999" })},
+		{"BadUnitSymbol", makeDuration(func(d *dtpb.Duration) { d.Code = fhir.Code("blarg") })},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := fhirconv.DurationToDuration(tc.duration)
+
+			if err == nil {
+				t.Errorf("DurationToDuration(%v): got nil, want err", tc.name)
+			}
+		})
+	}
+}
+
+func TestDurationToDuration_RoundTrip(t *testing.T) {
+	testCases := []struct {
+		name     string
+		duration *dtpb.Duration
+	}{
+		{"Nanoseconds", fhir.Nanoseconds(42)},
+		{"Microseconds", fhir.Microseconds(time.Microsecond * 3)},
+		{"Milliseconds", fhir.Milliseconds(time.Millisecond * 11)},
+		{"Seconds", fhir.Seconds(time.Second * 314)},
+		{"Minutes", fhir.Minutes(time.Minute * 8)},
+		{"Hours", fhir.Hours(time.Hour * 12)},
+		{"Days", fhir.Days(time.Hour * 24)},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			duration, err := fhirconv.DurationToDuration(tc.duration)
+			if err != nil {
+				t.Fatalf("DurationToDuration(%v): unexpected error %v", tc.name, err)
+			}
+
+			element := fhir.Duration(duration)
+
+			got, want := element, tc.duration
+			if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+				t.Errorf("DurationToDuration(%v): (-got +want):\n%v", tc.name, diff)
+			}
+		})
+	}
+}
diff --git a/internal/fhirtest/doc.go b/internal/fhirtest/doc.go
new file mode 100644
index 0000000..804e44f
--- /dev/null
+++ b/internal/fhirtest/doc.go
@@ -0,0 +1,12 @@
+/*
+Package fhirtest provides resource test dummies and useful utilities to enable
+better testing of the R4 FHIR Protos.
+
+This provides:
+  - Pseudo-randomized FHIR resource identity generation
+  - Construction utilities for forming new resources at runtime
+  - Utilities for emulating Meta updates to FHIR resources
+  - Resources are organized by their higher-level interface abstractions (e.g.
+    organized by Resource, DomainResource, etc), and are keyed by resource-name.
+*/
+package fhirtest
diff --git a/internal/fhirtest/elements.go b/internal/fhirtest/elements.go
new file mode 100644
index 0000000..344295d
--- /dev/null
+++ b/internal/fhirtest/elements.go
@@ -0,0 +1,46 @@
+package fhirtest
+
+import (
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/protofields"
+)
+
+var (
+	// Elements is a map of all element-names to an instance of that element type.
+	//
+	// The elements in this map are not guaranteed to contain any specific value;
+	// it is only guaranteed to contain a non-nil instance of a concrete element
+	// of the associated name.
+	Elements map[string]fhir.Element
+
+	// BackboneElements is a map of all backbone element-names to an instance of
+	// that element type.
+	//
+	// The elements in this map are not guaranteed to contain any specific value;
+	// it is only guaranteed to contain a non-nil instance of a concrete backbone
+	// element of the associated name.
+	BackboneElements map[string]fhir.BackboneElement
+)
+
+func init() {
+	Elements = map[string]fhir.Element{}
+	BackboneElements = map[string]fhir.BackboneElement{}
+
+	for _, msg := range protofields.Elements {
+		element, ok := msg.New().(fhir.Element)
+
+		// The proto definition of the XHtml type is missing `GetValue()`, and thus
+		// fails this check. This is added to avoid errors here that are otherwise
+		// correct for all other cases.
+		if !ok {
+			continue
+		}
+
+		name := protofields.DescriptorName(element)
+
+		Elements[name] = element
+		if val, ok := any(element).(fhir.BackboneElement); ok {
+			BackboneElements[name] = val
+		}
+	}
+}
diff --git a/internal/fhirtest/meta.go b/internal/fhirtest/meta.go
new file mode 100644
index 0000000..d05d929
--- /dev/null
+++ b/internal/fhirtest/meta.go
@@ -0,0 +1,65 @@
+package fhirtest
+
+import (
+	"time"
+
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/element/meta"
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+// TouchMeta touches the meta to generate a new version ID and use now as the
+// time. In most cases, this is likely what is required for tests -- since code
+// should seldomly rely on what the version-id or update times discretely are, and
+// this helps to ensure proper uniqueness just as the real FHIRStore would.
+func TouchMeta(resource fhir.Resource) {
+	UpdateMeta(resource, randomVersionID(), time.Now())
+}
+
+// UpdateID will update the resource's ID to the specified resourceID string.
+func UpdateID(resource fhir.Resource, resourceID string) {
+	reflect := resource.ProtoReflect()
+	field := reflect.Descriptor().Fields().ByName("id")
+
+	id := &datatypes_go_proto.Id{
+		Value: resourceID,
+	}
+	reflect.Set(field, protoreflect.ValueOfMessage(id.ProtoReflect()))
+}
+
+// UpdateMeta updates the meta contents of the fhir resource to use the new
+// version-ID and update-time.
+func UpdateMeta(resource fhir.Resource, versionID string, updateTime time.Time) {
+	reflect := resource.ProtoReflect()
+	metaField := getMetaField(reflect)
+	time := fhir.Instant(updateTime)
+	version := fhir.ID(versionID)
+
+	if resource.GetMeta() == nil {
+		meta := &datatypes_go_proto.Meta{
+			LastUpdated: time,
+			VersionId:   version,
+		}
+		reflect.Set(metaField, protoreflect.ValueOfMessage(meta.ProtoReflect()))
+	}
+
+	message := reflect.Get(metaField).Message()
+	descriptor := message.Descriptor()
+	fields := descriptor.Fields()
+	updateField := fields.ByName("last_updated")
+	versionField := fields.ByName("version_id")
+
+	message.Set(updateField, protoreflect.ValueOfMessage(time.ProtoReflect()))
+	message.Set(versionField, protoreflect.ValueOfMessage(version.ProtoReflect()))
+}
+
+// NOTE: This method is deprecated and should use the production one in
+// "github.com/verily-src/fhirpath-go/internal/element/meta"
+func ReplaceMeta(resource fhir.Resource, m *datatypes_go_proto.Meta) {
+	meta.ReplaceInResource(resource, m)
+}
+
+func getMetaField(reflect protoreflect.Message) protoreflect.FieldDescriptor {
+	return reflect.Descriptor().Fields().ByName("meta")
+}
diff --git a/internal/fhirtest/random.go b/internal/fhirtest/random.go
new file mode 100644
index 0000000..76f626f
--- /dev/null
+++ b/internal/fhirtest/random.go
@@ -0,0 +1,69 @@
+package fhirtest
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/stablerand"
+)
+
+// stableRandomID generates a random ID value that will be stable across
+// multiple test executions.
+func stableRandomID() *datatypes_go_proto.Id {
+	return &datatypes_go_proto.Id{
+		Value: randomID(),
+	}
+}
+
+// stableRandomVersionID generates a random version-ID value that will be
+// stable across multiple test executions.
+func stableRandomVersionID() *datatypes_go_proto.Id {
+	return &datatypes_go_proto.Id{
+		Value: randomVersionID(),
+	}
+}
+
+func stableRandomInstant() *datatypes_go_proto.Instant {
+	return fhir.Instant(stableRandomTime())
+}
+
+func StableRandomMeta() *datatypes_go_proto.Meta {
+	return &datatypes_go_proto.Meta{
+		LastUpdated: stableRandomInstant(),
+		VersionId:   stableRandomVersionID(),
+	}
+}
+
+func randomVersionID() string {
+	const versionIDLength = 26
+	return stablerand.AlnumString(versionIDLength)
+}
+
+// This is a different implementation than fhir.RandomID() since this test
+// library manually sets a random seed.
+func randomID() string {
+	uuidBase := stablerand.HexString(32)
+	return fmt.Sprintf(
+		"%v-%v-%v-%v-%v",
+		uuidBase[0:8],
+		uuidBase[8:12],
+		uuidBase[12:16],
+		uuidBase[16:20],
+		uuidBase[20:],
+	)
+}
+
+func stableRandomTime() time.Time {
+	const (
+		// Timestamp for 2020-01-01 T12:00:00
+		baseTime int64 = 1577898000
+
+		// Variation of up to 1 year
+		timeVariation = time.Hour * 24 * 365
+	)
+
+	base := time.Unix(baseTime, 0)
+	return stablerand.Time(base, timeVariation)
+}
diff --git a/internal/fhirtest/resources.go b/internal/fhirtest/resources.go
new file mode 100644
index 0000000..b1a0826
--- /dev/null
+++ b/internal/fhirtest/resources.go
@@ -0,0 +1,298 @@
+package fhirtest
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/protofields"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"google.golang.org/protobuf/encoding/protojson"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+// Resources is a map of all resource-names to an instance of that resource type.
+var Resources map[string]fhir.Resource
+
+// DomainResources is a map of all domain-resource-names to an instance of that
+// domain-resource type.
+var DomainResources map[string]fhir.DomainResource
+
+// CanonicalResources is a map of all canonical-resource-names to an instance of
+// that canonical-resource type.
+var CanonicalResources map[string]fhir.CanonicalResource
+
+// MetadataResources is a map of all metadata-resource-names to an instance of
+// that metadata-resource type
+var MetadataResources map[string]fhir.MetadataResource
+
+// ResourceOption is an option type acting on resources for setting up a resource.
+type ResourceOption func(*testing.T, fhir.Resource)
+
+// NewResource creates a dummy resource object for the purposes of testing
+// of type `resourceType. If `resourceType` does not name a valid R4 FHIR resource,
+// this will fail the calling test.
+func NewResource(t *testing.T, resourceType resource.Type, opts ...ResourceOption) fhir.Resource {
+	t.Helper()
+
+	if val, ok := Resources[string(resourceType)]; ok {
+		object := proto.Clone(val).(fhir.Resource)
+		ReplaceMeta(object, StableRandomMeta())
+		UpdateID(object, stableRandomID().Value)
+		for _, opt := range opts {
+			opt(t, object)
+		}
+		return object
+	}
+
+	// This should be unreachable in valid code
+	t.Fatalf("No resource of type %v found. Please specify a valid resource, or update this package.", resourceType)
+	return nil
+}
+
+// NewResource creates a dummy resource object for the purposes of testing
+// of type `T. If `T` is not a valid FHIR R4 resource, this will fail testing.
+func NewResourceOf[T fhir.Resource](t *testing.T, opts ...ResourceOption) T {
+	t.Helper()
+	var res T
+	resourceType := resource.TypeOf(res)
+
+	return NewResource(t, resourceType, opts...).(T)
+}
+
+// NewResourceFromBase returns a copy of a resource modified by given options. The
+// input resource remains unmodified. Useful for tweaking an existing resource
+// for test inputs.
+func NewResourceFromBase(t *testing.T, resource fhir.Resource, opts ...ResourceOption) fhir.Resource {
+	t.Helper()
+
+	modifiedResource := proto.Clone(resource).(fhir.Resource)
+	for _, opt := range opts {
+		opt(t, modifiedResource)
+	}
+
+	return modifiedResource
+}
+
+// WithResourceModification applies a modifier on a resource. Resource and
+// modifier must be the same type.
+func WithResourceModification[T fhir.Resource](modifier func(T)) ResourceOption {
+	return func(t *testing.T, res fhir.Resource) {
+		t.Helper()
+		val, ok := res.(T)
+		if !ok {
+			var modifierType T
+			t.Fatalf("Modifier type != applied resource type: (%s) != (%s)", resource.TypeOf(modifierType), resource.TypeOf(res))
+		}
+		modifier(val)
+	}
+}
+
+// WithJSONField creates a ResourceOpt for setting up the specified proto-field
+// with the given value in JSON format.
+//
+// This will fail tests if the field is invalid, or if the JSON does not
+// unmarshal correctly.
+func WithJSONField(field, value string) ResourceOption {
+	return func(t *testing.T, r fhir.Resource) {
+		t.Helper()
+		msg := r.ProtoReflect()
+		field := getProtoField(t, msg, field)
+
+		s := msg.Get(field).Message().New().Interface()
+		if err := protojson.Unmarshal([]byte(value), s); err != nil {
+			t.Fatalf("Invalid JSON '%v': %v", value, err)
+		}
+
+		msg.Set(field, protoreflect.ValueOfMessage(s.ProtoReflect()))
+	}
+}
+
+// WithCodeField creates a ResourceOpt for setting up the specified proto-field
+// with the value of a resource code.
+//
+// This can be used as a short-hand for constructing arbitrary strongly-typed
+// code-fields from a simple string, such as `WithCodeField("status", "DRAFT")`
+// for producing the "Draft" state for a CanonicalResource status.
+//
+// This will fail tests if the field is invalid, or if the JSON does not
+// unmarshal correctly.
+func WithCodeField(field, value string) ResourceOption {
+	// All "Code" objects are serialized in JSON as just { "value": "<value>" };
+	// using this property enables us to set different codes for the fhir protos
+	// which otherwise use strong-types (e.g. Questionnaire.Status is a different
+	// type from Account.Status, but both use the underlying PublicationCode string.)
+	return WithJSONField(field, fmt.Sprintf(`{ "value": "%v" }`, value))
+}
+
+// WithProtoField creates a ResourceOpt for setting up the specified proto-field
+// with the value set in the proto message.
+//
+// This will fail tests if the field is invalid, or panic if message is the
+// wrong input type.
+func WithProtoField(field string, message proto.Message) ResourceOption {
+	return func(t *testing.T, r fhir.Resource) {
+		t.Helper()
+		msg := r.ProtoReflect()
+		field := getProtoField(t, msg, field)
+
+		msg.Set(field, protoreflect.ValueOfMessage(message.ProtoReflect()))
+	}
+}
+
+// WithRepeatedProtoField creates a ResourceOpt for setting up the specified
+// repeated proto-field with the values set in the supplied proto messages.
+//
+// This will fail tests if the field is invalid, or panic if message is the
+// wrong input type.
+func WithRepeatedProtoField(field string, messages ...proto.Message) ResourceOption {
+	return func(t *testing.T, r fhir.Resource) {
+		t.Helper()
+		msg := r.ProtoReflect()
+		field := getProtoField(t, msg, field)
+		list := msg.Mutable(field).List()
+		for _, message := range messages {
+			list.Append(protoreflect.ValueOfMessage(message.ProtoReflect()))
+		}
+		msg.Set(field, protoreflect.ValueOfList(list))
+	}
+}
+
+// WithGeneratedIdentifier creates a ResourceOpt for automatically
+// generating an Identifier for the given resource with the specified system.
+// If the resource implements GetIdentifier(), an Identifier will be generated
+// and added.
+// If the resource does not, then we will fail the test.
+func WithGeneratedIdentifier(system string) ResourceOption {
+	return func(t *testing.T, r fhir.Resource) {
+		// set Identifier if available
+		if cast, ok := r.(resource.HasGetIdentifierList); ok {
+			ids := []*datatypes_go_proto.Identifier{generateIdentifier(system)}
+			setIdentifierList(cast, ids)
+		} else if cast, ok := r.(resource.HasGetIdentifierSingle); ok {
+			id := generateIdentifier(system)
+			setIdentifier(cast, id)
+		} else {
+			t.Errorf("WithGeneratedIdentifier: invalid resource type %v has no GetIdentifier()", r)
+		}
+	}
+}
+
+func init() {
+	Resources = make(map[string]fhir.Resource, len(protofields.Resources))
+	DomainResources = map[string]fhir.DomainResource{}
+	CanonicalResources = map[string]fhir.CanonicalResource{}
+	MetadataResources = map[string]fhir.MetadataResource{}
+
+	for name, res := range protofields.Resources {
+		resource := res.New().(fhir.Resource)
+
+		ReplaceMeta(resource, StableRandomMeta())
+		UpdateID(resource, stableRandomID().Value)
+
+		Resources[name] = resource
+		// The various resource interfaces grow off of one another, so we can
+		// minimize the number of checks here by leveraging this fact.
+		if v, ok := resource.(fhir.MetadataResource); ok {
+			setCanonicalFields(v)
+			MetadataResources[name] = v
+			CanonicalResources[name] = v
+			DomainResources[name] = v
+		} else if v, ok := resource.(fhir.CanonicalResource); ok {
+			setCanonicalFields(v)
+			CanonicalResources[name] = v
+			DomainResources[name] = v
+		} else if v, ok := resource.(fhir.DomainResource); ok {
+			DomainResources[name] = v
+		}
+	}
+}
+
+// setCanonicalFields will automatically set canonical fields.
+func setCanonicalFields(res fhir.CanonicalResource) {
+	setCanonicalURL(res, fmt.Sprintf(
+		"https://example.com/%v/%v",
+		strings.ToLower(string(resource.TypeOf(res))),
+		res.GetId().GetValue()),
+	)
+	setCanonicalVersion(res, "1.0")
+	setCanonicalStatus(res, "DRAFT")
+}
+
+// setResourceProtoField will set the resource's field to the given proto message type.
+func setResourceProtoField(resource fhir.Resource, fieldName string, value proto.Message) {
+	reflect := resource.ProtoReflect()
+	descriptor := reflect.Descriptor()
+	field := descriptor.Fields().ByName(protoreflect.Name(fieldName))
+	reflect.Set(field, protoreflect.ValueOfMessage(value.ProtoReflect()))
+}
+
+// setCanonicalURL will set a canonical URL for the given resource.
+func setCanonicalURL(resource fhir.CanonicalResource, url string) {
+	setResourceProtoField(resource, "url", &datatypes_go_proto.Uri{
+		Value: url,
+	})
+}
+
+// setCanonicalVersion will set a canonical version for the given resource.
+func setCanonicalVersion(resource fhir.CanonicalResource, version string) {
+	setResourceProtoField(resource, "version", &datatypes_go_proto.String{
+		Value: version,
+	})
+}
+
+func setCanonicalStatus(resource fhir.CanonicalResource, status string) {
+	msg := resource.ProtoReflect()
+	descriptor := msg.Descriptor()
+	field := descriptor.Fields().ByName("status")
+
+	s := msg.Get(field).Message().New().Interface()
+	payload := fmt.Sprintf(`{ "value": "%v" }`, status)
+	if err := protojson.Unmarshal([]byte(payload), s); err != nil {
+		// This is tested to not happen, and is only privately called internal to
+		// the setup of this library.
+		panic(err)
+	}
+
+	msg.Set(field, protoreflect.ValueOfMessage(s.ProtoReflect()))
+}
+
+// setIdentifier sets the singleton Identifier on the given resource.
+func setIdentifier(resource resource.HasGetIdentifierSingle, identifier *datatypes_go_proto.Identifier) {
+	setResourceProtoField(resource, "identifier", identifier)
+}
+
+// setIdentifierList sets the Identifier list on the given resource.
+func setIdentifierList(resource resource.HasGetIdentifierList, identifiers []*datatypes_go_proto.Identifier) {
+	msg := resource.ProtoReflect()
+	descriptor := msg.Descriptor()
+	field := descriptor.Fields().ByName("identifier")
+
+	list := msg.Mutable(field).List()
+	for _, identifier := range identifiers {
+		list.Append(protoreflect.ValueOfMessage(identifier.ProtoReflect()))
+	}
+	msg.Set(field, protoreflect.ValueOfList(list))
+}
+
+// generateIdentifier generates a (stable) random Identifier with the given system
+func generateIdentifier(system string) *datatypes_go_proto.Identifier {
+	return &datatypes_go_proto.Identifier{
+		System: &datatypes_go_proto.Uri{Value: system},
+		Value:  &datatypes_go_proto.String{Value: stableRandomID().Value},
+	}
+}
+
+// getProtoField is a helper function for retrieving the named proto field
+func getProtoField(t *testing.T, msg protoreflect.Message, fieldName string) protoreflect.FieldDescriptor {
+	t.Helper()
+	descriptor := msg.Descriptor()
+	field := descriptor.Fields().ByName(protoreflect.Name(fieldName))
+	if field == nil {
+		t.Fatalf("Proto field '%v' not found", field)
+	}
+	return field
+}
diff --git a/internal/fhirtest/resources_example_test.go b/internal/fhirtest/resources_example_test.go
new file mode 100644
index 0000000..1ac88da
--- /dev/null
+++ b/internal/fhirtest/resources_example_test.go
@@ -0,0 +1,46 @@
+package fhirtest_test
+
+import (
+	"fmt"
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/fhirtest"
+)
+
+func ExampleWithResourceModification() {
+	t := &testing.T{}
+
+	patient := fhirtest.NewResource(t, "Patient", fhirtest.WithResourceModification(func(p *ppb.Patient) {
+		p.Name = []*dtpb.HumanName{{Family: fhir.String("Ursa")}}
+	})).(*ppb.Patient)
+
+	fmt.Printf("patient.Name[0].Family = %v", patient.Name[0].Family)
+	// Output: patient.Name[0].Family = value:"Ursa"
+}
+
+func ExampleNewResourceFromBase() {
+	t := &testing.T{}
+	original := &ppb.Patient{
+		Id:   fhir.ID("uuid-a"),
+		Name: []*dtpb.HumanName{{Family: fhir.String("Ursa")}},
+	}
+
+	// Apply options on original.
+	modified := fhirtest.NewResourceFromBase(t, original,
+		fhirtest.WithResourceModification(func(p *ppb.Patient) {
+			p.Name[0].Family = fhir.String("Major")
+			p.Name[0].Given = fhir.Strings("Aseem")
+		}),
+		fhirtest.WithProtoField("id", fhir.ID("uuid-b")),
+	).(*ppb.Patient)
+
+	fmt.Printf("ID = '%v', Family = '%v', Given = '%v'",
+		modified.Id.Value,
+		modified.Name[0].Family.Value,
+		modified.Name[0].Given[0].Value,
+	)
+	// Output: ID = 'uuid-b', Family = 'Major', Given = 'Aseem'
+}
diff --git a/internal/fhirtest/resources_test.go b/internal/fhirtest/resources_test.go
new file mode 100644
index 0000000..88104d8
--- /dev/null
+++ b/internal/fhirtest/resources_test.go
@@ -0,0 +1,326 @@
+package fhirtest_test
+
+import (
+	"regexp"
+	"testing"
+
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/codes_go_proto"
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/questionnaire_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/fhirtest"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func TestNewResource_GeneratesIdentityInformation(t *testing.T) {
+	for name := range fhirtest.Resources {
+		sut := fhirtest.NewResource(t, resource.Type(name))
+
+		if sut.GetId().GetValue() == "" {
+			t.Errorf("NewResource(%v): expected random ID", name)
+		}
+		if sut.GetMeta().GetVersionId().GetValue() == "" {
+			t.Errorf("NewResource(%v): expected random version ID", name)
+		}
+		if sut.GetMeta().GetLastUpdated().GetValueUs() == 0 {
+			t.Errorf("NewResource(%v): expected random last-update time", name)
+		}
+	}
+}
+
+func TestNewResourceFromBase_PreservesInput(t *testing.T) {
+	for name := range fhirtest.Resources {
+		sut := fhirtest.NewResource(t, resource.Type(name))
+		sutDuplicate := proto.Clone(sut).(fhir.Resource)
+
+		fhirtest.NewResourceFromBase(t, sut,
+			fhirtest.WithProtoField("meta", &dtpb.Meta{Source: fhir.URI("urn:uuid:A")}))
+
+		if diff := cmp.Diff(sut, sutDuplicate, protocmp.Transform()); diff != "" {
+			t.Errorf("NewResourceFromBase() modified input (-got, +want): %s", diff)
+		}
+	}
+}
+
+func TestNewResourceFromBase_AppliesOption(t *testing.T) {
+	for name := range fhirtest.Resources {
+		sut := fhirtest.NewResource(t, resource.Type(name))
+		newMeta := &dtpb.Meta{Source: fhir.URI("urn:uuid:A")}
+		wantMsut := proto.Clone(sut).(fhir.Resource)
+		fhirtest.ReplaceMeta(wantMsut, newMeta)
+
+		gotMsut := fhirtest.NewResourceFromBase(t, sut,
+			fhirtest.WithProtoField("meta", newMeta))
+
+		if diff := cmp.Diff(gotMsut, wantMsut, protocmp.Transform()); diff != "" {
+			t.Errorf("NewResourceFromBase() didn't modify properly (-got, +want): %s", diff)
+		}
+	}
+}
+
+func TestNewResourceOf_WithPatient_CreatesPatient(t *testing.T) {
+	got := fhirtest.NewResourceOf[*patient_go_proto.Patient](t)
+
+	if got == nil {
+		t.Errorf("NewResourceOf: got nil, want patient")
+	}
+}
+
+func TestWithResourceModification_ChangesPatientName(t *testing.T) {
+	pIn := &patient_go_proto.Patient{
+		Name: []*dtpb.HumanName{
+			{
+				Family: fhir.String("VonBatchery"),
+				Given:  []*dtpb.String{fhir.String("GivenA"), fhir.String("GivenB")},
+			},
+		},
+	}
+	pWant := &patient_go_proto.Patient{
+		Name: []*dtpb.HumanName{
+			{
+				Family: fhir.String("Rosewater"),
+				Given:  []*dtpb.String{fhir.String("GivenB")},
+			},
+		},
+	}
+
+	fhirtest.WithResourceModification(func(patient *patient_go_proto.Patient) {
+		patient.Name[0].Family = fhir.String("Rosewater")
+		patient.Name[0].Given = patient.Name[0].Given[1:]
+	})(t, pIn)
+
+	if diff := cmp.Diff(pWant, pIn, protocmp.Transform()); diff != "" {
+		t.Errorf("WithResourceModification didn't produce correct patient (-got, +want): %s", diff)
+	}
+}
+
+func TestWithFieldJSON_SetsJSONField(t *testing.T) {
+	for name := range fhirtest.CanonicalResources {
+		sut := fhirtest.NewResource(t, resource.Type(name),
+			fhirtest.WithJSONField("status", `{"value": "DRAFT"}`),
+		)
+
+		// 'NewResource' would fail the test implicitly if this didn't work correctly;
+		// so if we make it here, it means we have passed.
+		// Since the "status" field is a different type for each resource, we can't
+		// generically test this value over an iterative sequence.
+		_ = sut
+	}
+}
+
+func TestWithFieldJSON_SetsAccountStatusField(t *testing.T) {
+	sut := fhirtest.NewResource(t, "Questionnaire",
+		fhirtest.WithJSONField("status", `{"value": "DRAFT"}`),
+	).(*questionnaire_go_proto.Questionnaire)
+
+	if got, want := sut.GetStatus().GetValue(), codes_go_proto.PublicationStatusCode_DRAFT; got != want {
+		t.Errorf("WithFieldJSON: got code %v, want code %v", got, want)
+	}
+}
+
+func TestWithFieldCodeJSON_SetsJSONField(t *testing.T) {
+	for name := range fhirtest.CanonicalResources {
+		sut := fhirtest.NewResource(t, resource.Type(name),
+			fhirtest.WithCodeField("status", "DRAFT"),
+		)
+
+		// 'NewResource' would fail the test implicitly if this didn't work correctly;
+		// so if we make it here, it means we have passed.
+		// Since the "status" field is a different type for each resource, we can't
+		// generically test this value over an iterative sequence.
+		_ = sut
+	}
+}
+
+func TestWithFieldCodeJSON_SetsAccountStatusField(t *testing.T) {
+	sut := fhirtest.NewResource(t, "Questionnaire",
+		fhirtest.WithCodeField("status", "DRAFT"),
+	).(*questionnaire_go_proto.Questionnaire)
+
+	if got, want := sut.GetStatus().GetValue(), codes_go_proto.PublicationStatusCode_DRAFT; got != want {
+		t.Errorf("WithFieldCodeJSON: got code %v, want code %v", got, want)
+	}
+}
+
+func TestWithFieldProto_SetsField(t *testing.T) {
+	want := &dtpb.String{
+		Value: "test",
+	}
+	for name := range fhirtest.CanonicalResources {
+		sut := fhirtest.NewResource(t, resource.Type(name),
+			fhirtest.WithProtoField("name", want),
+		).(fhir.CanonicalResource)
+
+		if got := sut.GetName(); got != want {
+			t.Errorf("WithFieldProto(%v): got '%v', wanted '%v'", name, got, want)
+		}
+	}
+}
+
+func TestWithRepeatedFieldProto_SetsField(t *testing.T) {
+	toProtoMessage := func(in []*dtpb.Identifier) []proto.Message {
+		result := make([]proto.Message, 0, len(in))
+		for _, v := range in {
+			result = append(result, v)
+		}
+		return result
+	}
+
+	want := []proto.Message{
+		&dtpb.Identifier{
+			Value: &dtpb.String{
+				Value: "foo",
+			},
+			System: &dtpb.Uri{
+				Value: "https://example.com/foo",
+			},
+		},
+		&dtpb.Identifier{
+			Value: &dtpb.String{
+				Value: "bar",
+			},
+			System: &dtpb.Uri{
+				Value: "https://example.com/bar",
+			},
+		},
+	}
+	for name := range fhirtest.CanonicalResources {
+		sut := fhirtest.NewResource(t, resource.Type(name),
+			fhirtest.WithRepeatedProtoField("identifier", want...),
+		).(fhir.CanonicalResource)
+
+		if got := sut.GetIdentifier(); !cmp.Equal(toProtoMessage(got), want, protocmp.Transform()) {
+			t.Errorf("WithRepeatedFieldProto(%v): got '%v', wanted '%v'", name, got, want)
+		}
+	}
+}
+
+// Assert that an identifier list is present and has expected values
+func assertIdentifier(t *testing.T, identifiers []*dtpb.Identifier, name string, resource fhir.Resource) {
+	if identifiers == nil {
+		t.Errorf("Nil list of ids for %v: %v", name, resource)
+		return
+	}
+	if len(identifiers) == 0 {
+		t.Errorf("Empty list of ids for %v: %v", name, resource)
+		return
+	}
+
+	id := identifiers[0]
+
+	if got, want := id.GetSystem().GetValue(), "http://example.com/fake-id"; got != want {
+		t.Errorf("%v Resource.Identifier[0].System: got %v, want %v", name, got, want)
+	}
+
+	value := id.GetValue().GetValue()
+	matched, _ := regexp.MatchString("^[a-f0-9-]+$", value)
+	if !matched {
+		t.Errorf("%v Resource.Identifier[0].Value: got %v, expected uuid", name, value)
+	}
+}
+
+// All CanonicalResources should implement GetIdentifier()
+func TestWithGeneratedIdentifier_CanonicalResources(t *testing.T) {
+	for name := range fhirtest.CanonicalResources {
+		t.Run(name, func(t *testing.T) {
+			res := fhirtest.NewResource(t, resource.Type(name),
+				fhirtest.WithGeneratedIdentifier("http://example.com/fake-id"),
+			).(fhir.CanonicalResource)
+
+			ids := res.GetIdentifier()
+			assertIdentifier(t, ids, name, res)
+		})
+	}
+}
+
+// Test that all resources can either be cast or not
+func TestWithGeneratedIdentifier_AllResources(t *testing.T) {
+	for name, stockRes := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			if _, ok := stockRes.(resource.HasGetIdentifierList); ok {
+				// GetIdentifier() is list
+
+				res := fhirtest.NewResource(t, resource.Type(name),
+					fhirtest.WithGeneratedIdentifier("http://example.com/fake-id"),
+				).(resource.HasGetIdentifierList)
+
+				ids := res.GetIdentifier()
+				assertIdentifier(t, ids, name, res)
+
+			} else if _, ok := stockRes.(resource.HasGetIdentifierSingle); ok {
+				// GetIdentifier() is singleton
+
+				res := fhirtest.NewResource(t, resource.Type(name),
+					fhirtest.WithGeneratedIdentifier("http://example.com/fake-id"),
+				).(resource.HasGetIdentifierSingle)
+
+				id := res.GetIdentifier()
+				assertIdentifier(t, []*dtpb.Identifier{id}, name, res)
+			} else {
+				// not compatible with GetIdentifier() interface
+				return
+			}
+		})
+	}
+}
+
+// Test a few specific types
+func TestWithGeneratedIdentifier_SpotTest(t *testing.T) {
+	// resources w/ Identifier as list
+	for _, name := range []string{"Patient", "DocumentReference"} {
+		t.Run(name, func(t *testing.T) {
+			res := fhirtest.NewResource(t, resource.Type(name),
+				fhirtest.WithGeneratedIdentifier("http://example.com/fake-id"),
+			).(resource.HasGetIdentifierList)
+
+			ids := res.GetIdentifier()
+			assertIdentifier(t, ids, name, res)
+		})
+	}
+
+	// resources w/ Identifier as singleton
+	for _, name := range []string{"Bundle", "QuestionnaireResponse"} {
+		t.Run(name, func(t *testing.T) {
+			res := fhirtest.NewResource(t, resource.Type(name),
+				fhirtest.WithGeneratedIdentifier("http://example.com/fake-id"),
+			).(resource.HasGetIdentifierSingle)
+
+			id := res.GetIdentifier()
+			assertIdentifier(t, []*dtpb.Identifier{id}, name, res)
+		})
+	}
+}
+
+func TestResources_ResourceHasNonEmptyID(t *testing.T) {
+	for name, resource := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			if resource.GetId().GetValue() == "" {
+				t.Errorf("Resource(%v): got empty id", name)
+			}
+		})
+	}
+}
+
+func TestResources_ResourceHasNonEmptyVersionID(t *testing.T) {
+	for name, resource := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			if resource.GetMeta().GetVersionId().GetValue() == "" {
+				t.Errorf("Resource(%v): got empty version id", name)
+			}
+		})
+	}
+}
+
+func TestResources_ResourceHasLastModified(t *testing.T) {
+	for name, resource := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			if resource.GetMeta().GetLastUpdated().GetValueUs() == 0 {
+				t.Errorf("Resource(%v): got unset last-update time", name)
+			}
+		})
+	}
+}
diff --git a/internal/narrow/narrow.go b/internal/narrow/narrow.go
new file mode 100644
index 0000000..c0d5037
--- /dev/null
+++ b/internal/narrow/narrow.go
@@ -0,0 +1,176 @@
+/*
+Package narrow provides conversion functionality for narrowing
+integer types in a safe and generic manner.
+
+These operations will return not only the casted integer, but also a boolean
+indicating whether the value was safely casted to the receiver type -- e.g.
+that the sign didn't change through casting, and that the value was not unexpectedly
+truncated. This can be really useful for cases where data requires explicit
+integer-widths, and the incoming data may exceed that integer width.
+
+Conversion is done through one of the two following function types:
+
+  - `narrow.ToInteger[To](from) (To, bool)` -- converts to the specified int-type
+  - `narrow.To*(from) (*, bool)` -- convenience functions for converting
+    to explicit integer-width types to avoid needing generics (just calls
+    into the above).
+
+The former's generic-interface allows better composability with other generic
+functions, whereas the latter allows a more idiomatic and visible call for
+explicit cases (e.g. `ints.ToInt32(...)`)
+*/
+package narrow
+
+import (
+	"math"
+
+	"golang.org/x/exp/constraints"
+)
+
+// isSigned checks that the integer input is a Signed integer type.
+func isSigned[T constraints.Integer]() bool {
+	// If T is unsigned, -1 will form a large positive number when casted to
+	// T -- which makes this a simple way to check for signedness.
+	val := -1
+	return T(val) < 0
+}
+
+// ToInteger converts `From` to a `To` type, additionally returning whether the
+// value was converted successfully without any truncation and loss-of-data occurring.
+func ToInteger[To constraints.Integer, From constraints.Integer](from From) (To, bool) {
+	if isSigned[From]() {
+		return To(from), canSafelyNarrowSigned[To](int64(from))
+	}
+	return To(from), canSafelyNarrowUnsigned[To](uint64(from))
+}
+
+// ToInt converts an integer type into an `int`, additionally returning whether the
+// value was converted successfully without any truncation and loss-of-data occurring.
+func ToInt[From constraints.Integer](from From) (int, bool) {
+	return ToInteger[int](from)
+}
+
+// ToInt8 converts an integer type into an `int8`, additionally returning whether the
+// value was converted successfully without any truncation and loss-of-data occurring.
+func ToInt8[From constraints.Integer](from From) (int8, bool) {
+	return ToInteger[int8](from)
+}
+
+// ToInt16 converts an integer type into an `int16`, additionally returning whether the
+// value was converted successfully without any truncation and loss-of-data occurring.
+func ToInt16[From constraints.Integer](from From) (int16, bool) {
+	return ToInteger[int16](from)
+}
+
+// ToInt32 converts an integer type into an `int32`, additionally returning whether the
+// value was converted successfully without any truncation and loss-of-data occurring.
+func ToInt32[From constraints.Integer](from From) (int32, bool) {
+	return ToInteger[int32](from)
+}
+
+// ToInt64 converts an integer type into an `int64`, additionally returning whether the
+// value was converted successfully without any truncation and loss-of-data occurring.
+func ToInt64[From constraints.Integer](from From) (int64, bool) {
+	return ToInteger[int64](from)
+}
+
+// ToUint converts an integer type into an `uint`, additionally returning whether the
+// value was converted successfully without any truncation and loss-of-data occurring.
+func ToUint[From constraints.Integer](from From) (uint, bool) {
+	return ToInteger[uint](from)
+}
+
+// ToUint8 converts an integer type into an `uint8`, additionally returning whether the
+// value was converted successfully without any truncation and loss-of-data occurring.
+func ToUint8[From constraints.Integer](from From) (uint8, bool) {
+	return ToInteger[uint8](from)
+}
+
+// ToUint16 converts an integer type into an `uint16`, additionally returning whether the
+// value was converted successfully without any truncation and loss-of-data occurring.
+func ToUint16[From constraints.Integer](from From) (uint16, bool) {
+	return ToInteger[uint16](from)
+}
+
+// ToUint32 converts an integer type into an `uint32`, additionally returning whether the
+// value was converted successfully without any truncation and loss-of-data occurring.
+func ToUint32[From constraints.Integer](from From) (uint32, bool) {
+	return ToInteger[uint32](from)
+}
+
+// ToUint64 converts an integer type into an `uint64`, additionally returning whether the
+// value was converted successfully without any truncation and loss-of-data occurring.
+func ToUint64[From constraints.Integer](from From) (uint64, bool) {
+	return ToInteger[uint64](from)
+}
+
+// ToUintptr converts an integer type into an `uintptr`, as well as whether the value was
+// truncated.
+func ToUintptr[From constraints.Integer](from From) (uintptr, bool) {
+	return ToInteger[uintptr](from)
+}
+
+// canSafelyNarrowSigned tests whether the supplied val can be converted into 'To'
+// without truncation or loss-of-data.
+func canSafelyNarrowSigned[To constraints.Integer](val int64) bool {
+	var v To
+	switch any(v).(type) {
+	case uint:
+		// Cast here is to avoid possible "overflow" errors if uint is 64-bits
+		return uint64(val) <= math.MaxUint && val >= 0
+	case uintptr:
+		return int64(uintptr(val)) == val && val >= 0
+	case uint8:
+		return val <= math.MaxUint8 && val >= 0
+	case uint16:
+		return val <= math.MaxUint16 && val >= 0
+	case uint32:
+		return val <= math.MaxUint32 && val >= 0
+	case uint64:
+		return val >= 0
+	case int:
+		return val <= math.MaxInt && val >= math.MinInt
+	case int8:
+		return val <= math.MaxInt8 && val >= math.MinInt8
+	case int16:
+		return val <= math.MaxInt16 && val >= math.MinInt16
+	case int32:
+		return val <= math.MaxInt32 && val >= math.MinInt32
+	case int64:
+		return true // trivially true (identity conversion)
+	}
+	return false // this should be unreachable
+}
+
+// canSafelyNarrowUnsigned tests whether the supplied val can be converted into 'To'
+// without truncation or loss-of-data.
+func canSafelyNarrowUnsigned[To constraints.Integer](val uint64) bool {
+	var v To
+	switch any(v).(type) {
+	case uint:
+		// Cast here is to avoid possible "overflow" errors if uint is 64-bits
+		return uint64(val) <= math.MaxUint
+	case uintptr:
+		return uint64(uintptr(val)) == val
+	case uint8:
+		return val <= math.MaxUint8
+	case uint16:
+		return val <= math.MaxUint16
+	case uint32:
+		return val <= math.MaxUint32
+	case uint64:
+		return true // trivially true (identity conversion)
+	case int:
+		// Cast here is to avoid possible "overflow" errors if int is 64-bits
+		return uint64(val) <= math.MaxInt
+	case int8:
+		return val <= math.MaxInt8
+	case int16:
+		return val <= math.MaxInt16
+	case int32:
+		return val <= math.MaxInt32
+	case int64:
+		return val <= math.MaxInt64
+	}
+	return false // this should be unreachable
+}
diff --git a/internal/narrow/narrow_example_test.go b/internal/narrow/narrow_example_test.go
new file mode 100644
index 0000000..8b767fd
--- /dev/null
+++ b/internal/narrow/narrow_example_test.go
@@ -0,0 +1,268 @@
+package narrow_test
+
+import (
+	"fmt"
+	"math"
+
+	"github.com/verily-src/fhirpath-go/internal/narrow"
+)
+
+func ExampleToInteger_narrows() {
+	from := -1
+
+	val, ok := narrow.ToInteger[uint8](from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Unable to convert to uint8!
+}
+
+func ExampleToInteger_no_narrowing() {
+	from := uint32(42)
+
+	val, ok := narrow.ToInteger[uint8](from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Converted 42 to uint8!
+}
+
+func ExampleToInt8_narrows() {
+	from := 10_000
+
+	val, ok := narrow.ToInt8(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Unable to convert to int8!
+}
+
+func ExampleToInt8_no_narrowing() {
+	from := 42
+
+	val, ok := narrow.ToInt8(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Converted 42 to int8!
+}
+
+func ExampleToUint8_narrows() {
+	from := 10_000
+
+	val, ok := narrow.ToUint8(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Unable to convert to uint8!
+}
+
+func ExampleToUint8_no_narrowing() {
+	from := 42
+
+	val, ok := narrow.ToUint8(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Converted 42 to uint8!
+}
+
+func ExampleToInt16_narrows() {
+	from := math.MaxInt32
+
+	val, ok := narrow.ToInt16(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Unable to convert to int16!
+}
+
+func ExampleToInt16_no_narrowing() {
+	from := 42
+
+	val, ok := narrow.ToInt16(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Converted 42 to int16!
+}
+
+func ExampleToUint16_narrows() {
+	from := math.MaxInt32
+
+	val, ok := narrow.ToUint16(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Unable to convert to uint16!
+}
+
+func ExampleToUint16_no_narrowing() {
+	from := 42
+
+	val, ok := narrow.ToUint16(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Converted 42 to uint16!
+}
+
+func ExampleToInt32_narrows() {
+	from := math.MaxInt64
+
+	val, ok := narrow.ToInt32(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Unable to convert to int32!
+}
+
+func ExampleToInt32_no_narrowing() {
+	from := 42
+
+	val, ok := narrow.ToInt32(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Converted 42 to int32!
+}
+
+func ExampleToUint32_narrows() {
+	from := math.MaxInt64
+
+	val, ok := narrow.ToUint32(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Unable to convert to uint32!
+}
+
+func ExampleToUint32_no_narrowing() {
+	from := 42
+
+	val, ok := narrow.ToUint32(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Converted 42 to uint32!
+}
+
+func ExampleToInt64_no_narrowing() {
+	from := 42
+
+	val, ok := narrow.ToInt64(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Converted 42 to int64!
+}
+
+func ExampleToUint64_narrows() {
+	from := -1
+
+	val, ok := narrow.ToUint64(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Unable to convert to uint64!
+}
+
+func ExampleToUint64_no_narrowing() {
+	from := 42
+
+	val, ok := narrow.ToUint64(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Converted 42 to uint64!
+}
+
+func ExampleToInt_no_narrowing() {
+	from := 42
+
+	val, ok := narrow.ToInt32(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Converted 42 to int32!
+}
+
+func ExampleToUint_narrows() {
+	from := -1
+
+	val, ok := narrow.ToUint(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Unable to convert to uint!
+}
+
+func ExampleToUint_no_narrowing() {
+	from := 42
+
+	val, ok := narrow.ToUint(from)
+	if !ok {
+		fmt.Printf("Unable to convert to %T!", val)
+	} else {
+		fmt.Printf("Converted %v to %T!", from, val)
+	}
+	// Output:
+	// Converted 42 to uint!
+}
diff --git a/internal/narrow/narrow_test.go b/internal/narrow/narrow_test.go
new file mode 100644
index 0000000..63f729f
--- /dev/null
+++ b/internal/narrow/narrow_test.go
@@ -0,0 +1,703 @@
+package narrow_test
+
+import (
+	"math"
+	"testing"
+
+	"github.com/verily-src/fhirpath-go/internal/narrow"
+	"golang.org/x/exp/constraints"
+)
+
+func wantTruncation[To constraints.Integer, From constraints.Integer](input From) func(*testing.T) {
+	return func(t *testing.T) {
+		t.Helper()
+		var to To
+
+		_, ok := narrow.ToInteger[To](input)
+
+		if got, want := ok, false; got != want {
+			t.Errorf("ToInteger[%T]: got %v, want %v", to, got, want)
+		}
+	}
+}
+
+func wantConversion[To constraints.Integer, From constraints.Integer](input From) func(*testing.T) {
+	return func(t *testing.T) {
+		t.Helper()
+		var to To
+
+		got, ok := narrow.ToInteger[To](input)
+		if ok == false {
+			t.Fatalf("ToInteger[%T]: unexpected truncation %v => %v", to, input, got)
+		}
+
+		if got, want := From(got), input; got != want {
+			t.Errorf("ToInteger[%T]: got %v, want %v", to, got, want)
+		}
+	}
+}
+
+func wantConversionFunc[To constraints.Integer, From constraints.Integer](fn func(From) (To, bool), from From) func(*testing.T) {
+	return func(t *testing.T) {
+		t.Helper()
+		var to To
+
+		got, ok := fn(from)
+		if ok == false {
+			t.Fatalf("%T => %T: unexpected truncation %v => %v", from, to, from, got)
+		}
+
+		if got, want := From(got), from; got != want {
+			t.Errorf("%T => %T: got %v, want %v", from, to, got, want)
+		}
+	}
+}
+
+func TestToInteger_ValueExceedsReceiver_ReturnsFalse(t *testing.T) {
+	// Note: nested sub-tests are being done both for organization and test naming.
+	// We also can't use table-driven tests here, since the input is a type -- so
+	// this forces duplication.
+	t.Run("FromUint8", func(t *testing.T) {
+		t.Run("ToInt8", wantTruncation[int8](uint8(math.MaxUint8)))
+	})
+
+	t.Run("FromUint16", func(t *testing.T) {
+		t.Run("ToInt8", wantTruncation[int8](uint16(math.MaxUint16)))
+		t.Run("ToInt16", wantTruncation[int16](uint16(math.MaxUint16)))
+		t.Run("ToUint8", wantTruncation[uint8](uint16(math.MaxUint16)))
+	})
+
+	t.Run("FromUint32", func(t *testing.T) {
+		t.Run("ToInt8", wantTruncation[int8](uint32(math.MaxUint32)))
+		t.Run("ToInt16", wantTruncation[int16](uint32(math.MaxUint32)))
+		t.Run("ToInt32", wantTruncation[int32](uint32(math.MaxUint32)))
+
+		t.Run("ToUint8", wantTruncation[uint8](uint32(math.MaxUint32)))
+		t.Run("ToUint16", wantTruncation[uint16](uint32(math.MaxUint32)))
+	})
+
+	t.Run("FromUint64", func(t *testing.T) {
+		t.Run("ToInt8", wantTruncation[int8](uint64(math.MaxUint64)))
+		t.Run("ToInt16", wantTruncation[int16](uint64(math.MaxUint64)))
+		t.Run("ToInt32", wantTruncation[int32](uint64(math.MaxUint64)))
+		t.Run("ToInt64", wantTruncation[int64](uint64(math.MaxUint64)))
+		t.Run("ToInt", wantTruncation[int](uint64(math.MaxUint64)))
+
+		t.Run("ToUint8", wantTruncation[uint8](uint64(math.MaxUint64)))
+		t.Run("ToUint16", wantTruncation[uint16](uint64(math.MaxUint64)))
+		t.Run("ToUint32", wantTruncation[uint32](uint64(math.MaxUint64)))
+	})
+
+	t.Run("FromUint", func(t *testing.T) {
+		t.Run("ToInt8", wantTruncation[int8](uint(math.MaxUint)))
+		t.Run("ToInt16", wantTruncation[int16](uint(math.MaxUint)))
+		t.Run("ToInt32", wantTruncation[int32](uint(math.MaxUint)))
+
+		t.Run("ToUint8", wantTruncation[uint8](uint(math.MaxUint)))
+		t.Run("ToUint16", wantTruncation[uint16](uint(math.MaxUint)))
+		// We can't reliably test against uint32, since uint may be AT LEAST 32 bits.
+	})
+
+	t.Run("FromInt8", func(t *testing.T) {
+		t.Run("ToUint8", wantTruncation[uint8](int8(-1)))
+		t.Run("ToUint16", wantTruncation[uint16](int8(-1)))
+		t.Run("ToUint32", wantTruncation[uint32](int8(-1)))
+		t.Run("ToUint32", wantTruncation[uint64](int8(-1)))
+		t.Run("ToUintptr", wantTruncation[uintptr](int8(-1)))
+		t.Run("ToUint", wantTruncation[uint](int8(-1)))
+	})
+
+	t.Run("FromInt16", func(t *testing.T) {
+		t.Run("ToInt8", wantTruncation[int8](int16(math.MaxInt16)))
+
+		t.Run("ToUint8", wantTruncation[uint8](int16(-1)))
+		t.Run("ToUint16", wantTruncation[uint16](int16(-1)))
+		t.Run("ToUint32", wantTruncation[uint32](int16(-1)))
+		t.Run("ToUint32", wantTruncation[uint64](int16(-1)))
+		t.Run("ToUintptr", wantTruncation[uintptr](int16(-1)))
+		t.Run("ToUint", wantTruncation[uint](int16(-1)))
+	})
+
+	t.Run("FromInt32", func(t *testing.T) {
+		t.Run("ToInt8", wantTruncation[int8](int32(math.MaxInt32)))
+		t.Run("ToInt16", wantTruncation[int16](int32(math.MaxInt32)))
+
+		t.Run("ToUint8", wantTruncation[uint8](int32(-1)))
+		t.Run("ToUint16", wantTruncation[uint16](int32(-1)))
+		t.Run("ToUint32", wantTruncation[uint32](int32(-1)))
+		t.Run("ToUint32", wantTruncation[uint64](int32(-1)))
+		t.Run("ToUintptr", wantTruncation[uintptr](int32(-1)))
+		t.Run("ToUint", wantTruncation[uint](int32(-1)))
+	})
+
+	t.Run("FromInt64", func(t *testing.T) {
+		t.Run("ToInt8", wantTruncation[int8](int64(math.MaxInt64)))
+		t.Run("ToInt16", wantTruncation[int16](int64(math.MaxInt64)))
+		t.Run("ToInt32", wantTruncation[int32](int64(math.MaxInt64)))
+
+		t.Run("ToUint8", wantTruncation[uint8](int64(-1)))
+		t.Run("ToUint16", wantTruncation[uint16](int64(-1)))
+		t.Run("ToUint32", wantTruncation[uint32](int64(-1)))
+		t.Run("ToUint32", wantTruncation[uint64](int64(-1)))
+		t.Run("ToUintptr", wantTruncation[uintptr](int64(-1)))
+		t.Run("ToUint", wantTruncation[uint](int64(-1)))
+	})
+
+	t.Run("FromInt", func(t *testing.T) {
+		t.Run("ToInt8", wantTruncation[int8](int(math.MaxInt)))
+		t.Run("ToInt16", wantTruncation[int16](int(math.MaxInt)))
+		// We can't reliably test against int32, since int is at LEAST 32 bits.
+
+		t.Run("ToUint8", wantTruncation[uint8](int(-1)))
+		t.Run("ToUint16", wantTruncation[uint16](int(-1)))
+		t.Run("ToUint32", wantTruncation[uint32](int(-1)))
+		t.Run("ToUint32", wantTruncation[uint64](int(-1)))
+		t.Run("ToUintptr", wantTruncation[uintptr](int(-1)))
+		t.Run("ToUint", wantTruncation[uint](int(-1)))
+	})
+}
+
+func TestToInteger_ValueFitsIntoReceiver_ReturnsValueAndTrue(t *testing.T) {
+	// Note: nested sub-tests are being done both for organization and test naming.
+	// We also can't use table-driven tests here, since the input is a type -- so
+	// this forces duplication.
+	t.Run("FromUint", func(t *testing.T) {
+		t.Run("ToInt8", wantConversion[int8](uint(0)))
+		t.Run("ToInt8Max", wantConversion[int8](uint(math.MaxInt8)))
+		t.Run("ToInt16", wantConversion[int16](uint(0)))
+		t.Run("ToInt16Max", wantConversion[int16](uint(math.MaxInt16)))
+		t.Run("ToInt32", wantConversion[int32](uint(0)))
+		t.Run("ToInt32Max", wantConversion[int32](uint(math.MaxInt32)))
+		t.Run("ToInt64", wantConversion[int64](uint(0)))
+		t.Run("ToInt64MaxUint32", wantConversion[int64](uint(math.MaxUint32)))
+		t.Run("ToInt", wantConversion[int](uint(0)))
+		t.Run("ToIntMaxInt32", wantConversion[int](uint(math.MaxInt32)))
+		t.Run("ToUint8", wantConversion[uint8](uint(0)))
+		t.Run("ToUint8Max", wantConversion[uint8](uint(math.MaxUint8)))
+		t.Run("ToUint16", wantConversion[uint16](uint(0)))
+		t.Run("ToUint16Max", wantConversion[uint16](uint(math.MaxUint16)))
+		t.Run("ToUint32", wantConversion[uint32](uint(0)))
+		t.Run("ToUint32Max", wantConversion[uint32](uint(math.MaxUint32)))
+		t.Run("ToUint64", wantConversion[uint64](uint(0)))
+		t.Run("ToUint64MaxUint32", wantConversion[uint64](uint(math.MaxUint32)))
+		t.Run("ToUint", wantConversion[uint](uint(0)))
+		t.Run("ToUintMaxUint32", wantConversion[uint](uint(math.MaxUint32)))
+		t.Run("ToUintptr", wantConversion[uintptr](uint(0)))
+	})
+
+	t.Run("FromUint8", func(t *testing.T) {
+		t.Run("ToInt8", wantConversion[int8](uint8(0)))
+		t.Run("ToInt8Max", wantConversion[int8](uint8(math.MaxInt8)))
+		t.Run("ToInt16", wantConversion[int16](uint8(0)))
+		t.Run("ToInt16MaxInt8", wantConversion[int16](uint8(math.MaxInt8)))
+		t.Run("ToInt32", wantConversion[int32](uint8(0)))
+		t.Run("ToInt32MaxUint8", wantConversion[int32](uint8(math.MaxUint8)))
+		t.Run("ToInt64", wantConversion[int64](uint8(0)))
+		t.Run("ToInt64MaxUint8", wantConversion[int64](uint8(math.MaxUint8)))
+		t.Run("ToInt", wantConversion[int](uint8(0)))
+		t.Run("ToIntMaxInt8", wantConversion[int](uint8(math.MaxInt8)))
+		t.Run("ToUint8", wantConversion[uint8](uint8(0)))
+		t.Run("ToUint8Max", wantConversion[uint8](uint8(math.MaxUint8)))
+		t.Run("ToUint16", wantConversion[uint16](uint8(0)))
+		t.Run("ToUint16MaxInt8", wantConversion[uint16](uint8(math.MaxUint8)))
+		t.Run("ToUint32", wantConversion[uint32](uint8(0)))
+		t.Run("ToUint32MaxUint8", wantConversion[uint32](uint8(math.MaxUint8)))
+		t.Run("ToUint64", wantConversion[uint64](uint8(0)))
+		t.Run("ToUint64MaxUint8", wantConversion[uint64](uint8(math.MaxUint8)))
+		t.Run("ToUint", wantConversion[uint](uint8(0)))
+		t.Run("ToUintMaxUint8", wantConversion[uint](uint8(math.MaxUint8)))
+		t.Run("ToUintptr", wantConversion[uintptr](uint8(0)))
+	})
+
+	t.Run("FromUint16", func(t *testing.T) {
+		t.Run("ToInt8", wantConversion[int8](uint16(0)))
+		t.Run("ToInt8Max", wantConversion[int8](uint16(math.MaxInt8)))
+		t.Run("ToInt16", wantConversion[int16](uint16(0)))
+		t.Run("ToInt16Max", wantConversion[int16](uint16(math.MaxInt16)))
+		t.Run("ToInt32", wantConversion[int32](uint16(0)))
+		t.Run("ToInt32MaxUint16", wantConversion[int32](uint16(math.MaxInt16)))
+		t.Run("ToInt64", wantConversion[int64](uint16(0)))
+		t.Run("ToInt64MaxUint16", wantConversion[int64](uint16(math.MaxUint16)))
+		t.Run("ToInt", wantConversion[int](uint16(0)))
+		t.Run("ToIntMaxInt16", wantConversion[int](uint16(math.MaxInt16)))
+		t.Run("ToUint8", wantConversion[uint8](uint16(0)))
+		t.Run("ToUint8Max", wantConversion[uint8](uint16(math.MaxUint8)))
+		t.Run("ToUint16", wantConversion[uint16](uint16(0)))
+		t.Run("ToUint16Max", wantConversion[uint16](uint16(math.MaxUint16)))
+		t.Run("ToUint32", wantConversion[uint32](uint16(0)))
+		t.Run("ToUint32MaxUint16", wantConversion[uint32](uint16(math.MaxUint16)))
+		t.Run("ToUint64", wantConversion[uint64](uint16(0)))
+		t.Run("ToUint64MaxUint16", wantConversion[uint64](uint16(math.MaxUint16)))
+		t.Run("ToUint", wantConversion[uint](uint16(0)))
+		t.Run("ToUintMaxUint16", wantConversion[uint](uint16(math.MaxUint16)))
+		t.Run("ToUintptr", wantConversion[uintptr](uint16(0)))
+	})
+
+	t.Run("FromUint32", func(t *testing.T) {
+		t.Run("ToInt8", wantConversion[int8](uint32(0)))
+		t.Run("ToInt8Max", wantConversion[int8](uint32(math.MaxInt8)))
+		t.Run("ToInt16", wantConversion[int16](uint32(0)))
+		t.Run("ToInt16Max", wantConversion[int16](uint32(math.MaxInt16)))
+		t.Run("ToInt32", wantConversion[int32](uint32(0)))
+		t.Run("ToInt32Max", wantConversion[int32](uint32(math.MaxInt32)))
+		t.Run("ToInt64", wantConversion[int64](uint32(0)))
+		t.Run("ToInt64MaxUint32", wantConversion[int64](uint32(math.MaxUint32)))
+		t.Run("ToInt", wantConversion[int](uint32(0)))
+		t.Run("ToIntMaxInt32", wantConversion[int](uint32(math.MaxInt32)))
+		t.Run("ToUint8", wantConversion[uint8](uint32(0)))
+		t.Run("ToUint8Max", wantConversion[uint8](uint32(math.MaxUint8)))
+		t.Run("ToUint16", wantConversion[uint16](uint32(0)))
+		t.Run("ToUint16Max", wantConversion[uint16](uint32(math.MaxUint16)))
+		t.Run("ToUint32", wantConversion[uint32](uint32(0)))
+		t.Run("ToUint32Max", wantConversion[uint32](uint32(math.MaxUint32)))
+		t.Run("ToUint64", wantConversion[uint64](uint32(0)))
+		t.Run("ToUint64MaxUint32", wantConversion[uint64](uint32(math.MaxUint32)))
+		t.Run("ToUint", wantConversion[uint](uint32(0)))
+		t.Run("ToUintMaxUint32", wantConversion[uint](uint32(math.MaxUint32)))
+		t.Run("ToUintptr", wantConversion[uintptr](uint32(0)))
+	})
+
+	t.Run("FromUint64", func(t *testing.T) {
+		t.Run("ToInt8", wantConversion[int8](uint64(0)))
+		t.Run("ToInt8Max", wantConversion[int8](uint64(math.MaxInt8)))
+		t.Run("ToInt16", wantConversion[int16](uint64(0)))
+		t.Run("ToInt16Max", wantConversion[int16](uint64(math.MaxInt16)))
+		t.Run("ToInt32", wantConversion[int32](uint64(0)))
+		t.Run("ToInt32Max", wantConversion[int32](uint64(math.MaxInt32)))
+		t.Run("ToInt64", wantConversion[int64](uint64(0)))
+		t.Run("ToInt64Max", wantConversion[int64](uint64(math.MaxInt64)))
+		t.Run("ToInt", wantConversion[int](uint64(0)))
+		t.Run("ToIntMax", wantConversion[int](uint64(math.MaxInt64)))
+		t.Run("ToUint8", wantConversion[uint8](uint64(0)))
+		t.Run("ToUint8Max", wantConversion[uint8](uint64(math.MaxUint8)))
+		t.Run("ToUint16", wantConversion[uint16](uint64(0)))
+		t.Run("ToUint16Max", wantConversion[uint16](uint64(math.MaxUint16)))
+		t.Run("ToUint32", wantConversion[uint32](uint64(0)))
+		t.Run("ToUint32Max", wantConversion[uint32](uint64(math.MaxUint32)))
+		t.Run("ToUint64", wantConversion[uint64](uint64(0)))
+		t.Run("ToUint64Max", wantConversion[uint64](uint64(math.MaxUint64)))
+		t.Run("ToUint", wantConversion[uint](uint64(0)))
+		t.Run("ToUintMax", wantConversion[uint](uint64(math.MaxUint64)))
+		t.Run("ToUintptr", wantConversion[uintptr](uint64(0)))
+	})
+
+	t.Run("FromInt", func(t *testing.T) {
+		t.Run("ToInt8", wantConversion[int8](int(0)))
+		t.Run("ToInt8Max", wantConversion[int8](int(math.MaxInt8)))
+		t.Run("ToInt8Min", wantConversion[int8](int(math.MinInt8)))
+		t.Run("ToInt16", wantConversion[int16](int(0)))
+		t.Run("ToInt16Max", wantConversion[int16](int(math.MaxInt16)))
+		t.Run("ToInt16Min", wantConversion[int16](int(math.MinInt16)))
+		t.Run("ToInt32", wantConversion[int32](int(0)))
+		t.Run("ToInt32Max", wantConversion[int32](int(math.MaxInt32)))
+		t.Run("ToInt32Min", wantConversion[int32](int(math.MinInt32)))
+		t.Run("ToInt64", wantConversion[int64](int(0)))
+		t.Run("ToInt64MaxInt32", wantConversion[int64](int(math.MaxInt32)))
+		t.Run("ToInt64MinInt32", wantConversion[int64](int(math.MinInt32)))
+		t.Run("ToInt", wantConversion[int](int(0)))
+		t.Run("ToIntMaxInt32", wantConversion[int](int(math.MaxInt32)))
+		t.Run("ToIntMinInt32", wantConversion[int](int(math.MinInt32)))
+		t.Run("ToUint8", wantConversion[uint8](int(0)))
+		t.Run("ToUint8Max", wantConversion[uint8](int(math.MaxUint8)))
+		t.Run("ToUint16", wantConversion[uint16](int(0)))
+		t.Run("ToUint16Max", wantConversion[uint16](int(math.MaxUint16)))
+		t.Run("ToUint32", wantConversion[uint32](int(0)))
+		t.Run("ToUint32MaxInt32", wantConversion[uint32](int(math.MaxInt32)))
+		t.Run("ToUint64", wantConversion[uint64](int(0)))
+		t.Run("ToUint64MaxInt32", wantConversion[uint64](int(math.MaxInt32)))
+		t.Run("ToUint", wantConversion[uint](int(0)))
+		t.Run("ToUintMaxInt32", wantConversion[uint](int(math.MaxInt32)))
+		t.Run("ToUintptr", wantConversion[uintptr](int(0)))
+	})
+
+	t.Run("FromInt8", func(t *testing.T) {
+		t.Run("ToInt8", wantConversion[int8](int8(0)))
+		t.Run("ToInt8Max", wantConversion[int8](int8(math.MaxInt8)))
+		t.Run("ToInt8Min", wantConversion[int8](int8(math.MinInt8)))
+		t.Run("ToInt16", wantConversion[int16](int8(0)))
+		t.Run("ToInt16MaxInt8", wantConversion[int16](int8(math.MaxInt8)))
+		t.Run("ToInt16MinInt8", wantConversion[int16](int8(math.MinInt8)))
+		t.Run("ToInt32", wantConversion[int32](int8(0)))
+		t.Run("ToInt32MaxInt8", wantConversion[int32](int8(math.MaxInt8)))
+		t.Run("ToInt32MinInt8", wantConversion[int32](int8(math.MinInt8)))
+		t.Run("ToInt64", wantConversion[int64](int8(0)))
+		t.Run("ToInt64MaxInt8", wantConversion[int64](int8(math.MaxInt8)))
+		t.Run("ToInt64MinInt8", wantConversion[int64](int8(math.MinInt8)))
+		t.Run("ToInt", wantConversion[int](int8(0)))
+		t.Run("ToIntMaxInt8", wantConversion[int](int8(math.MaxInt8)))
+		t.Run("ToIntMinInt8", wantConversion[int](int8(math.MinInt8)))
+		t.Run("ToUint8", wantConversion[uint8](int8(0)))
+		t.Run("ToUint8MaxInt8", wantConversion[uint8](int8(math.MaxInt8)))
+		t.Run("ToUint16", wantConversion[uint16](int8(0)))
+		t.Run("ToUint16MaxInt8", wantConversion[uint16](int8(math.MaxInt8)))
+		t.Run("ToUint32", wantConversion[uint32](int8(0)))
+		t.Run("ToUint32MaxInt9", wantConversion[uint32](int8(math.MaxInt8)))
+		t.Run("ToUint64", wantConversion[uint64](int8(0)))
+		t.Run("ToUint64MaxInt8", wantConversion[uint64](int8(math.MaxInt8)))
+		t.Run("ToUint", wantConversion[uint](int8(0)))
+		t.Run("ToUintMaxInt8", wantConversion[uint](int8(math.MaxInt8)))
+		t.Run("ToUintptr", wantConversion[uintptr](int8(0)))
+	})
+
+	t.Run("FromInt16", func(t *testing.T) {
+		t.Run("ToInt8", wantConversion[int8](int16(0)))
+		t.Run("ToInt8Max", wantConversion[int8](int16(math.MaxInt8)))
+		t.Run("ToInt8Min", wantConversion[int8](int16(math.MinInt8)))
+		t.Run("ToInt16", wantConversion[int16](int16(0)))
+		t.Run("ToInt16Max", wantConversion[int16](int16(math.MaxInt16)))
+		t.Run("ToInt16Min", wantConversion[int16](int16(math.MinInt16)))
+		t.Run("ToInt32", wantConversion[int32](int16(0)))
+		t.Run("ToInt32MaxInt16", wantConversion[int32](int16(math.MaxInt16)))
+		t.Run("ToInt32MinInt16", wantConversion[int32](int16(math.MinInt16)))
+		t.Run("ToInt64", wantConversion[int64](int16(0)))
+		t.Run("ToInt64MaxInt16", wantConversion[int64](int16(math.MaxInt16)))
+		t.Run("ToInt64MinInt16", wantConversion[int64](int16(math.MinInt16)))
+		t.Run("ToInt", wantConversion[int](int16(0)))
+		t.Run("ToIntMaxInt16", wantConversion[int](int16(math.MaxInt16)))
+		t.Run("ToIntMinInt16", wantConversion[int](int16(math.MinInt16)))
+		t.Run("ToUint8", wantConversion[uint8](int16(0)))
+		t.Run("ToUint8Max", wantConversion[uint8](int16(math.MaxUint8)))
+		t.Run("ToUint16", wantConversion[uint16](int16(0)))
+		t.Run("ToUint16MaxInt16", wantConversion[uint16](int16(math.MaxInt16)))
+		t.Run("ToUint32", wantConversion[uint32](int16(0)))
+		t.Run("ToUint32MaxInt16", wantConversion[uint32](int16(math.MaxInt16)))
+		t.Run("ToUint64", wantConversion[uint64](int16(0)))
+		t.Run("ToUint64MaxInt16", wantConversion[uint64](int16(math.MaxInt16)))
+		t.Run("ToUint", wantConversion[uint](int16(0)))
+		t.Run("ToUintMaxInt16", wantConversion[uint](int16(math.MaxInt16)))
+		t.Run("ToUintptr", wantConversion[uintptr](int16(0)))
+	})
+
+	t.Run("FromInt32", func(t *testing.T) {
+		t.Run("ToInt8", wantConversion[int8](int32(0)))
+		t.Run("ToInt8Max", wantConversion[int8](int32(math.MaxInt8)))
+		t.Run("ToInt8Min", wantConversion[int8](int32(math.MinInt8)))
+		t.Run("ToInt16", wantConversion[int16](int32(0)))
+		t.Run("ToInt16Max", wantConversion[int16](int32(math.MaxInt16)))
+		t.Run("ToInt16Min", wantConversion[int16](int32(math.MinInt16)))
+		t.Run("ToInt32", wantConversion[int32](int32(0)))
+		t.Run("ToInt32Max", wantConversion[int32](int32(math.MaxInt32)))
+		t.Run("ToInt32Min", wantConversion[int32](int32(math.MinInt32)))
+		t.Run("ToInt64", wantConversion[int64](int32(0)))
+		t.Run("ToInt64MaxInt32", wantConversion[int64](int32(math.MaxInt32)))
+		t.Run("ToInt64MinInt32", wantConversion[int64](int32(math.MinInt32)))
+		t.Run("ToInt", wantConversion[int](int32(0)))
+		t.Run("ToIntMaxInt32", wantConversion[int](int32(math.MaxInt32)))
+		t.Run("ToIntMinInt32", wantConversion[int](int32(math.MinInt32)))
+		t.Run("ToUint8", wantConversion[uint8](int32(0)))
+		t.Run("ToUint8Max", wantConversion[uint8](int32(math.MaxUint8)))
+		t.Run("ToUint16", wantConversion[uint16](int32(0)))
+		t.Run("ToUint16Max", wantConversion[uint16](int32(math.MaxUint16)))
+		t.Run("ToUint32", wantConversion[uint32](int32(0)))
+		t.Run("ToUint32MaxInt32", wantConversion[uint32](int32(math.MaxInt32)))
+		t.Run("ToUint64", wantConversion[uint64](int32(0)))
+		t.Run("ToUint64MaxInt32", wantConversion[uint64](int32(math.MaxInt32)))
+		t.Run("ToUint", wantConversion[uint](int32(0)))
+		t.Run("ToUintMaxInt32", wantConversion[uint](int32(math.MaxInt32)))
+		t.Run("ToUintptr", wantConversion[uintptr](int32(0)))
+	})
+
+	t.Run("FromInt64", func(t *testing.T) {
+		t.Run("ToInt8", wantConversion[int8](int64(0)))
+		t.Run("ToInt8Max", wantConversion[int8](int64(math.MaxInt8)))
+		t.Run("ToInt8Min", wantConversion[int8](int64(math.MinInt8)))
+		t.Run("ToInt16", wantConversion[int16](int64(0)))
+		t.Run("ToInt16Max", wantConversion[int16](int64(math.MaxInt16)))
+		t.Run("ToInt16Min", wantConversion[int16](int64(math.MinInt16)))
+		t.Run("ToInt32", wantConversion[int32](int64(0)))
+		t.Run("ToInt32Max", wantConversion[int32](int64(math.MaxInt32)))
+		t.Run("ToInt32Min", wantConversion[int32](int64(math.MinInt32)))
+		t.Run("ToInt64", wantConversion[int64](int64(0)))
+		t.Run("ToInt64Max", wantConversion[int64](int64(math.MaxInt64)))
+		t.Run("ToInt64Min", wantConversion[int64](int64(math.MinInt64)))
+		t.Run("ToInt", wantConversion[int](int64(0)))
+		t.Run("ToIntMaxInt", wantConversion[int](int64(math.MaxInt64)))
+		t.Run("ToIntMinInt", wantConversion[int](int64(math.MinInt64)))
+		t.Run("ToUint8", wantConversion[uint8](int64(0)))
+		t.Run("ToUint8Max", wantConversion[uint8](int64(math.MaxUint8)))
+		t.Run("ToUint16", wantConversion[uint16](int64(0)))
+		t.Run("ToUint16Max", wantConversion[uint16](int64(math.MaxUint16)))
+		t.Run("ToUint32", wantConversion[uint32](int64(0)))
+		t.Run("ToUint32Max", wantConversion[uint32](int64(math.MaxUint32)))
+		t.Run("ToUint64", wantConversion[uint64](int64(0)))
+		t.Run("ToUint64Max", wantConversion[uint64](int64(math.MaxInt64)))
+		t.Run("ToUint", wantConversion[uint](int64(0)))
+		t.Run("ToUintMaxInt", wantConversion[uint](int64(math.MaxInt64)))
+		t.Run("ToUintptr", wantConversion[uintptr](int64(0)))
+	})
+}
+
+func TestToInt_ValueFitsIntoReceiver_ReturnsValueAndTrue(t *testing.T) {
+	t.Run("FromInt", wantConversionFunc(narrow.ToInt[int], 0))
+	t.Run("FromIntMax", wantConversionFunc(narrow.ToInt[int], math.MaxInt))
+	t.Run("FromIntMin", wantConversionFunc(narrow.ToInt[int], math.MinInt))
+	t.Run("FromInt8", wantConversionFunc(narrow.ToInt[int8], 0))
+	t.Run("FromInt8Min", wantConversionFunc(narrow.ToInt[int8], math.MinInt8))
+	t.Run("FromInt8Max", wantConversionFunc(narrow.ToInt[int8], math.MaxInt8))
+	t.Run("FromInt16", wantConversionFunc(narrow.ToInt[int16], 0))
+	t.Run("FromInt16Min", wantConversionFunc(narrow.ToInt[int16], math.MinInt16))
+	t.Run("FromInt16Max", wantConversionFunc(narrow.ToInt[int16], math.MaxInt16))
+	t.Run("FromInt32", wantConversionFunc(narrow.ToInt[int32], 0))
+	t.Run("FromInt32Min", wantConversionFunc(narrow.ToInt[int32], math.MinInt32))
+	t.Run("FromInt32Max", wantConversionFunc(narrow.ToInt[int32], math.MaxInt32))
+	t.Run("FromInt64", wantConversionFunc(narrow.ToInt[int64], 0))
+	t.Run("FromInt64MaxInt", wantConversionFunc(narrow.ToInt[int64], math.MaxInt))
+	t.Run("FromInt64MinInt", wantConversionFunc(narrow.ToInt[int64], math.MinInt))
+
+	t.Run("FromUint", wantConversionFunc(narrow.ToInt[uint], 0))
+	t.Run("FromUintMaxInt", wantConversionFunc(narrow.ToInt[uint], math.MaxInt))
+	t.Run("FromUint8", wantConversionFunc(narrow.ToInt[uint8], 0))
+	t.Run("FromUint8MaxInt8", wantConversionFunc(narrow.ToInt[uint8], math.MaxInt8))
+	t.Run("FromUint16", wantConversionFunc(narrow.ToInt[uint16], 0))
+	t.Run("FromUint16MaxInt16", wantConversionFunc(narrow.ToInt[uint16], math.MaxInt16))
+	t.Run("FromUint32", wantConversionFunc(narrow.ToInt[uint32], 0))
+	t.Run("FromUint32MaxInt32", wantConversionFunc(narrow.ToInt[uint32], math.MaxInt32))
+	t.Run("FromUint64", wantConversionFunc(narrow.ToInt[uint64], 0))
+	t.Run("FromUint64MaxInt", wantConversionFunc(narrow.ToInt[uint64], math.MaxInt))
+	t.Run("FromUintptr", wantConversionFunc(narrow.ToInt[uintptr], 0))
+}
+
+func TestToInt8_ValueFitsIntoReceiver_ReturnsValueAndTrue(t *testing.T) {
+	t.Run("FromInt", wantConversionFunc(narrow.ToInt8[int], 0))
+	t.Run("FromIntMaxInt8", wantConversionFunc(narrow.ToInt8[int], math.MaxInt8))
+	t.Run("FromIntMinInt8", wantConversionFunc(narrow.ToInt8[int], math.MinInt8))
+	t.Run("FromInt8", wantConversionFunc(narrow.ToInt8[int8], 0))
+	t.Run("FromInt8Min", wantConversionFunc(narrow.ToInt8[int8], math.MinInt8))
+	t.Run("FromInt8Max", wantConversionFunc(narrow.ToInt8[int8], math.MaxInt8))
+	t.Run("FromInt16", wantConversionFunc(narrow.ToInt8[int16], 0))
+	t.Run("FromInt16MinInt8", wantConversionFunc(narrow.ToInt8[int16], math.MinInt8))
+	t.Run("FromInt16MaxInt8", wantConversionFunc(narrow.ToInt8[int16], math.MaxInt8))
+	t.Run("FromInt32", wantConversionFunc(narrow.ToInt8[int32], 0))
+	t.Run("FromInt32MinInt8", wantConversionFunc(narrow.ToInt8[int32], math.MinInt8))
+	t.Run("FromInt32MaxInt8", wantConversionFunc(narrow.ToInt8[int32], math.MaxInt8))
+	t.Run("FromInt64", wantConversionFunc(narrow.ToInt8[int64], 0))
+	t.Run("FromInt64MaxInt8", wantConversionFunc(narrow.ToInt8[int64], math.MaxInt8))
+	t.Run("FromInt64MinInt8", wantConversionFunc(narrow.ToInt8[int64], math.MinInt8))
+
+	t.Run("FromUint", wantConversionFunc(narrow.ToInt8[uint], 0))
+	t.Run("FromUintMaxInt8", wantConversionFunc(narrow.ToInt8[uint], math.MaxInt8))
+	t.Run("FromUint8", wantConversionFunc(narrow.ToInt8[uint8], 0))
+	t.Run("FromUint8Max", wantConversionFunc(narrow.ToInt8[uint8], math.MaxInt8))
+	t.Run("FromUint16", wantConversionFunc(narrow.ToInt8[uint16], 0))
+	t.Run("FromUint16MaxInt8", wantConversionFunc(narrow.ToInt8[uint16], math.MaxInt8))
+	t.Run("FromUint32", wantConversionFunc(narrow.ToInt8[uint32], 0))
+	t.Run("FromUint32MaxInt8", wantConversionFunc(narrow.ToInt8[uint32], math.MaxInt8))
+	t.Run("FromUint64", wantConversionFunc(narrow.ToInt8[uint64], 0))
+	t.Run("FromUint64MaxInt8", wantConversionFunc(narrow.ToInt8[uint64], math.MaxInt8))
+	t.Run("FromUintptr", wantConversionFunc(narrow.ToInt8[uintptr], 0))
+}
+
+func TestToInt16_ValueFitsIntoReceiver_ReturnsValueAndTrue(t *testing.T) {
+	t.Run("FromInt", wantConversionFunc(narrow.ToInt16[int], 0))
+	t.Run("FromIntMaxInt16", wantConversionFunc(narrow.ToInt16[int], math.MaxInt16))
+	t.Run("FromIntMinInt16", wantConversionFunc(narrow.ToInt16[int], math.MinInt16))
+	t.Run("FromInt8", wantConversionFunc(narrow.ToInt16[int8], 0))
+	t.Run("FromInt8Min", wantConversionFunc(narrow.ToInt16[int8], math.MinInt8))
+	t.Run("FromInt8Max", wantConversionFunc(narrow.ToInt16[int8], math.MaxInt8))
+	t.Run("FromInt16", wantConversionFunc(narrow.ToInt16[int16], 0))
+	t.Run("FromInt16Min", wantConversionFunc(narrow.ToInt16[int16], math.MinInt16))
+	t.Run("FromInt16Max", wantConversionFunc(narrow.ToInt16[int16], math.MaxInt16))
+	t.Run("FromInt32", wantConversionFunc(narrow.ToInt16[int32], 0))
+	t.Run("FromInt32MinInt16", wantConversionFunc(narrow.ToInt16[int32], math.MinInt16))
+	t.Run("FromInt32MaxInt16", wantConversionFunc(narrow.ToInt16[int32], math.MaxInt16))
+	t.Run("FromInt64", wantConversionFunc(narrow.ToInt16[int64], 0))
+	t.Run("FromInt64MaxInt16", wantConversionFunc(narrow.ToInt16[int64], math.MaxInt16))
+	t.Run("FromInt64MinInt16", wantConversionFunc(narrow.ToInt16[int64], math.MinInt16))
+
+	t.Run("FromUint", wantConversionFunc(narrow.ToInt16[uint], 0))
+	t.Run("FromUintMaxInt16", wantConversionFunc(narrow.ToInt16[uint], math.MaxInt16))
+	t.Run("FromUint8", wantConversionFunc(narrow.ToInt16[uint8], 0))
+	t.Run("FromUint8Max", wantConversionFunc(narrow.ToInt16[uint8], math.MaxInt8))
+	t.Run("FromUint16", wantConversionFunc(narrow.ToInt16[uint16], 0))
+	t.Run("FromUint16MaxInt16", wantConversionFunc(narrow.ToInt16[uint16], math.MaxInt16))
+	t.Run("FromUint32", wantConversionFunc(narrow.ToInt16[uint32], 0))
+	t.Run("FromUint32MaxInt16", wantConversionFunc(narrow.ToInt16[uint32], math.MaxInt16))
+	t.Run("FromUint64", wantConversionFunc(narrow.ToInt16[uint64], 0))
+	t.Run("FromUint64MaxInt16", wantConversionFunc(narrow.ToInt16[uint64], math.MaxInt16))
+	t.Run("FromUintptr", wantConversionFunc(narrow.ToInt16[uintptr], 0))
+}
+
+func TestToInt32_ValueFitsIntoReceiver_ReturnsValueAndTrue(t *testing.T) {
+	t.Run("FromInt", wantConversionFunc(narrow.ToInt32[int], 0))
+	t.Run("FromIntMaxInt32", wantConversionFunc(narrow.ToInt32[int], math.MaxInt32))
+	t.Run("FromIntMinInt32", wantConversionFunc(narrow.ToInt32[int], math.MinInt32))
+	t.Run("FromInt8", wantConversionFunc(narrow.ToInt32[int8], 0))
+	t.Run("FromInt8Min", wantConversionFunc(narrow.ToInt32[int8], math.MinInt8))
+	t.Run("FromInt8Max", wantConversionFunc(narrow.ToInt32[int8], math.MaxInt8))
+	t.Run("FromInt16", wantConversionFunc(narrow.ToInt32[int16], 0))
+	t.Run("FromInt16Min", wantConversionFunc(narrow.ToInt32[int16], math.MinInt16))
+	t.Run("FromInt16Max", wantConversionFunc(narrow.ToInt32[int16], math.MaxInt16))
+	t.Run("FromInt32", wantConversionFunc(narrow.ToInt32[int32], 0))
+	t.Run("FromInt32Min", wantConversionFunc(narrow.ToInt32[int32], math.MinInt32))
+	t.Run("FromInt32Max", wantConversionFunc(narrow.ToInt32[int32], math.MaxInt32))
+	t.Run("FromInt64", wantConversionFunc(narrow.ToInt32[int64], 0))
+	t.Run("FromInt64MaxInt32", wantConversionFunc(narrow.ToInt32[int64], math.MaxInt32))
+	t.Run("FromInt64MinInt32", wantConversionFunc(narrow.ToInt32[int64], math.MinInt32))
+
+	t.Run("FromUint", wantConversionFunc(narrow.ToInt32[uint], 0))
+	t.Run("FromUintMaxInt32", wantConversionFunc(narrow.ToInt32[uint], math.MaxInt32))
+	t.Run("FromUint8", wantConversionFunc(narrow.ToInt32[uint8], 0))
+	t.Run("FromUint8MaxInt8", wantConversionFunc(narrow.ToInt32[uint8], math.MaxInt8))
+	t.Run("FromUint16", wantConversionFunc(narrow.ToInt32[uint16], 0))
+	t.Run("FromUint16MaxInt16", wantConversionFunc(narrow.ToInt32[uint16], math.MaxInt16))
+	t.Run("FromUint32", wantConversionFunc(narrow.ToInt32[uint32], 0))
+	t.Run("FromUint32MaxInt32", wantConversionFunc(narrow.ToInt32[uint32], math.MaxInt32))
+	t.Run("FromUint64", wantConversionFunc(narrow.ToInt32[uint64], 0))
+	t.Run("FromUint64MaxInt32", wantConversionFunc(narrow.ToInt32[uint64], math.MaxInt32))
+	t.Run("FromUintptr", wantConversionFunc(narrow.ToInt32[uintptr], 0))
+}
+
+func TestToInt64_ValueFitsIntoReceiver_ReturnsValueAndTrue(t *testing.T) {
+	t.Run("FromInt", wantConversionFunc(narrow.ToInt64[int], 0))
+	t.Run("FromIntMaxInt32", wantConversionFunc(narrow.ToInt64[int], math.MaxInt32))
+	t.Run("FromIntMin", wantConversionFunc(narrow.ToInt64[int], math.MinInt32))
+	t.Run("FromInt8", wantConversionFunc(narrow.ToInt64[int8], 0))
+	t.Run("FromInt8Min", wantConversionFunc(narrow.ToInt64[int8], math.MinInt8))
+	t.Run("FromInt8Max", wantConversionFunc(narrow.ToInt64[int8], math.MaxInt8))
+	t.Run("FromInt16", wantConversionFunc(narrow.ToInt64[int16], 0))
+	t.Run("FromInt16Min", wantConversionFunc(narrow.ToInt64[int16], math.MinInt16))
+	t.Run("FromInt16Max", wantConversionFunc(narrow.ToInt64[int16], math.MaxInt16))
+	t.Run("FromInt32", wantConversionFunc(narrow.ToInt64[int32], 0))
+	t.Run("FromInt32Min", wantConversionFunc(narrow.ToInt64[int32], math.MinInt32))
+	t.Run("FromInt32Max", wantConversionFunc(narrow.ToInt64[int32], math.MaxInt32))
+	t.Run("FromInt64", wantConversionFunc(narrow.ToInt64[int64], 0))
+	t.Run("FromInt64Max", wantConversionFunc(narrow.ToInt64[int64], math.MaxInt64))
+	t.Run("FromInt64Min", wantConversionFunc(narrow.ToInt64[int64], math.MinInt64))
+
+	t.Run("FromUint", wantConversionFunc(narrow.ToInt64[uint], 0))
+	t.Run("FromUintMaxUint32", wantConversionFunc(narrow.ToInt64[uint], math.MaxUint32))
+	t.Run("FromUint8", wantConversionFunc(narrow.ToInt64[uint8], 0))
+	t.Run("FromUint8Max", wantConversionFunc(narrow.ToInt64[uint8], math.MaxUint8))
+	t.Run("FromUint16", wantConversionFunc(narrow.ToInt64[uint16], 0))
+	t.Run("FromUint16Max", wantConversionFunc(narrow.ToInt64[uint16], math.MaxUint16))
+	t.Run("FromUint32", wantConversionFunc(narrow.ToInt64[uint32], 0))
+	t.Run("FromUint32Max", wantConversionFunc(narrow.ToInt64[uint32], math.MaxUint32))
+	t.Run("FromUint64", wantConversionFunc(narrow.ToInt64[uint64], 0))
+	t.Run("FromUint64Max", wantConversionFunc(narrow.ToInt64[uint64], math.MaxInt64))
+	t.Run("FromUintptr", wantConversionFunc(narrow.ToInt64[uintptr], 0))
+}
+
+func TestToUint_ValueFitsIntoReceiver_ReturnsValueAndTrue(t *testing.T) {
+	t.Run("FromInt", wantConversionFunc(narrow.ToUint[int], 0))
+	t.Run("FromIntMaxInt32", wantConversionFunc(narrow.ToUint[int], math.MaxInt32))
+	t.Run("FromInt8", wantConversionFunc(narrow.ToUint[int8], 0))
+	t.Run("FromInt8Max", wantConversionFunc(narrow.ToUint[int8], math.MaxInt8))
+	t.Run("FromInt16", wantConversionFunc(narrow.ToUint[int16], 0))
+	t.Run("FromInt16Max", wantConversionFunc(narrow.ToUint[int16], math.MaxInt16))
+	t.Run("FromInt32", wantConversionFunc(narrow.ToUint[int32], 0))
+	t.Run("FromInt32Max", wantConversionFunc(narrow.ToUint[int32], math.MaxInt32))
+	t.Run("FromInt64", wantConversionFunc(narrow.ToUint[int64], 0))
+	t.Run("FromInt64MaxUint32", wantConversionFunc(narrow.ToUint[int64], math.MaxInt32))
+
+	t.Run("FromUint", wantConversionFunc(narrow.ToUint[uint], 0))
+	t.Run("FromUintMaxUint32", wantConversionFunc(narrow.ToUint[uint], math.MaxUint32))
+	t.Run("FromUint8", wantConversionFunc(narrow.ToUint[uint8], 0))
+	t.Run("FromUint8Max", wantConversionFunc(narrow.ToUint[uint8], math.MaxUint8))
+	t.Run("FromUint16", wantConversionFunc(narrow.ToUint[uint16], 0))
+	t.Run("FromUint16Max", wantConversionFunc(narrow.ToUint[uint16], math.MaxUint16))
+	t.Run("FromUint32", wantConversionFunc(narrow.ToUint[uint32], 0))
+	t.Run("FromUint32Max", wantConversionFunc(narrow.ToUint[uint32], math.MaxUint32))
+	t.Run("FromUint64", wantConversionFunc(narrow.ToUint[uint64], 0))
+	t.Run("FromUint64MaxUint32", wantConversionFunc(narrow.ToUint[uint64], math.MaxUint32))
+	t.Run("FromUintptr", wantConversionFunc(narrow.ToUint[uintptr], 0))
+}
+
+func TestToUint8_ValueFitsIntoReceiver_ReturnsValueAndTrue(t *testing.T) {
+	t.Run("FromInt", wantConversionFunc(narrow.ToUint8[int], 0))
+	t.Run("FromIntMaxUint8", wantConversionFunc(narrow.ToUint8[int], math.MaxUint8))
+	t.Run("FromInt8", wantConversionFunc(narrow.ToUint8[int8], 0))
+	t.Run("FromInt8Max", wantConversionFunc(narrow.ToUint8[int8], math.MaxInt8))
+	t.Run("FromInt16", wantConversionFunc(narrow.ToUint8[int16], 0))
+	t.Run("FromInt16MaxUint8", wantConversionFunc(narrow.ToUint8[int16], math.MaxInt8))
+	t.Run("FromInt32", wantConversionFunc(narrow.ToUint8[int32], 0))
+	t.Run("FromInt32MaxUint8", wantConversionFunc(narrow.ToUint8[int32], math.MaxUint8))
+	t.Run("FromInt64", wantConversionFunc(narrow.ToUint8[int64], 0))
+	t.Run("FromInt64MaxUint8", wantConversionFunc(narrow.ToUint8[int64], math.MaxUint8))
+
+	t.Run("FromUint", wantConversionFunc(narrow.ToUint8[uint], 0))
+	t.Run("FromUintMaxUint8", wantConversionFunc(narrow.ToUint8[uint], math.MaxUint8))
+	t.Run("FromUint8", wantConversionFunc(narrow.ToUint8[uint8], 0))
+	t.Run("FromUint8Max", wantConversionFunc(narrow.ToUint8[uint8], math.MaxUint8))
+	t.Run("FromUint16", wantConversionFunc(narrow.ToUint8[uint16], 0))
+	t.Run("FromUint16MaxUint8", wantConversionFunc(narrow.ToUint8[uint16], math.MaxUint8))
+	t.Run("FromUint32", wantConversionFunc(narrow.ToUint8[uint32], 0))
+	t.Run("FromUint32MaxUint8", wantConversionFunc(narrow.ToUint8[uint32], math.MaxUint8))
+	t.Run("FromUint64", wantConversionFunc(narrow.ToUint8[uint64], 0))
+	t.Run("FromUint64MaxUint8", wantConversionFunc(narrow.ToUint8[uint64], math.MaxUint8))
+	t.Run("FromUintptr", wantConversionFunc(narrow.ToUint8[uintptr], 0))
+}
+
+func TestToUint16_ValueFitsIntoReceiver_ReturnsValueAndTrue(t *testing.T) {
+	t.Run("FromInt", wantConversionFunc(narrow.ToUint16[int], 0))
+	t.Run("FromIntMaxUint16", wantConversionFunc(narrow.ToUint16[int], math.MaxUint16))
+	t.Run("FromInt8", wantConversionFunc(narrow.ToUint16[int8], 0))
+	t.Run("FromInt8Max", wantConversionFunc(narrow.ToUint16[int8], math.MaxInt8))
+	t.Run("FromInt16", wantConversionFunc(narrow.ToUint16[int16], 0))
+	t.Run("FromInt16Max", wantConversionFunc(narrow.ToUint16[int16], math.MaxInt16))
+	t.Run("FromInt32", wantConversionFunc(narrow.ToUint16[int32], 0))
+	t.Run("FromInt32MaxUint16", wantConversionFunc(narrow.ToUint16[int32], math.MaxUint16))
+	t.Run("FromInt64", wantConversionFunc(narrow.ToUint16[int64], 0))
+	t.Run("FromInt64MaxUint16", wantConversionFunc(narrow.ToUint16[int64], math.MaxUint16))
+
+	t.Run("FromUint", wantConversionFunc(narrow.ToUint16[uint], 0))
+	t.Run("FromUintMaxUint16", wantConversionFunc(narrow.ToUint16[uint], math.MaxUint16))
+	t.Run("FromUint8", wantConversionFunc(narrow.ToUint16[uint8], 0))
+	t.Run("FromUint8Max", wantConversionFunc(narrow.ToUint16[uint8], math.MaxUint8))
+	t.Run("FromUint16", wantConversionFunc(narrow.ToUint16[uint16], 0))
+	t.Run("FromUint16Max", wantConversionFunc(narrow.ToUint16[uint16], math.MaxUint16))
+	t.Run("FromUint32", wantConversionFunc(narrow.ToUint16[uint32], 0))
+	t.Run("FromUint32MaxUint16", wantConversionFunc(narrow.ToUint16[uint32], math.MaxUint16))
+	t.Run("FromUint64", wantConversionFunc(narrow.ToUint16[uint64], 0))
+	t.Run("FromUint64MaxUint16", wantConversionFunc(narrow.ToUint16[uint64], math.MaxUint16))
+	t.Run("FromUintptr", wantConversionFunc(narrow.ToUint16[uintptr], 0))
+}
+
+func TestToUint32_ValueFitsIntoReceiver_ReturnsValueAndTrue(t *testing.T) {
+	t.Run("FromInt", wantConversionFunc(narrow.ToUint32[int], 0))
+	t.Run("FromIntMaxInt32", wantConversionFunc(narrow.ToUint32[int], math.MaxInt32))
+	t.Run("FromInt8", wantConversionFunc(narrow.ToUint32[int8], 0))
+	t.Run("FromInt8Max", wantConversionFunc(narrow.ToUint32[int8], math.MaxInt8))
+	t.Run("FromInt16", wantConversionFunc(narrow.ToUint32[int16], 0))
+	t.Run("FromInt16Max", wantConversionFunc(narrow.ToUint32[int16], math.MaxInt16))
+	t.Run("FromInt32", wantConversionFunc(narrow.ToUint32[int32], 0))
+	t.Run("FromInt32Max", wantConversionFunc(narrow.ToUint32[int32], math.MaxInt32))
+	t.Run("FromInt64", wantConversionFunc(narrow.ToUint32[int64], 0))
+	t.Run("FromInt64MaxUint32", wantConversionFunc(narrow.ToUint32[int64], math.MaxInt32))
+
+	t.Run("FromUint", wantConversionFunc(narrow.ToUint32[uint], 0))
+	t.Run("FromUintMaxUint32", wantConversionFunc(narrow.ToUint32[uint], math.MaxUint32))
+	t.Run("FromUint8", wantConversionFunc(narrow.ToUint32[uint8], 0))
+	t.Run("FromUint8Max", wantConversionFunc(narrow.ToUint32[uint8], math.MaxUint8))
+	t.Run("FromUint16", wantConversionFunc(narrow.ToUint32[uint16], 0))
+	t.Run("FromUint16Max", wantConversionFunc(narrow.ToUint32[uint16], math.MaxUint16))
+	t.Run("FromUint32", wantConversionFunc(narrow.ToUint32[uint32], 0))
+	t.Run("FromUint32Max", wantConversionFunc(narrow.ToUint32[uint32], math.MaxUint32))
+	t.Run("FromUint64", wantConversionFunc(narrow.ToUint32[uint64], 0))
+	t.Run("FromUint64MaxUint32", wantConversionFunc(narrow.ToUint32[uint64], math.MaxUint32))
+	t.Run("FromUintptr", wantConversionFunc(narrow.ToUint32[uintptr], 0))
+}
+
+func TestToUint64_ValueFitsIntoReceiver_ReturnsValueAndTrue(t *testing.T) {
+	t.Run("FromInt", wantConversionFunc(narrow.ToUint64[int], 0))
+	t.Run("FromIntMaxInt32", wantConversionFunc(narrow.ToUint64[int], math.MaxInt32))
+	t.Run("FromInt8", wantConversionFunc(narrow.ToUint64[int8], 0))
+	t.Run("FromInt8Max", wantConversionFunc(narrow.ToUint64[int8], math.MaxInt8))
+	t.Run("FromInt16", wantConversionFunc(narrow.ToUint64[int16], 0))
+	t.Run("FromInt16Max", wantConversionFunc(narrow.ToUint64[int16], math.MaxInt16))
+	t.Run("FromInt32", wantConversionFunc(narrow.ToUint64[int32], 0))
+	t.Run("FromInt32Max", wantConversionFunc(narrow.ToUint64[int32], math.MaxInt32))
+	t.Run("FromInt64", wantConversionFunc(narrow.ToUint64[int64], 0))
+	t.Run("FromInt64Max", wantConversionFunc(narrow.ToUint64[int64], math.MaxInt64))
+
+	t.Run("FromUint", wantConversionFunc(narrow.ToUint64[uint], 0))
+	t.Run("FromUintMaxUint32", wantConversionFunc(narrow.ToUint64[uint], math.MaxUint32))
+	t.Run("FromUint8", wantConversionFunc(narrow.ToUint64[uint8], 0))
+	t.Run("FromUint8Max", wantConversionFunc(narrow.ToUint64[uint8], math.MaxUint8))
+	t.Run("FromUint16", wantConversionFunc(narrow.ToUint64[uint16], 0))
+	t.Run("FromUint16Max", wantConversionFunc(narrow.ToUint64[uint16], math.MaxUint16))
+	t.Run("FromUint32", wantConversionFunc(narrow.ToUint64[uint32], 0))
+	t.Run("FromUint32Max", wantConversionFunc(narrow.ToUint64[uint32], math.MaxUint32))
+	t.Run("FromUint64", wantConversionFunc(narrow.ToUint64[uint64], 0))
+	t.Run("FromUint64Max", wantConversionFunc(narrow.ToUint64[uint64], math.MaxUint64))
+	t.Run("FromUintptr", wantConversionFunc(narrow.ToUint64[uintptr], 0))
+}
diff --git a/internal/protofields/descriptor.go b/internal/protofields/descriptor.go
new file mode 100644
index 0000000..e90d936
--- /dev/null
+++ b/internal/protofields/descriptor.go
@@ -0,0 +1,12 @@
+package protofields
+
+import "google.golang.org/protobuf/proto"
+
+// DescriptorName gets the type name of a proto Message. If value is nil, this
+// returns an empty string.
+func DescriptorName(value proto.Message) string {
+	if value == nil {
+		return ""
+	}
+	return string(value.ProtoReflect().Descriptor().Name())
+}
diff --git a/internal/protofields/dummies.go b/internal/protofields/dummies.go
new file mode 100644
index 0000000..9253fd4
--- /dev/null
+++ b/internal/protofields/dummies.go
@@ -0,0 +1,364 @@
+package protofields
+
+import (
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/account_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/activity_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/adverse_event_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/allergy_intolerance_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/appointment_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/appointment_response_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/audit_event_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/basic_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/binary_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/biologically_derived_product_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/body_structure_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/capability_statement_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/care_plan_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/care_team_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/catalog_entry_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/charge_item_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/charge_item_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/claim_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/claim_response_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/clinical_impression_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/code_system_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/communication_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/communication_request_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/compartment_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/composition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/concept_map_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/condition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/consent_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/contract_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/coverage_eligibility_request_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/coverage_eligibility_response_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/coverage_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/detected_issue_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_metric_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_request_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_use_statement_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/diagnostic_report_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/document_manifest_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/document_reference_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/effect_evidence_synthesis_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/encounter_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/endpoint_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/enrollment_request_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/enrollment_response_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/episode_of_care_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/event_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/evidence_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/evidence_variable_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/example_scenario_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/explanation_of_benefit_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/family_member_history_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/flag_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/goal_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/graph_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/group_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/guidance_response_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/healthcare_service_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/imaging_study_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/immunization_evaluation_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/immunization_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/immunization_recommendation_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/implementation_guide_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/insurance_plan_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/invoice_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/library_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/linkage_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/list_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/location_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/measure_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/measure_report_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/media_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_administration_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_dispense_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_knowledge_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_request_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_statement_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_authorization_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_contraindication_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_indication_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_ingredient_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_interaction_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_manufactured_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_packaged_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_pharmaceutical_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medicinal_product_undesirable_effect_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/message_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/message_header_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/molecular_sequence_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/naming_system_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/nutrition_order_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/observation_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/observation_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/operation_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/operation_outcome_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/organization_affiliation_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/organization_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/parameters_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/payment_notice_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/payment_reconciliation_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/person_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/plan_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/practitioner_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/practitioner_role_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/procedure_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/provenance_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/questionnaire_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/questionnaire_response_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/related_person_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/request_group_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/research_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/research_element_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/research_study_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/research_subject_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/risk_assessment_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/risk_evidence_synthesis_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/schedule_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/search_parameter_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/service_request_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/slot_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/specimen_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/specimen_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/structure_definition_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/structure_map_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/subscription_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/substance_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/substance_nucleic_acid_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/substance_polymer_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/substance_protein_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/substance_reference_information_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/substance_source_material_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/substance_specification_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/supply_delivery_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/supply_request_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/task_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/terminology_capabilities_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/test_report_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/test_script_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/value_set_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/verification_result_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/vision_prescription_go_proto"
+	"google.golang.org/protobuf/proto"
+)
+
+// dummyResources is an array of nil pointers to all resource messages.
+var dummyResources = []proto.Message{
+	(*account_go_proto.Account)(nil),
+	(*activity_definition_go_proto.ActivityDefinition)(nil),
+	(*adverse_event_go_proto.AdverseEvent)(nil),
+	(*allergy_intolerance_go_proto.AllergyIntolerance)(nil),
+	(*appointment_go_proto.Appointment)(nil),
+	(*appointment_response_go_proto.AppointmentResponse)(nil),
+	(*audit_event_go_proto.AuditEvent)(nil),
+	(*basic_go_proto.Basic)(nil),
+	(*biologically_derived_product_go_proto.BiologicallyDerivedProduct)(nil),
+	(*body_structure_go_proto.BodyStructure)(nil),
+	(*capability_statement_go_proto.CapabilityStatement)(nil),
+	(*care_plan_go_proto.CarePlan)(nil),
+	(*care_team_go_proto.CareTeam)(nil),
+	(*catalog_entry_go_proto.CatalogEntry)(nil),
+	(*charge_item_go_proto.ChargeItem)(nil),
+	(*charge_item_definition_go_proto.ChargeItemDefinition)(nil),
+	(*claim_go_proto.Claim)(nil),
+	(*claim_response_go_proto.ClaimResponse)(nil),
+	(*clinical_impression_go_proto.ClinicalImpression)(nil),
+	(*code_system_go_proto.CodeSystem)(nil),
+	(*communication_go_proto.Communication)(nil),
+	(*communication_request_go_proto.CommunicationRequest)(nil),
+	(*compartment_definition_go_proto.CompartmentDefinition)(nil),
+	(*composition_go_proto.Composition)(nil),
+	(*concept_map_go_proto.ConceptMap)(nil),
+	(*condition_go_proto.Condition)(nil),
+	(*consent_go_proto.Consent)(nil),
+	(*contract_go_proto.Contract)(nil),
+	(*coverage_go_proto.Coverage)(nil),
+	(*coverage_eligibility_request_go_proto.CoverageEligibilityRequest)(nil),
+	(*coverage_eligibility_response_go_proto.CoverageEligibilityResponse)(nil),
+	(*detected_issue_go_proto.DetectedIssue)(nil),
+	(*device_go_proto.Device)(nil),
+	(*device_definition_go_proto.DeviceDefinition)(nil),
+	(*device_metric_go_proto.DeviceMetric)(nil),
+	(*device_request_go_proto.DeviceRequest)(nil),
+	(*device_use_statement_go_proto.DeviceUseStatement)(nil),
+	(*diagnostic_report_go_proto.DiagnosticReport)(nil),
+	(*document_manifest_go_proto.DocumentManifest)(nil),
+	(*document_reference_go_proto.DocumentReference)(nil),
+	(*effect_evidence_synthesis_go_proto.EffectEvidenceSynthesis)(nil),
+	(*encounter_go_proto.Encounter)(nil),
+	(*endpoint_go_proto.Endpoint)(nil),
+	(*enrollment_request_go_proto.EnrollmentRequest)(nil),
+	(*enrollment_response_go_proto.EnrollmentResponse)(nil),
+	(*episode_of_care_go_proto.EpisodeOfCare)(nil),
+	(*event_definition_go_proto.EventDefinition)(nil),
+	(*evidence_go_proto.Evidence)(nil),
+	(*evidence_variable_go_proto.EvidenceVariable)(nil),
+	(*example_scenario_go_proto.ExampleScenario)(nil),
+	(*explanation_of_benefit_go_proto.ExplanationOfBenefit)(nil),
+	(*family_member_history_go_proto.FamilyMemberHistory)(nil),
+	(*flag_go_proto.Flag)(nil),
+	(*goal_go_proto.Goal)(nil),
+	(*graph_definition_go_proto.GraphDefinition)(nil),
+	(*group_go_proto.Group)(nil),
+	(*guidance_response_go_proto.GuidanceResponse)(nil),
+	(*healthcare_service_go_proto.HealthcareService)(nil),
+	(*imaging_study_go_proto.ImagingStudy)(nil),
+	(*immunization_go_proto.Immunization)(nil),
+	(*immunization_evaluation_go_proto.ImmunizationEvaluation)(nil),
+	(*immunization_recommendation_go_proto.ImmunizationRecommendation)(nil),
+	(*implementation_guide_go_proto.ImplementationGuide)(nil),
+	(*insurance_plan_go_proto.InsurancePlan)(nil),
+	(*invoice_go_proto.Invoice)(nil),
+	(*library_go_proto.Library)(nil),
+	(*linkage_go_proto.Linkage)(nil),
+	(*list_go_proto.List)(nil),
+	(*location_go_proto.Location)(nil),
+	(*measure_go_proto.Measure)(nil),
+	(*measure_report_go_proto.MeasureReport)(nil),
+	(*media_go_proto.Media)(nil),
+	(*medication_go_proto.Medication)(nil),
+	(*medication_administration_go_proto.MedicationAdministration)(nil),
+	(*medication_dispense_go_proto.MedicationDispense)(nil),
+	(*medication_knowledge_go_proto.MedicationKnowledge)(nil),
+	(*medication_request_go_proto.MedicationRequest)(nil),
+	(*medication_statement_go_proto.MedicationStatement)(nil),
+	(*medicinal_product_go_proto.MedicinalProduct)(nil),
+	(*medicinal_product_authorization_go_proto.MedicinalProductAuthorization)(nil),
+	(*medicinal_product_contraindication_go_proto.MedicinalProductContraindication)(nil),
+	(*medicinal_product_indication_go_proto.MedicinalProductIndication)(nil),
+	(*medicinal_product_ingredient_go_proto.MedicinalProductIngredient)(nil),
+	(*medicinal_product_interaction_go_proto.MedicinalProductInteraction)(nil),
+	(*medicinal_product_manufactured_go_proto.MedicinalProductManufactured)(nil),
+	(*medicinal_product_packaged_go_proto.MedicinalProductPackaged)(nil),
+	(*medicinal_product_pharmaceutical_go_proto.MedicinalProductPharmaceutical)(nil),
+	(*medicinal_product_undesirable_effect_go_proto.MedicinalProductUndesirableEffect)(nil),
+	(*message_definition_go_proto.MessageDefinition)(nil),
+	(*message_header_go_proto.MessageHeader)(nil),
+	(*molecular_sequence_go_proto.MolecularSequence)(nil),
+	(*naming_system_go_proto.NamingSystem)(nil),
+	(*nutrition_order_go_proto.NutritionOrder)(nil),
+	(*observation_go_proto.Observation)(nil),
+	(*observation_definition_go_proto.ObservationDefinition)(nil),
+	(*operation_definition_go_proto.OperationDefinition)(nil),
+	(*operation_outcome_go_proto.OperationOutcome)(nil),
+	(*organization_go_proto.Organization)(nil),
+	(*organization_affiliation_go_proto.OrganizationAffiliation)(nil),
+	(*patient_go_proto.Patient)(nil),
+	(*payment_notice_go_proto.PaymentNotice)(nil),
+	(*payment_reconciliation_go_proto.PaymentReconciliation)(nil),
+	(*person_go_proto.Person)(nil),
+	(*plan_definition_go_proto.PlanDefinition)(nil),
+	(*practitioner_go_proto.Practitioner)(nil),
+	(*practitioner_role_go_proto.PractitionerRole)(nil),
+	(*procedure_go_proto.Procedure)(nil),
+	(*provenance_go_proto.Provenance)(nil),
+	(*questionnaire_go_proto.Questionnaire)(nil),
+	(*questionnaire_response_go_proto.QuestionnaireResponse)(nil),
+	(*related_person_go_proto.RelatedPerson)(nil),
+	(*request_group_go_proto.RequestGroup)(nil),
+	(*research_definition_go_proto.ResearchDefinition)(nil),
+	(*research_element_definition_go_proto.ResearchElementDefinition)(nil),
+	(*research_study_go_proto.ResearchStudy)(nil),
+	(*research_subject_go_proto.ResearchSubject)(nil),
+	(*risk_assessment_go_proto.RiskAssessment)(nil),
+	(*risk_evidence_synthesis_go_proto.RiskEvidenceSynthesis)(nil),
+	(*schedule_go_proto.Schedule)(nil),
+	(*search_parameter_go_proto.SearchParameter)(nil),
+	(*service_request_go_proto.ServiceRequest)(nil),
+	(*slot_go_proto.Slot)(nil),
+	(*specimen_go_proto.Specimen)(nil),
+	(*specimen_definition_go_proto.SpecimenDefinition)(nil),
+	(*structure_definition_go_proto.StructureDefinition)(nil),
+	(*structure_map_go_proto.StructureMap)(nil),
+	(*subscription_go_proto.Subscription)(nil),
+	(*substance_go_proto.Substance)(nil),
+	(*substance_nucleic_acid_go_proto.SubstanceNucleicAcid)(nil),
+	(*substance_polymer_go_proto.SubstancePolymer)(nil),
+	(*substance_protein_go_proto.SubstanceProtein)(nil),
+	(*substance_reference_information_go_proto.SubstanceReferenceInformation)(nil),
+	(*substance_source_material_go_proto.SubstanceSourceMaterial)(nil),
+	(*substance_specification_go_proto.SubstanceSpecification)(nil),
+	(*supply_delivery_go_proto.SupplyDelivery)(nil),
+	(*supply_request_go_proto.SupplyRequest)(nil),
+	(*task_go_proto.Task)(nil),
+	(*terminology_capabilities_go_proto.TerminologyCapabilities)(nil),
+	(*test_report_go_proto.TestReport)(nil),
+	(*test_script_go_proto.TestScript)(nil),
+	(*value_set_go_proto.ValueSet)(nil),
+	(*verification_result_go_proto.VerificationResult)(nil),
+	(*vision_prescription_go_proto.VisionPrescription)(nil),
+	(*parameters_go_proto.Parameters)(nil),
+	(*binary_go_proto.Binary)(nil),
+	(*bundle_and_contained_resource_go_proto.Bundle)(nil),
+}
+
+// dummyElements is an array of nil pointers to all Element messages.
+var dummyElements = []proto.Message{
+	(*datatypes_go_proto.Address)(nil),
+	(*datatypes_go_proto.Age)(nil),
+	(*datatypes_go_proto.Annotation)(nil),
+	(*datatypes_go_proto.Attachment)(nil),
+	(*datatypes_go_proto.Base64Binary)(nil),
+	(*datatypes_go_proto.Boolean)(nil),
+	(*datatypes_go_proto.Canonical)(nil),
+	(*datatypes_go_proto.Code)(nil),
+	(*datatypes_go_proto.CodeableConcept)(nil),
+	(*datatypes_go_proto.Coding)(nil),
+	(*datatypes_go_proto.ContactDetail)(nil),
+	(*datatypes_go_proto.ContactPoint)(nil),
+	(*datatypes_go_proto.Contributor)(nil),
+	(*datatypes_go_proto.Count)(nil),
+	(*datatypes_go_proto.DataRequirement)(nil),
+	(*datatypes_go_proto.Date)(nil),
+	(*datatypes_go_proto.DateTime)(nil),
+	(*datatypes_go_proto.Decimal)(nil),
+	(*datatypes_go_proto.Distance)(nil),
+	(*datatypes_go_proto.Dosage)(nil),
+	(*datatypes_go_proto.Duration)(nil),
+	(*datatypes_go_proto.ElementDefinition)(nil),
+	(*datatypes_go_proto.Expression)(nil),
+	(*datatypes_go_proto.Extension)(nil),
+	(*datatypes_go_proto.HumanName)(nil),
+	(*datatypes_go_proto.Id)(nil),
+	(*datatypes_go_proto.Identifier)(nil),
+	(*datatypes_go_proto.Instant)(nil),
+	(*datatypes_go_proto.Integer)(nil),
+	(*datatypes_go_proto.Markdown)(nil),
+	(*datatypes_go_proto.MarketingStatus)(nil),
+	(*datatypes_go_proto.Meta)(nil),
+	(*datatypes_go_proto.Money)(nil),
+	(*datatypes_go_proto.MoneyQuantity)(nil),
+	(*datatypes_go_proto.Narrative)(nil),
+	(*datatypes_go_proto.Oid)(nil),
+	(*datatypes_go_proto.ParameterDefinition)(nil),
+	(*datatypes_go_proto.Period)(nil),
+	(*datatypes_go_proto.PositiveInt)(nil),
+	(*datatypes_go_proto.ProductShelfLife)(nil),
+	(*datatypes_go_proto.Quantity)(nil),
+	(*datatypes_go_proto.Range)(nil),
+	(*datatypes_go_proto.Ratio)(nil),
+	(*datatypes_go_proto.Reference)(nil),
+	(*datatypes_go_proto.RelatedArtifact)(nil),
+	(*datatypes_go_proto.SampledData)(nil),
+	(*datatypes_go_proto.Signature)(nil),
+	(*datatypes_go_proto.SimpleQuantity)(nil),
+	(*datatypes_go_proto.String)(nil),
+	(*datatypes_go_proto.Time)(nil),
+	(*datatypes_go_proto.Timing)(nil),
+	(*datatypes_go_proto.TriggerDefinition)(nil),
+	(*datatypes_go_proto.UnsignedInt)(nil),
+	(*datatypes_go_proto.Uri)(nil),
+	(*datatypes_go_proto.Url)(nil),
+	(*datatypes_go_proto.UsageContext)(nil),
+	(*datatypes_go_proto.Uuid)(nil),
+	(*datatypes_go_proto.Xhtml)(nil),
+}
diff --git a/internal/protofields/fields.go b/internal/protofields/fields.go
new file mode 100644
index 0000000..5d2a097
--- /dev/null
+++ b/internal/protofields/fields.go
@@ -0,0 +1,212 @@
+package protofields
+
+import (
+	"strings"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	bcrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	"github.com/iancoleman/strcase"
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+// FieldToValueFunc is a function converting a protoreflect.Message's field-descriptor
+// into a Value.
+//
+// This corresponds to either `protoreflect.Message.Mutable` or `protoreflect.Message.NewField`.
+//
+// This type is an implementation-detail shared through various APIs that support
+// a mix of appending or overwriting sequences.
+type FieldToValueFunc func(protoreflect.Message, protoreflect.FieldDescriptor) protoreflect.Value
+
+// ResourceFieldRefs is a container of back-references for a specified Resource.
+// This enables finding one-ofs that can work with the resource it represents,
+// for easier/faster lookup than waiting on protoreflect APIs.
+type ResourceFieldRefs struct {
+	// ContainedResource contains back-references for the ContainedResource to
+	// this type
+	ContainedResource struct {
+		// Resource is the ContainedResource.Resource field that corresponds to
+		// this resource.
+		//
+		// This field cannot be nil, as all R4 resources are valid contained-resource
+		// types.
+		Resource protoreflect.FieldDescriptor
+	}
+
+	// New is a function that will create a new instance of this FHIR Resource.
+	New func() proto.Message
+}
+
+// ElementFieldRefs is a container of back-references for a specified Element.
+// This enables finding one-ofs that can work with the resource it represents,
+// for easier/faster lookup than waiting on protoreflect APIs.
+type ElementFieldRefs struct {
+	// Extension contains back-references for the Extension to this type
+	Extension struct {
+		// ValueX is the Extension.ValueX field that corresponds to
+		// this resource.
+		//
+		// This field cannot be nil, as all R4 resources are valid contained-resource
+		// types.
+		ValueX protoreflect.FieldDescriptor
+	}
+
+	// New is a function that will create a new instance of this FHIR element.
+	New func() proto.Message
+}
+
+var (
+	// Resources is a map of all resource names to their corresponding field references.
+	Resources map[string]*ResourceFieldRefs
+
+	// Elements is a map of all element names to their corresponding field references.
+	Elements map[string]*ElementFieldRefs
+)
+
+// IsValidResourceType checks that the given name is a valid resource name
+func IsValidResourceType(name string) bool {
+	_, ok := Resources[name]
+	return ok
+}
+
+// IsValidElementType checks that the given name is a valid element name
+func IsValidElementType(name string) bool {
+	_, ok := Elements[name]
+	return ok
+}
+
+// TypeToContainedResourceOneOfFieldName converts a resource type name into their
+// respective ContainedResource "OneOf" field.
+func TypeToContainedResourceOneOfFieldName(resource string) protoreflect.Name {
+	return protoreflect.Name(toSnakeCase(resource))
+}
+
+// UnwrapOneofField obtains the underlying Message for "Oneof" elements
+// contained in fields with the given fieldName. Returns nil if the input message
+// doesn't have the given field, or if the Oneof descriptor is unpopulated.
+func UnwrapOneofField(element proto.Message, fieldName string) proto.Message {
+	message := element.ProtoReflect()
+	oneOfDescriptor := message.Descriptor().Oneofs().ByName(protoreflect.Name(fieldName))
+	if oneOfDescriptor == nil {
+		return nil
+	}
+	fd := message.WhichOneof(oneOfDescriptor)
+	if fd == nil {
+		return nil
+	}
+	return message.Get(fd).Message().Interface()
+}
+
+// IsCodeField returns true if the message represents a FHIR code type.
+// Codes with enum values and string values are both considered valid.
+func IsCodeField(message proto.Message) bool {
+	reflect := message.ProtoReflect()
+	name := string(reflect.Descriptor().Name())
+	field := reflect.Descriptor().Fields().ByName(protoreflect.Name("value"))
+	if field != nil {
+		allowedKinds := []protoreflect.Kind{protoreflect.EnumKind, protoreflect.StringKind}
+		isValidFieldType := slices.Includes(allowedKinds, field.Kind())
+		return strings.HasSuffix(name, "Code") && isValidFieldType
+	}
+	return false
+}
+
+// StringValueFromCodeField gets the Field Descriptor of a message that
+// represents a FHIR Code type. Returns the string value of the enum or
+// string value of the Code, along with a boolean flag representing
+// whether or not the input is a code type.
+func StringValueFromCodeField(message proto.Message) (string, bool) {
+	if IsCodeField(message) {
+		reflect := message.ProtoReflect()
+		field := reflect.Descriptor().Fields().ByName(protoreflect.Name("value"))
+		if field.Kind() == protoreflect.EnumKind {
+			enum := reflect.Get(field).Enum()
+			code := string(field.Enum().Values().ByNumber(enum).Name())
+			return strcase.ToKebab(code), true
+		}
+		if field.Kind() == protoreflect.StringKind {
+			return reflect.Get(field).String(), true
+		}
+	}
+	return "", false
+}
+
+// Field is a struct containing both the Value and FieldDescriptor for a proto field.
+type Field struct {
+	Value      protoreflect.Value
+	Descriptor protoreflect.FieldDescriptor
+}
+
+// GetField retrieves the proto field of the specified name from the message.
+func GetField(message proto.Message, name string) (*Field, bool) {
+	fieldName := strcase.ToSnake(name)
+	msg := message.ProtoReflect()
+	descriptor := msg.Descriptor()
+	field := descriptor.Fields().ByName(protoreflect.Name(fieldName))
+	if field == nil {
+		return nil, false
+	}
+
+	value := msg.Get(field)
+	return &Field{
+		Value:      value,
+		Descriptor: field,
+	}, true
+}
+
+func getContainedResourceOneOf(message proto.Message) protoreflect.FieldDescriptor {
+	cr := (*bcrpb.ContainedResource)(nil)
+
+	name := DescriptorName(message)
+	fieldName := TypeToContainedResourceOneOfFieldName(name)
+	return cr.ProtoReflect().Descriptor().Fields().ByName(fieldName)
+}
+
+// typeToExtensionFieldName converts a data-type name into their expected
+// Extension ValueX field name.
+func typeToExtensionFieldName(name string) protoreflect.Name {
+	fieldName := toSnakeCase(name)
+	if fieldName == "string" {
+		// The protobufs use "string_value" rather than "string" because "string" is
+		// a keyword.
+		fieldName = "string_value"
+	}
+	return protoreflect.Name(fieldName)
+}
+
+func getExtensionValueX(message proto.Message) protoreflect.FieldDescriptor {
+	valueX := (*dtpb.Extension_ValueX)(nil)
+
+	reflect := valueX.ProtoReflect()
+	name := DescriptorName(message)
+	fieldName := typeToExtensionFieldName(name)
+	return reflect.Descriptor().Fields().ByName(fieldName)
+}
+
+func newProto(msg protoreflect.ProtoMessage) func() proto.Message {
+	return func() proto.Message {
+		return msg.ProtoReflect().New().Interface()
+	}
+}
+
+func init() {
+	Resources = make(map[string]*ResourceFieldRefs)
+	Elements = make(map[string]*ElementFieldRefs)
+
+	for _, msg := range dummyResources {
+		name := DescriptorName(msg)
+		fields := &ResourceFieldRefs{}
+		fields.ContainedResource.Resource = getContainedResourceOneOf(msg)
+		fields.New = newProto(msg)
+		Resources[name] = fields
+	}
+	for _, msg := range dummyElements {
+		name := DescriptorName(msg)
+		fields := &ElementFieldRefs{}
+		fields.New = newProto(msg)
+		fields.Extension.ValueX = getExtensionValueX(msg)
+		Elements[name] = fields
+	}
+}
diff --git a/internal/protofields/fields_test.go b/internal/protofields/fields_test.go
new file mode 100644
index 0000000..eb24aa4
--- /dev/null
+++ b/internal/protofields/fields_test.go
@@ -0,0 +1,61 @@
+package protofields_test
+
+import (
+	"testing"
+
+	opb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/observation_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/protofields"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func TestUnwrapChoiceField_GetsUnderlyingMessage(t *testing.T) {
+	dateTime := fhir.DateTimeNow()
+
+	testCases := []struct {
+		name  string
+		input proto.Message
+		want  proto.Message
+	}{
+		{
+			name: "gets boolean of Patient deceased field",
+			input: &ppb.Patient_DeceasedX{
+				Choice: &ppb.Patient_DeceasedX_Boolean{
+					Boolean: fhir.Boolean(true),
+				},
+			},
+			want: fhir.Boolean(true),
+		},
+		{
+			name: "gets date of Patient deceased field",
+			input: &ppb.Patient_DeceasedX{
+				Choice: &ppb.Patient_DeceasedX_DateTime{
+					DateTime: dateTime,
+				},
+			},
+			want: dateTime,
+		},
+		{
+			name: "",
+			input: &opb.Observation_Component_ValueX{
+				Choice: &opb.Observation_Component_ValueX_StringValue{
+					StringValue: fhir.String("some string"),
+				},
+			},
+			want: fhir.String("some string"),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := protofields.UnwrapOneofField(tc.input, "choice")
+
+			if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("UnwrapChoiceField returned unexpected diff (-want, +got)\n%s", diff)
+			}
+		})
+	}
+}
diff --git a/internal/protofields/strcase.go b/internal/protofields/strcase.go
new file mode 100644
index 0000000..0c145ad
--- /dev/null
+++ b/internal/protofields/strcase.go
@@ -0,0 +1,22 @@
+package protofields
+
+import (
+	"regexp"
+	"strings"
+)
+
+var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
+var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
+
+// toSnakeCase is a helper function to convert CamelCase names to snake_case.
+// This is needed for finding fields in the Proto descriptors, which are snake_case,
+// from resource-names that are CamelCase.
+//
+// Note: strcase.ToSnake does not work for converting Base64Binary to
+// base64_binary, so this function exists to do it for us with the semantics we
+// want.
+func toSnakeCase(str string) string {
+	snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}")
+	snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}")
+	return strings.ToLower(snake)
+}
diff --git a/internal/protofields/update.go b/internal/protofields/update.go
new file mode 100644
index 0000000..5288abf
--- /dev/null
+++ b/internal/protofields/update.go
@@ -0,0 +1,61 @@
+package protofields
+
+import (
+	"fmt"
+
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+// Overwrite overwrites a field of the given name for the specified message.
+// If `values` is empty, the field is cleared.
+// If `values` contains more than one entry for a non-repeated field, this panics.
+func Overwrite(in proto.Message, fieldName string, values ...proto.Message) {
+	msg := in.ProtoReflect()
+	descriptor := msg.Descriptor()
+	field := descriptor.Fields().ByName(protoreflect.Name(fieldName))
+
+	// No values -- remove the field entirely
+	if len(values) == 0 {
+		msg.Clear(field)
+		return
+	}
+
+	// For lists, append each one after clearing the previously stored value
+	if field.IsList() {
+		msg.Clear(field)
+		list := msg.Mutable(field).List()
+		for _, v := range values {
+			list.Append(protoreflect.ValueOfMessage(v.ProtoReflect()))
+		}
+		return
+	}
+
+	// For single values on non-repeated fields, just set it.
+	if len(values) == 1 {
+		msg.Set(field, protoreflect.ValueOfMessage(values[0].ProtoReflect()))
+		return
+	}
+
+	panic(
+		fmt.Sprintf(
+			"invalid use of Overwrite; non-repeated field '%v' used with '%v' values",
+			fieldName,
+			len(values),
+		),
+	)
+}
+
+// AppendList updates a field of the given name in-place for the specified message.
+//
+// This function will panic if the field is not a repeated-field.
+func AppendList(in proto.Message, fieldName string, values ...proto.Message) {
+	msg := in.ProtoReflect()
+	descriptor := msg.Descriptor()
+	field := descriptor.Fields().ByName(protoreflect.Name(fieldName))
+
+	list := msg.Mutable(field).List()
+	for _, v := range values {
+		list.Append(protoreflect.ValueOfMessage(v.ProtoReflect()))
+	}
+}
diff --git a/internal/protofields/update_test.go b/internal/protofields/update_test.go
new file mode 100644
index 0000000..3e96fcb
--- /dev/null
+++ b/internal/protofields/update_test.go
@@ -0,0 +1,138 @@
+package protofields_test
+
+import (
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/protofields"
+	"google.golang.org/protobuf/proto"
+)
+
+func TestOverwrite(t *testing.T) {
+	value := &dtpb.String{
+		Value: "hello world",
+	}
+	testCases := []struct {
+		name   string
+		field  string
+		values []proto.Message
+		input  proto.Message
+		want   proto.Message
+	}{
+		{
+			name:   "Solo field",
+			field:  "text",
+			values: []proto.Message{value},
+			input:  &dtpb.HumanName{},
+			want: &dtpb.HumanName{
+				Text: value,
+			},
+		}, {
+			name:   "Solo field no input",
+			field:  "text",
+			values: []proto.Message{},
+			input: &dtpb.HumanName{
+				Text: value,
+			},
+			want: &dtpb.HumanName{},
+		}, {
+			name:   "Repeated field with single input",
+			field:  "prefix",
+			values: []proto.Message{value},
+			input:  &dtpb.HumanName{},
+			want: &dtpb.HumanName{
+				Prefix: []*dtpb.String{value},
+			},
+		}, {
+			name:   "Repeated field with multiple inputs",
+			field:  "prefix",
+			values: []proto.Message{value, value},
+			input:  &dtpb.HumanName{},
+			want: &dtpb.HumanName{
+				Prefix: []*dtpb.String{value, value},
+			},
+		}, {
+			name:   "Repeated field no input",
+			field:  "prefix",
+			values: []proto.Message{},
+			input: &dtpb.HumanName{
+				Prefix: []*dtpb.String{value, value},
+			},
+			want: &dtpb.HumanName{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			protofields.Overwrite(tc.input, tc.field, tc.values...)
+
+			if got, want := tc.input, tc.want; !proto.Equal(got, want) {
+				t.Errorf("Overwrite(%v): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestOverwrite_WrongCardinality_Panics(t *testing.T) {
+	defer func() { _ = recover() }()
+	value := &dtpb.String{
+		Value: "hello world",
+	}
+	name := &dtpb.HumanName{}
+
+	protofields.Overwrite(name, "text", value, value)
+
+	t.Errorf("Overwrite: expected panic")
+}
+
+func TestAppendList(t *testing.T) {
+	toAppend := &dtpb.String{
+		Value: "hello world",
+	}
+	value := &dtpb.String{
+		Value: "another string",
+	}
+	testCases := []struct {
+		name  string
+		field string
+		input proto.Message
+		want  proto.Message
+	}{
+		{
+			name:  "Repeated field with no inputs",
+			field: "prefix",
+			input: &dtpb.HumanName{},
+			want: &dtpb.HumanName{
+				Prefix: []*dtpb.String{toAppend},
+			},
+		}, {
+			name:  "Repeated field with 1 input",
+			field: "prefix",
+			input: &dtpb.HumanName{
+				Prefix: []*dtpb.String{value},
+			},
+			want: &dtpb.HumanName{
+				Prefix: []*dtpb.String{value, toAppend},
+			},
+		}, {
+			name:  "Repeated field with multiple inputs",
+			field: "prefix",
+			input: &dtpb.HumanName{
+				Prefix: []*dtpb.String{value, value},
+			},
+			want: &dtpb.HumanName{
+				Prefix: []*dtpb.String{value, value, toAppend},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			protofields.AppendList(tc.input, tc.field, toAppend)
+
+			if got, want := tc.input, tc.want; !proto.Equal(got, want) {
+				t.Errorf("AppendList(%v): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
diff --git a/internal/resource/canonical_identity.go b/internal/resource/canonical_identity.go
new file mode 100644
index 0000000..0f6a039
--- /dev/null
+++ b/internal/resource/canonical_identity.go
@@ -0,0 +1,58 @@
+package resource
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+)
+
+var (
+	// ErrMissingCanonicalURL is thrown when creating a canonical identity without having a URL.
+	ErrMissingCanonicalURL = errors.New("missing canonical url")
+
+	delimiter = "/"
+)
+
+// CanonicalIdentity is a canonical representation of a FHIR Resource.
+//
+// This object stores the individual pieces of id used in creating a canonical reference.
+type CanonicalIdentity struct {
+	Version  string
+	Url      string
+	Fragment string // only used if a fragment of resource is targetted
+}
+
+// Type attempts to identify the resource type associated with the identity.
+func (c *CanonicalIdentity) Type() (Type, bool) {
+	for _, r := range strings.Split(c.Url, delimiter) {
+		if IsType(r) {
+			return Type(r), true
+		}
+	}
+	return Type(""), false
+}
+
+// String returns a string representation of this CanonicalIdentity.
+func (c *CanonicalIdentity) String() string {
+	res := c.Url
+	if c.Version != "" {
+		res = fmt.Sprintf("%s|%s", res, c.Version)
+	}
+	if c.Fragment != "" {
+		res = fmt.Sprintf("%s#%s", res, c.Fragment)
+	}
+	return res
+}
+
+// NewCanonicalIdentity creates a canonicalIdentity based on the given url, version and fragment
+func NewCanonicalIdentity(url, version, fragment string) (*CanonicalIdentity, error) {
+	if url == "" {
+		return nil, ErrMissingCanonicalURL
+	}
+
+	return &CanonicalIdentity{
+		Url:      url,
+		Version:  version,
+		Fragment: fragment,
+	}, nil
+}
diff --git a/internal/resource/canonical_identity_test.go b/internal/resource/canonical_identity_test.go
new file mode 100644
index 0000000..57aaf3d
--- /dev/null
+++ b/internal/resource/canonical_identity_test.go
@@ -0,0 +1,88 @@
+package resource_test
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func TestCanonicalIdentity_EmptyURL_ReturnsError(t *testing.T) {
+	_, got := resource.NewCanonicalIdentity("", "v1", "")
+
+	if got != resource.ErrMissingCanonicalURL {
+		t.Errorf("NewCanonicalIdentity: got %v, want %v", got, resource.ErrMissingCanonicalURL)
+	}
+}
+
+func TestCanonicalIdentity(t *testing.T) {
+	testCases := []struct {
+		name, url, version, fragment string
+		want                         *resource.CanonicalIdentity
+		wantString                   string
+		wantType                     resource.Type
+		hasType                      bool
+	}{
+		{
+			name:       "basic",
+			url:        "http://someurl/test-value",
+			wantString: "http://someurl/test-value",
+		},
+		{
+			name:       "long url",
+			url:        "https://fhir.acme.com/Questionnaire/example",
+			wantString: "https://fhir.acme.com/Questionnaire/example",
+			hasType:    true,
+			wantType:   resource.Questionnaire,
+		},
+		{
+			name:       "with version",
+			url:        "https://fhir.acme.com/PlanDefinition/example",
+			version:    "1.0.0",
+			wantString: "https://fhir.acme.com/PlanDefinition/example|1.0.0",
+			hasType:    true,
+			wantType:   resource.PlanDefinition,
+		},
+		{
+			name:       "with fragment",
+			url:        "http://hl7.org/fhir/ValueSet/my-valueset",
+			fragment:   "vs1",
+			wantString: "http://hl7.org/fhir/ValueSet/my-valueset#vs1",
+		},
+		{
+			name:       "with version and fragment",
+			url:        "http://fhir.acme.com/ActivityDefinition/example",
+			version:    "1.0",
+			fragment:   "vs1",
+			wantString: "http://fhir.acme.com/ActivityDefinition/example|1.0#vs1",
+			hasType:    true,
+			wantType:   resource.ActivityDefinition,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, _ := resource.NewCanonicalIdentity(tc.url, tc.version, tc.fragment)
+			want := &resource.CanonicalIdentity{
+				Url:      tc.url,
+				Version:  tc.version,
+				Fragment: tc.fragment,
+			}
+
+			if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+				t.Errorf("CanonicalIdentity(%s): %v", tc.name, diff)
+			}
+
+			if s := got.String(); tc.wantString != s {
+				t.Errorf("CanonicalIdentity(%s).String: want: %s, got: %s", tc.name, tc.wantString, s)
+			}
+			if tc.hasType {
+				gt, ok := got.Type()
+				if !ok || gt != tc.wantType {
+					t.Errorf("CanonicalIdentity(%s).Type: want: %s, got: %s", tc.name, tc.wantType, gt)
+				}
+			}
+		})
+	}
+}
diff --git a/internal/resource/consts.go b/internal/resource/consts.go
new file mode 100644
index 0000000..7e4ee9c
--- /dev/null
+++ b/internal/resource/consts.go
@@ -0,0 +1,441 @@
+package resource
+
+const (
+	// Account is the TypeSpecifier constant for the "Account" resource.
+	Account Type = "Account"
+
+	// ActivityDefinition is the TypeSpecifier constant for the "ActivityDefinition" resource.
+	ActivityDefinition Type = "ActivityDefinition"
+
+	// AdverseEvent is the TypeSpecifier constant for the "AdverseEvent" resource.
+	AdverseEvent Type = "AdverseEvent"
+
+	// AllergyIntolerance is the TypeSpecifier constant for the "AllergyIntolerance" resource.
+	AllergyIntolerance Type = "AllergyIntolerance"
+
+	// Appointment is the TypeSpecifier constant for the "Appointment" resource.
+	Appointment Type = "Appointment"
+
+	// AppointmentResponse is the TypeSpecifier constant for the "AppointmentResponse" resource.
+	AppointmentResponse Type = "AppointmentResponse"
+
+	// AuditEvent is the TypeSpecifier constant for the "AuditEvent" resource.
+	AuditEvent Type = "AuditEvent"
+
+	// Basic is the TypeSpecifier constant for the "Basic" resource.
+	Basic Type = "Basic"
+
+	// Binary is the TypeSpecifier constant for the "Binary" resource.
+	Binary Type = "Binary"
+
+	// BiologicallyDerivedProduct is the TypeSpecifier constant for the "BiologicallyDerivedProduct" resource.
+	BiologicallyDerivedProduct Type = "BiologicallyDerivedProduct"
+
+	// BodyStructure is the TypeSpecifier constant for the "BodyStructure" resource.
+	BodyStructure Type = "BodyStructure"
+
+	// Bundle is the TypeSpecifier constant for the "Bundle" resource.
+	Bundle Type = "Bundle"
+
+	// CapabilityStatement is the TypeSpecifier constant for the "CapabilityStatement" resource.
+	CapabilityStatement Type = "CapabilityStatement"
+
+	// CarePlan is the TypeSpecifier constant for the "CarePlan" resource.
+	CarePlan Type = "CarePlan"
+
+	// CareTeam is the TypeSpecifier constant for the "CareTeam" resource.
+	CareTeam Type = "CareTeam"
+
+	// CatalogEntry is the TypeSpecifier constant for the "CatalogEntry" resource.
+	CatalogEntry Type = "CatalogEntry"
+
+	// ChargeItem is the TypeSpecifier constant for the "ChargeItem" resource.
+	ChargeItem Type = "ChargeItem"
+
+	// ChargeItemDefinition is the TypeSpecifier constant for the "ChargeItemDefinition" resource.
+	ChargeItemDefinition Type = "ChargeItemDefinition"
+
+	// Claim is the TypeSpecifier constant for the "Claim" resource.
+	Claim Type = "Claim"
+
+	// ClaimResponse is the TypeSpecifier constant for the "ClaimResponse" resource.
+	ClaimResponse Type = "ClaimResponse"
+
+	// ClinicalImpression is the TypeSpecifier constant for the "ClinicalImpression" resource.
+	ClinicalImpression Type = "ClinicalImpression"
+
+	// CodeSystem is the TypeSpecifier constant for the "CodeSystem" resource.
+	CodeSystem Type = "CodeSystem"
+
+	// Communication is the TypeSpecifier constant for the "Communication" resource.
+	Communication Type = "Communication"
+
+	// CommunicationRequest is the TypeSpecifier constant for the "CommunicationRequest" resource.
+	CommunicationRequest Type = "CommunicationRequest"
+
+	// CompartmentDefinition is the TypeSpecifier constant for the "CompartmentDefinition" resource.
+	CompartmentDefinition Type = "CompartmentDefinition"
+
+	// Composition is the TypeSpecifier constant for the "Composition" resource.
+	Composition Type = "Composition"
+
+	// ConceptMap is the TypeSpecifier constant for the "ConceptMap" resource.
+	ConceptMap Type = "ConceptMap"
+
+	// Condition is the TypeSpecifier constant for the "Condition" resource.
+	Condition Type = "Condition"
+
+	// Consent is the TypeSpecifier constant for the "Consent" resource.
+	Consent Type = "Consent"
+
+	// Contract is the TypeSpecifier constant for the "Contract" resource.
+	Contract Type = "Contract"
+
+	// Coverage is the TypeSpecifier constant for the "Coverage" resource.
+	Coverage Type = "Coverage"
+
+	// CoverageEligibilityRequest is the TypeSpecifier constant for the "CoverageEligibilityRequest" resource.
+	CoverageEligibilityRequest Type = "CoverageEligibilityRequest"
+
+	// CoverageEligibilityResponse is the TypeSpecifier constant for the "CoverageEligibilityResponse" resource.
+	CoverageEligibilityResponse Type = "CoverageEligibilityResponse"
+
+	// DetectedIssue is the TypeSpecifier constant for the "DetectedIssue" resource.
+	DetectedIssue Type = "DetectedIssue"
+
+	// Device is the TypeSpecifier constant for the "Device" resource.
+	Device Type = "Device"
+
+	// DeviceDefinition is the TypeSpecifier constant for the "DeviceDefinition" resource.
+	DeviceDefinition Type = "DeviceDefinition"
+
+	// DeviceMetric is the TypeSpecifier constant for the "DeviceMetric" resource.
+	DeviceMetric Type = "DeviceMetric"
+
+	// DeviceRequest is the TypeSpecifier constant for the "DeviceRequest" resource.
+	DeviceRequest Type = "DeviceRequest"
+
+	// DeviceUseStatement is the TypeSpecifier constant for the "DeviceUseStatement" resource.
+	DeviceUseStatement Type = "DeviceUseStatement"
+
+	// DiagnosticReport is the TypeSpecifier constant for the "DiagnosticReport" resource.
+	DiagnosticReport Type = "DiagnosticReport"
+
+	// DocumentManifest is the TypeSpecifier constant for the "DocumentManifest" resource.
+	DocumentManifest Type = "DocumentManifest"
+
+	// DocumentReference is the TypeSpecifier constant for the "DocumentReference" resource.
+	DocumentReference Type = "DocumentReference"
+
+	// EffectEvidenceSynthesis is the TypeSpecifier constant for the "EffectEvidenceSynthesis" resource.
+	EffectEvidenceSynthesis Type = "EffectEvidenceSynthesis"
+
+	// Encounter is the TypeSpecifier constant for the "Encounter" resource.
+	Encounter Type = "Encounter"
+
+	// Endpoint is the TypeSpecifier constant for the "Endpoint" resource.
+	Endpoint Type = "Endpoint"
+
+	// EnrollmentRequest is the TypeSpecifier constant for the "EnrollmentRequest" resource.
+	EnrollmentRequest Type = "EnrollmentRequest"
+
+	// EnrollmentResponse is the TypeSpecifier constant for the "EnrollmentResponse" resource.
+	EnrollmentResponse Type = "EnrollmentResponse"
+
+	// EpisodeOfCare is the TypeSpecifier constant for the "EpisodeOfCare" resource.
+	EpisodeOfCare Type = "EpisodeOfCare"
+
+	// EventDefinition is the TypeSpecifier constant for the "EventDefinition" resource.
+	EventDefinition Type = "EventDefinition"
+
+	// Evidence is the TypeSpecifier constant for the "Evidence" resource.
+	Evidence Type = "Evidence"
+
+	// EvidenceVariable is the TypeSpecifier constant for the "EvidenceVariable" resource.
+	EvidenceVariable Type = "EvidenceVariable"
+
+	// ExampleScenario is the TypeSpecifier constant for the "ExampleScenario" resource.
+	ExampleScenario Type = "ExampleScenario"
+
+	// ExplanationOfBenefit is the TypeSpecifier constant for the "ExplanationOfBenefit" resource.
+	ExplanationOfBenefit Type = "ExplanationOfBenefit"
+
+	// FamilyMemberHistory is the TypeSpecifier constant for the "FamilyMemberHistory" resource.
+	FamilyMemberHistory Type = "FamilyMemberHistory"
+
+	// Flag is the TypeSpecifier constant for the "Flag" resource.
+	Flag Type = "Flag"
+
+	// Goal is the TypeSpecifier constant for the "Goal" resource.
+	Goal Type = "Goal"
+
+	// GraphDefinition is the TypeSpecifier constant for the "GraphDefinition" resource.
+	GraphDefinition Type = "GraphDefinition"
+
+	// Group is the TypeSpecifier constant for the "Group" resource.
+	Group Type = "Group"
+
+	// GuidanceResponse is the TypeSpecifier constant for the "GuidanceResponse" resource.
+	GuidanceResponse Type = "GuidanceResponse"
+
+	// HealthcareService is the TypeSpecifier constant for the "HealthcareService" resource.
+	HealthcareService Type = "HealthcareService"
+
+	// ImagingStudy is the TypeSpecifier constant for the "ImagingStudy" resource.
+	ImagingStudy Type = "ImagingStudy"
+
+	// Immunization is the TypeSpecifier constant for the "Immunization" resource.
+	Immunization Type = "Immunization"
+
+	// ImmunizationEvaluation is the TypeSpecifier constant for the "ImmunizationEvaluation" resource.
+	ImmunizationEvaluation Type = "ImmunizationEvaluation"
+
+	// ImmunizationRecommendation is the TypeSpecifier constant for the "ImmunizationRecommendation" resource.
+	ImmunizationRecommendation Type = "ImmunizationRecommendation"
+
+	// ImplementationGuide is the TypeSpecifier constant for the "ImplementationGuide" resource.
+	ImplementationGuide Type = "ImplementationGuide"
+
+	// InsurancePlan is the TypeSpecifier constant for the "InsurancePlan" resource.
+	InsurancePlan Type = "InsurancePlan"
+
+	// Invoice is the TypeSpecifier constant for the "Invoice" resource.
+	Invoice Type = "Invoice"
+
+	// Library is the TypeSpecifier constant for the "Library" resource.
+	Library Type = "Library"
+
+	// Linkage is the TypeSpecifier constant for the "Linkage" resource.
+	Linkage Type = "Linkage"
+
+	// List is the TypeSpecifier constant for the "List" resource.
+	List Type = "List"
+
+	// Location is the TypeSpecifier constant for the "Location" resource.
+	Location Type = "Location"
+
+	// Measure is the TypeSpecifier constant for the "Measure" resource.
+	Measure Type = "Measure"
+
+	// MeasureReport is the TypeSpecifier constant for the "MeasureReport" resource.
+	MeasureReport Type = "MeasureReport"
+
+	// Media is the TypeSpecifier constant for the "Media" resource.
+	Media Type = "Media"
+
+	// Medication is the TypeSpecifier constant for the "Medication" resource.
+	Medication Type = "Medication"
+
+	// MedicationAdministration is the TypeSpecifier constant for the "MedicationAdministration" resource.
+	MedicationAdministration Type = "MedicationAdministration"
+
+	// MedicationDispense is the TypeSpecifier constant for the "MedicationDispense" resource.
+	MedicationDispense Type = "MedicationDispense"
+
+	// MedicationKnowledge is the TypeSpecifier constant for the "MedicationKnowledge" resource.
+	MedicationKnowledge Type = "MedicationKnowledge"
+
+	// MedicationRequest is the TypeSpecifier constant for the "MedicationRequest" resource.
+	MedicationRequest Type = "MedicationRequest"
+
+	// MedicationStatement is the TypeSpecifier constant for the "MedicationStatement" resource.
+	MedicationStatement Type = "MedicationStatement"
+
+	// MedicinalProduct is the TypeSpecifier constant for the "MedicinalProduct" resource.
+	MedicinalProduct Type = "MedicinalProduct"
+
+	// MedicinalProductAuthorization is the TypeSpecifier constant for the "MedicinalProductAuthorization" resource.
+	MedicinalProductAuthorization Type = "MedicinalProductAuthorization"
+
+	// MedicinalProductContraindication is the TypeSpecifier constant for the "MedicinalProductContraindication" resource.
+	MedicinalProductContraindication Type = "MedicinalProductContraindication"
+
+	// MedicinalProductIndication is the TypeSpecifier constant for the "MedicinalProductIndication" resource.
+	MedicinalProductIndication Type = "MedicinalProductIndication"
+
+	// MedicinalProductIngredient is the TypeSpecifier constant for the "MedicinalProductIngredient" resource.
+	MedicinalProductIngredient Type = "MedicinalProductIngredient"
+
+	// MedicinalProductInteraction is the TypeSpecifier constant for the "MedicinalProductInteraction" resource.
+	MedicinalProductInteraction Type = "MedicinalProductInteraction"
+
+	// MedicinalProductManufactured is the TypeSpecifier constant for the "MedicinalProductManufactured" resource.
+	MedicinalProductManufactured Type = "MedicinalProductManufactured"
+
+	// MedicinalProductPackaged is the TypeSpecifier constant for the "MedicinalProductPackaged" resource.
+	MedicinalProductPackaged Type = "MedicinalProductPackaged"
+
+	// MedicinalProductPharmaceutical is the TypeSpecifier constant for the "MedicinalProductPharmaceutical" resource.
+	MedicinalProductPharmaceutical Type = "MedicinalProductPharmaceutical"
+
+	// MedicinalProductUndesirableEffect is the TypeSpecifier constant for the "MedicinalProductUndesirableEffect" resource.
+	MedicinalProductUndesirableEffect Type = "MedicinalProductUndesirableEffect"
+
+	// MessageDefinition is the TypeSpecifier constant for the "MessageDefinition" resource.
+	MessageDefinition Type = "MessageDefinition"
+
+	// MessageHeader is the TypeSpecifier constant for the "MessageHeader" resource.
+	MessageHeader Type = "MessageHeader"
+
+	// MolecularSequence is the TypeSpecifier constant for the "MolecularSequence" resource.
+	MolecularSequence Type = "MolecularSequence"
+
+	// NamingSystem is the TypeSpecifier constant for the "NamingSystem" resource.
+	NamingSystem Type = "NamingSystem"
+
+	// NutritionOrder is the TypeSpecifier constant for the "NutritionOrder" resource.
+	NutritionOrder Type = "NutritionOrder"
+
+	// Observation is the TypeSpecifier constant for the "Observation" resource.
+	Observation Type = "Observation"
+
+	// ObservationDefinition is the TypeSpecifier constant for the "ObservationDefinition" resource.
+	ObservationDefinition Type = "ObservationDefinition"
+
+	// OperationDefinition is the TypeSpecifier constant for the "OperationDefinition" resource.
+	OperationDefinition Type = "OperationDefinition"
+
+	// OperationOutcome is the TypeSpecifier constant for the "OperationOutcome" resource.
+	OperationOutcome Type = "OperationOutcome"
+
+	// Organization is the TypeSpecifier constant for the "Organization" resource.
+	Organization Type = "Organization"
+
+	// OrganizationAffiliation is the TypeSpecifier constant for the "OrganizationAffiliation" resource.
+	OrganizationAffiliation Type = "OrganizationAffiliation"
+
+	// Parameters is the TypeSpecifier constant for the "Parameters" resource.
+	Parameters Type = "Parameters"
+
+	// Patient is the TypeSpecifier constant for the "Patient" resource.
+	Patient Type = "Patient"
+
+	// PaymentNotice is the TypeSpecifier constant for the "PaymentNotice" resource.
+	PaymentNotice Type = "PaymentNotice"
+
+	// PaymentReconciliation is the TypeSpecifier constant for the "PaymentReconciliation" resource.
+	PaymentReconciliation Type = "PaymentReconciliation"
+
+	// Person is the TypeSpecifier constant for the "Person" resource.
+	Person Type = "Person"
+
+	// PlanDefinition is the TypeSpecifier constant for the "PlanDefinition" resource.
+	PlanDefinition Type = "PlanDefinition"
+
+	// Practitioner is the TypeSpecifier constant for the "Practitioner" resource.
+	Practitioner Type = "Practitioner"
+
+	// PractitionerRole is the TypeSpecifier constant for the "PractitionerRole" resource.
+	PractitionerRole Type = "PractitionerRole"
+
+	// Procedure is the TypeSpecifier constant for the "Procedure" resource.
+	Procedure Type = "Procedure"
+
+	// Provenance is the TypeSpecifier constant for the "Provenance" resource.
+	Provenance Type = "Provenance"
+
+	// Questionnaire is the TypeSpecifier constant for the "Questionnaire" resource.
+	Questionnaire Type = "Questionnaire"
+
+	// QuestionnaireResponse is the TypeSpecifier constant for the "QuestionnaireResponse" resource.
+	QuestionnaireResponse Type = "QuestionnaireResponse"
+
+	// RelatedPerson is the TypeSpecifier constant for the "RelatedPerson" resource.
+	RelatedPerson Type = "RelatedPerson"
+
+	// RequestGroup is the TypeSpecifier constant for the "RequestGroup" resource.
+	RequestGroup Type = "RequestGroup"
+
+	// ResearchDefinition is the TypeSpecifier constant for the "ResearchDefinition" resource.
+	ResearchDefinition Type = "ResearchDefinition"
+
+	// ResearchElementDefinition is the TypeSpecifier constant for the "ResearchElementDefinition" resource.
+	ResearchElementDefinition Type = "ResearchElementDefinition"
+
+	// ResearchStudy is the TypeSpecifier constant for the "ResearchStudy" resource.
+	ResearchStudy Type = "ResearchStudy"
+
+	// ResearchSubject is the TypeSpecifier constant for the "ResearchSubject" resource.
+	ResearchSubject Type = "ResearchSubject"
+
+	// RiskAssessment is the TypeSpecifier constant for the "RiskAssessment" resource.
+	RiskAssessment Type = "RiskAssessment"
+
+	// RiskEvidenceSynthesis is the TypeSpecifier constant for the "RiskEvidenceSynthesis" resource.
+	RiskEvidenceSynthesis Type = "RiskEvidenceSynthesis"
+
+	// Schedule is the TypeSpecifier constant for the "Schedule" resource.
+	Schedule Type = "Schedule"
+
+	// SearchParameter is the TypeSpecifier constant for the "SearchParameter" resource.
+	SearchParameter Type = "SearchParameter"
+
+	// ServiceRequest is the TypeSpecifier constant for the "ServiceRequest" resource.
+	ServiceRequest Type = "ServiceRequest"
+
+	// Slot is the TypeSpecifier constant for the "Slot" resource.
+	Slot Type = "Slot"
+
+	// Specimen is the TypeSpecifier constant for the "Specimen" resource.
+	Specimen Type = "Specimen"
+
+	// SpecimenDefinition is the TypeSpecifier constant for the "SpecimenDefinition" resource.
+	SpecimenDefinition Type = "SpecimenDefinition"
+
+	// StructureDefinition is the TypeSpecifier constant for the "StructureDefinition" resource.
+	StructureDefinition Type = "StructureDefinition"
+
+	// StructureMap is the TypeSpecifier constant for the "StructureMap" resource.
+	StructureMap Type = "StructureMap"
+
+	// Subscription is the TypeSpecifier constant for the "Subscription" resource.
+	Subscription Type = "Subscription"
+
+	// Substance is the TypeSpecifier constant for the "Substance" resource.
+	Substance Type = "Substance"
+
+	// SubstanceNucleicAcid is the TypeSpecifier constant for the "SubstanceNucleicAcid" resource.
+	SubstanceNucleicAcid Type = "SubstanceNucleicAcid"
+
+	// SubstancePolymer is the TypeSpecifier constant for the "SubstancePolymer" resource.
+	SubstancePolymer Type = "SubstancePolymer"
+
+	// SubstanceProtein is the TypeSpecifier constant for the "SubstanceProtein" resource.
+	SubstanceProtein Type = "SubstanceProtein"
+
+	// SubstanceReferenceInformation is the TypeSpecifier constant for the "SubstanceReferenceInformation" resource.
+	SubstanceReferenceInformation Type = "SubstanceReferenceInformation"
+
+	// SubstanceSourceMaterial is the TypeSpecifier constant for the "SubstanceSourceMaterial" resource.
+	SubstanceSourceMaterial Type = "SubstanceSourceMaterial"
+
+	// SubstanceSpecification is the TypeSpecifier constant for the "SubstanceSpecification" resource.
+	SubstanceSpecification Type = "SubstanceSpecification"
+
+	// SupplyDelivery is the TypeSpecifier constant for the "SupplyDelivery" resource.
+	SupplyDelivery Type = "SupplyDelivery"
+
+	// SupplyRequest is the TypeSpecifier constant for the "SupplyRequest" resource.
+	SupplyRequest Type = "SupplyRequest"
+
+	// Task is the TypeSpecifier constant for the "Task" resource.
+	Task Type = "Task"
+
+	// TerminologyCapabilities is the TypeSpecifier constant for the "TerminologyCapabilities" resource.
+	TerminologyCapabilities Type = "TerminologyCapabilities"
+
+	// TestReport is the TypeSpecifier constant for the "TestReport" resource.
+	TestReport Type = "TestReport"
+
+	// TestScript is the TypeSpecifier constant for the "TestScript" resource.
+	TestScript Type = "TestScript"
+
+	// ValueSet is the TypeSpecifier constant for the "ValueSet" resource.
+	ValueSet Type = "ValueSet"
+
+	// VerificationResult is the TypeSpecifier constant for the "VerificationResult" resource.
+	VerificationResult Type = "VerificationResult"
+
+	// VisionPrescription is the TypeSpecifier constant for the "VisionPrescription" resource.
+	VisionPrescription Type = "VisionPrescription"
+)
diff --git a/internal/resource/identity.go b/internal/resource/identity.go
new file mode 100644
index 0000000..ea2de53
--- /dev/null
+++ b/internal/resource/identity.go
@@ -0,0 +1,187 @@
+package resource
+
+import (
+	"fmt"
+	"regexp"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+)
+
+const (
+	resourceTypePattern = "[A-Za-z]+"
+	idPattern           = "[0-9A-Za-z.-]{1,64}" // https://www.hl7.org/fhir/R4/datatypes.html#id
+	resourceIDPattern   = idPattern             // https://www.hl7.org/fhir/R4/resource.html#resource
+	versionIDPattern    = idPattern             // https://www.hl7.org/fhir/R4/resource.html#Meta
+)
+
+var (
+	historyURLRegexp = regexp.MustCompile(fmt.Sprintf("^.*/(%s)/(%s)/_history/(%s)$", resourceTypePattern, resourceIDPattern, versionIDPattern))
+)
+
+// Identity is a representation of a FHIR Resource's temporal instance.
+//
+// This is similar to a FHIR Reference, except without any explicit sementics of
+// a referential relationship. Rather, this object simply acts as a carrier for
+// the data that may be used for general identity purposes, such as logging.
+type Identity struct {
+	typeName Type
+	id       string
+	version  string
+}
+
+// Equal implements equality comparison between identity instances.
+//
+// The Equal() method, when used by the "cmp" package MUST
+// support nils: see "...even if x or y is nil" from second bullet
+// in https://pkg.go.dev/github.com/google/go-cmp/cmp#Equal.
+func (i *Identity) Equal(other *Identity) bool {
+	if i == other {
+		return true
+	}
+	if i == nil || other == nil {
+		return false
+	}
+	return i.typeName == other.typeName && i.id == other.id && i.version == other.version
+}
+
+// Type returns the resource Identity's underlying type. This is guaranteed to
+// always be a valid resource type.
+func (i *Identity) Type() Type {
+	return i.typeName
+}
+
+// ID returns the resource's ID.
+func (i *Identity) ID() string {
+	return i.id
+}
+
+// VersionID returns the explicit version of the resource, if it is known.
+func (i *Identity) VersionID() (string, bool) {
+	return i.version, i.version != ""
+}
+
+// RelativeURI returns a relative URI of this resource.
+func (i *Identity) RelativeURI() *dtpb.Uri {
+	return fhir.URI(fmt.Sprintf("%v/%v", i.typeName, i.id))
+}
+
+// RelativeURIString returns a string representation of the RelativeURI for
+// convenience.
+func (i *Identity) RelativeURIString() string {
+	return i.RelativeURI().GetValue()
+}
+
+// RelativeVersionedURI returns a relative URI of this resource including the
+// version identifier, if it is known.
+func (i *Identity) RelativeVersionedURI() (*dtpb.Uri, bool) {
+	if i.version == "" {
+		return nil, false
+	}
+	return fhir.URI(fmt.Sprintf("%v/%v/_history/%v", i.typeName, i.id, i.version)), true
+}
+
+// RelativeVersionedURIString returns a string representation of the RelativeVersionedURI
+// for convenience.
+func (i *Identity) RelativeVersionedURIString() (string, bool) {
+	val, ok := i.RelativeVersionedURI()
+	if ok {
+		return val.GetValue(), true
+	}
+	return "", false
+}
+
+// PreferRelativeVersionURI returns the relative version URI if available,
+// otherwise the relative URI only.
+func (i *Identity) PreferRelativeVersionedURI() *dtpb.Uri {
+	if uri, ok := i.RelativeVersionedURI(); ok {
+		return uri
+	}
+	return i.RelativeURI()
+}
+
+// PreferRelativeVersionedURIString returns the relative version URI string if
+// available, otherwise the relative URI only.
+func (i *Identity) PreferRelativeVersionedURIString() string {
+	if uri, ok := i.RelativeVersionedURIString(); ok {
+		return uri
+	}
+	return i.RelativeURIString()
+}
+
+// String returns a string representation of this Identity.
+//
+// The exact representation should not be relied on for any practical purpose;
+// the only thing that is guaranteed is that for the unique triple of data
+// containing (type, id, version), the String will contain these details -- but
+// the exact form is unspecified.
+func (i *Identity) String() string {
+	if i.version == "" {
+		return fmt.Sprintf("%v/%v", i.typeName, i.id)
+	}
+	return fmt.Sprintf("%v/%v/_history/%v", i.typeName, i.id, i.version)
+}
+
+// Unversioned returns a new Identity that does not have a VersionID.
+func (i *Identity) Unversioned() *Identity {
+	return &Identity{
+		typeName: i.typeName,
+		id:       i.id,
+	}
+}
+
+// WithNewVersion returns a new Identity that has the specified VersionID.
+func (i *Identity) WithNewVersion(versionID string) *Identity {
+	return &Identity{
+		typeName: i.typeName,
+		id:       i.id,
+		version:  versionID,
+	}
+}
+
+// NewIdentity attempts to create a new Identity object from a runtime-provided
+// string resourceType name, and its id/versionID. If the provided resourceType
+// does not name a valid resource-type (case-sensitive), this function will
+// return an ErrBadType error.
+func NewIdentity(resourceType, id, versionID string) (*Identity, error) {
+	name, err := NewType(resourceType)
+	if err != nil {
+		return nil, err
+	}
+
+	return &Identity{
+		typeName: name,
+		id:       id,
+		version:  versionID,
+	}, nil
+}
+
+// NewIdentityFromHistoryURL attempts to create a new Identity object from a
+// runtime-provided history URL.
+//
+// Input: [FHIR Proxy/Store URL]/fhir/[resourceType]/[resourceId]/_history/[versionId]
+// Output: [resourceID]
+func NewIdentityFromHistoryURL(url string) (*Identity, error) {
+	matches := historyURLRegexp.FindStringSubmatch(url)
+	if len(matches) != 4 {
+		return nil, fmt.Errorf("error parsing history URL: %s", url)
+	}
+	return NewIdentity(matches[1], matches[2], matches[3])
+}
+
+// IdentityOf attempts to form a resource Identity object to the named
+// resource. If the specified resource is either nil, or does not contain an
+// ID value, no resource identity will be formed and this function will return
+// nil.
+func IdentityOf(resource fhir.Resource) (*Identity, bool) {
+	if resource == nil || resource.GetId() == nil {
+		return nil, false
+	}
+
+	return &Identity{
+		typeName: TypeOf(resource),
+		id:       resource.GetId().GetValue(),
+		version:  resource.GetMeta().GetVersionId().GetValue(),
+	}, true
+}
diff --git a/internal/resource/identity_test.go b/internal/resource/identity_test.go
new file mode 100644
index 0000000..06ce42f
--- /dev/null
+++ b/internal/resource/identity_test.go
@@ -0,0 +1,137 @@
+package resource_test
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/fhirtest"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+)
+
+func TestIdentityOf_NilInputs_ReturnsNoValue(t *testing.T) {
+	_, got := resource.IdentityOf(nil)
+
+	if got, want := got, false; got != want {
+		t.Errorf("IdentityOf: got %v, want %v", got, want)
+	}
+}
+
+func TestIdentityOf(t *testing.T) {
+	for name, res := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			want, err := resource.NewIdentity(
+				string(resource.TypeOf(res)),
+				res.GetId().GetValue(),
+				res.GetMeta().GetVersionId().GetValue(),
+			)
+			if err != nil {
+				t.Fatalf("IdentityOf(%v): got unexpected err: %v", name, err)
+			}
+
+			got, ok := resource.IdentityOf(res)
+			if !ok {
+				t.Fatalf("IdentityOf(%v): got false for ok", name)
+			}
+
+			if !cmp.Equal(got, want) {
+				t.Errorf("IdentityOf(%v): got %v, want %v", name, got, want)
+			}
+		})
+	}
+}
+
+func TestNewIdentity_BadInput_ReturnsErrBadType(t *testing.T) {
+	_, err := resource.NewIdentity("", "1234", "5678")
+
+	if got, want := err, resource.ErrBadType; !errors.Is(got, want) {
+		t.Errorf("NewIdentity: got err '%v', want err '%v'", got, want)
+	}
+}
+
+func TestNewIdentityFromHistoryURL(t *testing.T) {
+	testCases := []struct {
+		name          string
+		historyUrl    string
+		expectedValue *resource.Identity
+	}{
+		{
+			"URL",
+			"https://healthcare.googleapis.com/v1/projects/my-project-name/locations/us-east4/datasets/my-dataset-name/fhirStores/my-fhir-store-name/fhir/Binary/123/_history/456",
+			mustNewIdentity("Binary", "123", "456"),
+		},
+		{
+			"URI",
+			"projects/my-project-name/locations/us-east4/datasets/my-dataset-name/fhirStores/my-fhir-store-name/fhir/Patient/abc/_history/def",
+			mustNewIdentity("Patient", "abc", "def"),
+		},
+		{
+			"Invalid",
+			"ThisIsNotAValidResourceName",
+			nil,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			result, _ := resource.NewIdentityFromHistoryURL(tc.historyUrl)
+			if ((result != nil) != (tc.expectedValue != nil)) ||
+				(result != nil && tc.expectedValue != nil && *result != *tc.expectedValue) {
+				t.Errorf("%s: Got = %v, want = %v", tc.name, result, tc.expectedValue)
+			}
+		})
+	}
+}
+
+func TestIdentityEqual(t *testing.T) {
+	identityA := mustNewIdentity("Patient", "A", "v1")
+	testCases := []struct {
+		name      string
+		lhs       *resource.Identity
+		rhs       *resource.Identity
+		wantEqual bool
+	}{
+		{"both nil", nil, nil, true},
+		{"lhs nil", nil, identityA, false},
+		{"rhs nil", identityA, nil, false},
+		{"same", identityA, mustNewIdentity("Patient", "A", "v1"), true},
+		{"different type", identityA, mustNewIdentity("Person", "A", "v1"), false},
+		{"different id", identityA, mustNewIdentity("Patient", "B", "v1"), false},
+		{"different version", identityA, mustNewIdentity("Patient", "A", "v2"), false},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			gotEqual := tc.lhs.Equal(tc.rhs)
+			if gotEqual != tc.wantEqual {
+				t.Errorf("Equal(%s) got %v want %v", tc.name, gotEqual, tc.wantEqual)
+			}
+		})
+	}
+}
+
+func TestIdentity_Unversioned(t *testing.T) {
+	withVersion := mustNewIdentity("Patient", "123", "abc")
+	got := withVersion.Unversioned()
+	want := mustNewIdentity("Patient", "123", "")
+	if !cmp.Equal(got, want) {
+		t.Errorf("Unversioned: got %v, want %v", got, want)
+	}
+}
+
+func mustNewIdentity(resourceType, id, versionID string) *resource.Identity {
+	identity, err := resource.NewIdentity(resourceType, id, versionID)
+	if err != nil {
+		panic(err)
+	}
+	return identity
+}
+
+func TestWithNewVersion(t *testing.T) {
+	originalIdentity := mustNewIdentity("Patient", "foo", "")
+	wantIdentity := mustNewIdentity("Patient", "foo", "bar")
+
+	gotIdentity := originalIdentity.WithNewVersion("bar")
+
+	if !cmp.Equal(gotIdentity, wantIdentity) {
+		t.Errorf("WithNewVersion: got %v, want %v", gotIdentity, wantIdentity)
+	}
+}
diff --git a/internal/resource/options.go b/internal/resource/options.go
new file mode 100644
index 0000000..32911ad
--- /dev/null
+++ b/internal/resource/options.go
@@ -0,0 +1,31 @@
+package resource
+
+import (
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/resourceopt"
+)
+
+// WithMeta returns a resource Option for setting the Resource Meta with the
+// specified meta entry.
+func WithMeta(meta *dtpb.Meta) Option {
+	return resourceopt.WithProtoField("meta", meta)
+}
+
+// WithID returns a resource Option for setting the Resourec ID with the id of
+// the provided string.
+func WithID(id string) Option {
+	return resourceopt.WithProtoField("id", fhir.ID(id))
+}
+
+// WithImplicitRules returns a resource Option for setting the Resource implicit
+// rules with the provided string.
+func WithImplicitRules(rules string) Option {
+	return resourceopt.WithProtoField("implicit_rules", fhir.URI(rules))
+}
+
+// WithLanguage returns a resource Option for setting the Resource language to
+// the code of the provided string.
+func WithLanguage(language string) Option {
+	return resourceopt.WithProtoField("language", fhir.Code(language))
+}
diff --git a/internal/resource/options_test.go b/internal/resource/options_test.go
new file mode 100644
index 0000000..569567f
--- /dev/null
+++ b/internal/resource/options_test.go
@@ -0,0 +1,82 @@
+package resource_test
+
+import (
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/fhirtest"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"google.golang.org/protobuf/proto"
+)
+
+func TestWithMeta(t *testing.T) {
+	want := &dtpb.Meta{
+		VersionId: &dtpb.Id{
+			Value: "deadbeef",
+		},
+	}
+
+	for name, res := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			got := proto.Clone(res).(fhir.Resource)
+
+			resource.Update(got, resource.WithMeta(want))
+
+			if got, want := got.GetMeta(), want; !proto.Equal(got, want) {
+				t.Errorf("WithMeta(%v): got %v, want %v", name, got, want)
+			}
+		})
+	}
+}
+
+func TestWithID(t *testing.T) {
+	const id = "123456789"
+	want := fhir.ID(id)
+
+	for name, res := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			got := proto.Clone(res).(fhir.Resource)
+
+			resource.Update(got, resource.WithID(id))
+
+			if got, want := got.GetId(), want; !proto.Equal(got, want) {
+				t.Errorf("WithID(%v): got %v, want %v", name, got, want)
+			}
+		})
+	}
+}
+
+func TestWithImplicitRules(t *testing.T) {
+	const rules = "https://example.com/some/rules"
+	want := fhir.URI(rules)
+
+	for name, res := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			got := proto.Clone(res).(fhir.Resource)
+
+			resource.Update(got, resource.WithImplicitRules(rules))
+
+			if got, want := got.GetImplicitRules(), want; !proto.Equal(got, want) {
+				t.Errorf("WithImplicitRules(%v): got %v, want %v", name, got, want)
+			}
+		})
+	}
+}
+
+func TestWithLanguage(t *testing.T) {
+	const language = "en-gb"
+	want := fhir.Code(language)
+
+	for name, res := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			got := proto.Clone(res).(fhir.Resource)
+
+			resource.Update(got, resource.WithLanguage(language))
+
+			if got, want := got.GetLanguage(), want; !proto.Equal(got, want) {
+				t.Errorf("WithLanguage(%v): got %v, want %v", name, got, want)
+			}
+		})
+	}
+}
diff --git a/internal/resource/patient/patient.go b/internal/resource/patient/patient.go
new file mode 100644
index 0000000..31b6121
--- /dev/null
+++ b/internal/resource/patient/patient.go
@@ -0,0 +1,172 @@
+package patient
+
+import (
+	"errors"
+	"fmt"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	appb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/appointment_go_proto"
+	arpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/appointment_response_go_proto"
+	cppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/care_plan_go_proto"
+	clpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/claim_go_proto"
+	commpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/communication_go_proto"
+	crpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/communication_request_go_proto"
+	cpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/condition_go_proto"
+	cerpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/coverage_eligibility_request_go_proto"
+	dpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_go_proto"
+	drpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_request_go_proto"
+	epb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/encounter_go_proto"
+	erpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/enrollment_request_go_proto"
+	eobpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/explanation_of_benefit_go_proto"
+	irpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/immunization_recommendation_go_proto"
+	lpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/list_go_proto"
+	mrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_request_go_proto"
+	nopb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/nutrition_order_go_proto"
+	opb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/observation_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	procpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/procedure_go_proto"
+	qrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/questionnaire_response_go_proto"
+	rppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/related_person_go_proto"
+	rgpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/request_group_go_proto"
+	rspb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/research_subject_go_proto"
+	rapb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/risk_assessment_go_proto"
+	srpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/service_request_go_proto"
+	surpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/supply_request_go_proto"
+	tpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/task_go_proto"
+	vppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/vision_prescription_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+)
+
+var (
+	ErrExtractingPatientID = errors.New("extracting patient id")
+	ErrUnsupportedType     = errors.New("extraction unsupported for type")
+)
+
+// IDFromResource returns the patient's ID from a FHIR Resource.
+// This function provides a mapping from an input resource to the patient ID it references.
+func IDFromResource(res fhir.Resource) (string, error) {
+	idOrError := func(patientRef *dtpb.Reference) (string, error) {
+		if id := patientRef.GetPatientId().GetValue(); id != "" {
+			return id, nil
+		}
+		return "", fmt.Errorf("%w from %T", ErrExtractingPatientID, res)
+	}
+
+	switch res := res.(type) {
+
+	//---------------------------------------------------------------------------
+	// Unpatterned Resources
+	//---------------------------------------------------------------------------
+
+	case *ppb.Patient:
+		if id := res.GetId().GetValue(); id != "" {
+			return id, nil
+		}
+		return "", fmt.Errorf("%w from %T", ErrExtractingPatientID, res)
+	case *epb.Encounter:
+		return idOrError(res.GetSubject())
+	case *dpb.Device:
+		return idOrError(res.GetPatient())
+	case *eobpb.ExplanationOfBenefit:
+		return idOrError(res.GetPatient())
+	case *rspb.ResearchSubject:
+		return idOrError(res.GetIndividual())
+	case *rppb.RelatedPerson:
+		return idOrError(res.GetPatient())
+	case *lpb.List:
+		return idOrError(res.GetSubject())
+
+	//---------------------------------------------------------------------------
+	// Event Pattern Resources
+	//---------------------------------------------------------------------------
+
+	case *qrpb.QuestionnaireResponse:
+		return idOrError(res.GetSubject())
+	case *rapb.RiskAssessment:
+		return idOrError(res.GetSubject())
+	case *cpb.Condition:
+		return idOrError(res.GetSubject())
+	case *procpb.Procedure:
+		return idOrError(res.GetSubject())
+	case *opb.Observation:
+		return idOrError(res.GetSubject())
+	case *tpb.Task:
+		// TODO(b/254654059): Remove usage of Task.for once decision engine supports event-patient extraction override.
+		if id := res.GetFocus().GetPatientId().GetValue(); id != "" {
+			return id, nil
+		} else if id := res.GetForValue().GetPatientId().GetValue(); id != "" {
+			return id, nil
+		}
+		return "", fmt.Errorf("%w from %T", ErrExtractingPatientID, res)
+	case *commpb.Communication:
+		return idOrError(res.GetSubject())
+
+	//---------------------------------------------------------------------------
+	// Request Pattern Resources
+	//---------------------------------------------------------------------------
+
+	case *appb.Appointment:
+		// TODO(b/240690479): Appointments are a request-pattern type that supports
+		// multiple participants, which may be of type Practitioner, Patient, etc.
+		// This means we may have to support multiple patient IDs. Currently we
+		// assume only 1 patient by searching for and returning the first patient we
+		// discover.
+		for _, participant := range res.GetParticipant() {
+			if id := participant.GetActor().GetPatientId().GetValue(); id != "" {
+				return id, nil
+			}
+		}
+		return "", fmt.Errorf("%w from %T", ErrExtractingPatientID, res)
+	case *arpb.AppointmentResponse:
+		return idOrError(res.GetActor())
+	case *cppb.CarePlan:
+		return idOrError(res.GetSubject())
+	case *clpb.Claim:
+		return idOrError(res.GetPatient())
+	case *crpb.CommunicationRequest:
+		return idOrError(res.GetSubject())
+	case *cerpb.CoverageEligibilityRequest:
+		return idOrError(res.GetPatient())
+	case *drpb.DeviceRequest:
+		return idOrError(res.GetSubject())
+	case *erpb.EnrollmentRequest:
+		return idOrError(res.GetCandidate())
+	case *irpb.ImmunizationRecommendation:
+		return idOrError(res.GetPatient())
+	case *mrpb.MedicationRequest:
+		return idOrError(res.GetSubject())
+	case *nopb.NutritionOrder:
+		return idOrError(res.GetPatient())
+	case *rgpb.RequestGroup:
+		return idOrError(res.GetSubject())
+	case *srpb.ServiceRequest:
+		return idOrError(res.GetSubject())
+	case *surpb.SupplyRequest:
+		// Supply requests may contain PatientID in two possible locations: either as
+		// the source of the request, or as the destination for the request
+		if id := res.GetDeliverTo().GetPatientId().GetValue(); id != "" {
+			return id, nil
+		} else if id := res.GetRequester().GetPatientId().GetValue(); id != "" {
+			return id, nil
+		}
+		return "", fmt.Errorf("%w from %T", ErrExtractingPatientID, res)
+	case *vppb.VisionPrescription:
+		return idOrError(res.GetPatient())
+	default:
+		return "", fmt.Errorf("%w: %T", ErrUnsupportedType, res)
+	}
+}
+
+// Reference creates a literal Patient reference.
+// This replaces verily-go-fhir/protohelpers.PatientReference.
+func Reference(patientID string) *dtpb.Reference {
+	return &dtpb.Reference{
+		Type: fhir.URI(resource.Patient.String()),
+		Reference: &dtpb.Reference_PatientId{
+			PatientId: &dtpb.ReferenceId{
+				Value: patientID,
+			},
+		},
+	}
+}
diff --git a/internal/resource/patient/patient_test.go b/internal/resource/patient/patient_test.go
new file mode 100644
index 0000000..d63635c
--- /dev/null
+++ b/internal/resource/patient/patient_test.go
@@ -0,0 +1,172 @@
+package patient_test
+
+import (
+	"fmt"
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	apb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/account_go_proto"
+	appb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/appointment_go_proto"
+	arpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/appointment_response_go_proto"
+	cppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/care_plan_go_proto"
+	clpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/claim_go_proto"
+	crpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/communication_request_go_proto"
+	cpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/condition_go_proto"
+	cerpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/coverage_eligibility_request_go_proto"
+	dpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_go_proto"
+	drpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_request_go_proto"
+	epb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/encounter_go_proto"
+	erpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/enrollment_request_go_proto"
+	eobpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/explanation_of_benefit_go_proto"
+	irpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/immunization_recommendation_go_proto"
+	mrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/medication_request_go_proto"
+	nopb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/nutrition_order_go_proto"
+	opb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/observation_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	procpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/procedure_go_proto"
+	qrpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/questionnaire_response_go_proto"
+	rppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/related_person_go_proto"
+	rgpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/request_group_go_proto"
+	rspb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/research_subject_go_proto"
+	rapb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/risk_assessment_go_proto"
+	srpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/service_request_go_proto"
+	surpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/supply_request_go_proto"
+	tpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/task_go_proto"
+	vppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/vision_prescription_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/resource/patient"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+// TODO(PHP-7652): Update testing
+func TestIDFromResource(t *testing.T) {
+	mockPatientID := "patient-id"
+	mockPatientRef := patient.Reference(mockPatientID)
+	mockAccount := &apb.Account{}
+
+	type TestCase struct {
+		name          string
+		resource      fhir.Resource
+		expected      string
+		expectedError error
+	}
+
+	// Helpers to make pass and fail cases consistent
+	pass := func(name string, res fhir.Resource) TestCase {
+		return TestCase{
+			name:          fmt.Sprintf("%v as input, contains patient id, returns id", name),
+			resource:      res,
+			expected:      mockPatientID,
+			expectedError: nil,
+		}
+	}
+	fail := func(name string, res fhir.Resource) TestCase {
+		return TestCase{
+			name:          fmt.Sprintf("%v as input, does not contain id, returns err", name),
+			resource:      res,
+			expected:      "",
+			expectedError: patient.ErrExtractingPatientID,
+		}
+	}
+
+	testCases := []TestCase{
+
+		// General Error Conditions
+		{"unsupported resource type, returns err", mockAccount, "", patient.ErrUnsupportedType},
+
+		// Resources
+		pass("patient", &ppb.Patient{Id: fhir.ID(mockPatientID)}),
+		fail("patient", &ppb.Patient{}),
+		pass("encounter", &epb.Encounter{Subject: mockPatientRef}),
+		fail("encounter", &epb.Encounter{}),
+		pass("device", &dpb.Device{Patient: mockPatientRef}),
+		fail("device", &dpb.Device{}),
+		pass("explanation of benefit", &eobpb.ExplanationOfBenefit{Patient: mockPatientRef}),
+		fail("explanation of benefit", &eobpb.ExplanationOfBenefit{}),
+		pass("research subject", &rspb.ResearchSubject{Individual: mockPatientRef}),
+		fail("research subject", &rspb.ResearchSubject{}),
+		pass("related person", &rppb.RelatedPerson{Patient: mockPatientRef}),
+		fail("related person", &rppb.RelatedPerson{}),
+
+		// Event Patterns
+		pass("questionnaire response", &qrpb.QuestionnaireResponse{Subject: mockPatientRef}),
+		fail("questionnaire response", &qrpb.QuestionnaireResponse{}),
+		pass("risk assessment", &rapb.RiskAssessment{Subject: mockPatientRef}),
+		fail("risk assessment", &rapb.RiskAssessment{}),
+		pass("condition", &cpb.Condition{Subject: mockPatientRef}),
+		fail("condition", &cpb.Condition{}),
+		pass("procedure", &procpb.Procedure{Subject: mockPatientRef}),
+		fail("procedure", &procpb.Procedure{}),
+		pass("observation", &opb.Observation{Subject: mockPatientRef}),
+		fail("observation", &opb.Observation{}),
+		pass("task", &tpb.Task{ForValue: mockPatientRef}),
+		pass("task", &tpb.Task{Focus: mockPatientRef}),
+		fail("task", &tpb.Task{}),
+
+		// Request Patterns
+		pass("appointment response", &arpb.AppointmentResponse{Actor: mockPatientRef}),
+		fail("appointment response", &arpb.AppointmentResponse{}),
+		pass("appointment", &appb.Appointment{Participant: []*appb.Appointment_Participant{
+			{Actor: mockPatientRef},
+		}}),
+		fail("appointment", &appb.Appointment{}),
+		pass("care plan", &cppb.CarePlan{Subject: mockPatientRef}),
+		fail("care plan", &cppb.CarePlan{}),
+		pass("claim", &clpb.Claim{Patient: mockPatientRef}),
+		fail("claim", &clpb.Claim{}),
+		pass("communication request", &crpb.CommunicationRequest{Subject: mockPatientRef}),
+		fail("communication reuqest", &crpb.CommunicationRequest{}),
+		pass("coverage eligibility request", &cerpb.CoverageEligibilityRequest{Patient: mockPatientRef}),
+		fail("coverage eligibility request", &cerpb.CoverageEligibilityRequest{}),
+		pass("device request", &drpb.DeviceRequest{Subject: mockPatientRef}),
+		fail("device request", &drpb.DeviceRequest{}),
+		pass("enrollment request", &erpb.EnrollmentRequest{Candidate: mockPatientRef}),
+		fail("enrollment request", &erpb.EnrollmentRequest{}),
+		pass("immunization recommendation", &irpb.ImmunizationRecommendation{Patient: mockPatientRef}),
+		fail("immunization recommendation", &irpb.ImmunizationRecommendation{}),
+		pass("medication request", &mrpb.MedicationRequest{Subject: mockPatientRef}),
+		fail("medication request", &mrpb.MedicationRequest{}),
+		pass("nutrition order", &nopb.NutritionOrder{Patient: mockPatientRef}),
+		fail("nutrition order", &nopb.NutritionOrder{}),
+		pass("request group", &rgpb.RequestGroup{Subject: mockPatientRef}),
+		fail("request group", &rgpb.RequestGroup{}),
+		pass("service request", &srpb.ServiceRequest{Subject: mockPatientRef}),
+		fail("service request", &srpb.ServiceRequest{}),
+		pass("supply request", &surpb.SupplyRequest{DeliverTo: mockPatientRef}),
+		pass("supply request", &surpb.SupplyRequest{Requester: mockPatientRef}),
+		fail("supply request", &surpb.SupplyRequest{}),
+		pass("vision prescription", &vppb.VisionPrescription{Patient: mockPatientRef}),
+		fail("vision prescription", &vppb.VisionPrescription{}),
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			patientID, err := patient.IDFromResource(tc.resource)
+			if got, want := err, tc.expectedError; !cmp.Equal(got, want, cmpopts.EquateErrors()) {
+				t.Fatalf("IDFromResource(%s) error got = %v, want = %v", tc.name, got, want)
+			}
+			if got, want := patientID, tc.expected; got != want {
+				t.Errorf("IDFromResource(%s) got = %v, want = %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestReference(t *testing.T) {
+	want := &dtpb.Reference{
+		Type: fhir.URI("Patient"),
+		Reference: &dtpb.Reference_PatientId{
+			PatientId: &dtpb.ReferenceId{
+				Value: "123",
+			},
+		},
+	}
+
+	got := patient.Reference("123")
+
+	if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
+		t.Errorf("Reference mismatch (-want, +got)\n%s", diff)
+	}
+}
diff --git a/internal/resource/resource.go b/internal/resource/resource.go
new file mode 100644
index 0000000..f4214ce
--- /dev/null
+++ b/internal/resource/resource.go
@@ -0,0 +1,244 @@
+/*
+Package resource contains utilities for working with abstract FHIR Resource
+objects.
+*/
+package resource
+
+import (
+	"errors"
+	"fmt"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/resourceopt"
+	"github.com/verily-src/fhirpath-go/internal/protofields"
+)
+
+var (
+	ErrGetIdentifierList = errors.New("GetIdentifierList()")
+)
+
+// Option is an option that may be supplied to updates or creations of Resource
+// types.
+type Option = resourceopt.Option
+
+// NewFromString attempts to construct a resource of the specified string name type,
+// using the specified options to construct it. This function returns an error
+// if 'name' does not name a valid type.
+func NewFromString(name string, opts ...Option) (fhir.Resource, error) {
+	fields, ok := protofields.Resources[name]
+	if !ok {
+		return nil, fmt.Errorf("invalid resource name '%v'", name)
+	}
+	resource := fields.New().(fhir.Resource)
+
+	return Update(resource, opts...), nil
+}
+
+// New constructs a new resource from the input type, using the specified
+// options to construct it.
+//
+// This function assumes that Type is a validly-constructed type object.
+// Failure to pass a valid type will result in a panic.
+func New(name Type, opts ...Option) fhir.Resource {
+	resource, err := NewFromString(string(name), opts...)
+	if err != nil {
+		// This is unreachable with validly-constructed Type objects
+		panic(err)
+	}
+	return resource
+}
+
+// NewOf constructs a new resource of the named T resource type, using the
+// specified options to construct it.
+func NewOf[T fhir.Resource](opts ...Option) fhir.Resource {
+	var t T
+	return New(TypeOf(t), opts...)
+}
+
+// Update modifies the input resource in-place with the specified options.
+func Update(res fhir.Resource, opts ...Option) fhir.Resource {
+	return resourceopt.ApplyOptions(res, opts...)
+}
+
+// ID gets the ID of the specified resource as a string. If `nil` is
+// provided, this returns an empty string.
+func ID(resource fhir.Resource) string {
+	if resource == nil {
+		return ""
+	}
+	return resource.GetId().GetValue()
+}
+
+// VersionID gets the version-ID of the specified resource as a string.
+// If `nil` is provided, this returns an empty string.
+//
+// This function on its own just simplifies the need of calling
+// `GetMeta().GetVersionId().GetValue()` all the time.
+func VersionID(resource fhir.Resource) string {
+	if resource == nil {
+		return ""
+	}
+	return resource.GetMeta().GetVersionId().GetValue()
+}
+
+// VersionETag pulls the "version" from the resource if it's an existing resource
+// that was queried from a FHIR store; the version returned matches the ETag header
+// returned by GET fhir-prefix/{resourceType}/{id} for this resource. This is used
+// for optimistic locking on resources per https://hl7.org/fhir/http.html#concurrency
+func VersionETag(r fhir.Resource) string {
+	version := VersionID(r)
+	if version == "" {
+		return ""
+	}
+	return fmt.Sprintf(`W/"%s"`, version)
+}
+
+// URI is a helper for getting the URI of a resource as a URI object.
+// The URI is returned in the format Type/ID, e.g. Patient/123.
+//
+// If the resource is nil, this will return a nil URI.
+func URI(resource fhir.Resource) *dtpb.Uri {
+	uri := URIString(resource)
+	if uri == "" {
+		return nil
+	}
+	return fhir.URI(uri)
+}
+
+// URIString is a helper for getting the URI of a resource in
+// string form. The URI is returned in the format Type/ID, e.g. Patient/123.
+//
+// If the resource is nil, this will return an empty string.
+func URIString(resource fhir.Resource) string {
+	if resource == nil {
+		return ""
+	}
+	id := resource.GetId().GetValue()
+	return fmt.Sprintf("%v/%v", TypeOf(resource), id)
+}
+
+// VersionedURI is a helper for getting the URI of a resource as a URI object.
+// The URI is returned in the format Type/ID/_history/VERSION.
+//
+// If the resource is nil, this will return a nil URI.
+func VersionedURI(resource fhir.Resource) *dtpb.Uri {
+	uri, found := VersionedURIString(resource)
+	if !found {
+		return nil
+	}
+	return fhir.URI(uri)
+}
+
+// VersionedURIString is a helper for getting the URI of a resource in
+// string form. The URI is returned in the format Type/ID/_history/VERSION.
+//
+// If the resource is nil, this will return an empty string.
+func VersionedURIString(resource fhir.Resource) (string, bool) {
+	if resource == nil {
+		return "", false
+	}
+	vID := VersionID(resource)
+	if vID == "" {
+		return "", false
+	}
+	id := resource.GetId().GetValue()
+	return fmt.Sprintf("%v/%v/_history/%v", TypeOf(resource), id, vID), true
+}
+
+// RemoveDuplicates finds all duplicates of resources -- determined by the
+// same <resource>/<id>/<version-id> -- and removes them, returning an
+// updated list of resources.
+//
+// Nil resources are skipped.
+func RemoveDuplicates(resources []fhir.Resource) []fhir.Resource {
+	deduper := map[string]struct{}{}
+
+	result := make([]fhir.Resource, 0, len(resources))
+	for _, res := range resources {
+		if res == nil {
+			continue
+		}
+		// Note: using this instead of VersionedURIString, since whether a version-id
+		// exists or not will not affect the behavior here.
+		key := fmt.Sprintf("%v/%v/%v", TypeOf(res), ID(res), res.GetMeta().GetVersionId().GetValue())
+
+		if _, ok := deduper[key]; ok {
+			continue
+		}
+		result = append(result, res)
+		deduper[key] = struct{}{}
+	}
+	return result
+}
+
+// GroupResources organizes all resources by their underlying resource Type,
+// and returns a map of the Type to the list of resources of that given type.
+//
+// Nil resources are skipped.
+// Resources with existing IDs are skipped
+func GroupResources(resources []fhir.Resource) map[Type][]fhir.Resource {
+	result := map[Type][]fhir.Resource{}
+	seen := map[string]struct{}{}
+	for _, res := range resources {
+		if res == nil {
+			continue
+		}
+		if id := res.GetId(); id != nil {
+			uri := URIString(res)
+			if _, ok := seen[uri]; ok {
+				continue // skip ones we have seen before
+			}
+			// add in new ones
+			seen[uri] = struct{}{}
+		}
+
+		key := TypeOf(res)
+		result[key] = append(result[key], res)
+	}
+	return result
+}
+
+// HasGetIdentifierList is a custom interface for duck typing resources that
+// have a GetIdentifier method that returns a slice of Identifiers.
+type HasGetIdentifierList interface {
+	GetIdentifier() []*dtpb.Identifier
+
+	// embed Resource since anything with an Identifier is also a Resource
+	fhir.Resource
+}
+
+// HasGetIdentifierSingle is a custom interface for duck typing resources that
+// have a GetIdentifier method that returns a single Identifier.
+type HasGetIdentifierSingle interface {
+	GetIdentifier() *dtpb.Identifier
+
+	// embed Resource since anything with an Identifier is also a Resource
+	fhir.Resource
+}
+
+// GetIdentifierList takes a Resource and returns a list of Identifiers.
+// It uses duck typing to determine whether the resource has a GetIdentifier()
+// method, and if so, whether it returns a list or a single Identifier.
+// It returns ErrGetIdentifierList if the resource does not implement GetIdentifier().
+// The list may be nil or empty if no identifiers are present.
+// See interfaces: fhir.HasGetIdentifierList, fhir.HasGetIdentifierSingle
+func GetIdentifierList(res fhir.Resource) ([]*dtpb.Identifier, error) {
+
+	if cast, ok := res.(HasGetIdentifierList); ok {
+		// resource implements GetIdentifier() as a list
+		return cast.GetIdentifier(), nil
+	}
+
+	if cast, ok := res.(HasGetIdentifierSingle); ok {
+		// resource implements GetIdentifier() as a single Identifier
+		id := cast.GetIdentifier()
+		if id == nil {
+			return nil, nil
+		}
+		return []*dtpb.Identifier{id}, nil
+	}
+
+	// This is likely a bug / results from passing an unexpected type of resource
+	return nil, fmt.Errorf("%w: Resource does not implement GetIdentifier(): %v", ErrGetIdentifierList, res)
+}
diff --git a/internal/resource/resource_example_test.go b/internal/resource/resource_example_test.go
new file mode 100644
index 0000000..db7928a
--- /dev/null
+++ b/internal/resource/resource_example_test.go
@@ -0,0 +1,32 @@
+package resource_test
+
+import (
+	"fmt"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+)
+
+func ExampleGetIdentifierList() {
+	patient := &patient_go_proto.Patient{
+		Id: fhir.ID("12345"),
+		Identifier: []*dtpb.Identifier{
+			&dtpb.Identifier{
+				System: &dtpb.Uri{Value: "http://fake.com"},
+				Value:  &dtpb.String{Value: "9efbf82d-7a58-4d14-bec1-63f8fda148a8"},
+			},
+		},
+	}
+
+	ids, err := resource.GetIdentifierList(patient)
+	if err != nil {
+		panic(err)
+	} else if ids == nil || len(ids) == 0 {
+		panic("no identifiers")
+	} else {
+		fmt.Printf("Identifier value: %#v", ids[0].GetValue().Value)
+		// Output: Identifier value: "9efbf82d-7a58-4d14-bec1-63f8fda148a8"
+	}
+}
diff --git a/internal/resource/resource_test.go b/internal/resource/resource_test.go
new file mode 100644
index 0000000..a90ef8d
--- /dev/null
+++ b/internal/resource/resource_test.go
@@ -0,0 +1,390 @@
+package resource_test
+
+import (
+	"errors"
+	"regexp"
+	"strings"
+	"testing"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/bundle_and_contained_resource_go_proto"
+	dpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/device_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/document_reference_go_proto"
+	ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto"
+	"github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/questionnaire_response_go_proto"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/fhirtest"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+func TestVersionETag(t *testing.T) {
+	testCases := []struct {
+		name string
+		res  fhir.Resource
+		want string
+	}{
+		{
+			"failure: VersionId is the empty string",
+			&ppb.Patient{Meta: &dtpb.Meta{VersionId: fhir.ID("")}},
+			"",
+		},
+		{
+			"extracted the VersionId from a Patient",
+			&ppb.Patient{Meta: &dtpb.Meta{VersionId: fhir.ID("abc")}},
+			`W/"abc"`,
+		},
+		{
+			"extracted the VersionId from a Device",
+			&dpb.Device{Meta: &dtpb.Meta{VersionId: fhir.ID("xyz")}},
+			`W/"xyz"`,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := resource.VersionETag(tc.res)
+
+			if got != tc.want {
+				t.Errorf("VersionETag(%s) version got = %v, want = %v", tc.name, got, tc.want)
+			}
+		})
+	}
+}
+
+func TestVersionedURI(t *testing.T) {
+	testCases := []struct {
+		name string
+		res  fhir.Resource
+		want *dtpb.Uri
+	}{
+		{
+			"nil resource",
+			&ppb.Patient{Meta: &dtpb.Meta{VersionId: fhir.ID("")}},
+			nil,
+		},
+		{
+			"no version",
+			&ppb.Patient{Id: fhir.ID("abc")},
+			nil,
+		},
+		{
+			"versioned resource",
+			&dpb.Device{Id: fhir.ID("123"), Meta: &dtpb.Meta{VersionId: fhir.ID("abc")}},
+			&dtpb.Uri{Value: "Device/123/_history/abc"},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := resource.VersionedURI(tc.res)
+
+			if diff := cmp.Diff(got, tc.want, protocmp.Transform()); diff != "" {
+				t.Fatalf("VersionedURI(%s): (-got, +want):\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestVersionedURIString(t *testing.T) {
+	testCases := []struct {
+		name      string
+		res       fhir.Resource
+		want      string
+		wantFound bool
+	}{
+		{
+			"nil resource",
+			&ppb.Patient{Meta: &dtpb.Meta{VersionId: fhir.ID("")}},
+			"",
+			false,
+		},
+		{
+			"no version",
+			&ppb.Patient{Id: fhir.ID("abc")},
+			"",
+			false,
+		},
+		{
+			"versioned resource",
+			&dpb.Device{Id: fhir.ID("123"), Meta: &dtpb.Meta{VersionId: fhir.ID("abc")}},
+			"Device/123/_history/abc",
+			true,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, found := resource.VersionedURIString(tc.res)
+
+			if found != tc.wantFound {
+				t.Fatalf("VersionedURIString(%s) found got = %v, want = %v", tc.name, got, tc.wantFound)
+			}
+			if got != tc.want {
+				t.Errorf("VersionedURIString(%s) got = %v, want = %v", tc.name, got, tc.want)
+			}
+		})
+	}
+}
+
+func TestRemoveDuplicates(t *testing.T) {
+	patient := fhirtest.NewResource(t, resource.Patient)
+	device := fhirtest.NewResource(t, resource.Device)
+	account := fhirtest.NewResource(t, resource.Account)
+
+	testCases := []struct {
+		name  string
+		input []fhir.Resource
+		want  []fhir.Resource
+	}{
+		{
+			name:  "Inputs are unique",
+			input: []fhir.Resource{patient, device, account},
+			want:  []fhir.Resource{patient, device, account},
+		},
+		{
+			name:  "Duplicates are removed",
+			input: []fhir.Resource{patient, device, account, device, account, patient},
+			want:  []fhir.Resource{patient, device, account},
+		},
+		{
+			name:  "Removes nil",
+			input: []fhir.Resource{nil, patient, nil},
+			want:  []fhir.Resource{patient},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := resource.RemoveDuplicates(tc.input)
+
+			opts := []cmp.Option{
+				cmpopts.SortSlices(func(lhs, rhs fhir.Resource) bool {
+					return resource.URIString(lhs) < resource.URIString(rhs)
+				}),
+				protocmp.Transform(),
+			}
+			if want := tc.want; !cmp.Equal(got, want, opts...) {
+				t.Errorf("RemoveDuplicates(%v): got '%v', want '%v'", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestGroupResources(t *testing.T) {
+	patient := fhirtest.NewResource(t, resource.Patient)
+	device := fhirtest.NewResource(t, resource.Device)
+	account := fhirtest.NewResource(t, resource.Account)
+
+	testCases := []struct {
+		name  string
+		input []fhir.Resource
+		want  map[resource.Type][]fhir.Resource
+	}{
+		{
+			name:  "Inputs are sorted",
+			input: []fhir.Resource{patient, device, account},
+			want: map[resource.Type][]fhir.Resource{
+				resource.Patient: {patient},
+				resource.Device:  {device},
+				resource.Account: {account},
+			},
+		},
+		{
+			name:  "Removes nil",
+			input: []fhir.Resource{nil, patient, nil},
+			want: map[resource.Type][]fhir.Resource{
+				resource.Patient: {patient},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := resource.GroupResources(tc.input)
+
+			opts := []cmp.Option{
+				protocmp.Transform(),
+				cmpopts.SortMaps(func(lhs, rhs resource.Type) bool {
+					return lhs < rhs
+				}),
+			}
+			if want := tc.want; !cmp.Equal(got, want, opts...) {
+				t.Errorf("GroupResources(%v): got '%v', want '%v'", tc.name, got, want)
+			}
+		})
+	}
+}
+
+// Assert that an identifier list is present and has expected values
+func assertIdentifier(t *testing.T, identifiers []*dtpb.Identifier, name string, resource fhir.Resource) {
+	if identifiers == nil {
+		t.Errorf("Nil list of ids for %v: %v", name, resource)
+		return
+	}
+	if len(identifiers) == 0 {
+		t.Errorf("Empty list of ids for %v: %v", name, resource)
+		return
+	}
+
+	id := identifiers[0]
+
+	if got, want := id.GetSystem().Value, "http://example.com/fake-id"; got != want {
+		t.Errorf("%v Resource.Identifier[0].System: got %v, want %v", name, got, want)
+	}
+
+	value := id.GetValue().Value
+	matched, _ := regexp.MatchString("^[a-f0-9-]+$", value)
+	if !matched {
+		t.Errorf("%v Resource.Identifier[0].Value: got %v, expected uuid", name, value)
+	}
+}
+
+func TestGetIdentifier(t *testing.T) {
+	// Test that all resources return nil by default
+	for name := range fhirtest.Resources {
+		t.Run("Resources/"+name, func(t *testing.T) {
+			res := fhirtest.NewResource(
+				t,
+				resource.Type(name),
+			)
+
+			ids, _ := resource.GetIdentifierList(res)
+
+			if ids != nil {
+				t.Errorf("%v Resource.Identifier: got %v, want nil -- not supposed to have Identifier", name, ids)
+			}
+		})
+	}
+
+	// Test that all types compatible with CanonicalResource actually return an identifier
+	for name := range fhirtest.CanonicalResources {
+		t.Run("CanonicalResources/"+name, func(t *testing.T) {
+			res := fhirtest.NewResource(
+				t,
+				resource.Type(name),
+				fhirtest.WithGeneratedIdentifier("http://example.com/fake-id"),
+			)
+			ids, err := resource.GetIdentifierList(res)
+			if err != nil {
+				t.Errorf("got %v, want nil -- unexpected error", err)
+			}
+
+			assertIdentifier(t, ids, name, res)
+		})
+	}
+
+	// Sanity check a few specific types that we know have Identifier
+	for _, name := range []string{"Patient", "DocumentReference", "AdverseEvent", "Bundle"} {
+		t.Run("CanonicalResources/"+name, func(t *testing.T) {
+			res := fhirtest.NewResource(
+				t,
+				resource.Type(name),
+				fhirtest.WithGeneratedIdentifier("http://example.com/fake-id"),
+			)
+
+			ids, err := resource.GetIdentifierList(res)
+			if err != nil {
+				t.Errorf("got %v, want nil -- unexpected error", err)
+			}
+
+			assertIdentifier(t, ids, name, res)
+		})
+	}
+
+}
+
+func TestGetIdentifier_single(t *testing.T) {
+	// Sanity check a few specific types that have a singleton Identifier
+	// Bundle, QuestionnaireResponse
+
+	testIds := []*dtpb.Identifier{
+		{
+			System: &dtpb.Uri{Value: "http://example.com/fake-id"},
+			Value:  &dtpb.String{Value: "35c423fc-0651-4c83-b63f-9008e0c96445"},
+		},
+		{
+			System: &dtpb.Uri{Value: "http://example.com/fake-id"},
+			Value:  &dtpb.String{Value: "ddec1b6e-4539-4aae-becf-b4dced32189f"},
+		},
+	}
+
+	testCases := []struct {
+		name string
+		res  fhir.Resource
+		want *dtpb.Identifier
+	}{
+		{
+			"Bundle",
+			&bundle_and_contained_resource_go_proto.Bundle{
+				Identifier: testIds[0],
+			},
+			testIds[0],
+		},
+		{
+			"QuestionnaireResponse",
+			&questionnaire_response_go_proto.QuestionnaireResponse{
+				Identifier: testIds[1],
+			},
+			testIds[1],
+		},
+	}
+	for _, tc := range testCases {
+
+		ids, err := resource.GetIdentifierList(tc.res)
+		want := []*dtpb.Identifier{tc.want}
+
+		if err != nil {
+			t.Errorf("got %v, want nil", err)
+			return
+		}
+
+		assertIdentifier(t, ids, tc.name, tc.res)
+
+		if len(ids) != len(want) {
+			t.Errorf("got %v, want %v", ids, want)
+			continue
+		}
+		if ids[0] != want[0] {
+			t.Errorf("got %v, want %v", ids[0], want[0])
+		}
+	}
+}
+
+func TestGetIdentifier_nil(t *testing.T) {
+	// Sanity check a few specific types that we know do NOT have Identifier
+	resourcesWithoutIdentifiers := []string{
+		"Provenance",
+		"Linkage",
+	}
+	for _, name := range resourcesWithoutIdentifiers {
+		t.Run("CanonicalResources/"+name, func(t *testing.T) {
+			res := fhirtest.NewResource(t, resource.Type(name))
+
+			ids, err := resource.GetIdentifierList(res)
+
+			if ids != nil {
+				t.Errorf("%v Resource.Identifier: got %v, want nil -- not supposed to have Identifier", name, ids)
+			}
+
+			if err == nil {
+				t.Errorf("got nil, want error")
+			}
+
+			wanterr := "Resource does not implement GetIdentifier()"
+			if !strings.Contains(err.Error(), wanterr) {
+				t.Errorf("got %#v, want %#v", err.Error(), wanterr)
+			}
+			if !errors.Is(err, resource.ErrGetIdentifierList) {
+				t.Errorf("got error %#v, want errors.Is(..., ErrGenerateIfNoneExist)", err)
+			}
+		})
+	}
+}
+
+// Sanity check that a few resources have GetIdentifier() as a list. This is not a complete list.
+var _ resource.HasGetIdentifierList = (*ppb.Patient)(nil)
+var _ resource.HasGetIdentifierList = (*document_reference_go_proto.DocumentReference)(nil)
+
+// Sanity check that a few resources have GetIdentifier() as a single ID. This is not a complete list.
+var _ resource.HasGetIdentifierSingle = (*bundle_and_contained_resource_go_proto.Bundle)(nil)
+var _ resource.HasGetIdentifierSingle = (*questionnaire_response_go_proto.QuestionnaireResponse)(nil)
diff --git a/internal/resource/type.go b/internal/resource/type.go
new file mode 100644
index 0000000..d2acc63
--- /dev/null
+++ b/internal/resource/type.go
@@ -0,0 +1,86 @@
+package resource
+
+import (
+	"errors"
+	"fmt"
+
+	dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/protofields"
+)
+
+// ErrBadType is an error raised when a bad type is provided.
+var ErrBadType = errors.New("bad resource type")
+
+// Type is a FHIR Resource type object. This is similar to a reflect.Type that
+// encodes its name identifier
+//
+// Type objects should never be constructed manually; rather, use the `CheckType`
+// or `TypeOf` functions to get a valid type object. Invalid instances of Type
+// may lead to unexpected implicit `panic` behavior, as any code consuming this
+// is allowed to assume that `Type` always names a valid instance.
+type Type string
+
+// TypeOf gets the underlying type of the named resource.
+//
+// This function panics if resource is nil. Note that this is only an issue if
+// the interface `fhir.Resource` is nil, *not* if the underlying resource is a
+// pointer that is nil. E.g. the following holds true:
+//
+//	assert.True(resource.TypeOf((*ppb.Patient)(nil)) == resource.Patient)
+func TypeOf(resource fhir.Resource) Type {
+	if resource == nil {
+		panic("TypeOf provided nil Resource")
+	}
+	return Type(resource.ProtoReflect().Descriptor().Name())
+}
+
+// NewType checks whether the string type name is a valid resource.Type instance.
+// If it is, an instance of the type is returned. If the provided type is not a
+// valid type, an ErrBadType is returned, and the type result is garbage.
+//
+// Note: This is case-sensitive, and expects CamelCase, just as the FHIR spec uses.
+func NewType(resourceType string) (Type, error) {
+	if !IsType(resourceType) {
+		return "", fmt.Errorf("%w '%v'", ErrBadType, resourceType)
+	}
+	return Type(resourceType), nil
+}
+
+// String converts this Type into a string.
+func (t Type) String() string {
+	return string(t)
+}
+
+// New returns an instance of the FHIR Resource which this type names, using
+// the provided options to toggle.
+//
+// This function will panic if this does not name a valid Resource Type.
+func (t Type) New(opts ...Option) fhir.Resource {
+	return New(t, opts...)
+}
+
+// URI returns a URI object containing the resource type name.
+func (t Type) URI() *dtpb.Uri {
+	return &dtpb.Uri{
+		Value: string(t),
+	}
+}
+
+// StructureDefinitionURI returns an absolute URI to the structure-definition
+// URL.
+func (t Type) StructureDefinitionURI() *dtpb.Uri {
+	const baseURL = "http://hl7.org/fhir/StructureDefinition"
+
+	return &dtpb.Uri{
+		Value: fmt.Sprintf("%v/%v", baseURL, t),
+	}
+}
+
+// IsType queries whether the given string names a Resource type.
+//
+// Note: This is case-sensitive, and expects CamelCase, jus as the FHIR spec uses.
+func IsType(name string) bool {
+	_, ok := protofields.Resources[name]
+	return ok
+}
diff --git a/internal/resource/type_test.go b/internal/resource/type_test.go
new file mode 100644
index 0000000..432e9be
--- /dev/null
+++ b/internal/resource/type_test.go
@@ -0,0 +1,127 @@
+package resource_test
+
+import (
+	"errors"
+	"reflect"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/fhirtest"
+	"github.com/verily-src/fhirpath-go/internal/resource"
+)
+
+func TestTypeOf_ReturnsType(t *testing.T) {
+	for name, res := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			want := string(res.ProtoReflect().Descriptor().Name())
+
+			got := resource.TypeOf(res)
+
+			if !cmp.Equal(string(got), want) {
+				t.Errorf("TypeOf(%v): got '%v', want '%v'", name, got, want)
+			}
+		})
+	}
+}
+
+func TestTypeOf_NilInput_Panics(t *testing.T) {
+	defer func() { _ = recover() }()
+
+	resource.TypeOf(nil)
+
+	t.Errorf("TypeOf: expected panic")
+}
+
+func TestNewType_ValidTypeName_ReturnsType(t *testing.T) {
+	for name, res := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			want := resource.TypeOf(res)
+
+			got, err := resource.NewType(name)
+			if err != nil {
+				t.Fatalf("NewType: got unexpected err '%v' from NewType", err)
+			}
+
+			if !cmp.Equal(got, want) {
+				t.Errorf("NewType(%v): got %v, want %v", name, got, want)
+			}
+		})
+	}
+}
+
+func TestNewType_InvalidTypeName_ReturnsErrBadType(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value string
+	}{
+		{"Empty", ""},
+		{"NotAnElement", "Bad-Element"},
+		{"AnonymousElement", "Bundle_Entry"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := resource.NewType(tc.value)
+
+			if got, want := err, resource.ErrBadType; !errors.Is(got, want) {
+				t.Errorf("NewType(%v): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestIsType_ValidTypeName_ReturnsTrue(t *testing.T) {
+	for name := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			got := resource.IsType(name)
+
+			if got != true {
+				t.Errorf("IsType(%v): got %v, want true", name, got)
+			}
+		})
+	}
+}
+
+func TestIsType_InvalidTypeName_ReturnsFalse(t *testing.T) {
+	testCases := []struct {
+		name  string
+		value string
+	}{
+		{"Empty", ""},
+		{"NotAResource", "ContainedResource"},
+		{"Element", "String"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := resource.IsType(tc.value)
+
+			if got != false {
+				t.Errorf("IsType(%v): got %v, want false", tc.name, got)
+			}
+		})
+	}
+}
+
+func TestTypeNew_ReturnsElementOfType(t *testing.T) {
+	for name, elem := range fhirtest.Resources {
+		t.Run(name, func(t *testing.T) {
+			ty := resource.TypeOf(elem)
+			want := reflect.TypeOf(elem)
+
+			got := ty.New()
+
+			if reflect.TypeOf(got) != want {
+				t.Errorf("Type.New: got %v, want %v", got, want)
+			}
+		})
+	}
+}
+
+func TestTypeNew_Unspecified_ReturnsNil(t *testing.T) {
+	defer func() { _ = recover() }()
+
+	resource.Type("").New()
+
+	t.Errorf("Type.New: expected panic")
+}
diff --git a/internal/resourceopt/resourceopt.go b/internal/resourceopt/resourceopt.go
new file mode 100644
index 0000000..d82f97a
--- /dev/null
+++ b/internal/resourceopt/resourceopt.go
@@ -0,0 +1,83 @@
+/*
+Package resourceopt is an internal package that provides helper utilities
+for forming resource-options in resource packages.
+*/
+package resourceopt
+
+import (
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"github.com/verily-src/fhirpath-go/internal/protofields"
+	"google.golang.org/protobuf/proto"
+)
+
+// Option is the definition of a resource Option used for creating and updating
+// FHIR Resources.
+type Option interface {
+	update(fhir.Resource)
+}
+
+// ApplyOptions applies the specified options to the input resource.
+//
+// This function is defined here due to the Option interface providing an
+// unexported field. This is needed so that the other packages using this can
+// accumulate the options without having access to the unexported call.
+func ApplyOptions[T fhir.Resource, O Option](r T, opts ...O) T {
+	for _, opt := range opts {
+		opt.update(r)
+	}
+	return r
+}
+
+// WithProtoField is a resource Option that sets the specified 'field' in the proto
+// to the values. If values is empty, the field is cleared. If values is
+// not 1, and a field is not repeated, this functon will panic.
+//
+// Note: This is an internal function intended to be used to form generic
+// resource options that will work with all FHIR resources.
+func WithProtoField[T proto.Message](fieldName string, values ...T) Option {
+	// SAFETY:
+	//   MustConvert cannot fail here, since the 'T' constraint above ensures that
+	//   all inputs will be valid proto.Message types.
+	return withProtoFieldImpl(fieldName, slices.MustConvert[proto.Message](values)...)
+}
+
+func withProtoFieldImpl(fieldName string, values ...proto.Message) Option {
+	return WithCallback(func(r fhir.Resource) {
+		protofields.Overwrite(r, fieldName, values...)
+	})
+}
+
+// IncludeProtoField is a resource Option that appends the specified entries to
+// the given 'field' in the proto. This function will panic if the given field
+// is not a repeated field in the proto.
+//
+// Note: This is an internal function intended to be used to form generic
+// resource options that will work with all FHIR resources.
+func IncludeProtoField[T proto.Message](fieldName string, values ...T) Option {
+	// SAFETY:
+	//   MustConvert cannot fail here, since the 'T' constraint above ensures that
+	//   all inputs will be valid proto.Message types.
+	return includeProtoFieldImpl(fieldName, slices.MustConvert[proto.Message](values)...)
+}
+
+func includeProtoFieldImpl(fieldName string, values ...proto.Message) Option {
+	return WithCallback(func(r fhir.Resource) {
+		protofields.AppendList(r, fieldName, values...)
+	})
+}
+
+// WithCallback returns a resource Option that simply passes the resource being
+// created back into the specified callback. This exists to be built into
+// larger, more strongly-typed options.
+func WithCallback[T fhir.Resource](callback func(T)) Option {
+	return &callbackOpt[T]{callback}
+}
+
+type callbackOpt[T fhir.Resource] struct {
+	callback func(T)
+}
+
+func (o *callbackOpt[T]) update(r fhir.Resource) {
+	o.callback(r.(T))
+}
diff --git a/internal/slices/slices.go b/internal/slices/slices.go
new file mode 100644
index 0000000..4e0e26f
--- /dev/null
+++ b/internal/slices/slices.go
@@ -0,0 +1,219 @@
+// Package slices provides helpful functions
+// for searching, sorting and manipulating slices.
+package slices
+
+import (
+	"fmt"
+	"reflect"
+	"sort"
+
+	"golang.org/x/exp/constraints"
+	"google.golang.org/protobuf/proto"
+)
+
+// IsIdentical checks if two slices refer to the same underlying slice object.
+func IsIdentical[S2 ~[]T, S1 ~[]T, T any](lhs S1, rhs S2) bool {
+	if len(lhs) != len(rhs) {
+		return false
+	}
+	if len(lhs) == 0 {
+		return true
+	}
+	return &lhs[0] == &rhs[0]
+}
+
+// All returns true if every element in a slice satisfies the
+// given condition. Defaults to true for an empty slice.
+func All[S ~[]T, T any](vals S, comp func(T) bool) bool {
+	for _, val := range vals {
+		if !comp(val) {
+			return false
+		}
+	}
+	return true
+}
+
+// Any returns true if at least one element in a slice satisfies
+// the given condition. Defaults to false for an empty slice.
+func Any[S ~[]T, T any](vals S, comp func(T) bool) bool {
+	for _, val := range vals {
+		if comp(val) {
+			return true
+		}
+	}
+	return false
+}
+
+// Filter returns a subset of the original slice, consisting of all the
+// elements in the original slice which satisfy the given condition.
+func Filter[S ~[]T, T any](vals S, comp func(T) bool) S {
+	matches := make(S, 0)
+	for _, val := range vals {
+		if comp(val) {
+			matches = append(matches, val)
+		}
+	}
+	return matches
+}
+
+// Count returns the number of elements in a slice that match the given
+// condition.
+func Count[S ~[]T, T any](vals S, comp func(T) bool) int {
+	matches := 0
+	for _, val := range vals {
+		if comp(val) {
+			matches += 1
+		}
+	}
+	return matches
+}
+
+// Includes returns true if a slice contains the target value.
+func Includes[S ~[]T, T any](vals S, target T) bool {
+	return IndexOf(vals, target) > -1
+}
+
+// IndexOf returns the index of the target value in a slice.
+// Returns the first index found, and -1 if the value is not found.
+func IndexOf[S ~[]T, T any](vals S, target T) int {
+	_, isProto := any(target).(proto.Message)
+	for index, val := range vals {
+		if isProto && proto.Equal(any(val).(proto.Message), any(target).(proto.Message)) {
+			return index
+		} else if reflect.DeepEqual(val, target) {
+			return index
+		}
+	}
+	return -1
+}
+
+// Join returns a string consisting of all elements in a slice,
+// separated by the given delimiter.
+func Join[S ~[]T, T any](vals S, delimiter string) string {
+	if len(vals) == 0 {
+		return ""
+	}
+	combined := fmt.Sprintf("%v", vals[0])
+	for _, val := range vals[1:] {
+		combined += fmt.Sprintf("%s%v", delimiter, val)
+	}
+	return combined
+}
+
+// Map returns a new slice with the elements of the original
+// slice transformed according to the provided function.
+func Map[T any, U any](vals []T, mapper func(T) U) []U {
+	mapped := make([]U, 0, len(vals))
+	for _, val := range vals {
+		mapped = append(mapped, mapper(val))
+	}
+	return mapped
+}
+
+// Reverse performs an in-place reversal of the elements in a slice.
+// Performance testing has not been done to compare to alternatives.
+func Reverse[S ~[]T, T any](t S) {
+	sort.SliceStable(t, func(i, j int) bool {
+		return i > j
+	})
+}
+
+// Sort performs an in-place sort of a slice.
+// Performance testing has not been done to compare to alternatives.
+func Sort[S ~[]T, T constraints.Ordered](vals S) {
+	sort.SliceStable(vals, func(i, j int) bool {
+		return vals[i] < vals[j]
+	})
+}
+
+// Convert returns an array of objects of type To from an array of objects of
+// type From. If any conversion fails, this will return an error.
+//
+// This function, along with the non-failing `MustConvert` equivalent, are
+// useful for converting one array type []T to an array of another type []U,
+// which requires manual iteration in Go. This provides a more convenient
+// mechanism for casting between arrays of interfaces and concrete types,
+// provided all elements can safely be casted to.
+//
+// Note: this function only works with language-level type-casts (e.g. `t.(U)`).
+// For converting between concrete struct types, use `Map`.
+func Convert[To any, S ~[]From, From any](from S) ([]To, error) {
+	result := make([]To, 0, len(from))
+	for _, val := range from {
+		if to, ok := any(val).(To); ok {
+			result = append(result, to)
+		} else {
+			return nil, fmt.Errorf("slices.Convert[%T](from): unable to convert from %T", to, from)
+		}
+	}
+	return result, nil
+}
+
+// MustConvert returns an array of To objects by converting every element in
+// From to To. This will panic on failure to convert.
+//
+// This function, along with the failing `Convert` equivalent, are
+// useful for converting one array type []T to an array of another type []U,
+// which requires manual iteration in Go. This provides a more convenient
+// mechanism for casting between arrays of interfaces and concrete types,
+// provided all elements can safely be casted to.
+//
+// This function in particular should only be used if the 'To' type is guaranteed
+// to always be convertible -- such as converting a concrete type to its base
+// interface, or to any.
+func MustConvert[To any, S ~[]From, From any](from S) []To {
+	return Map(from, func(from From) To { return any(from).(To) })
+}
+
+// Transform performs an in-place transformation of a slice.
+func Transform[S ~[]T, T any](vals S, transform func(T) T) {
+	for i := range vals {
+		vals[i] = transform(vals[i])
+	}
+}
+
+// IsUnique returns true if all elements of a slice are unique.
+func IsUnique[T comparable](vals []T) bool {
+	seen := map[T]struct{}{}
+	for _, val := range vals {
+		if _, found := seen[val]; found {
+			return false
+		}
+		seen[val] = struct{}{}
+	}
+	return true
+}
+
+// Unique returns a new slice of the unique elements in a given slice.
+// It keeps the first instance of any duplicate values and ignores any
+// subsequent instances of the value.
+func Unique[S ~[]T, T comparable](vals S) S {
+	set := map[T]struct{}{}
+	uniqueSlice := make(S, 0)
+	for _, val := range vals {
+		if _, found := set[val]; !found {
+			set[val] = struct{}{}
+			uniqueSlice = append(uniqueSlice, val)
+		}
+	}
+	return uniqueSlice
+}
+
+// Chunk divides the given slice into multiple new slices, each at most
+// having lengths of the given size. The last chunk may have a smaller
+// length if the length of vals is not evenly divisible by size.
+func Chunk[T any](vals []T, size int) [][]T {
+	var chunks [][]T
+	for i := 0; i < len(vals); {
+		chunkLen := size
+		remainingLen := len(vals[i:])
+		if remainingLen < size {
+			chunkLen = remainingLen
+		}
+		chunk := make([]T, chunkLen)
+		copy(chunk, vals[i:i+chunkLen])
+		chunks = append(chunks, chunk)
+		i += chunkLen
+	}
+	return chunks
+}
diff --git a/internal/slices/slices_example_test.go b/internal/slices/slices_example_test.go
new file mode 100644
index 0000000..90a6f05
--- /dev/null
+++ b/internal/slices/slices_example_test.go
@@ -0,0 +1,220 @@
+package slices_test
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/verily-src/fhirpath-go/internal/slices"
+)
+
+func ExampleAll() {
+	greaterThanFive := func(i int) bool { return i > 5 }
+	arr := []int{1, 2, 3}
+	arr2 := []int{7, 8, 9}
+
+	all := slices.All(arr, greaterThanFive)
+	fmt.Printf("arr all > 5: %v\n", all)
+
+	all = slices.All(arr2, greaterThanFive)
+	fmt.Printf("arr2 all > 5: %v\n", all)
+
+	// Output:
+	// arr all > 5: false
+	// arr2 all > 5: true
+}
+
+func ExampleAny() {
+	isEven := func(i int) bool { return i%2 == 0 }
+	oddArr := []int{1, 3, 5, 7}
+	evenArr := []int{1, 2, 9}
+
+	any := slices.Any(oddArr, isEven)
+	fmt.Printf("oddArr any even: %v\n", any)
+
+	any = slices.Any(evenArr, isEven)
+	fmt.Printf("evenArr any even: %v\n", any)
+
+	// Output:
+	// oddArr any even: false
+	// evenArr any even: true
+}
+
+func ExampleFilter() {
+	isNegative := func(i int) bool { return i < 0 }
+	arr := []int{1, 3, -9, -5, 6}
+
+	filtered := slices.Filter(arr, isNegative)
+	fmt.Printf("filtered arr: %v\n", filtered)
+
+	// Output:
+	// filtered arr: [-9 -5]
+}
+
+func ExampleCount() {
+	isNegative := func(i int) bool { return i < 0 }
+	arr := []int{1, 3, -9, -5, 6}
+
+	countNeg := slices.Count(arr, isNegative)
+	fmt.Printf("count of negative nums: %v\n", countNeg)
+
+	// Output:
+	// count of negative nums: 2
+}
+
+func ExampleIncludes() {
+	arr := []string{"a", "c", "e"}
+
+	includesB := slices.Includes(arr, "b")
+	fmt.Printf("arr includes b: %v\n", includesB)
+
+	includesA := slices.Includes(arr, "a")
+	fmt.Printf("arr includes a: %v\n", includesA)
+
+	// Output:
+	// arr includes b: false
+	// arr includes a: true
+}
+
+func ExampleIndexOf() {
+	letters := []string{"w", "o", "o", "a", "h"}
+
+	indexM := slices.IndexOf(letters, "m")
+	fmt.Printf("index of m: %v\n", indexM)
+
+	indexO := slices.IndexOf(letters, "o")
+	fmt.Printf("index of o: %v\n", indexO)
+
+	// Output:
+	// index of m: -1
+	// index of o: 1
+}
+
+func ExampleJoin() {
+	arr := []int{0, 0}
+
+	joined := slices.Join(arr, ".")
+	fmt.Printf("joined string: %v\n", joined)
+
+	// Output:
+	// joined string: 0.0
+}
+
+func ExampleMap() {
+	arr := []int{1, 2, 3}
+	mapper := func(i int) string { return fmt.Sprintf("%v_%v", i, i) }
+
+	mapped := slices.Map(arr, mapper)
+	fmt.Printf("mapped arr: %v\n", mapped)
+
+	// Output:
+	// mapped arr: [1_1 2_2 3_3]
+}
+
+func ExampleReverse() {
+	arr := []int{1, 2, 3, 3, 4}
+
+	slices.Reverse(arr)
+	fmt.Printf("reversed arr: %v\n", arr)
+
+	// Output:
+	// reversed arr: [4 3 3 2 1]
+}
+
+func ExampleSort() {
+	arr := []int{1, -2, 3, -4, 5}
+
+	slices.Sort(arr)
+	fmt.Printf("sorted arr: %v\n", arr)
+
+	// Output:
+	// sorted arr: [-4 -2 1 3 5]
+}
+
+func ExampleConvert_good_conversion() {
+	arr := []any{1, 2, 3}
+
+	intArr, err := slices.Convert[int](arr)
+	if err != nil {
+		fmt.Printf("Unable to convert slice!")
+	} else {
+		fmt.Printf("int array: %v", intArr)
+	}
+
+	// Output:
+	// int array: [1 2 3]
+}
+
+func ExampleConvert_bad_conversion() {
+	arr := []any{1, 2, 3}
+
+	intArr, err := slices.Convert[string](arr)
+	if err != nil {
+		fmt.Printf("Unable to convert slice!")
+	} else {
+		fmt.Printf("int array: %v", intArr)
+	}
+
+	// Output:
+	// Unable to convert slice!
+}
+
+func ExampleMustConvert() {
+	// Easy mechanism to cast arrays to interfaces and back
+	arr := []any{1, 2, 3}
+
+	intArr := slices.MustConvert[int](arr)
+	fmt.Printf("int array: %v", intArr)
+
+	// Output:
+	// int array: [1 2 3]
+}
+
+func ExampleTransform() {
+	arr := []string{"\t hello\n", "\t world\n"}
+
+	slices.Transform(arr, strings.TrimSpace)
+	fmt.Printf("transformed arr: %v\n", arr)
+
+	// Output:
+	// transformed arr: [hello world]
+}
+
+func ExampleIsUnique() {
+	strArr := []string{"hello", "world"}
+	intArr := []int{1, 2, 3, 2, 1}
+
+	strUnique := slices.IsUnique(strArr)
+	fmt.Printf("str arr unique: %v\n", strUnique)
+
+	intUnique := slices.IsUnique(intArr)
+	fmt.Printf("int arr unique: %v\n", intUnique)
+
+	// Output:
+	// str arr unique: true
+	// int arr unique: false
+}
+
+func ExampleUnique() {
+	arr := []int{1, 2, 3, 2, 1}
+
+	uniqueArr := slices.Unique(arr)
+	fmt.Printf("unique arr: %v\n", uniqueArr)
+
+	// Output:
+	// unique arr: [1 2 3]
+}
+
+func ExampleChunk() {
+	strArr := []string{"a", "b", "c", "d", "e"}
+	strChunks := slices.Chunk(strArr, 5)
+	fmt.Printf("str arr chunks: %v\n", strChunks)
+
+	intArr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
+	intChunks := slices.Chunk(intArr, 3)
+	fmt.Printf("int arr chunks: %v\n", intChunks)
+
+	// Output:
+	// str arr chunks: [[a b c d e]]
+	// int arr chunks: [[1 2 3] [4 5 6] [7 8 9] [10]]
+
+}
diff --git a/internal/slices/slices_test.go b/internal/slices/slices_test.go
new file mode 100644
index 0000000..c080d3c
--- /dev/null
+++ b/internal/slices/slices_test.go
@@ -0,0 +1,707 @@
+package slices_test
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/verily-src/fhirpath-go/internal/slices"
+	"github.com/verily-src/fhirpath-go/internal/fhir"
+	"golang.org/x/exp/constraints"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/testing/protocmp"
+)
+
+type StrongSlice[T any] []T
+
+type arrTestCase[T constraints.Ordered] struct {
+	name     string
+	arr      StrongSlice[T]
+	expected StrongSlice[T]
+}
+
+type boolTestCase[T any] struct {
+	name     string
+	arr      StrongSlice[T]
+	expected bool
+}
+
+type includesTestCase[T any] struct {
+	name     string
+	arr      StrongSlice[T]
+	target   T
+	expected bool
+}
+
+type indexTestCase[T any] struct {
+	name     string
+	arr      StrongSlice[T]
+	target   T
+	expected int
+}
+
+type intTestCase[T any] struct {
+	name     string
+	arr      StrongSlice[T]
+	expected int
+}
+
+type mapTestCase[T any, U any] struct {
+	name     string
+	arr      StrongSlice[T]
+	expected []U
+}
+
+type stringTestCase[T any] struct {
+	name      string
+	arr       StrongSlice[T]
+	delimiter string
+	expected  string
+}
+
+func TestIsIdentical(t *testing.T) {
+	type s1 []int
+	type s2 []int
+
+	base := []int{1, 2, 3, 4}
+	testCases := []struct {
+		name string
+		lhs  s1
+		rhs  s2
+		want bool
+	}{
+		{
+			name: "Same reference",
+			lhs:  s1(base),
+			rhs:  s2(base),
+			want: true,
+		}, {
+			name: "Different size",
+			lhs:  s1(base),
+			rhs:  append(s2(base), 5),
+			want: false,
+		}, {
+			name: "Same size, empty",
+			lhs:  nil,
+			rhs:  nil,
+			want: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := slices.IsIdentical(tc.lhs, tc.rhs)
+
+			if got != tc.want {
+				t.Errorf("IsIdentical(%v): got %v, want %v", tc.name, got, tc.want)
+			}
+		})
+	}
+}
+
+func TestAll(t *testing.T) {
+	lengthFive := func(a string) bool { return len(a) == 5 }
+	testCases := []boolTestCase[string]{
+		{
+			"empty slice",
+			[]string{},
+			true,
+		}, {
+			"no matching element",
+			[]string{"cat", "dog"},
+			false,
+		}, {
+			"some matching elements",
+			[]string{"apple", "orange", "banana"},
+			false,
+		}, {
+			"all matching elements",
+			[]string{"daisy", "tulip"},
+			true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			result := slices.All(tc.arr, lengthFive)
+			if got, want := result, tc.expected; got != want {
+				t.Errorf("All(%s) want = %v, got = %v", tc.name, want, got)
+			}
+		})
+	}
+}
+
+func TestAny(t *testing.T) {
+	multOfFour := func(a int) bool { return a%4 == 0 }
+	testCases := []boolTestCase[int]{
+		{
+			"empty slice",
+			[]int{},
+			false,
+		}, {
+			"no matching element",
+			[]int{1, 2, 3},
+			false,
+		}, {
+			"matching element",
+			[]int{1, 2, 3, 4, 5},
+			true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			result := slices.Any(tc.arr, multOfFour)
+			if got, want := result, tc.expected; got != want {
+				t.Errorf("Any(%s) want = %v, got = %v", tc.name, want, got)
+			}
+		})
+	}
+}
+
+func TestFilter(t *testing.T) {
+	lessThanPi := func(a float32) bool { return a < 3.14159 }
+	testCases := []arrTestCase[float32]{
+		{
+			"empty slice",
+			[]float32{},
+			[]float32{},
+		}, {
+			"no matching elements",
+			[]float32{4.1, 9.12},
+			[]float32{},
+		}, {
+			"matching elements",
+			[]float32{1.0, 2.718, 5.56},
+			[]float32{1.0, 2.718},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			result := slices.Filter(tc.arr, lessThanPi)
+			got, want := result, tc.expected
+			if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Filter(%s) mismatch (-want, +got):\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestCount(t *testing.T) {
+	lessThanPi := func(a float32) bool { return a < 3.14159 }
+	testCases := []intTestCase[float32]{
+		{
+			"empty slice",
+			[]float32{},
+			0,
+		}, {
+			"no matching elements",
+			[]float32{4.1, 9.12},
+			0,
+		}, {
+			"matching elements",
+			[]float32{1.0, 2.718, 5.56, -3.0},
+			3,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			result := slices.Count(tc.arr, lessThanPi)
+			got, want := result, tc.expected
+			if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Count(%s) mismatch (-want, +got):\n%s", tc.name, diff)
+			}
+
+			// Count() should equal len(Filter())
+			filtered := slices.Filter(tc.arr, lessThanPi)
+			got = len(filtered)
+			if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Count / len(Filter(%s)) mismatch (-want, +got):\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestIncludes(t *testing.T) {
+	testCases := []includesTestCase[int]{
+		{
+			"empty slice",
+			[]int{},
+			1,
+			false,
+		}, {
+			"no matching elements",
+			[]int{0, 1},
+			2,
+			false,
+		}, {
+			"found element",
+			[]int{5, 7, 8},
+			7,
+			true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			result := slices.Includes(tc.arr, tc.target)
+			if got, want := result, tc.expected; got != want {
+				t.Errorf("Includes(%s) want = %v, got = %v", tc.name, want, got)
+			}
+		})
+	}
+}
+
+func TestIndexOf(t *testing.T) {
+	firstCoding := fhir.CodeableConcept("", fhir.Coding("test-system", "1"))
+	secondCoding := fhir.CodeableConcept("", fhir.Coding("test-system", "2"))
+	firstProto := proto.Message(firstCoding)
+	secondProto := proto.Message(secondCoding)
+	testCases := []indexTestCase[any]{
+		{
+			"empty slice",
+			StrongSlice[any]{},
+			firstCoding,
+			-1,
+		}, {
+			"no matching elements",
+			StrongSlice[any]{secondCoding},
+			firstCoding,
+			-1,
+		}, {
+			"found element - struct pointer",
+			StrongSlice[any]{firstCoding, secondCoding},
+			secondCoding,
+			1,
+		}, {
+			"found element - proto pointer",
+			StrongSlice[any]{firstProto, secondProto},
+			secondProto,
+			1,
+		}, {
+			"multiple matching elements",
+			StrongSlice[any]{secondCoding, firstCoding, firstCoding},
+			firstCoding,
+			1,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			result := slices.IndexOf(tc.arr, tc.target)
+			if got, want := result, tc.expected; got != want {
+				t.Errorf("IndexOf(%s) want = %v, got = %v", tc.name, want, got)
+			}
+		})
+	}
+}
+
+func TestJoin(t *testing.T) {
+	testCases := []stringTestCase[uint]{
+		{
+			"empty slice",
+			StrongSlice[uint]{},
+			",",
+			"",
+		}, {
+			"one element",
+			StrongSlice[uint]{0},
+			",",
+			"0",
+		}, {
+			"multiple elements no delimiter",
+			StrongSlice[uint]{0, 3},
+			"",
+			"03",
+		}, {
+			"multiple elements with delimiter",
+			StrongSlice[uint]{0, 3, 7, 8},
+			",",
+			"0,3,7,8",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			result := slices.Join(tc.arr, tc.delimiter)
+			if got, want := result, tc.expected; got != want {
+				t.Errorf("Join(%s) want = %v, got = %v", tc.name, want, got)
+			}
+		})
+	}
+}
+
+func TestMap(t *testing.T) {
+	strLen := func(s string) int { return len(s) }
+	testCases := []mapTestCase[string, int]{
+		{
+			"empty slice",
+			StrongSlice[string]{},
+			[]int{},
+		}, {
+			"one element",
+			StrongSlice[string]{"four"},
+			[]int{4},
+		}, {
+			"multiple elements",
+			StrongSlice[string]{"one", "two", "three"},
+			[]int{3, 3, 5},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			result := slices.Map(tc.arr, strLen)
+			got, want := result, tc.expected
+			if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Map(%s) mismatch (-want, +got):\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestReverse(t *testing.T) {
+	testCases := []arrTestCase[int]{
+		{
+			"empty slice",
+			StrongSlice[int]{},
+			StrongSlice[int]{},
+		}, {
+			"one element",
+			StrongSlice[int]{4},
+			StrongSlice[int]{4},
+		}, {
+			"multiple elements",
+			StrongSlice[int]{1, 2, 3, 4, 5},
+			StrongSlice[int]{5, 4, 3, 2, 1},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			slices.Reverse(tc.arr)
+			got, want := tc.arr, tc.expected
+			if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Reverse(%s) mismatch (-want, +got):\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestSort(t *testing.T) {
+	testCases := []arrTestCase[int]{
+		{
+			"empty slice",
+			StrongSlice[int]{},
+			StrongSlice[int]{},
+		}, {
+			"one element",
+			StrongSlice[int]{4},
+			StrongSlice[int]{4},
+		}, {
+			"multiple elements",
+			StrongSlice[int]{5, 4, 3, 2, 1},
+			StrongSlice[int]{1, 2, 3, 4, 5},
+		}, {
+			"duplicate values",
+			StrongSlice[int]{5, 4, 3, 2, 5, 1, 3},
+			StrongSlice[int]{1, 2, 3, 3, 4, 5, 5},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			slices.Sort(tc.arr)
+			got, want := tc.arr, tc.expected
+			if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Sort(%s) mismatch (-want, +got):\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+type testType int
+
+func (t testType) String() string {
+	return fmt.Sprintf("%v", int(t))
+}
+
+type stringer interface {
+	String() string
+}
+
+func TestConvert_Upcast_ReturnsConvertedType(t *testing.T) {
+	input := StrongSlice[testType]{1, 2, 3}
+	want := []stringer{input[0], input[1], input[2]}
+
+	got, err := slices.Convert[stringer](input)
+
+	if err != nil {
+		t.Fatalf("Convert: got unexpected err %v", err)
+	}
+	if !cmp.Equal(got, want) {
+		t.Errorf("Convert: got %v, want %v", got, want)
+	}
+}
+
+func TestConvert_Downcast_ReturnsConvertedType(t *testing.T) {
+	input := StrongSlice[testType]{1, 2, 3}
+	want := []any{input[0], input[1], input[2]}
+
+	got, err := slices.Convert[any](input)
+
+	if err != nil {
+		t.Fatalf("Convert: got unexpected err %v", err)
+	}
+	if !cmp.Equal(got, want) {
+		t.Errorf("Convert: got %v, want %v", got, want)
+	}
+}
+
+func TestConvert_Identity_ReturnsSelf(t *testing.T) {
+	want := []int{1, 2, 3}
+
+	got, err := slices.Convert[int](want)
+
+	if err != nil {
+		t.Fatalf("Convert: got unexpected err %v", err)
+	}
+	if !cmp.Equal(got, want) {
+		t.Errorf("Convert: got %v, want %v", got, want)
+	}
+}
+
+func TestConvert_InvalidCast_ReturnsErr(t *testing.T) {
+	want := StrongSlice[int]{1, 2, 3}
+
+	_, err := slices.Convert[string](want)
+
+	if err == nil {
+		t.Fatalf("Convert: expected err, got nil")
+	}
+}
+
+func TestMustConvert_OnSuccess_ReturnsConversion(t *testing.T) {
+	want := []int{4, 5, 6}
+	input := StrongSlice[any]{4, 5, 6}
+
+	result := slices.MustConvert[int](input)
+
+	if got := result; !cmp.Equal(got, want) {
+		t.Errorf("MustConvert: got %v, want %v", got, want)
+	}
+}
+
+func TestMustConvert_OnBadConversion_Panics(t *testing.T) {
+	defer func() {
+		if r := recover(); r == nil {
+			t.Errorf("The code did not panic")
+		}
+	}()
+	input := []any{4, 5, 6}
+
+	slices.MustConvert[string](input)
+}
+
+func TestTransform(t *testing.T) {
+	testCases := []struct {
+		name      string
+		slice     StrongSlice[string]
+		transform func(string) string
+		want      StrongSlice[string]
+	}{
+		{
+			name:      "append",
+			slice:     []string{"hello", "world"},
+			transform: func(s string) string { return "_" + s + "_" },
+			want:      []string{"_hello_", "_world_"},
+		}, {
+			name:      "trim",
+			slice:     []string{" \thello ", "\t world\n"},
+			transform: strings.TrimSpace,
+			want:      []string{"hello", "world"},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			slices.Transform(tc.slice, tc.transform)
+
+			got, want := tc.slice, tc.want
+			if diff := cmp.Diff(want, got); diff != "" {
+				t.Errorf("Transform(%s) mismatch (-want, +got):\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestIsUnique_SimpleType(t *testing.T) {
+	testCases := []struct {
+		name  string
+		slice StrongSlice[string]
+		want  bool
+	}{
+		{
+			name:  "unique",
+			slice: []string{"hello", "world"},
+			want:  true,
+		}, {
+			name:  "duplicate",
+			slice: []string{"hello", "hello", "world"},
+			want:  false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			unique := slices.IsUnique(tc.slice)
+
+			if got, want := unique, tc.want; got != want {
+				t.Errorf("IsUnique(%s): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestIsUnique_Proto(t *testing.T) {
+	firstCoding := fhir.CodeableConcept("", fhir.Coding("test-system", "1"))
+	secondCoding := fhir.CodeableConcept("", fhir.Coding("test-system", "2"))
+	firstProto := proto.Message(firstCoding)
+	secondProto := proto.Message(secondCoding)
+
+	testCases := []struct {
+		name  string
+		slice StrongSlice[protoreflect.ProtoMessage]
+		want  bool
+	}{
+		{
+			name:  "unique",
+			slice: []protoreflect.ProtoMessage{firstProto, secondProto},
+			want:  true,
+		}, {
+			name:  "duplicate",
+			slice: []protoreflect.ProtoMessage{secondProto, firstProto, secondProto},
+			want:  false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			unique := slices.IsUnique(tc.slice)
+
+			if got, want := unique, tc.want; got != want {
+				t.Errorf("IsUnique_Proto(%s): got %v, want %v", tc.name, got, want)
+			}
+		})
+	}
+}
+
+func TestUnique_SimpleType(t *testing.T) {
+	testCases := []struct {
+		name  string
+		slice StrongSlice[string]
+		want  StrongSlice[string]
+	}{
+		{
+			name:  "already unique",
+			slice: []string{"hello", "world"},
+			want:  []string{"hello", "world"},
+		}, {
+			name:  "duplicates removed",
+			slice: []string{"hello", "hello", "world"},
+			want:  []string{"hello", "world"},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			uniqueSlice := slices.Unique(tc.slice)
+
+			got, want := uniqueSlice, tc.want
+			if diff := cmp.Diff(want, got); diff != "" {
+				t.Errorf("Unique(%s) mismatch (-want, +got):\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestUnique_Proto(t *testing.T) {
+	firstCoding := fhir.CodeableConcept("", fhir.Coding("test-system", "1"))
+	secondCoding := fhir.CodeableConcept("", fhir.Coding("test-system", "2"))
+	firstProto := proto.Message(firstCoding)
+	secondProto := proto.Message(secondCoding)
+
+	testCases := []struct {
+		name  string
+		slice StrongSlice[protoreflect.ProtoMessage]
+		want  StrongSlice[protoreflect.ProtoMessage]
+	}{
+		{
+			name:  "already unique",
+			slice: []protoreflect.ProtoMessage{firstProto, secondProto},
+			want:  []protoreflect.ProtoMessage{firstProto, secondProto},
+		}, {
+			name:  "duplicates removed",
+			slice: []protoreflect.ProtoMessage{firstProto, firstProto, secondProto},
+			want:  []protoreflect.ProtoMessage{firstProto, secondProto},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			uniqueSlice := slices.Unique(tc.slice)
+
+			got, want := uniqueSlice, tc.want
+			if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("Unique(%s) mismatch (-want, +got):\n%s", tc.name, diff)
+			}
+		})
+	}
+}
+
+func TestChunk(t *testing.T) {
+	testCases := []struct {
+		name string
+		vals []int
+		size int
+		want [][]int
+	}{
+		{
+			name: "less than 1 chunk size",
+			vals: []int{1, 2, 3, 4},
+			size: 5,
+			want: [][]int{{1, 2, 3, 4}},
+		},
+		{
+			name: "exactly 1 chunk size",
+			vals: []int{1, 2, 3, 4, 5},
+			size: 5,
+			want: [][]int{{1, 2, 3, 4, 5}},
+		},
+		{
+			name: "over 1 chunk size",
+			vals: []int{1, 2, 3, 4, 5, 6},
+			size: 5,
+			want: [][]int{{1, 2, 3, 4, 5}, {6}},
+		},
+		{
+			name: "4 chunks",
+			vals: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
+			size: 3,
+			want: [][]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10}},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := slices.Chunk(tc.vals, tc.size)
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("Chunk(%s) mismatch (-want, +got):\n%s", tc.name, diff)
+			}
+		})
+	}
+}
diff --git a/internal/stablerand/doc.go b/internal/stablerand/doc.go
new file mode 100644
index 0000000..f20349c
--- /dev/null
+++ b/internal/stablerand/doc.go
@@ -0,0 +1,18 @@
+/*
+Package stablerand is a small helper utility that encapsulates its random engine
+and always uses the same seed value for its randomness.
+
+This ensures reproducibility and stability across executions, giving a
+pseudo-random distribution, but with deterministic predictability. This is
+primarily intended for generating content for tests, which ensures that inputs
+are still pseudo-random, but predictible and consistent across unchanged
+executions.
+
+Functions in this package are thread-safe, although use in threaded contexts
+will remove any guarantees of determinism.
+
+Note: This is primarily used internally for the fhirtest package to implement
+"random" IDs and meta-IDs so that test resources retain the same general
+values across executions.
+*/
+package stablerand
diff --git a/internal/stablerand/rand.go b/internal/stablerand/rand.go
new file mode 100644
index 0000000..7c44b7c
--- /dev/null
+++ b/internal/stablerand/rand.go
@@ -0,0 +1,120 @@
+package stablerand
+
+import (
+	"math/rand"
+	"sync"
+	"time"
+)
+
+var (
+	// stableRand is the random engine used for generating random data in package
+	// fhirtest.
+	stableRand *rand.Rand
+
+	// randMutex provides thread-safety for stableRand, in case any tests are
+	// executed with t.Parallel(). Parallelism will affect the stability of the
+	// randomness, since the generated values will no longer be deterministic once
+	// concurrency is involved; but this doesn't mean the code should fail.
+	randMutex sync.Mutex
+)
+
+const (
+	// randSeed is the seed used for the random engine used in package fhirtest.
+	// This seed is constant so that subsequent test executions will always receive
+	// the same data.
+	randSeed = 0xbadc0ffee
+
+	// alnumAlphabet is a string containing all the upper and lowercase ascii
+	// characters for letters and digits.
+	alnumAlphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+
+	// hexAlphabet is a string containing lowercase hex characters.
+	hexAlphabet = "abcdef0123456789"
+
+	// decAlphabet is a string containing all decimal ascii characters.
+	decAlphabet = "0123456789"
+)
+
+func init() {
+	// Seed the random engine with a static value so that generation is consistent
+	// across test executions, but still produces "unique" values.
+	stableRand = rand.New(rand.NewSource(randSeed))
+}
+
+// Intn returns, as an int, a non-negative pseudo-random number in the half-open
+// interval [0,n). It panics if n <= 0.
+func Intn(n int) int {
+	randMutex.Lock()
+	defer randMutex.Unlock()
+
+	return stableRand.Intn(n)
+}
+
+// Int63n returns, as an int64, a non-negative pseudo-random number in the
+// half-open interval [0,n). It panics if n <= 0.
+func Int63n(n int64) int64 {
+	randMutex.Lock()
+	defer randMutex.Unlock()
+
+	return stableRand.Int63n(n)
+}
+
+// String returns, as a string, a pseudo-random string containing n characters
+// all consisting of values within the supplied alphabet string.
+// It panics if the alphabet string is empty.
+func String(n int, alphabet string) string {
+	if alphabet == "" {
+		panic("No alphabet specified")
+	}
+	randMutex.Lock()
+	defer randMutex.Unlock()
+	b := make([]rune, n)
+	for i := range b {
+		b[i] = rune(alphabet[stableRand.Intn(len(alphabet))])
+	}
+	return string(b)
+}
+
+// AlnumString returns, as a string, a pseudo-random string containing n
+// alphanumeric characters.
+func AlnumString(n int) string {
+	return String(n, alnumAlphabet)
+}
+
+// HexString returns, as a string, a pseudo-random string containing n
+// hex characters.
+func HexString(n int) string {
+	return String(n, hexAlphabet)
+}
+
+// DecString returns, as a string, a pseudo-random string containing n
+// decimal characters.
+func DecString(n int) string {
+	return String(n, decAlphabet)
+}
+
+// Time returns, as a time.Time object, a pseudo-random time starting with the
+// base time, and adding a random amount between the half-open interval
+// [0, variation) to the time. It panics if variation is negative.
+func Time(base time.Time, variation time.Duration) time.Time {
+	randMutex.Lock()
+	defer randMutex.Unlock()
+
+	offset := time.Duration(stableRand.Int63n(int64(variation)))
+	base.Add(offset)
+	return base
+}
+
+// OneOf returns, as a T object, a pseudo-randomly selected value from args.
+// It panics if args is empty.
+func OneOf[T any](args ...T) T {
+	if len(args) == 0 {
+		panic("No arguments specified to OneOf")
+	}
+	randMutex.Lock()
+	defer randMutex.Unlock()
+
+	i := stableRand.Intn(len(args))
+
+	return args[i]
+}
diff --git a/internal/units/doc.go b/internal/units/doc.go
new file mode 100644
index 0000000..6b4c9e9
--- /dev/null
+++ b/internal/units/doc.go
@@ -0,0 +1,5 @@
+/*
+Package units provides basic unit constants that are used for various FHIR
+Quantity types.
+*/
+package units
diff --git a/internal/units/time.go b/internal/units/time.go
new file mode 100644
index 0000000..7784b1e
--- /dev/null
+++ b/internal/units/time.go
@@ -0,0 +1,88 @@
+package units
+
+import "fmt"
+
+// Time is a unit of measure for measuring the passage of time.
+type Time int
+
+const (
+	// Nanoseconds is a Time unit that measures time in nanoseconds.
+	Nanoseconds Time = iota
+
+	// Microseconds is a Time unit that measures time in microseconds.
+	Microseconds
+
+	// Milliseconds is a Time unit that measures time in milliseconds.
+	Milliseconds
+
+	// Seconds is a Time unit that measures time in seconds.
+	Seconds
+
+	// Minutes is a Time unit that measures time in minutes.
+	Minutes
+
+	// Hours is a Time unit that measures time in hours.
+	Hours
+
+	// Days is a Time unit that measures time in days.
+	Days
+)
+
+const (
+	nanosecondSymbol  = "ns"
+	microsecondSymbol = "us"
+	millisecondSymbol = "ms"
+	secondsSymbol     = "s"
+	minutesSymbol     = "min"
+	hoursSymbol       = "h"
+	daysSymbol        = "d"
+)
+
+// Symbol returns the symbol used to represent the underlying unit.
+func (t Time) Symbol() string {
+	switch t {
+	case Nanoseconds:
+		return nanosecondSymbol
+	case Microseconds:
+		return microsecondSymbol
+	case Milliseconds:
+		return millisecondSymbol
+	case Seconds:
+		return secondsSymbol
+	case Minutes:
+		return minutesSymbol
+	case Hours:
+		return hoursSymbol
+	case Days:
+		return daysSymbol
+	}
+	// This is a closed enumeration in an internal package. If this panic ever
+	// gets reached, it means that a developer is using this package wrong.
+	panic(fmt.Sprintf("invalid time value %v", t))
+}
+
+// System returns the time system that this unit comes from.
+func (t Time) System() string {
+	return "http://unitsofmeasure.org"
+}
+
+// TimeFromSymbol creates the Time object
+func TimeFromSymbol(symbol string) (Time, error) {
+	switch symbol {
+	case nanosecondSymbol:
+		return Nanoseconds, nil
+	case microsecondSymbol:
+		return Microseconds, nil
+	case millisecondSymbol:
+		return Milliseconds, nil
+	case secondsSymbol:
+		return Seconds, nil
+	case minutesSymbol:
+		return Minutes, nil
+	case hoursSymbol:
+		return Hours, nil
+	case daysSymbol:
+		return Days, nil
+	}
+	return Time(0), fmt.Errorf("unknown Time symbol '%v'", symbol)
+}