This page is part of the FHIR Shorthand (v1.0.0: STU 1) based on FHIR R4. The current version which supercedes this version is 2.0.0. For a full list of available versions, see the Directory of published versions
This chapter contains the formal specification of the FHIR Shorthand (FSH) language. It is intended as a reference, not a tutorial.
In this specification, the key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” are to be interpreted as described in RFC2119.
The FSH specification uses syntax expressions to illustrate the FSH language. While FSH has a formal grammar (see Appendix), most readers will find the syntax expressions more instructive.
Syntax expressions uses the following conventions:
Style | Explanation | Example |
---|---|---|
Code |
Code fragments, such as FSH keywords, FSH statements, and FSH syntax expressions | * status = #open |
{curly braces} |
An item to be substituted in a syntax expression | {display string} |
<datatype> |
An element or path to an element with the given data type, to be substituted in the syntax expression | <CodeableConcept> |
italics | An optional item in a syntax expression | "{string}" |
ellipsis (…) | Indicates a pattern that can be repeated | {flag1} {flag2} {flag3} ... |
bold | A directory path or file name | example-1.fsh |
vertical bar | A choice of items or data types in the syntax | name|id|url |
Examples:
A FSH rule to assign the value of a Quantity:
* <Quantity> = {decimal or integer} '{UCUM unit}'
A FSH statement following this pattern would be written as:
A rule to constrain an element to a certain data type or types:
* <element> only {datatype1} or {datatype2} or {datatype3}...
A FSH statement following this pattern would be written as:
only
, followed byor
Here are some examples of curly braces and angle brackets used in this Guide:
Symbol | Meaning | Examples |
---|---|---|
{Coding} |
Instance of a Coding | SCT#961000205106 "Wearing street clothes, no shoes" |
<CodeableConcept> |
An element or path to an element whose data type is CodeableConcept | category |
<bindable> |
An element or path to an element whose data type allows it to be bound to a value set | code |
{flag} |
One of the valid FSH flags | MS |
{flags} |
A sequence of 1 or more flags, separated by whitespace | MS SU TU |
{card} |
A cardinality expression | 0..1 |
<element> |
Any element or path to any element | method.type |
<Extension> |
An element or path to an element with data type Extension | extension modifierExtension bodySite.extension |
{Extension name|id|url} |
The name, id, or canonical URL (or alias) of an Extension | duration allergyintolerance-duration http://hl7.org/fhir/StructureDefinition/allergyintolerance-duration |
{rule} |
Any FSH rule | * category 1..1 MS |
{RuleSet name} |
The name of a RuleSet | RuleSet1 |
{Invariant id} |
The id of an Invariant | us-core-8 |
{datatype} |
Any primitive or complex data type | decimal ContactPoint |
{ValueSet name|id|url} |
The name, id, or canonical URL (or alias) of a ValueSet | http://hl7.org/fhir/ValueSet/address-type |
{ResourceType name|id|url} |
The name, id, or canonical URL (or alias) for any type of Resource or Profile | Condition http://hl7.org/fhir/us/core/StructureDefinition/us-core-location |
The main organizing construct is a FSH project (sometimes called a “FSH Tank”). Each project MUST have an associated canonical URL, used for constructing canonical URLs for items created in the project. It is up to implementations to decide how this association is made. Typically, one FSH project equates to one FHIR Implementation Guide (IG).
Content in one FSH project MAY be contained in one or more FSH files. Files MUST use the .fsh extension. It is up to implementations to define the association between FSH files and FSH projects.
Note: FSH can also be contained in other ways, such as in a database or in a form field, and still be valid FSH. We assume FSH files for presentation purposes.
The items defined by FSH are: Aliases, Profiles, Extensions, Instances, Value Sets, Code Systems, Mappings, Rule Sets, and Invariants. How items are divided among files is not meaningful in FSH, and items from all files in one project can be considered pooled together for the purposes of FSH.
Items can appear in any order within .fsh files, and can be moved around within a file or to other .fsh files in the same project without affecting the interpretation of the content.
Each FSH project MUST declare the version of FHIR it depends upon. The form of this declaration is outside the scope of the FSH specification, and SHOULD be managed by implementations. The FSH specification is not explicitly FHIR-version dependent, but implementations MAY support only a specific version or versions of FHIR.
The FSH language specification has been designed around FHIR R4. Compatibility with previous versions has not been evaluated. FSH depends primarily on normative parts of the FHIR R4 specification (in particular, StructureDefinition and primitive data types). It is conceivable that future changes in FHIR could impact the FSH language specification, for example, if FHIR introduces new data types.
Dependencies between a FSH project and other IGs MUST be declared. The form of this declaration is outside the scope of the FSH language and SHOULD be managed by implementations. In this Guide, these are referred to as “external” IGs.
The FSH specification, like other FHIR Implementation Guides (IGs), expresses versions in terms of three integers, x.y.z, indicating the sequence of releases. Release 2 is later than release 1 if x2 > x1 or (x2 = x1 and y2 > y1), or (x2 = x1 and y2 = y1 and z2 > z1). Implementations SHOULD indicate what version or versions of the FSH specification they implement.
Like other HL7 FHIR IGs, the version numbering of the FSH specification does not entirely follow the semantic versioning convention. Consistent with semantic versioning, an increment of z indicates a patch release containing minor updates and bug fixes, while maintaining backwards compatibility with the previous version. An increment in y indicates new or modified features, and potentially, non-backward-compatible changes (i.e., a minor or major release in semantic versioning). By HL7 convention, the major version number x typically does not increment until the release of a new balloted version.
FSH has a formal grammar defined in ANTLR4. The grammar is looser than the language specification since many things, such as data type agreement, are not enforced by the grammar. If there is discrepancy between the grammar and the FSH language description, the language description is considered correct until the discrepancy is clarified and addressed.
FSH has a number of reserved words, symbols, and patterns. Reserved words and symbols with special meaning in FSH are: contains
, named
, and
, only
, or
, obeys
, true
, false
, include
, exclude
, codes
, where
, valueset
, system
, from
, insert
, !?
, MS
, SU
, N
, TU
, D
, =
, *
, :
, ->
, .
,[
, ]
.
The following words are reserved only if followed by a colon (intervening white spaces allowed): Alias
, CodeSystem
, Extension
, Instance
, Invariant
, Mapping
, Profile
, RuleSet
, ValueSet
, Description
, Expression
, Id
, InstanceOf
, Parent
, Severity
, Source
, Target
, Title
, Usage
, XPath
.
The following words are reserved only when enclosed in parentheses (intervening white spaces allowed): example
, preferred
, extensible
, required
, exactly
.
The primitive data types and value formats in FSH are identical to the primitive types and value formats in FHIR. References in this document to code, id, oid, etc. refer to the primitive datatypes defined in FHIR.
FSH strings support the escape sequences that FHIR already defines as valid in its regex for strings: \r, \n, and \t. Strings MUST be delimited by non-directional (neutral) quotes. Left and right directional quotes (unicode U+201C and U+201D) sometimes automatically inserted by “smart” text editors SHALL NOT be accepted. Left and right directional single quotes (U+2018 and U+2019) SHALL NOT be accepted in contexts requiring a single quotation mark.
FSH item names follow FHIR naming guidance. Names MUST be between 1 and 255 characters, begin with an uppercase letter, and contain only letters, numbers, and “_”.
By convention, item names SHOULD use PascalCase (also known as UpperCamelCase). Slice names and local slice names for extensions SHOULD use lower camelCase. These conventions are consistent with FHIR naming conventions.
Alias names MAY begin with $
. Choosing alias names beginning with $
allows for additional error checking (see Defining Aliases for details).
Items in FSH MAY have an identifier (id), typically specified using the Id
keyword. Each id MUST be unique within the scope of its item type in the FSH project. For example, two Profiles with the same id cannot coexist, but it is possible to have a Profile and a ValueSet with the same id in the same FSH Project. However, to minimize potential confusion, it is best to use a unique id for every item in a FSH project.
If no id is provided by a FSH author, implementations MAY create an id. It is RECOMMENDED that the id be based on the item’s name, with _ replaced by -, and the overall length truncated to 64 characters (per the requirements of the FHIR id datatype).
FSH allows internal and external items to be referred to by name, id, or canonical URL.
A FSH item within the same project SHOULD be referred to by the name or id given in the item’s declaration statement. Core FHIR resources SHOULD be referred to by name, e.g., Patient
or Observation
. Items from external IGs and extensions, profiles, code systems, and value sets in core FHIR SHOULD be referred to by their canonical URLs, since this approach minimizes the chance of name collisions. In cases where an external name or id clashes with an internal name or id, then the internal item takes precedence, and external item MUST be referred to by its canonical URL.
FHIR resources can contain two types of references, Resource references and Canonical references.
FSH represents Resource references using the syntax Reference({Resource name|id|url})
. For elements that require a Reference data type, Reference()
MUST be included, except in the case of a reference choice path.
Canonical references refer to the standard URL associated with FHIR items. For elements that require a canonical data type, FSH will accept a URL or an expression in the form Canonical({name|id})
. Canonical()
stands for the canonical URL of the referenced item. For items defined in the same FSH project, the canonical URL is constructed using the FSH project’s canonical URL. Canonical()
therefore enables a user to change the FSH project’s canonical URL in a single place with no changes to FSH definitions.
Repeated whitespace is not meaningful within FSH files (except within string literals). New lines are considered whitespace. Whitespace insensitivity can be used to improve readability. For example:
* component contains appearanceScore 0..3 and pulseScore 0..3 and grimaceScore 0..3 and activityScore 0..3 and respirationScore 0..3
can be reformatted as:
* component contains
appearanceScore 0..3 and
pulseScore 0..3 and
grimaceScore 0..3 and
activityScore 0..3 and
respirationScore 0..3
FSH follows JavaScript syntax for code comments:
// Use a double-slash for comments on a single line
/*
Use slash-asterisk and asterisk-slash for larger block comments.
These comments can take up multiple lines.
*/
The formal grammar for FSH discards all comments during import; they are not retained or used during IG generation. Implementations are free to modify the grammar to allow retention of comments.
All rules in FSH begin with an asterisk (*
) symbol followed by at least one space. Rules SHALL be interpreted logically in a top-down manner.
In most cases, the order of rules is flexible. However, there are some situations where FSH requires rules to appear in a certain order. For example, slicing rules require that a slice MUST first be defined by a contains
rule before the slice is referenced. Implementations MUST enforce rule-order requirements where they are specified in FSH.
Logical order dependencies can also arise. For example, in setting properties of a choice element (an element involving FHIR’s [x]
syntax), this order is problematic:
* value[x].unit 0..0
* value[x] only Quantity
but this order is acceptable:
* value[x] only Quantity
* value[x].unit 0..0
In the first approach, value[x]
is not yet known to be restricted to a Quantity, so value[x].unit
is ambiguous. In the second approach, value[x]
is known to be a Quantity, so value[x].unit
is unambiguous.
In cases with no explicit or logical restrictions on rule ordering, users MAY list rules in any order, bearing in mind that insert
rules expand into other rules that could have order constraints or logical ordering requirements.
It is possible for a user to specify contradictory rules, for example, two rules constraining the cardinality of an element to different values, or constraining an element to different data types. Implementations SHOULD detect such contradictions and issue appropriate warning or error messages.
FSH provides special grammar for expressing codes and Codings. Codes are denoted with #
sign. The FSH syntax is:
#{code}
or
#"{code}"
In general, the first syntax is sufficient. Quotes are only required when a code contains white space.
FSH represents Codings in the following ways:
{CodeSystem name|id|url}#{code} "{display string}"
{CodeSystem name|id|url}|{version string}#{code} "{display string}"
As indicated by italics, the "{display string}"
is OPTIONAL. CodeSystem
represents the controlled terminology the code is taken from. The bar syntax for the version of the code system is the same approach used in the canonical data type in FHIR. To set the less-common properties of a Coding or to set properties individually, assignment rules can be used.
This syntax is also used with CodeableConcepts (see Assignments with the CodeableConcept Data Type)
Examples:
The code postal used in Address.type:
#postal
The code <= from the Quantity Comparator value set:
#<=
A code containing white space:
#"VL 1-1, 18-65_1.2.2"
A Coding from SNOMED-CT:
http://snomed.info/sct#363346000 "Malignant neoplastic disease (disorder)"
The same Coding, assuming SCT has been defined as an alias for http://snomed.info/sct:
SCT#363346000 "Malignant neoplastic disease (disorder)"
A Coding from ICD10-CM, assuming the alias $ICD for that code system:
$ICD#C004 "Malignant neoplasm of lower lip, inner aspect"
A Coding with an explicit version specified with bar syntax:
http://hl7.org/fhir/CodeSystem/example-supplement|201801103#chol-mmol
FSH provides a shorthand that allows quantities with units of measure to be specified simultaneously, provided the units of measure are Unified Code for Units of Measure (UCUM) codes. This syntax is borrowed from the Clinical Quality Language:
{decimal} '{UCUM code}'
The value and units can also be expressed independently (see Assignments with the Quantity Data Type).
While line breaks are supported using normal strings, FSH also supports different processing for strings demarcated with three double quotation marks """
. This feature can help authors to maintain consistent indentation in the FSH file. As an example, an author might use a triple-quote string to write markdown so that the markdown is neatly indented:
* ^purpose = """
* This profile is intended to support workflows where:
* this happens; or
* that happens
* This profile is not intended to support workflows where:
* nothing happens
"""
Using a single-quoted string requires the following spacing to accomplish the same markdown formatting:
* ^purpose = "* This profile is intended to support workflows where:
* this happens; or
* that happens
* This profile is not intended to support workflows where:
* nothing happens"
The difference between these two approaches is that the latter obscures the fact that the first and fourth line are at the same indentation level, and makes it appear there are two rules because of the asterisk in the first column. The former approach allows the first line to be empty so the string is defined as a block, and allows the entire block to be indented, so visually, it does not appear a second rule is involved (because of the asterisk in the first column). Using triple-quoted strings is entirely a matter of preference.
When processing triple-quote strings, the following approach is used:
FSH path grammar allows you to refer to any element of a profile, extension, or instance, regardless of nesting. Here are examples of things paths can refer to:
In the following, the various types of path references are discussed.
The path to a top-level element is denoted by the element’s name. Because paths are used within the context of a FSH definition or instance, the path MUST NOT include the resource name. For example, when defining a profile of Observation, the path to Observation.code is just code
.
To refer to nested elements, the path lists the properties in order, separated by a dot (.
). Since the resource can be inferred from the context, the resource name is not included in the path.
Example:
The path to the text property of Observation.method:
method.text
FSH uses indices to address array elements. The array index is represented using square brackets containing the 0-based index of the item (e.g., first item is [0]
, second item is [1]
, etc.).
If the index is omitted, the first element of the array ([0]
) is assumed.
Examples:
Path to a patient’s second given name in the first name field:
name[0].given[1]
Equivalent path expression, since the zero index is assumed when omitted:
name.given[1]
Elements can offer a choice of reference types. In the FHIR specification, these choices are presented in the style Reference(Procedure | Observation). To address a specific resource or profile among the choices, the path to the element appends the target data type (represented by a name, id, or url) enclosed in square brackets.
Note: It is not permissible to cross reference boundaries in paths. This means that when a path gets to a Reference, that path cannot be extended further. For example, if Procedure has a subject element that has data type Reference(Patient), and Patient has a gender, then
subject
is a valid path, butsubject.gender
is not, because it crosses into the Patient resource.
Examples:
Path to the Reference(Practitioner) option of DiagnosticReport.performer, whose acceptable data types are Reference(Practitioner), Reference(PractitionerRole), Reference(Organization) or Reference(CareTeam):
performer[Practitioner]
Path to the Reference(US Core Organization) option of the performer element in US Core DiagnosticReport Lab, using the canonical URL:
performer[http://hl7.org/fhir/us/core/StructureDefinition/us-core-organization]
The same path, using the id of the US Core Organization profile instead of its canonical URL:
performer[us-core-organization]
FHIR represents an element with a choice of data types using the style foo[x]. For example, Condition.onset[x] can be a dateTime, Age, Period, Range or string. In FSH, as in FHIR, to refer to one of these data types, replace the [x]
with the data type name, capitalizing the first letter. For Condition.onset[x], the individual choices are onsetDateTime, onsetAge, onsetPeriod, onsetRange, and onsetString.
Note: foo[x] choices are NOT represented as foo[dateTime], foo[Period], etc.
Examples:
The path to the string data type of Observation.value[x]:
valueString
The path to the Reference data type choice of Medication.ingredient.item[x]:
ingredient.itemReference
Given that the itemReference has further choices Reference(Substance) or Reference(Medication), the path to the Reference(Substance) choice:
ingredient.itemReference[Substance]
Extension arrays are found at the root level of every resource, nested inside every element, and recursively inside each extension. Extensions are elements in these arrays. When an extension is added to an extension array, a name (technically, a slice name) is assigned. Extensions can be identified by that slice name, or the extension’s URL.
The path to an extension is constructed by combining the path to the extension array with a reference to the extension in square brackets:
<Extension>[{extension slice name|id|URL}]
For locally-defined extensions, using the slice name is the simplest choice. For externally-defined extensions, the canonical URL can be easier to find than the slice name.
Note: The same path construction applies to modifierExtension arrays; simply replace
extension
withmodifierExtension
.
Examples:
Path to the value of the birth sex extension in US Core Patient, whose local name is birthsex:
extension[birthsex].valueCode
Path to an extension on the telecom element of Patient, assuming the extension has been given the local slice name directMailAddress:
telecom.extension[directMailAddress]
Same as the previous example, but using the canonical URL for the direct mail extension defined in US Core:
telecom.extension[http://hl7.org/fhir/us/core/StructureDefinition/us-core-direct]
Path to the Coding data type of the value[x] in the nested extension ombCategory under the ethnicity extension in US Core, using the slice names of the extensions:
extension[ethnicity].extension[ombCategory].valueCoding
Path to the Coding value in second element in the nested extension array named detailed, under USCoreEthnicity extension:
extension[ethnicity].extension[detailed][1].valueCoding
FHIR allows lists to be compartmentalized into sublists called “slices”. To address a specific slice, follow the path with square brackets containing the slice name. Since slices are most often unordered, slice names rather than array indices SHOULD be used. Note that slice names (like other FSH names) cannot be purely numerical, so slice names cannot be confused with indices.
To access a slice of a slice (a resliced array), follow the first pair of brackets with a second pair containing the resliced slice name.
Examples:
Path to the coded value of the respirationScore component within an Observation profile representing an Apgar test:
component[respirationScore].code
Paths to the codes representing the one minute and five minute respiration scores, assuming the Apgar respiration component has been resliced:
component[respirationScore][oneMinuteScore].code
component[respirationScore][fiveMinuteScore].code
FSH uses the caret (^) symbol to access to elements of definitional item corresponding to the current context. Caret paths can be used in the following FSH items: Profile, Extension, ValueSet, and CodeSystem. Caret syntax SHOULD be reserved for situations not addressed through FSH Keywords or external configuration files. Examples of elements that require the caret syntax include StructureDefinition.experimental, StructureDefinition.abstract and ValueSet.purpose. The caret syntax also provides a simple way to set metadata attributes in the ElementDefinitions that comprise the snapshot and differential tables (e.g., short, meaningWhenMissing, and various slicing discriminator properties).
For a path to an element of an SD, excluding the differential and snapshot, use the following syntax inside a Profile or Extension:
^<element of SD>
For a path to an element of an ElementDefinition within an SD, use this syntax:
<element in Profile> ^<element of corresponding ElementDefinition>
Note: There is a required space before the ^ character.
A special case of the ElementDefinition path is setting properties of the first element of the differential (i.e., StructureDefinition.differential.element[0]). This element always refers to the profile or standalone extension itself. Since this element does not correspond to a named element appearing in an instance, we use the dot or full stop (.
) to represent it. (The dot symbol is often used to represent “current context” in other languages.) It is important to note that the “self” elements are not the elements of an SD directly, but elements of the first ElementDefinition contained in the SD. The syntax is:
. ^<element of ElementDefinition[0]>
Examples:
In a profile definition, path to the corresponding StructureDefinition.experimental attribute:
^experimental
In a profile of Patient, the path to binding.description in the ElementDefinition corresponding to communication.language:
communication.language ^binding.description
The path to the short description of an extension (defined in the first ElementDefinition):
. ^short
- For rules applicable to code systems, see Defining Code Systems
- For rules applicable to mappings, see Defining Mappings.
- For rules applicable to value sets, see Defining Value Sets.
Rules are the mechanism for setting cardinality, applying Must Support flags, defining extensions, creating slices, and more. All rules begin with an asterisk:
* {rule statement}
The following table is a summary of the rules applicable to profiles, extensions, and instances:
Rule Type | Syntax |
---|---|
Assignment | * <element> = {value} * <element> = {value} (exactly) |
Binding | * <bindable> from {ValueSet name|id|url} * <bindable> from {ValueSet name|id|url} ({strength}) |
Cardinality | * <element> {min}..{max} * <element> {min}.. * <element> ..{max} |
Contains (for inline extensions) | * <Extension> contains {name} {card} {flags} * <Extension> contains {name1} {card1} {flags1} and {name2} {card2} {flags2} ... |
Contains (for standalone extensions) | * <Extension> contains {Extension name|id|url} named {name} {card} {flags} * <Extension> contains {Extension1 name|id|url} named {name1} {card1} {flags1} and {Extension2 name|id|url} named {name2} {card2} {flags2} ... |
Contains (for slicing) | * <array> contains {name} {card} {flags} * <array> contains {name1} {card1} {flags1} and {name2} {card2} {flags2} ... |
Flag | * <element> {flag} * <element> {flag1} {flag2} ... * <element1> and <element2> and <element3> ... {flag1} {flag2} ... |
Insert | * insert {RuleSet name} |
Obeys | * obeys {Invariant id} * obeys {Invariant1 id} and {Invariant2 id} ... * <element> obeys {Invariant id} * <element> obeys {Invariant1 id} and {Invariant2 id} ... |
Type | * <element> only {datatype} * <element> only {datatype1} or {datatype2} or {datatype3} ... * <element> only Reference({ResourceType name|id|url}) * <element> only Reference({ResourceType1 name|id|url} or {ResourceType2 name|id|url} or {ResourceType3 name|id|url} ...) |
Notes:
Assignment rules follow this syntax:
* <element> = {value}
The left side of this expression follows the FSH path grammar. The data type on the right side MUST align with the data type of the final element in the path.
Assignment rules have two different interpretations, depending on context:
If conformance to a profile requires a precise match to the specified value (which is rare), then the following syntax can be used:
* <element> = {value} (exactly)
Adding (exactly)
indicates that conformance to the profile requires a precise match to the specified values. No additional values or extensions are allowed. In general, using (exactly)
is not the best option for interoperability because it creates conformance criteria that could be too tight, risking the rejection of valid, useful data. FSH offers this option primarily because exact value matching is used in some current IGs and profiles.
Note: The
(exactly)
modifier does not apply to instances.
Consider the interpretation of the following assignment statements in instances and profile, assuming the code element is a CodeableConcept and LNC is an alias for http://loinc.org:
* code = LNC#69548-6
* code = LNC#69548-6 "Genetic variant assessment"
* code = LNC#69548-6 (exactly)
In the context of an instance:
In the context of a profile:
In a profiling context, typically only the system and code are important conformance criteria, so the first statement is preferred. In the context of an instance, the display text conveys additional information useful to the information receiver, so the second statement would be preferred.
In the following, we give details and examples of assignments involving various data types.
Examples:
Assignment of a code data type:
* status = #arrived
Assignment of a boolean:
* active = true
Assignment of a date:
* onsetDateTime = "2019-04-02"
Assignment of a dateTime:
* recordedDate = "2013-06-08T09:57:34.2112Z"
FSH provides a shortcut for setting several of the most common properties of a Coding simultaneously, namely, system, version, code, and display. The syntax is:
<Coding> = {CodeSystem name|id|url}|{version string}#{code} "{display string}"
Examples:
Assign a Coding that includes an explicit code system version:
myCoding = http://hl7.org/fhir/CodeSystem/example-supplement|201801103#chol-mmol
As an alternative to the bar syntax for version, set the code system version only:
* myCoding.version = "201801103"
Set the userSelected property of a Coding (one of the lesser-used attributes of Codings):
* myCoding.userSelected = true
In an instance of a Signature, set Signature.type (a Coding data type):
* type = urn:iso-astm:E1762-95:2013#1.2.840.10065.1.12.1.2 "Coauthor's Signature"
A CodeableConcept consists of an array of Codings. To populate the array, array indices, denoted by brackets, are used. The shorthand is:
* <CodeableConcept>.coding[{index}] = {CodeSystem name|id|url}#{code} "{display string}"
To set the first Coding in a CodeableConcept, FSH offers the following shortcut:
* <CodeableConcept> = {CodeSystem name|id|url}#{code} "{display string}"
To set the top-level text of a CodeableConcept, the FSH expression is:
* <CodeableConcept>.text = "{string}"
Examples:
Set the first Coding in Condition.code (a CodeableConcept type):
* code = SCT#363346000 "Malignant neoplastic disease (disorder)"
An equivalent representation, using explicit array index on the coding array:
* code.coding[0] = SCT#363346000 "Malignant neoplastic disease (disorder)"
Another equivalent representation, using the shorthand that allows dropping the [0] index:
* code.coding = SCT#363346000 "Malignant neoplastic disease (disorder)"
Add a second value to the array of Codings:
* code.coding[1] = ICD10#C80.1 "Malignant (primary) neoplasm, unspecified"
Set the top-level text of Condition.code:
* code.text = "Diagnosis of malignant neoplasm left breast."
FSH provides a shorthand that allows quantities with units of measure to be specified simultaneously, provided the units of measure are Unified Code for Units of Measure (UCUM) codes:
* <Quantity> = {decimal} '{UCUM code}'
The value and units can also be set independently. To assign a value, use the Quantity.value property:
* <Quantity>.value = {decimal}
The units of measure can either be assigned a coded value or bound to a value set:
* <Quantity> = {CodeSystem name|id|url}#{code} "{display string}"
* <Quantity> from {ValueSet name|id|url}
In the first expression, CodeSystem
corresponds to Quantity.system, code
to Quantity.code, and display string
to Quantity.unit.
Note: The ability to assign a coded value or bind a value set directly to a Quantity is a consequence of FHIR’s definition of Quantity as a coded data type, rather than using a Coding to represent the units of measure.
Examples:
Set the valueQuantity of an Observation to 55 millimeters using UCUM units:
* valueQuantity = 55.0 'mm'
Set the numerical value of Observation.valueQuantity to 55.0 without setting the units:
* valueQuantity.value = 55.0
Set the units of the same valueQuantity to millimeters, without setting the value (assuming UCUM has been defined as an alias for http://unitsofmeasure.org):
* valueQuantity = UCUM#mm "millimeters"
Bind a value set to a Quantity, constraining the units of that Quantity:
* valueQuantity from http://hl7.org/fhir/ValueSet/distance-units
Resource instances can refer to other resource instances. The referred resources can either exist independently or be contained inline in the DomainResource.contained array. Less commonly, the value of an element can be a resource, rather than a reference to a resource.
A resource reference is assigned using this grammar:
* <Reference> = Reference({Resource id|url})
For assignment of a resource to the value of an element directly:
* <Resource> = {Resource id|url}
As advised in FHIR, the URL is the preferred way to reference an instance for the resource types on which it is defined. One advantage is that the URL can include a version. For an internal FSH-defined instance, referring to an instance by its id (as defined in the Instance
declaration) is more typical (see examples).
Examples:
Assignment of a reference to an example of a Patient resource to Observation.subject:
* subject = Reference(EveAnyperson)
where, for example:
Instance: EveAnyperson // this is the id of the example instance
InstanceOf: Patient
Usage: #example
* name.given = "Eve"
* name.family = "Anyperson"
Assignment of the same instance in Bundle.entry.resource, whose data type is Resource (not Reference(Resource)):
* entry[0].resource = EveAnyperson
Binding is the process of associating a coded element with a set of possible values. The syntaxes to bind a value set, or alter an inherited binding, use the reserved word from
:
* <bindable> from {ValueSet name|id|url} ({strength})
The bindable types in FHIR are code, Coding, CodeableConcept, Quantity, string, and uri.
The strengths are the same as the binding strengths defined in FHIR, namely: example, preferred, extensible, and required. If strength is not specified, a required binding is assumed.
The binding rules defined in FHIR are applicable to FSH. In particular:
Examples:
Bind to an externally-defined value set using its canonical URL:
* telecom.system from http://hl7.org/fhir/ValueSet/contact-point-system (required)
Bind to an externally-defined value set with required binding by default:
* gender from http://hl7.org/fhir/ValueSet/administrative-gender
Bind to a value set using an alias name:
* address.state from USPSTwoLetterAlphabeticCodes (extensible)
Cardinality rules constrain (narrow) the number of repetitions of an element. Every element has a cardinality inherited from its parent resource or profile. If the inheriting profile does not alter the cardinality, no cardinality rule is required.
To change the cardinality, the grammar is:
* <element> {min}..{max}
* <element> {min}.. // leave max as-is
* <element> ..{max} // leave min as-is
As in FHIR, min and max are non-negative integers, and max can also be *, representing unbounded. It is valid to include both the min and max, even if one of them remains the same as in the original cardinality. In this case, FSH implementations SHOULD only generate constraints for the changed values.
Cardinalities MUST follow rules of FHIR profiling, namely that the min and max cardinalities comply within the constraints of the parent.
For convenience and compactness, cardinality rules can be combined with flag rules via the following grammar:
* <element> {card} {flag}
* <element> {card} {flag1} {flag2} {flag3}...
Examples:
Set the cardinality of the subject element to 1..1 (required, non-repeating):
* subject 1..1
Set the cardinality of the subject element to 1..1 and declare it Must Support:
* subject 1..1 MS
Set the cardinality of a sub-element to 0..0 (not permitted):
* component.referenceRange 0..0
Require at least one category without changing its upper bound:
* category 1..
Allow at most one category without changing its lower bound:
* category ..1
Extensions are created by adding elements to built-in extension arrays. Extension arrays are found at the root level of every resource, nested inside every element, and recursively inside each extension. The structure of extensions is defined by FHIR (see Extension element). Profiling extensions is discussed in Defining Extensions.
Extensions are specified using the contains
keyword. There are two types of extensions, standalone and inline:
The syntaxes to specify standalone extension(s) are:
* <Extension> contains {Extension name|id|url} named {name} {card} {flags}
* <Extension> contains
{Extension1 name|id|url} named {name1} {card1} {flags1} and
{Extension2 name|id|url} named {name2} {card2} {flags2} and
{Extension3 name|id|url} named {name3} {card3} {flags3} ...
The syntaxes to define inline extension(s) are:
* <Extension> contains {name} {card} {flags}
* <Extension> contains
{name1} {card1} {flags1} and
{name2} {card2} {flags2} and
{name3} {card3} {flags3} ...
In these expressions, the names (name
, name1
, name2
, etc.) are new local names created by the rule author. They are used to refer to that extension in later rules. By convention, the local names SHOULD be lower camelCase.
Note: Contains rules can also be applied to modifierExtension arrays; simply replace
extension
withmodifierExtension
.
Examples:
Add standalone FHIR extensions patient-disability and patient-genderIdentity to a profile of the Patient resource, using the canonical URLs for the extensions:
* extension contains http://hl7.org/fhir/StructureDefinition/patient-disability named disability 0..1 MS and http://hl7.org/fhir/StructureDefinition/patient-genderIdentity named genderIdentity 0..1 MS
The same statement, using aliases and whitespace flexibility for better readability:
Alias: $Disability = http://hl7.org/fhir/StructureDefinition/patient-disability
Alias: $GenderIdentity = http://hl7.org/fhir/StructureDefinition/patient-genderIdentity
// intervening lines not shown
* extension contains
$Disability named disability 0..1 MS and
$GenderIdentity named genderIdentity 0..1 MS
Add a standalone extension Laterality, defined in the same FSH project, to a bodySite attribute (second level extension):
* bodySite.extension contains Laterality named laterality 0..1
// intervening lines not shown
Extension: Laterality
Description: "Body side of a body location."
* value[x] only CodeableConcept
* valueCodeableConcept from LateralityVS (required)
Show how the inline extensions defined in US Core Race would be defined using FSH:
* extension contains
ombCategory 0..5 MS and
detailed 0..* and
text 1..1 MS
// rules defining the inline extensions would typically follow:
* extension[ombCategory].value[x] only Coding
* extension[ombCategory].valueCoding from http://hl7.org/fhir/us/core/ValueSet/omb-race-category (required)
* extension[text].value[x] only string
// etc.
Slicing is an advanced, but necessary, feature of FHIR. It is helpful to have a basic understanding of slicing and discriminators before attempting slicing in FSH.
In FSH, slicing is addressed in three steps: (1) specify the slicing logic, (2) define the slices, and (3) constrain each slice’s contents.
Note: The rules from each step MUST be sequentially ordered, i.e., step (1) slicing logic rules before step (2) slice definition rules before step (3) slice content rules.
Slicing in FHIR requires authors to specify a discriminator path, type, and rules. In addition, authors can optionally declare the slice as ordered or unordered (default: unordered), and/or provide a description. The meaning and allowable values are exactly as defined in FHIR.
The slicing logic parameters are specified using caret paths. The discriminator path identifies the element to be sliced, which is typically a multi-cardinality (array) element. The discriminator type determines how the slices are differentiated, e.g., by value, pattern, existence of the sliced element, data type of sliced element, or profile conformance.
Example:
Provide slicing logic for slices on Observation.component that are be distinguished by their code:
* component ^slicing.discriminator.type = #pattern
* component ^slicing.discriminator.path = "code"
* component ^slicing.rules = #open
* component ^slicing.ordered = false // can be omitted, since false is the default
* component ^slicing.description = "Slice based on the component.code pattern"
The second step in slicing is to populate the array that is to be sliced, using the contains
keyword. The syntaxes are very similar to contains rules for inline extensions:
* <array> contains {name} {card} {flags}
* <array> contains
{name1} {card1} {flags1} and
{name2} {card2} {flags2} and
{name3} {card3} {flags3} ...
In this pattern, <array>
is a path to the element that is to be sliced and to which the slicing rules defined in step (1) will applied. The names (name
, name1
, etc.) are created by the rule author to describe the slice in the context of the profile. These names are used to refer to the slice in later rules. By convention, the slice names SHOULD be lower camelCase.
Each slice will match or constrain the data type of the array it slices. In particular:
Examples:
Slice the Observation.component array for blood pressure:
* component contains systolicBP 1..1 MS and diastolicBP 1..1 MS
Because FSH is white-space invariant, the previous example can be rewritten so the slices appear one-per-line for readability:
* component contains
systolicBP 1..1 MS and
diastolicBP 1..1 MS
Reslicing (slicing an existing slice) uses a similar syntax, but the left-hand side uses slice path syntax to refer to the slice that is being resliced:
* <array[{slice name}]> contains {name} {card} {flags}
* <array[{slice name}]> contains
{name1} {card1} {flags1} and
{name2} {card2} {flags2} and
{name3} {card3} {flags3} ...
Example:
In an Observation for Apgar score, reslice the Apgar respiration score component into one-, five-, and ten-minute scores:
* component contains
appearanceScore 0..3 and
pulseScore 0..3 and
grimaceScore 0..3 and
activityScore 0..3 and
respirationScore 0..3
* component[respirationScore] contains
oneMinuteScore 0..1 and
fiveMinuteScore 0..1 and
tenMinuteScore 0..1
The final step is to define the properties of each slice. FSH requires slice contents to be defined inline. The rule syntax is the same as constraining any other element, but the slice path syntax is used to specify the path:
* <array>[{slice name}].<element> {constraint}
The slice content rules MUST appear after the contains rule that creates the slices.
Examples:
Constrain the content of the systolicBP and diastolicBP slices:
* component[systolicBP].code = LNC#8480-6 // Systolic blood pressure
* component[systolicBP].value[x] only Quantity
* component[systolicBP].valueQuantity = UCUM#mm[Hg] "mmHg"
* component[diastolicBP].code = LNC#8462-4 // Diastolic blood pressure
* component[diastolicBP].value[x] only Quantity
* component[diastolicBP].valueQuantity = UCUM#mm[Hg] "mmHg"
At minimum, each slice MUST be constrained such that it can be uniquely identified via the discriminator (see Step 1). For example, if the discriminator path points to an element that is a CodeableConcept, and it discriminates by value or pattern, then each slice must constrain that CodeableConcept using an assignment rule or binding rule that uniquely distinguishes it from the other slices.
Flags are a set of information about the element that impacts how implementers handle them. The flags defined in FHIR, and the symbols used to describe them, are as follows:
FHIR Flag | FSH Flag | Meaning |
---|---|---|
S | MS |
Must Support |
Σ | SU | Include in summary |
?! | ?! |
Modifier |
N | N |
Normative element |
TU | TU |
Trial use element |
D | D |
Draft element |
FHIR also defines the flags I and NE, representing elements affected by constraints, and elements that cannot have extensions, respectively. These flags are not supported in flag syntax, since the I flag is determined by the presence of invariants, and NE flags apply only to infrastructural elements in base resources.
The following syntaxes can be used to assign flags:
* <element> {flag}
* <element> {flag1} {flag2} ...
* <element1> and <element2> and <element3> ... {flag}
* <element1> and <element2> and <element3> ... {flag1} {flag2} ...
Examples:
Declare communication to be a Must Support and Summary element:
* communication MS SU
Declare a list of elements and nested elements to be Must Support:
* identifier and identifier.system and identifier.value and name and name.family MS
Rule sets are reusable groups of rules that are defined independently of other items. An insert rule is used to add a rule set:
* insert {RuleSet name}
The rules in the named rule set are interpreted as if they were copied and pasted in the designated location.
Each rule in the rule set should be compatible with the item where the rule set is inserted, in the sense that all the rules defined in the rule set apply to elements actually present in the target. Implementations SHOULD check the legality of a rule set at compile time. If a particular rule from a rule set does not match an element in the target, that rule will not be applied, and an error SHOULD be emitted. It is up to implementations if other valid rules from the rule set are applied.
Examples:
Insert the rule set named RuleSet1 into a profile:
Profile: MyPatientProfile
Parent: Patient
* insert RuleSet1
* deceased[x] only deceasedBoolean
// More profile rules
This is equivalent to the following:
Profile: MyPatientProfile
Parent: Patient
* ^status = #draft
* ^experimental = true
* ^publisher = "Elbonian Medical Society"
* deceased[x] only deceasedBoolean
// More profile rules
Use rule sets to define two different national profiles, both using a common clinical profile:
Profile: USCoreBreastRadiologyObservationProfile
Parent: BreastRadiologyObservationProfile
* insert USObservationRuleSet
Profile: FranceBreastRadiologyObservationProfile
Parent: BreastRadiologyObservationProfile
* insert FranceObservationRuleSet
Invariants are constraints that apply to one or more values in instances, expressed as FHIRPath or XPath expressions. An invariant can apply to an instance as a whole or a single element. Multiple invariants can be applied to an instance as a whole or to a single element. The syntax for applying invariants in profiles is:
* obeys {Invariant id}
* obeys {Invariant1 id} and {Invariant2 id} ...
* <element> obeys {Invariant id}
* <element> obeys {Invariant1 id} and {Invariant2 id} ...
The first case applies the invariant to the profile as a whole. The second case applies the invariant to a single element. The third case applies multiple invariants to the profile as a whole, and the fourth case applies multiple invariants to a single element.
The referenced invariant and its properties MUST be declared somewhere within the same FSH project, using the Invariant
keyword. See Defining Invariants.
Examples:
Assign invariant to US Core Implantable Device (invariant applies to profile as a whole):
* obeys us-core-9
Assign invariant to Patient.name in US Core Patient:
* name obeys us-core-8
FSH rules can be used to restrict the data type of an element. The syntaxes to restrict the type are:
* <element> only {datatype}
* <element> only {datatype1} or {datatype2} or {datatype3} ...
* <element> only Reference({ResourceType name|id|url})
* <element> only Reference({ResourceType1 name|id|url} or {ResourceType2 name|id|url} or {ResourceType3 name|id|url} ...)
Certain elements in FHIR offer a choice of data types using the [x] syntax. Choices also frequently appear in references. For example, Condition.recorder has the choice Reference(Practitioner or PractitionerRole or Patient or RelatedPerson). In both cases, choices can be restricted in two ways: reducing the number or choices, and/or substituting a more restrictive data type or profile for one of the choices appearing in the parent profile or resource.
Following standard profiling rules established in FHIR, the data type(s) in a type rule MUST always be more restrictive than the original data type. For example, if the parent data type is Quantity, it can be replaced by SimpleQuantity, since SimpleQuantity is a profile on Quantity (hence more restrictive than Quantity itself), but cannot be replaced with Ratio, because Ratio is not a type of Quantity. Similarly, Condition.subject, defined as Reference(Patient or Group), can be constrained to Reference(Patient), Reference(Group), or Reference(us-core-patient), but cannot be restricted to Reference(RelatedPerson), since that is neither a Patient nor a Group.
Examples:
Restrict a Quantity type to SimpleQuantity:
* valueQuantity only SimpleQuantity
Condition.onset[x] is a choice of dateTime, Age, Period, Range or string. To restrict onset[x] to dateTime:
* onset[x] only dateTime
Restrict onset[x] to either Period or Range:
* onset[x] only Period or Range
Restrict onset[x] to Age, AgeRange, or DateRange, assuming AgeRange and DateRange are profiles of FHIR’s Range datatype (thus permissible restrictions on Range):
* onset[x] only Age or AgeRange or DateRange
Restrict Observation.performer (a choice of reference to Practitioner, PractitionerRole, Organization, CareTeam, Patient, or RelatedPerson) to allow only Practitioner:
* performer only Reference(Practitioner)
Restrict Observation.performer to either a Practitioner or a PractitionerRole:
* performer only Reference(Practitioner or PractitionerRole)
Restrict performer to PrimaryCarePhysician or EmergencyRoomPhysician (assuming these are profiles on Practitioner):
* performer only Reference(PrimaryCarePhysician or EmergencyRoomPhysician)
Restrict the Practitioner choice of performer to a PrimaryCarePhysician, without restricting other choices. Because the path specifically calls out the Practitioner choice, the result is that performer can reference a Practitioner resource that validates against the PrimaryCareProvider profile or any of the other choices (PractitionerRole, Organization, CareTeam, Patient, and RelatedPerson):
* performer[Practitioner] only Reference(PrimaryCareProvider)
This section explains how to define items in FSH. The general pattern used to define an item in FSH is:
Keyword statements follow the syntax:
{Keyword}: {value}
For example:
Extension: TreatmentIntent
Description: "The purpose of the treatment."
Declaration keywords, corresponding to the items defined by FSH, are as follows:
Declaration Keyword | Purpose | Data Type |
---|---|---|
Alias |
Declares an alias for a URL or OID | name or $name |
CodeSystem |
Declares a new code system | name |
Extension |
Declares a new extension | name |
Instance |
Declares a new instance | id |
Invariant |
Declares a new invariant | id |
Mapping |
Declares a new mapping | id |
Profile |
Declares a new profile | name |
RuleSet |
Declares a set of rules that can be reused | name |
ValueSet |
Declares a new value set | name |
Additional keywords are as follows:
Additional Keyword | Purpose | Data Type |
---|---|---|
Description |
Provides a human-readable description | string or markdown |
Expression |
The FHIR path expression in an invariant | FHIRPath string |
Id |
An identifier for an item | id |
InstanceOf |
The profile or resource an instance instantiates | name or id or url |
Parent |
Specifies the base class for a profile or extension | name or id or url |
Severity |
whether violation of an invariant represents an error or a warning | code |
Source |
The profile the mapping applies to | name |
Target |
The standard being mapped to | uri |
Title |
Short human-readable name | string |
Usage |
Specifies how an instance is intended to be used in the IG | code |
XPath |
the XPath in an invariant | XPath string |
Note: Keywords are case-sensitive.
The following table shows the relationship between declaration keywords and additional keywords.
Declaration \ Keyword | Id | Description | Title | Parent | InstanceOf | Usage | Source | Target | Severity | XPath | Expression |
---|---|---|---|---|---|---|---|---|---|---|---|
Alias | |||||||||||
Code System | S | S | S | ||||||||
Extension | S | S | S | O | |||||||
Instance | x | S | S | R | O | ||||||
Invariant | x | R | R | O | O | ||||||
Mapping | x | S | S | R | R | ||||||
Profile | S | S | S | R | |||||||
Rule Set | |||||||||||
Value Set | S | S | S |
KEY: R = REQUIRED, S = suggested (SHOULD be used), O = OPTIONAL, blank = disallowed, x = Id is required but specified in the declaration statement
Aliases allow the user to replace a lengthy url or oid with a short string. Aliases are for readability only, and do not change the meaning of rules. Typical uses of aliases are to represent code systems and canonical URLs.
Alias definitions follow this syntax:
Alias: {Alias name} = {url or oid}
Several things to note about aliases:
In contrast with other names in FSH (for profiles, extensions, etc.), alias names can optionally begin with a dollar sign ($). If you define an alias with a leading $, you are protected against misspellings. For example, if you choose the alias name $RaceAndEthnicityCDC and accidentally type $RaceEthnicityCDC, implementations can easily detect there is no alias by that name. However, if the alias is RaceAndEthnicityCDC and the misspelling is RaceEthnicityCDC, implementations do not know an alias is intended, and will look through FHIR Core and all external implementation guides for anything with that name or id, or in some contexts, assume it is a new item, with unpredictable results.
Examples:
Alias: SCT = http://snomed.info/sct
Alias: $RaceAndEthnicityCDC = urn:oid:2.16.840.1.113883.6.238
Alias: $ObsCat = http://terminology.hl7.org/CodeSystem/observation-category
It is sometimes necessary to define new codes inside an IG that are not drawn from an external code system (aka local codes). Local codes MUST be defined in the context of a code system.
Note: Defining local codes is not best practice, since those codes will not be part of recognized terminology systems. However, when existing vocabularies do not contain necessary codes, it might be necessary to define them – at least temporarily – as local codes.
Creating a code system uses the keywords CodeSystem
, Id
, Title
and Description
. Codes are then added, one per rule, using the following syntax:
* #{code} "{display string}" "{definition string}"
Notes:
#
. The code system name is given by the CodeSystem
keyword.include
in a code system rule. The rule is creating a brand new code, not including an existing code defined elsewhere.Example: Define a code system for yoga poses.
CodeSystem: YogaCS
Id: yoga-code-system
Title: "Yoga Code System."
Description: "A brief vocabulary of yoga-related terms."
* #Sirsasana "Headstand"
"An inverted asana, also called mudra in classical hatha yoga, involves standing on one's head."
* #Halasana "Plough Pose"
"Halasana or Plough pose is an inverted asana in hatha yoga and modern yoga as exercise. Its variations include Karnapidasana with the knees by the ears, and Supta Konasana with the feet wide apart."
* #Matsyasana "Fish Pose"
"Matsyasana is a reclining back-bending asana in hatha yoga and modern yoga as exercise. It is commonly considered a counterasana to Sarvangasana, or shoulder stand, specifically within the context of the Ashtanga Vinyasa Yoga Primary Series."
* #Bhujangasana "Cobra Pose"
"Bhujangasana, or Cobra Pose is a reclining back-bending asana in hatha yoga and modern yoga as exercise. It is commonly performed in a cycle of asanas in Surya Namaskar (Salute to the Sun) as an alternative to Urdhva Mukha Svanasana (Upwards Dog Pose)."
Note: FSH does not support definition of relationships between local codes, such as parent-child (is-a) relationships.
Defining extensions is similar to defining a profile, except that the parent of an extension is not required. Extensions can also inherit from other extensions, but if the Parent
keyword is omitted, the parent is assumed to be FHIR’s Extension element.
All extensions have the same structure, but extensions can either have a value (i.e. a value[x] element) or sub-extensions, but not both. To create a simple extension, the value[x] element should be constrained. To create a complex extension, the extension array of the extension MUST be sliced (see Contains Rules for Extensions).
Since simple and complex extensions are mutually-exclusive, FSH implementations should set the value[x] cardinality to 0..0 if sub-extensions are specified, set extension cardinality to 0..0 if constraints are applied to value[x], and signal an error if value[x] and extensions are simultaneously specified.
Examples:
Show how the US Core BirthSex extension (a simple extension) would be defined in FSH:
Extension: USCoreBirthSexExtension
Id: us-core-birthsex
Title: "US Core Birth Sex Extension"
Description: "A code classifying the person's sex assigned at birth as specified by the [Office of the National Coordinator for Health IT (ONC)](https://www.healthit.gov/newsroom/about-onc). This extension aligns with the C-CDA Birth Sex Observation (LOINC 76689-9)."
// publisher, contact, and other metadata could be defined here using caret syntax (omitted)
* value[x] only code
* valueCode from http://hl7.org/fhir/us/core/ValueSet/birthsex (required)
Show how US Core Ethnicity extension (a complex extension with inline sub-extensions) would be defined in FSH:
Extension: USCoreEthnicityExtension
Id: us-core-ethnicity
Title: "US Core Ethnicity Extension"
Description: "Concepts classifying the person into a named category of humans sharing common history, traits, geographical origin or nationality. The ethnicity codes used to represent these concepts are based upon the [CDC ethnicity and Ethnicity Code Set Version 1.0](http://www.cdc.gov/phin/resources/vocabulary/index.html) which includes over 900 concepts for representing race and ethnicity of which 43 reference ethnicity. The ethnicity concepts are grouped by and pre-mapped to the 2 OMB ethnicity categories: - Hispanic or Latino - Not Hispanic or Latino."
* extension contains
ombCategory 0..1 MS and
detailed 0..* and
text 1..1 MS
* extension[ombCategory] ^short = "Hispanic or Latino|Not Hispanic or Latino"
* extension[ombCategory].value[x] only Coding
* extension[ombCategory].valueCoding from OmbEthnicityCategories (required)
* extension[detailed] ^short = "Extended ethnicity codes"
* extension[detailed].value[x] only Coding
* extension[detailed].valueCoding from DetailedEthnicity (required)
* extension[text] ^short = "Ethnicity text"
* extension[text].value[x] only string
Define an extension with an explicit parent, constraining the US Core Birth Sex extension for US states that do not recognize non-binary birth sex:
Extension: BinaryBirthSexExtension
Parent: USCoreBirthSexExtension
Id: binary-birthsex
Title: "Binary Birth Sex Extension"
Description: "As of 2019, certain US states only allow M or F on birth certificates."
* valueCode from BinaryBirthSexValueSet (required)
Instances are defined using the keywords Instance
, InstanceOf
, Title
, Usage
and Description
. The InstanceOf
is required, and plays a role analogous to the Parent
of a profile. The value of InstanceOf
can be the name, id, or url for any profile, resource, or complex data type defined internally or externally.
Instances inherit structures and values from their StructureDefinition (i.e. assigned codes, extensions). Assignment rules are used to set additional values.
The Usage
keyword specifies how the instance should be presented in the IG:
Usage: #example
means the instance is intended as an illustration of a profile, and will be presented on the Examples tab for the corresponding profile.Usage: #definition
means the instance is a conformance item that is an instance of a resource such as a search parameter, operation definition, or questionnaire. These items will be presented on their own IG page.Usage: #inline
means the instance should not be instantiated as an independent resource, but appears as part of another instance (for example, in a composition or bundle).If Usage
is unspecified, the default is #example
.
Examples:
Define an example instance of US Core Patient, with name, birthdate, race, and ethnicity:
Instance: EveAnyperson
InstanceOf: http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient
Title: "Eve Anyperson"
Usage: #example
* name.given = "Eve"
* name.family = "Anyperson"
* birthDate = 1960-04-25
* extension[us-core-race].extension[ombCategory].valueCoding = RaceAndEthnicityCDC#2106-3 "White"
* extension[us-core-ethnicity].extension[ombCategory].valueCoding = RaceAndEthnicityCDC#2186-5 "Non Hispanic or Latino"
Define an instance of US Core Practitioner, with name and NPI, meant to be inlined in a composition:
Instance: DrDavidAnydoc
InstanceOf: http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitioner
Title: "Dr. David Anydoc"
Usage: #inline
* name.family = "Anydoc"
* name.given = "David"
* name.suffix = "MD"
* identifier[NPI].value = "8274017284"
Define an instance of PrimaryCancerCondition, using many available features:
Instance: mCODEPrimaryCancerConditionExample01
InstanceOf: PrimaryCancerCondition
Description: "mCODE Example for Primary Cancer Condition"
Usage: #example
* id = "mCODEPrimaryCancerConditionExample01"
* clinicalStatus = $ClinStatus#active "Active"
* verificationStatus = $VerStatus#confirmed "Confirmed"
* code = SCT#254637007 "Non-small cell lung cancer (disorder)"
* extension[HistologyMorphologyBehavior].valueCodeableConcept = SCT#35917007 "Adenocarcinoma"
* bodySite = SCT#39607008 "Lung structure (body structure)"
* bodySite.extension[Laterality].valueCodeableConcept = SCT#7771000 "Left (qualifier value)"
* subject = Reference(mCODEPatientExample01)
* onsetDateTime = "2019-04-01"
* asserter = Reference(mCODEPractitionerExample01)
* stage.summary = AJCC#3C "IIIC"
* stage.assessment = Reference(mCODETNMClinicalStageGroupExample01)
The FSH language is designed to support creation of StructureDefinitions for Profiles and Extensions, ValueSets, and CodeSystems. Tools like SUSHI address the creation of the ImplementationGuide resource, which is important for producing an IG. However, there are other conformance resources involved with IG creation not explicitly supported by FSH. These include CapabilityStatement, OperationDefinition, SearchParameter, and CompartmentDefinition.
These conformance resources are created using FSH instance grammar. For example, to create a CapabilityStatement, use InstanceOf: CapabilityStatement
. The CapabilityStatement is populated using assignment statements.
Invariants are defined using the keywords Invariant
, Description
, Expression
, Severity
, and XPath
. The keywords correspond directly to elements in ElementDefinition.constraint. An invariant definition cannot have rules, and are incorporated into a profile via obeys rules.
Keyword | Usage | Corresponding element in ElementDefinition | Data Type | Required |
---|---|---|---|---|
Invariant | Identifier for the invariant | constraint.key | id | yes |
Description | Human description of constraint | constraint.human | string | yes |
Expression | FHIRPath expression of constraint | constraint.expression | FHIRPath string | no |
Severity | Either #error or #warning, as defined in ConstraintSeverity | constraint.severity | code | yes |
XPath | XPath expression of constraint | constraint.xpath | XPath string | no |
Example:
Define an invariant found in US Core using FSH:
Invariant: us-core-8
Description: "Patient.name.given or Patient.name.family or both SHALL be present"
Expression: "family.exists() or given.exists()"
Severity: #error
XPath: "f:given or f:family"
Mappings are an optional part of an SD, intended to help implementers understand the SD in relation to other standards. While it is possible to define mappings using escape (caret) syntax, FSH provides a more concise approach. These mappings are informative and are not to be confused with the computable mappings provided by FHIR Mapping Language and the StructureMap resource.
To create a mapping, the keywords Mapping
, Source
, and Target
are required, and Title
and Description
are OPTIONAL.
Keyword | Usage | SD element |
---|---|---|
Mapping | Appears first and provides a unique name for the mapping | n/a |
Source | The name or id of the profile the mapping applies to | n/a |
Target | The URL, URI, or OID for the specification being mapped to | mapping.uri |
Id | An internal identifier for the target specification | mapping.identity |
Title | A human-readable name for the target specification | mapping.name |
Description | Additional information such as version notes, issues, or scope limitations. | mapping.comment |
The mappings themselves are declared in rules with the following syntaxes:
* -> "{map string}" "{comment string}" #{mime-type code}
* <element> -> "{map string}" "{comment string}" #{mime-type code}
The first type of rule applies to mapping the profile as a whole to the target specification. The second type of rule maps a specific element to the target.
The map
, comment
, and mime-type
are as defined in FHIR and correspond to elements in StructureDefinition.mapping and ElementDefinition.mapping (map corresponds to mapping.map, mime-type to mapping.language, and comment to mapping.comment). The mime type code MUST come from FHIR’s MimeType value set. For further information, the reader is referred to the FHIR definitions of these elements.
Note: Unlike setting the mapping.map directly in the SD, mapping rules within a Mapping item do not include the name of the resource in the path on the left hand side.
Examples:
Map the entire profile to a Patient item in another specification:
* -> "Patient" "This profile maps to Patient in Argonaut"
Map the identifier.value element from one IG to another:
* identifier.value -> "Patient.identifier.value"
Define a map between USCorePatient and Argonaut:
Mapping: USCorePatientToArgonaut
Source: USCorePatient
Target: "http://unknown.org/Argonaut-DQ-DSTU2"
Title: "Argonaut DSTU2"
Id: argonaut-dq-dstu2
* -> "Patient"
* extension[USCoreRaceExtension] -> "Patient.extension[http://fhir.org/guides/argonaut/StructureDefinition/argo-race]"
* extension[USCoreEthnicityExtension] -> "Patient.extension[http://fhir.org/guides/argonaut/StructureDefinition/argo-ethnicity]"
* extension[USCoreBirthSexExtension] -> "Patient.extension[http://fhir.org/guides/argonaut/StructureDefinition/argo-birthsex]"
* identifier -> "Patient.identifier"
* identifier.system -> "Patient.identifier.system"
* identifier.value -> "Patient.identifier.value"
To define a profile, the keywords Profile
and Parent
are required, and Id
, Title
, and Description
are OPTIONAL. Rules defining the profile follow immediately after the keyword section.
Example:
Define a profile for exposure to a pathogen:
Profile: KnownExposureSetting
Parent: Observation
Id: known-exposure-setting
Title: "Known Exposure Setting Profile"
Description: "The setting where an individual was exposed to a contagion."
* code = LNC#81267-7 // Setting of exposure to illness
* value[x] only CodeableConcept
* valueCodeableConcept from https://loinc.org/vs/LL3991-8 (extensible)
Rule sets provide the ability to define a group rules as an independent entity. Through insert rules, they can be incorporated into a compatible target. FSH behaves as if the rules in a rule set are copied into the target. As such, the inserted rules have to make sense where they are inserted. Once defined, a single rule set can be used in multiple places.
All types of rules can be used in rule sets, including insert rules, enabling the nesting of rule sets in other rule sets. However, circular dependencies are not allowed.
Rule sets are defined by using the keyword RuleSet
:
RuleSet: {name}
{rule1}
{rule2}
// More rules
Example:
Define a rule set for metadata to be used in multiple profiles:
RuleSet: RuleSet1
* ^status = #draft
* ^experimental = true
* ^publisher = "Elbonian Medical Society"
A value set is a group of coded values representing acceptable values for a FHIR element whose data type is code, Coding, CodeableConcept, Quantity, string, or url.
Value sets are defined using the declarative keyword ValueSet
, with OPTIONAL keywords Id
, Title
and Description
.
Codes MUST be taken from one or more terminology systems (also called code systems or vocabularies). Codes cannot be defined inside a value set. If necessary, you can define your own code system.
The contents of a value set are defined by a set of rules. There are four types of rules to populate a value set:
Note: In value set rules, the word
include
is OPTIONAL.
To include… | Syntax | Example |
---|---|---|
A single code | * include {Coding} |
* include SCT#961000205106 "Wearing street clothes, no shoes" or equivalently, * SCT#961000205106 "Wearing street clothes, no shoes" |
All codes from another value set | * include codes from valueset {ValueSet name|id|url} |
* include codes from valueset http://hl7.org/fhir/ValueSet/data-absent-reason or equivalently, * codes from valueset http://hl7.org/fhir/ValueSet/data-absent-reason |
All codes from a code system | * include codes from system {CodeSystem name|id|url} |
* include codes from system http://snomed.info/sct or equivalently, * codes from system http://snomed.info/sct |
Selected codes from a code system (filters are code system dependent) | * include codes from system {CodeSystem name|id|url} where {filter} and {filter} and ... |
* include codes from system SCT where concept is-a #254837009 or equivalently, * codes from system SCT where concept is-a #254837009 |
See below for discussion of filters.
Analogous rules can be used to leave out certain codes, with the word exclude
replacing the word include
:
To exclude… | Syntax | Example |
---|---|---|
A single code | * exclude {Coding} |
* exclude SCT#961000205106 "Wearing street clothes, no shoes" |
All codes from another value set | * exclude codes from valueset {ValueSet name|id|url} |
* exclude codes from valueset http://hl7.org/fhir/ValueSet/data-absent-reason |
All codes from a code system | * exclude codes from system {CodeSystem name|id|url} |
* exclude codes from system http://snomed.info/sct |
Selected codes from a code system (filters are code system dependent) | * exclude codes from system {CodeSystem name|id|url} where {filter} |
* exclude codes from system SCT where concept is-a #254837009 |
A filter is a logical statement in the form {property} {operator} {value}
, where operator is chosen from the FilterOperator value set. Not all operators in that value set are valid for all code systems. The property
and value
are dependent on the code system. For choices for the most common code systems, see the FHIR documentation on filters.
Examples
Define a value set using extensional rules. This example demonstrates the optionality of the word include
:
ValueSet: BodyWeightPreconditionVS
Title: "Body weight preconditions."
Description: "Circumstances for body weight measurement."
* SCT#971000205103 "Wearing street clothes with shoes"
* SCT#961000205106 "Wearing street clothes, no shoes"
* SCT#951000205108 "Wearing underwear or less"
Define a value set using intensional rules:
ValueSet: HistologyMorphologyBehaviorVS
Id: mcode-histology-morphology-behavior-vs
Title: "Histology Morphology Behavior Value Set"
Description: "Codes representing the structure, arrangement, and behavioral characteristics of malignant neoplasms, and cancer cells.
* include codes from system SCT where concept is-a #367651003 "Malignant neoplasm of primary, secondary, or uncertain origin (morphologic abnormality)"
* include codes from system SCT where concept is-a #399919001 "Carcinoma in situ - category (morphologic abnormality)"
* include codes from system SCT where concept is-a #399983006 "In situ adenomatous neoplasm - category (morphologic abnormality)"
* exclude codes from system SCT where concept is-a #450893003 "Papillary neoplasm, pancreatobiliary-type, with high grade intraepithelial neoplasia (morphologic abnormality)"
* exclude codes from system SCT where concept is-a #128640002 "Glandular intraepithelial neoplasia, grade III (morphologic abnormality)"
* exclude codes from system SCT where concept is-a #450890000 "Glandular intraepithelial neoplasia, low grade (morphologic abnormality)"
* exclude codes from system SCT where concept is-a #703548001 "Endometrioid intraepithelial neoplasia (morphologic abnormality)"
Note: Intensional and extensional forms can be used together in a single value set definition.
Abbreviation | Description |
---|---|
ANTLR4 | ANother Tool for Language Recognition, version 4 |
D | Flag denoting draft status |
FHIR | Fast Healthcare Interoperability Resources |
FSH | FHIR Shorthand |
IG | Implementation Guide |
JSON | JavaScript Object Notation |
LNC | Common FSH alias for LOINC |
LOINC | Logical Observation Identifiers Names and Codes |
NPI | National Provider Identifier (US) |
N | Flag denoting normative element |
MS | Flag denoting a Must Support element |
OID | Object Identifier |
SCT | Common FSH alias for SNOMED Clinical Terms |
SD | StructureDefinition |
SU | Flag denoting “include in summary” |
TU | Flag denoting trial use element |
UCUM | Unified Code for Units of Measure |
URL | Uniform Resource Locator |
XML | Extensible Markup Language |
The grammar of FSH described in ANTLR4:
grammar FSH;
doc: entity* EOF;
entity: alias | profile | extension | invariant | instance | valueSet | codeSystem | ruleSet | mapping;
alias: KW_ALIAS SEQUENCE EQUAL SEQUENCE;
profile: KW_PROFILE SEQUENCE sdMetadata+ sdRule*;
extension: KW_EXTENSION SEQUENCE sdMetadata* sdRule*;
sdMetadata: parent | id | title | description | mixins;
sdRule: cardRule | flagRule | valueSetRule | fixedValueRule | containsRule | onlyRule | obeysRule | caretValueRule | insertRule;
instance: KW_INSTANCE SEQUENCE instanceMetadata* instanceRule*;
instanceMetadata: instanceOf | title | description | usage | mixins;
instanceRule: fixedValueRule | insertRule;
invariant: KW_INVARIANT SEQUENCE invariantMetadata+;
invariantMetadata: description | expression | xpath | severity;
valueSet: KW_VALUESET SEQUENCE vsMetadata* vsRule*;
vsMetadata: id | title | description;
vsRule: vsComponent | caretValueRule | insertRule;
codeSystem: KW_CODESYSTEM SEQUENCE csMetadata* csRule*;
csMetadata: id | title | description;
csRule: concept | caretValueRule | insertRule;
ruleSet: KW_RULESET SEQUENCE ruleSetRule+;
ruleSetRule: sdRule | concept | vsComponent;
mapping: KW_MAPPING SEQUENCE mappingMetadata* mappingEntityRule*;
mappingMetadata: id | source | target | description | title;
mappingEntityRule: mappingRule | insertRule;
// METADATA FIELDS
parent: KW_PARENT SEQUENCE;
id: KW_ID SEQUENCE;
title: KW_TITLE STRING;
description: KW_DESCRIPTION (STRING | MULTILINE_STRING);
expression: KW_EXPRESSION STRING;
xpath: KW_XPATH STRING;
severity: KW_SEVERITY CODE;
instanceOf: KW_INSTANCEOF SEQUENCE;
usage: KW_USAGE CODE;
mixins: KW_MIXINS ((SEQUENCE KW_AND)* SEQUENCE | COMMA_DELIMITED_SEQUENCES);
source: KW_SOURCE SEQUENCE;
target: KW_TARGET STRING;
// RULES
cardRule: STAR path CARD flag*;
flagRule: STAR ((path KW_AND)* path | paths) flag+;
valueSetRule: STAR path KW_UNITS? KW_FROM SEQUENCE strength?;
fixedValueRule: STAR path KW_UNITS? EQUAL value KW_EXACTLY?;
containsRule: STAR path KW_CONTAINS item (KW_AND item)*;
onlyRule: STAR path KW_ONLY targetType (KW_OR targetType)*;
obeysRule: STAR path? KW_OBEYS SEQUENCE (KW_AND SEQUENCE)*;
caretValueRule: STAR path? caretPath EQUAL value;
mappingRule: STAR path? ARROW STRING STRING? CODE?;
insertRule: STAR KW_INSERT SEQUENCE;
// VALUESET COMPONENTS
vsComponent: STAR ( KW_INCLUDE | KW_EXCLUDE )? ( vsConceptComponent | vsFilterComponent );
vsConceptComponent: code vsComponentFrom?
| (code KW_AND)+ code vsComponentFrom
| COMMA_DELIMITED_CODES vsComponentFrom;
vsFilterComponent: KW_CODES vsComponentFrom (KW_WHERE vsFilterList)?;
vsComponentFrom: KW_FROM (vsFromSystem (KW_AND vsFromValueset)? | vsFromValueset (KW_AND vsFromSystem)?);
vsFromSystem: KW_SYSTEM SEQUENCE;
vsFromValueset: KW_VSREFERENCE ((SEQUENCE KW_AND)* SEQUENCE | COMMA_DELIMITED_SEQUENCES);
vsFilterList: (vsFilterDefinition KW_AND)* vsFilterDefinition;
vsFilterDefinition: SEQUENCE vsFilterOperator vsFilterValue?;
vsFilterOperator: EQUAL | SEQUENCE;
vsFilterValue: code | KW_TRUE | KW_FALSE | REGEX | STRING;
// MISC
path: SEQUENCE | KW_SYSTEM;
paths: COMMA_DELIMITED_SEQUENCES;
caretPath: CARET_SEQUENCE;
flag: KW_MOD | KW_MS | KW_SU | KW_TU | KW_NORMATIVE | KW_DRAFT;
strength: KW_EXAMPLE | KW_PREFERRED | KW_EXTENSIBLE | KW_REQUIRED;
value: SEQUENCE | STRING | MULTILINE_STRING | NUMBER | DATETIME | TIME | reference | canonical | code | quantity | ratio | bool ;
item: SEQUENCE (KW_NAMED SEQUENCE)? CARD flag*;
code: CODE STRING?;
concept: STAR code (STRING | MULTILINE_STRING)?;
quantity: NUMBER UNIT;
ratio: ratioPart COLON ratioPart;
reference: (OR_REFERENCE | PIPE_REFERENCE) STRING?;
canonical: CANONICAL;
ratioPart: NUMBER | quantity;
bool: KW_TRUE | KW_FALSE;
targetType: SEQUENCE | reference;
// KEYWORDS
KW_ALIAS: 'Alias' WS* ':';
KW_PROFILE: 'Profile' WS* ':';
KW_EXTENSION: 'Extension' WS* ':';
KW_INSTANCE: 'Instance' WS* ':';
KW_INSTANCEOF: 'InstanceOf' WS* ':';
KW_INVARIANT: 'Invariant' WS* ':';
KW_VALUESET: 'ValueSet' WS* ':';
KW_CODESYSTEM: 'CodeSystem' WS* ':';
KW_RULESET: 'RuleSet' WS* ':';
KW_MAPPING: 'Mapping' WS* ':';
KW_MIXINS: 'Mixins' WS* ':';
KW_PARENT: 'Parent' WS* ':';
KW_ID: 'Id' WS* ':';
KW_TITLE: 'Title' WS* ':';
KW_DESCRIPTION: 'Description' WS* ':';
KW_EXPRESSION: 'Expression' WS* ':';
KW_XPATH: 'XPath' WS* ':';
KW_SEVERITY: 'Severity' WS* ':';
KW_USAGE: 'Usage' WS* ':';
KW_SOURCE: 'Source' WS* ':';
KW_TARGET: 'Target' WS* ':';
KW_MOD: '?!';
KW_MS: 'MS';
KW_SU: 'SU';
KW_TU: 'TU';
KW_NORMATIVE: 'N';
KW_DRAFT: 'D';
KW_FROM: 'from';
KW_EXAMPLE: '(' WS* 'example' WS* ')';
KW_PREFERRED: '(' WS* 'preferred' WS* ')';
KW_EXTENSIBLE: '(' WS* 'extensible' WS* ')';
KW_REQUIRED: '(' WS* 'required' WS* ')';
KW_CONTAINS: 'contains';
KW_NAMED: 'named';
KW_AND: 'and';
KW_ONLY: 'only';
KW_OR: 'or';
KW_OBEYS: 'obeys';
KW_TRUE: 'true';
KW_FALSE: 'false';
KW_INCLUDE: 'include';
KW_EXCLUDE: 'exclude';
KW_CODES: 'codes';
KW_WHERE: 'where';
KW_VSREFERENCE: 'valueset';
KW_SYSTEM: 'system';
KW_UNITS: 'units';
KW_EXACTLY: '(' WS* 'exactly' WS* ')';
KW_INSERT: 'insert';
// SYMBOLS
EQUAL: '=';
STAR: '*' [0-9]*;
COLON: ':';
COMMA: ',';
ARROW: '->';
// PATTERNS
// " CHARS "
STRING: '"' (~[\\"] | '\\r' | '\\n' | '\\t' | '\\"' | '\\\\')* '"';
// """ CHARS """
MULTILINE_STRING: '"""' .*? '"""';
// +/- ? DIGITS( . DIGITS)?
NUMBER: [+\-]? [0-9]+('.' [0-9]+)?;
// ' UCUM UNIT '
UNIT: '\'' (~[\\'])* '\'';
// SYSTEM # SYSTEM
CODE: SEQUENCE? '#' (SEQUENCE | CONCEPT_STRING);
CONCEPT_STRING: '"' (NONWS_STR | '\\"' | '\\\\')+ (WS (NONWS_STR | '\\"' | '\\\\')+)* '"';
// YEAR ( - MONTH ( - DAY ( T TIME )?)?)?
DATETIME: [0-9][0-9][0-9][0-9]('-'[0-9][0-9]('-'[0-9][0-9]('T' TIME)?)?)?;
// HOUR ( : MINUTE ( : SECOND ( . MILLI )?)?)?( Z | +/- HOUR : MINUTE )?
TIME: [0-9][0-9](':'[0-9][0-9](':'[0-9][0-9]('.'[0-9]+)?)?)?('Z' | ('+' | '-')[0-9][0-9]':'[0-9][0-9])?;
// DIGITS .. (DIGITS | * )
CARD: ([0-9]+)? '..' ([0-9]+ | '*')?;
// Reference ( ITEM | ITEM )
OR_REFERENCE: 'Reference' WS* '(' WS* SEQUENCE WS* (WS 'or' WS+ SEQUENCE WS*)* ')';
PIPE_REFERENCE: 'Reference' WS* '(' WS* SEQUENCE WS* ('|' WS* SEQUENCE WS*)* ')';
// Canonical(Item)
CANONICAL: 'Canonical' WS* '(' WS* SEQUENCE WS* ('|' WS* SEQUENCE WS*)? ')';
// ^ NON-WHITESPACE
CARET_SEQUENCE: '^' NONWS+;
// '/' EXPRESSION '/'
REGEX: '/' ('\\/' | ~[*/\r\n])('\\/' | ~[/\r\n])* '/';
COMMA_DELIMITED_CODES: (CODE (WS+ STRING)? WS* COMMA WS+)+ CODE (WS+ STRING)?;
// (NON-WS WS , WS )+ NON-WS
COMMA_DELIMITED_SEQUENCES: (SEQUENCE WS* COMMA WS*)+ SEQUENCE;
// NON-WHITESPACE
SEQUENCE: NONWS+;
// FRAGMENTS
fragment WS: [ \t\r\n\f\u00A0];
fragment NONWS: ~[ \t\r\n\f\u00A0];
fragment NONWS_STR: ~[ \t\r\n\f\u00A0\\"];
// IGNORED TOKENS
WHITESPACE: WS -> channel(HIDDEN);
BLOCK_COMMENT: '/*' .*? '*/' -> skip;
LINE_COMMENT: '//' .*? [\r\n] -> skip;