XULE is an XBRL processing syntax that lets users manipulate and evaluate XBRL content, including facts, reports, and taxonomies, so that the information can be normalized, verified and displayed consistently.

1. Get started with XULE

How XULE works

Compiled XULE files (.zip format) read XBRL instance documents through XBRL processing software. Arelle is an open-source processor with a command line interface that supports compiled XULE expressions using the XULE plugin included with the software distribution.

XULE expressions can be compiled as part of the run time command or compiled and saved, then processed in a separate command.

Requirements for these modules

  1. The Arelle processor (download from https://arelle.org/arelle/pub)
  2. The XULE plugin, which is included in the desktop distributions at the Arelle download URL
  3. Command line familiarity and access to the Internet from a command or terminal window
  4. An XBRL instance or a URL to an instance file
  5. Text editing software and a valid XULE expression

Create, Compile and Run a XULE expression

1. Create a valid XULE expression. Copy/paste the example below in a text editing program (Notepad, Notepad++, VS Code) and save it as a file called ‘sample.xule’ (the file must use the .xule extension – it cannot be sample.xule.txt). Once compiled as a .zip file and invoked against an XBRL instance document, this expression will return the non-dimensional value(s) for Assets from the target report.

output example
[@concept.local-name = 'Assets']

In the Arelle command below, the sample.xule is compiled and processed in a single step.

2. Create an Arelle command for XULE and navigate to Arelle. For the purposes of these modules, copy and paste the command below into the bottom of your sample.xule file. Modify the command to work with your OS program and path/to details. The path/to details are the locations where the sample.xule is located and where the sample.zip will be saved – these can be in the same directory. The command you create from the template will be re-used throughout these introductory modules. Save the modified Arelle command in the sample.xule file.

Windows users:

// .\arellecmdline.exe --plugins "xule | transforms/SEC" -f "https://www.sec.gov/Archives/edgar/data/1753673/000121390022027184/f20f2021_scienjoyhold.htm" --xule-compile "path/to/sample.xule" --xule-run --xule-rule-set "path/to/sample.zip"

Unix and Mac users: // python3.9 arellecmdline.py --plugins "xule | transforms/SEC" -f "https://www.sec.gov/Archives/edgar/data/1753673/000121390022027184/f20f2021_scienjoyhold.htm" --xule-compile "path/to/sample.xule" --xule-run --xule-rule-set "path/to/sample.zip"

Open a command prompt or terminal window and navigate to the directory where Arelle is installed (for Windows users, the default install location is C:\program files\Arelle.).

3. Execute the Arelle command to compile and run XULE. Copy and paste the modified command without the “//” and space preceding the command into the command line / terminal window and press enter (these characters are used to prevent XULE from reading this line). NOTE: If the computer’s command line or terminal is restricted from accessing the Internet, you can try saving the .htm file from a web browsing window and modifying the command to point to the file on the computer.

Since the command line example does not specify an output file (see below for additional options), three [example] results are returned to the screen. Also, the order of facts is not defined for the output in the XULE expression, so the sequence of facts on the screen might vary from below:

[example] 811,837,000 – https://www.sec.gov/Archives/edgar/data/1753673/000121390022027184/f20f2021_scienjoyhold.htm 16130
[example] 1,089,355,000 – https://www.sec.gov/Archives/edgar/data/1753673/000121390022027184/f20f2021_scienjoyhold.htm 16131
[example] 170,946,000 – https://www.sec.gov/Archives/edgar/data/1753673/000121390022027184/f20f2021_scienjoyhold.htm 16132

Understanding the Arelle command with XULE options (arguments)

XULE is one of several plugins included in the Arelle distribution. In the example, the Arelle command line program (arellecmdline.py) invoked the XULE plugin and also the SEC transformations for inline XBRL with the --plugins argument. XULE has its own set of arguments (see --help for a full list), including:

  • -f – specifies the instance document that will be processed. This can be a url or a local file
  • --xule-compile – identifies the location of the XULE expression file. In this case, the file is sample.xule
  • --xule-rule-set – tells the command line where to save the compiled XULE rule(s)
  • --xule-run – instructs XULE to run the compiled file over the target instance

Arelle’s command line program has some additional options, too:

  • --logFile – specifies where the output of the XULE expression should be saved (either as .xml or .json). If a local file is not specified, output will be displayed in the command window
  • --noCertificateCheck – performs an https connection to the SEC

Using in list() for multiple items

To include more than one concept, replace the = with in list() and put the concepts in a comma-separated list

output example
[@concept.local-name in list('Assets','Liabilities')]

The next module will introduce concepts for getting: additional facts from an XBRL instance, and; details about the facts returned.

2. Getting a XULE message with factset details

An XBRL fact is surrounded by metadata, and XULE can also include these details. In this module, you’ll be editing XULE expressions, which is detailed work (like writing computer programming). Consider installing the free syntax highlighter that works with VS Code to keep your process on-track and error-free https://xbrl.us/xule-editor.

Understanding a simple XULE expression

When you open the sample.xule file from the prior module in VS Code with the XULE Editor enabled, the different parts of the expression are highlighted, similar to what’s below:

output example
[@concept.local-name in list('Assets','Liabilities')]

  • output is a key word that tells XULE what to do for a specific step in the script; other options include message, constant, namespace and assert
  • example is the name for the rule used in these modules. It appears at the beginning of each output.
  • Square brackets [ ] are non-dimensional fact containers for a filter expression. IMPORTANT: these square brackets tell XULE to filter the factset for only non-dimensional facts. Curly braces { } are used to evaluate all facts in an instance document.
  • @ is placed at the beginning of a filter expression to indicate the fact dimension under evaluation (@concept, @period or @unit for example). Using this one character alone in a XULE expression will return all the facts in the instance.
  • concept is the dimension that’s being evaluated by the rule; other factset options include period, unit, entity and taxonomy-specific dimensions.
  • local-name represents the string value of the concept. local-name follows dot-notation from the dimension and filters a specific property of the dimension; other factset properties of concept include period-type, data-type, namespace-uri, balance and more.
  • in is an operator that evaluates if the item before the in is included within the following set or list of items.
  • the list function allows the definition of a collection of items (an array).
  • 'Assets' is a string value included in the list. The concept filter of local-name @concept.local-name returns a list of string items from the instance, these are then compared to the string values in the list.

Creating a rule message with details

The bracketed XULE expression in the example above is a rule. A message declaration following the rule can use the variable $rule-value (the returned fact value of the rule). The properties of the fact can be returned using dot notation to report out properties of the facts returned. Building on the earlier fact example, the following message can be added to return the concept, period, value and unit properties of the fact:

output example
[@concept.local-name in list('Assets','Liabilities')]
message "The value of {$rule-value.concept.local-name} for {$rule-value.period}: {$rule-value} {$rule-value.unit}"

Filtering the rule by fact metadata

The functions of an aspect can be combined to create rules that refine results. For example, adding the following to the sample.xule rule within the bracket following the list of concepts will isolate the results to show these facts for 2019 only:

@period = date('2021-12-31')

The next module will introduce concepts for setting constants, using variables and evaluating facts.

3. Setting constants and using simple functions to evaluate facts

One of the simplest logic functions is counting the number of results in an output. In the exercise, you’ll create constants from the @period and @unit aspects that serve as filters for the facts in a report, add a second XULE output and message that reports the number of qualified facts in the entire report, and build a simple calculation output with a conditional message.

The keyword constant in XULE is used to set a global variable that can be used across a group of XULE rules.

In the preceding exercise, the @period aspect was used to filter two concepts by a specific date

output example
[@concept.local-name in list('Assets','Liabilities') @period = date('2020-12-31')]
message "The value of {$rule-value.concept.local-name} for {$rule-value.period}: {$rule-value} {$rule-value.unit}"

To define a constant for a period, place the following line in the sample.xule file.

constant $period = date('2021-12-31')

Also change the period filter to use $period:

@period = $period

The sample.xule should now look like this:

constant $period = date('2021-12-31')
output example
[@concept.local-name in list('Assets','Liabilities') @period = $period]
message "{$rule-value.concept.local-name} for {$rule-value.period}: {$rule-value} {$rule-value.unit}"

The results for the report output will be:

[example] Assets for 2021-12-31: 170,946,000 USD – https://www.sec.gov/Archives/edgar/data/1753673/000121390022027184/f20f2021_scienjoyhold.htm 16132
[example] Liabilities for 2021-12-31: 43,926,000 USD – https://www.sec.gov/Archives/edgar/data/1753673/000121390022027184/f20f2021_scienjoyhold.htm 16217
[example] Assets for 2021-12-31: 1,089,355,000 CNY – https://www.sec.gov/Archives/edgar/data/1753673/000121390022027184/f20f2021_scienjoyhold.htm 16131
[example] Liabilities for 2021-12-31: 279,910,000 CNY – https://www.sec.gov/Archives/edgar/data/1753673/000121390022027184/f20f2021_scienjoyhold.htm 16216

Since the example filing contains multiple unit types (USD, CNY), setting a constant for the unit aspect will make it possible to further filter, evaluate and perform operations. All units (like currency here, or a volume measure, which could be utr:gal) require a prefix that must reference a namespace and be declared using the keyword namespace. There’s more about namespaces in another module.

Place the following two lines above the output to create a constant – $unit – for China’s yuan currency.

constant $unit = unit(iso4217:CNY)
iso4217 = http://www.xbrl.org/2003/iso4217

To count how many facts fit report with this unit type, create a second XULE output and message at the bottom of the sample.xule file:

output examplecount
list([covered @concept.local-name in list('AssetsNoncurrent','AssetsCurrent') @period = $period @unit = $unit]).length
message"There are {$rule-value} non-dimensional {$unit} facts for {$period} in the list of evaluated concepts."

In the new output, the operator list().length reports the number of times the XULE expression finds a matching fact within the evaluated filing. The keyword covered is used as part of the non-dimensional expression to unalign (or cover) all aspects of the fact. In this case, the keyword covered has the effect of grouping the iterations of a rule into a single result. Note – by using .length, the rule returns the number of items in the list as an integer; as a simplification for this module, the concept local names have been dropped from the message.

Evaluating XULE expressions

In accounting, total assets is the sum of non-current assets and current assets. While total assets is a reported number in the report, XULE can also calculate this total (among the many logic and math functions available) to confirm data veracity. The ‘example sum’ output creates variables for these three elements and evaluates whether they are correct. The conditional message reports the outcome.

output examplesum
$nca = [@concept.local-name = 'AssetsNoncurrent' @period = $period @unit = $unit]
$ca = [@concept.local-name = 'AssetsCurrent' @period = $period @unit = $unit]
$a = [@concept.local-name = 'Assets' @period = $period @unit = $unit]
$ncaa = $nca + $ca
$a == $ncaa

   {if $a == $ncaa
    "In the report, the sum of the values of " + $nca.concept.local-name.string + ", which is " + $nca + " " + $unit.string + " and " + $ca.concept.local-name.string + ", which is " + $ca + " " + $unit.string + " for " + $period.end.string + " is equal to " + $ncaa + " " + $unit.string + ", which is the value of Assets: " + $a + " " + $unit.string + "."
   "There might be an issue in the calculation for " + $a.concept.local-name.string + " at " + $period.end.string + ", which is calculated as " + $ncaa + " " + $unit.string + " and reported as " + $a + " " + $unit.string + " in this report."}

Please provide feedback on these Introduction to XULE modules.

code key