Introduction

OcaltQL is a query language engineered by Ocalt. Before learning verbs, variables, or IF, it is important to understand the rule that governs how statements connect: the chain keyword, not the line break, determines how a script is interpreted.

What Connects Statements

An OcaltQL script is a sequence of verbs, subjects, and prepositions that describes what should happen and in what order. The order is not determined by how the script is arranged on the page. Instead, statements are connected by one of three chain keywords: AFTER, AND, or OR. Understanding these keywords is fundamental to understanding how OcaltQL scripts are structured and executed.

Keyword Description
AFTER Wait for the previous statement to finish, then execute this statement. Sequential execution.
AND Execute this statement in parallel with the previous statement. Parallel execution.
OR Execute this statement only when the previous statement returns false, null, or a soft error. Fallback execution.

A common assumption is that a new line ends the current chain. It does not. Line breaks do not connect statements; chain keywords do. The visual layout of the script — including line breaks, indentation, and comments — is intended for readability and has no role in determining execution order.

The remainder of this page examines this rule in detail so that the distinction between formatting and program structure is completely clear.

What a Statement Is

Every OcaltQL statement follows one pattern:

Example
[CHAIN] [SUBJECT] VERB [MODE] [ARGS] [PREPOSITION TARGET] [SET ?var]

The elements of this pattern are described below.

Two important consequences follow directly from this pattern.

Fact one. The first statement in a chain has no chain keyword. This is the only position in which a statement may begin with a bare verb.

Example
EMIT "Hello World"

This is valid because it is the first statement in the script; there is no preceding statement to follow.

Fact two. Every statement after the first must include a chain keyword, regardless of whether it appears on a new line.

Example
EMIT "Hello World"
AFTER EMIT "Done"

The second line is the second statement in the same chain, not a new script. AFTER connects the statements; the line break has no effect on their relationship.

A full stop at the end of a line is optional and has no effect:

Example
EMIT "Hello World".
AFTER EMIT "Same result"

Keywords are case-insensitive: EMIT, emit, and Emit are equivalent. Variable names are case-sensitive: ?var and ?Var refer to different variables. Double quotes, single quotes, and backticks are interchangeable as string delimiters. None of these details changes the way the chain is formed.

Example
emit "Hello World"
after emit "This works too"
AFTER Emit "So does this"

AFTER Runs in Sequence, Not by Line

AFTER waits for the preceding statement to finish before executing the next statement. Output is appended in execution order. A common assumption is that starting a new line ends a chain; it does not. AFTER continues the chain across as many lines as necessary.

The following three scripts represent the same program.

All statements on one line:

Example
EMIT "A" AFTER EMIT "B" AFTER EMIT "C"
(* Output: ABC *)

One statement per line, with the chain keyword at the beginning. The same chain is represented across multiple lines and behaves identically:

Example
EMIT "start"
AFTER EMIT "middle"
AFTER EMIT "end"
(* Output: startmiddleend *)

The chain keyword may also appear on its own line, with the statement indented beneath it. The result is still the same chain:

Example
EMIT "begin"
AFTER
  EMIT "middle"
AFTER
  EMIT "end"
(* Output: beginmiddleend *)

The runtime does not interpret these as three independent lines. It interprets them as one chain because AFTER connects the statements. Neither the line break, the indentation, nor the full stop determines the sequence.

Several statements may appear on the same line because the chain keyword, rather than the line break, is the delimiter:

Example
EMIT "Hello" AFTER EMIT "World" AFTER EMIT "!"

A statement may also be split across multiple lines for readability, with the chain keyword placed at the beginning:

Example
EMIT "begin"
AFTER
  STRING "line one" SET ?log
AFTER EMIT ?log

Each example expresses the same structure: execute one statement, after another, after another. The keyword carries the structural meaning; the line break is only formatting.

Step-by-Step: What the Runtime Does

Consider the following script:

Example
EMIT "start"
AFTER EMIT "middle"
AFTER EMIT "end"
  1. The first statement has no chain keyword: EMIT "start". The runtime writes start to the response, and the statement completes successfully.
  2. The runtime encounters AFTER and waits for step 1 to finish. It has finished, so EMIT "middle" executes. The response becomes startmiddle.
  3. The runtime encounters the next AFTER and waits for step 2 to finish. It has finished, so EMIT "end" executes. The response becomes startmiddleend.
  4. There are no additional statements, so the script terminates.

The runtime never applies a rule such as “new line, new script.” No such rule exists in OcaltQL.

A Common Mistake

A common mistake is to assume that two lines represent two separate statements:

Example
EMIT "Hello World"
EMIT "Done"

OcaltQL does not use a “next line means next statement” rule. Because the second EMIT has no chain keyword, it does not form a valid second statement. A chain keyword is required at that position.

The intended form is:

Example
EMIT "Hello World"
AFTER EMIT "Done"

The same chain may also be written on a single line:

Example
EMIT "Hello World" AFTER EMIT "Done"

The chain is identical; only its formatting has changed.

The First Statement Is Bare; Every Later Statement Is Not

The same rule is important from the opposite direction because it is also possible to overcorrect after learning that later statements require a chain keyword.

A common mistake is to place AFTER on the first statement as well:

Example
AFTER EMIT "no"

There is no preceding statement to follow. The first statement in a chain therefore has no chain keyword. This is a grammar rule, not a formatting preference: [CHAIN] is absent from the first statement.

A chain begins in the following locations:

In every other position, the script is already inside a chain, so the next statement requires a chain keyword.

Remember the following distinction:

A new line does not start a new chain. A new block starts a new chain only within that block. The block itself remains one statement in the outer chain. This distinction becomes important when working with nested blocks.

Comments Do Not Break the Chain

A (* comment *) placed between chained statements is ignored by the runtime. It documents the script without affecting sequencing. A comment may appear inline, on its own line, or between a chain keyword and the statement that follows it.

Example
(* This is a comment *)
EMIT "Hello World" (* inline comment *)
AFTER EMIT "Done"

A comment placed between chained statements does not terminate the chain, pause execution, or start a new chain:

Example
EMIT "start"
AFTER EMIT "middle"
(* the chain continues unchanged *)
AFTER EMIT "end"
(* Output: startmiddleend *)

Blank lines have the same status: they affect presentation only.

This is why the execution flow can refer to “AFTER THIRD LINE (OR FIFTH).” When counting statements, the EMIT after the comment is the third statement. When counting physical lines, it appears on line five because the blank line and the comment occupy space in the source. The chain is concerned only with statements. Comments and blank lines do not occupy statement positions and do not reset the chain. The next actual statement therefore still requires a chain keyword.

A comment may also appear inside a chain join:

Example
EMIT "one"
AFTER (* the same chain continues *) EMIT "two"
(* Output: onetwo *)

The parser removes the comment, leaving EMIT "one" AFTER EMIT "two". Because comments are not statements, they cannot interrupt a chain of statements.

A Block Is a Single Link in the Chain

This distinction becomes especially important with blocks. An IF ... OPEN ... CLOSE construct may appear self-contained because it has its own opening and closing delimiters. However, the statement following CLOSE does not automatically begin a new outer chain.

Example
EMIT "before"
AFTER IF ?a IS GREATER THAN 10
OPEN
  EMIT "inside the block"
CLOSE
AFTER EMIT "after the block"

Read this example as four outer statements or structural events, rather than as separate lines:

  1. EMIT "before" — the first outer statement. It is bare.
  2. AFTER IF … — the second outer statement. The IF and its body together form the statement.
  3. OPEN … CLOSE — the body of statement 2, not a separate outer statement.
  4. AFTER EMIT "after the block" — the third outer statement. AFTER waits for the entire IF statement to finish before continuing.

The block therefore behaves as a single statement in the outer sequence. AFTER waits for that statement to complete before continuing.

Inside the Block, a New Chain Starts

OPEN starts a nested chain, so the first-statement rule applies again within the block.

Example
STRING "hello" SET ?var
AFTER IF ?var IS SET
OPEN
  EMIT "first block line"
  AFTER EMIT "second block line"
CLOSE

EMIT "first block line" is the first statement of the inner chain, so no AFTER is required.

AFTER EMIT "second block line" is the second statement of the inner chain, so it requires AFTER. Being inside a block does not make line breaks significant; it only establishes a new chain in which the first statement is once again bare.

A WHILE block follows the same structure:

Example
NUMBER 0 SET ?i
AFTER WHILE ?i IS LESS THAN 5
OPEN
  EMIT ?i
  AFTER CALCULATE ?i + 1 SET ?i
CLOSE

Consider one iteration of the inner chain:

  1. EMIT ?i — the first inner statement. It is bare and writes the current value of ?i.
  2. AFTER CALCULATE ?i + 1 SET ?i — the second inner statement. It waits for the preceding EMIT, then increments ?i.

The increment is not “the next line of the loop.” It is the second statement in the loop body and therefore must be chained. Omitting AFTER does not merely change the formatting; it breaks the inner chain.

After CLOSE, the Outer Chain Continues

When CLOSE completes, the inner chain has ended and execution returns to the outer chain. The block counted as one statement in that outer chain, so the next statement is not the first. It therefore requires a chain keyword.

Example
NUMBER 15 SET ?x
AFTER IF ?x IS GREATER THAN 0
OPEN
  EMIT "positive"
  AFTER CALCULATE ?x * 2 SET ?double
CLOSE
AFTER EMIT "doubled to " & ?double

The final AFTER does not mean “after the last line inside the block.” It means “after the entire block.” Without it, the statement intended to run after the block is not correctly connected to the outer chain.

Variables assigned inside IF, WHILE, and LOOP remain accessible outside those blocks because scope is flat across the script. The exception is NEW OPERATION, which executes in its own isolated frame. Thus, ?double remains available after the block. The outer AFTER ensures that it is accessed only after the block has finished.

Example
IF true
OPEN
  STRING "inside" SET ?inner
CLOSE
AFTER EMIT ?inner
(* Output: inside — a variable assigned inside a block is accessible outside it *)

Nested Blocks Are Nested Chains

Blocks may be nested to any depth. Each OPEN starts another inner chain, and each CLOSE returns execution to the enclosing chain. There are no curly braces and no indentation requirements.

Example
LOOP 1 TO 3 SET ?i
OPEN
  IF ?i IS EQUAL TO 2
  OPEN
    EMIT "matched: " & ?i
  CLOSE
  AFTER EMIT "processed " & ?i
CLOSE
AFTER EMIT "loop finished"

Trace one iteration in which ?i is 2:

  1. In the outer script, LOOP is a statement and its body is opened.
  2. Inside the loop body, the first statement is IF ?i IS EQUAL TO 2. It is bare because it is the first statement in that OPEN block.
  3. That IF opens its own chain. Its first, and only, statement is EMIT "matched: 2", which is therefore bare.
  4. The inner CLOSE returns execution to the loop body. The IF was the first statement in that body.
  5. AFTER EMIT "processed 2" is the second statement in the loop body, so it requires AFTER and runs after the IF finishes.
  6. The loop CLOSE is reached after all iterations, returning execution to the outer chain.
  7. AFTER EMIT "loop finished" is the next outer statement, so it requires AFTER and waits for the entire loop to finish.

Two common errors can be identified in this example.

Adding AFTER to the IF would be incorrect when it is the first statement in the loop body because there is no preceding statement in that inner chain.

Omitting AFTER from EMIT "processed" would also be incorrect because that statement is not the first statement in the loop body; the IF already occupies the first position.

OR After CLOSE Is the IF Fallback, Not a New Script

OcaltQL does not use ELSE. Fallback behavior is expressed with OR, while chained conditions use OR IF. These are chain keywords associated with the IF statement itself.

Example
NUMBER 3 SET ?a
AFTER IF ?a IS GREATER THAN 10
OPEN
  EMIT "Big"
CLOSE
OR
OPEN
  EMIT "Small"
CLOSE
AFTER EMIT "always"

Read the chain joins rather than the physical lines:

OR IF is used in the same way:

Example
NUMBER 2 SET ?a
AFTER IF ?a IS EQUAL TO 1
OPEN
  EMIT "One"
CLOSE
OR IF ?a IS EQUAL TO 2
OPEN
  EMIT "Two"
CLOSE
OR IF ?a IS EQUAL TO 3
OPEN
  EMIT "Three"
CLOSE
OR
OPEN
  EMIT "Other"
CLOSE

Each OR IF, as well as the final OR, is a chain keyword attached to the condition. It is not a new statement created by a line break.

Putting It All Together

Combining the rules above — a first statement without a keyword, multi-line AFTER, comments that do not count as statements, an IF that forms one outer statement, a nested chain inside OPEN … CLOSE, and an AFTER following CLOSE — still produces one continuous chain from the first statement to the last.

The following example represents the execution structure seen by the runtime, rather than the visual layout of the source code.

Example
FIRST LINE....
AFTER SECOND LINE....

(* comment *)
AFTER THIRD LINE (OR FIFTH)
AFTER IF CONDITION....
OPEN
FIRST BLOCK LINE
AFTER SECOND BLOCK LINE
CLOSE

AFTER FIRST LINE AFTER BLOCK...

The same structure can be expressed with concrete verbs:

Example
EMIT "first"
AFTER EMIT "second"

(* comment *)
AFTER EMIT "third"
AFTER IF ?a IS GREATER THAN 10
OPEN
  EMIT "first block line"
  AFTER EMIT "second block line"
CLOSE

AFTER EMIT "after the block"

Every line break in this example exists for readability. The comment has no effect. The IF...OPEN...CLOSE block is a single statement in the outer chain, and the AFTER following CLOSE continues that same chain after the block has completed.

Statement-by-Statement Execution

Assume that ?a is 15, so the condition evaluates to true. Blank lines and comments can be ignored because they do not form part of the statement sequence.

# What is written Which chain Why the Keyword Is Used What happens
1 EMIT "first" Outer First statement of the script; therefore, it is bare. Writes first.
2 AFTER EMIT "second" Outer Second statement. Its position is determined by AFTER, not by the new line. Waits for #1, then writes second.
— (* comment *) None Not a statement and therefore has no place in the chain. Ignored by the parser; the chain position is unchanged.
3 AFTER EMIT "third" Outer Third statement. It remains the third statement even though it appears on physical line five. Waits for #2, then writes third.
4 AFTER IF ?a IS GREATER THAN 10 Outer Fourth statement. The IF construct is a statement in the outer chain. Waits for #3. The condition is true, so the body executes.
4a EMIT "first block line" Inner (the IF body) First statement inside OPEN; therefore, it is bare. Writes first block line.
4b AFTER EMIT "second block line" Inner Second statement inside the same OPEN block. Waits for 4a, then writes second block line.
— CLOSE — Ends the inner chain but not the outer chain. Returns to the outer chain. Statement #4, the complete IF, is now finished.
5 AFTER EMIT "after the block" Outer Fifth outer statement. The block counts as one outer link in the chain. Waits for the entire IF, then writes after the block.

If ?a were 3, step 4 would still execute as an outer statement. Its condition would evaluate to false, so the body statements 4a and 4b would not execute. Step 5 would nevertheless wait for the IF statement to complete and would then emit after the block. The outer chain depends on completion of the IF statement, not on whether its body executed.

The Same Script on One Line

The following form is less readable for humans, but it demonstrates that the runtime does not depend on line breaks:

Example
EMIT "first" AFTER EMIT "second" AFTER EMIT "third" AFTER IF ?a IS GREATER THAN 10 OPEN EMIT "first block line" AFTER EMIT "second block line" CLOSE AFTER EMIT "after the block"

This demonstrates the rule directly: if removing the line breaks does not change the meaning, then the line breaks were never part of that meaning.

AND and OR — the Other Two Chain Keywords

AFTER expresses sequence. AND and OR represent different forms of connection. They follow the same placement rule — the first statement is bare, later statements require a keyword, and line breaks are irrelevant — but each keyword defines different execution behavior.

AND — Parallel Execution with a Random Result

AND executes both branches in parallel. The output is intentionally nondeterministic: one result is selected, and the other is discarded. This behavior is defined by the runtime.

Example
EMIT "heads" AND EMIT "tails"
(* Output: heads OR tails — never both *)

AND never produces both results. One result is selected at random, and the other is discarded.

When AND is used with variables, the selected branch supplies the resulting value unless both branches SET the same variable name. In that case, the variable becomes a multivariable containing all assigned values. Direct EMIT of a multivariable remains random, while COLLAPSE converts it to an ordered array.

Example
STRING "alpha" SET ?arr AND STRING "beta" SET ?arr AND STRING "gamma" SET ?arr
AFTER COLLAPSE ?arr SET ?arr
AFTER EMIT ?arr(0)
AFTER EMIT ?arr(1)
AFTER EMIT ?arr(2)
(* Output: alphabetagamma *)

Reassigning a variable with AFTER does not produce a multivariable. AFTER replaces the previous value.

Example
STRING "first" SET ?var
AFTER STRING "second" SET ?var
AFTER EMIT ?var
(* Output: second — the previous value was overwritten *)

This distinction is fundamental to the different roles of AND and AFTER.

If an AND branch fails, that branch returns null while the other branches continue. The parallel group as a whole still completes.

AFTER Waits for Every AND Branch

Chain keywords may be combined. When AFTER follows an AND group, it waits for all preceding branches, including all parallel AND branches, to complete before continuing.

Example
EMIT "first"
AFTER STRING "X" SET ?x AND STRING "Y" SET ?x
(* Output: first — then ?x is either "X" or "Y"
   AFTER waits for both AND branches to complete before proceeding *)

The same principle applies when a later statement consumes the results:

Example
EMIT "start"
AFTER STRING "A" SET ?a AND STRING "B" SET ?b
AFTER EMIT ?a & ?b
(* Output: startAB — both branches complete before EMIT runs *)

This further demonstrates why the chain keyword, rather than the line break, is the unit of meaning. An AFTER following a parallel group waits for the entire group to complete. Whether the AND branches are written on one line, across multiple lines, or with a comment between them does not change that behavior.

Note that UNSET cannot be used as an AND branch to remove the original variable. A parallel branch receives its own copy of the variable, so UNSET affects only that branch. The original variable survives when the branches rejoin. To remove the original variable, use UNSET as its own AFTER step.

Incorrect — UNSET used directly as an AND branch:

Example
STRING "hhhh" SET ?u
AFTER CALCULATE 1 + 1 SET ?v AND UNSET ?u
AFTER EMIT ?u
(* Output: hhhh — UNSET affects only the branch's copy *)

Correct — UNSET used as its own AFTER step:

Example
STRING "hhhh" SET ?u
AFTER CALCULATE 1 + 1 SET ?v
AFTER UNSET ?u
AFTER EMIT ?u
(* Output: no output — ?u has been removed *)

Within an IF, WHILE, or LOOP body, UNSET behaves normally because the block shares the surrounding execution state. Only a parallel AND branch receives a separate copy.

OR — Fallback Execution

OR executes its statement only when the preceding statement returns false, null, or a soft error. When the preceding statement succeeds, the OR branch is skipped.

Example
EMIT "primary" OR EMIT "fallback"
(* Output: primary if it succeeds; fallback if primary returns false, null, or a soft error *)
Example
STRING "" SET ?empty OR STRING "fallback" SET ?result
AFTER EMIT ?result
(* Output: fallback *)

OR following an IF block provides the fallback branch. OR CATCH ERROR uses the same joining mechanism for a failed statement whose error information must be captured rather than simply bypassed:

Example
FILE READ "/root/missing.txt" SET ?data OR CATCH ERROR SET ?err
AFTER EMIT ?err("message")

AFTER, OR, and Failure Behave Differently

These cases are closely related but must be distinguished carefully.

Example
FILE READ "/root/missing.txt" SET ?data OR CATCH ERROR SET ?err
AFTER EMIT "we got here"

If the file is missing and the resulting ERROR is catchable, OR CATCH executes, ?err is populated, and the subsequent AFTER also executes.

If a FATAL error occurs, no subsequent statement executes. The chain terminates rather than continuing.

Hard errors, such as syntax errors, fatal timeouts, or illegal indexing of a multivariable, are not handled as OR conditions. They terminate the script.

Combining AFTER, AND, and OR

Chain keywords may be combined in the same script.

Example
EMIT "try" AFTER EMIT "setup" OR EMIT "fallback"

Apply the same rule throughout: chain keywords connect statements, while line breaks provide formatting only. When the grouping is difficult to read, place each keyword at the beginning of the statement it introduces. This changes only the presentation, not the meaning.

Example
EMIT "setup"
AFTER EMIT "try"
OR EMIT "fallback"

The chain is unchanged; the second form simply makes its structure easier to read.

Example
EMIT "begin"
AFTER
  STRING "line one" SET ?log
  AND
  STRING "line two" SET ?log
AFTER EMIT ?log
(* Output: begin — then ?log is either "line one" or "line two" *)

AND and OR Inside Conditions

Context matters. The same keywords can have different roles depending on where they appear.

Example
NUMBER 20 SET ?a
AFTER NUMBER 5 SET ?x
AFTER NUMBER 5 SET ?b
AFTER IF ?a IS GREATER THAN 10 AND ?b IS EQUAL TO ?x
OPEN
  EMIT "Both true"
CLOSE

Here, AND is not a parallel chain keyword. It means that both conditions must be true. The parser determines its role from its position in the expression.

Compound conditions follow PHP-style precedence: AND binds more tightly than OR. Evaluation proceeds from left to right and short-circuits: an AND expression stops at the first false condition, while an OR expression stops at the first true condition. Conditions after the short-circuit point are not evaluated.

Example
IF ?a IS IDENTICAL TO "this" AND ?a IS NOT EQUAL TO ?x OR ?b IS GREATER THAN 44
(* Groups as: (?a IS IDENTICAL TO "this" AND ?a IS NOT EQUAL TO ?x)
   OR ?b IS GREATER THAN 44 *)
OPEN
  EMIT "Complex condition met"
CLOSE

A common source of confusion is that GREATER THAN OR EQUAL TO and LESS THAN OR EQUAL TO contain the text OR. In these phrases, OR is part of the comparison operator, not a logical connector.

Example
IF ?a IS GREATER THAN OR EQUAL TO 10 AND ?b IS LESS THAN OR EQUAL TO 20
(* One AND combines two comparisons; this is not an OR chain. *)
OPEN
  EMIT "In range"
CLOSE

IS and ARE are interchangeable. ARE is conventional when working with arrays and objects. IF ?arr ARE … means that every item matches, while IF SOME ?arr ARE … means that at least one item matches.

These rules do not change the outer chain surrounding the IF. The IF still requires AFTER when it is not the first statement in its chain. Its body still begins a nested chain, and CLOSE still does not terminate the outer chain.

These are two distinct layers of syntax. The same words can serve different purposes because their position determines their role.

Why This Matters

Treating the chain keyword — rather than the line break — as the unit of meaning affects two behaviors that are fundamental to OcaltQL:

Once the chain keyword is recognized as the structural join, the useful question is no longer “is this a new line?” but “is this the first statement in the current chain, or a subsequent statement?” That distinction determines which syntax is required.

Common Syntax Mistakes

The following examples illustrate common errors caused by treating line breaks as statement boundaries.

Two bare verbs, two lines

Example
EMIT "Hello World"
EMIT "Done"

The second line is not a separate statement merely because it appears on a new line. Add AFTER.

IF under an assignment, no AFTER

Example
STRING "hello" SET ?var
IF ?var IS SET
OPEN
  EMIT "yes"
CLOSE

The IF is the second statement in the outer chain, so it must be introduced with AFTER IF.

A comment treated as a break, then a bare verb

Example
EMIT "one"
(* note: the comment does not affect the chain *)
EMIT "two"

The comment does not participate in the chain. EMIT "two" is still the second statement, so it must be written as AFTER EMIT "two".

AFTER on the first line of a block

Example
OPEN
  AFTER EMIT "no"
CLOSE

There is no preceding statement inside the block. The first statement of a chain is therefore bare.

Missing AFTER on the second line of a block

Example
OPEN
  EMIT "one"
  EMIT "two"
CLOSE

The same rule applies inside the block: the second inner statement requires AFTER.

Missing AFTER after CLOSE

Example
EMIT "before"
AFTER IF true
OPEN
  EMIT "inside"
CLOSE
EMIT "after"

CLOSE terminates the block body, not the outer chain. EMIT "after" is therefore the next outer statement and must be written as AFTER EMIT "after".

ELSE

There is no ELSE keyword. Use OR for a fallback branch and OR IF for an additional condition.

UNSET on an AND branch, expecting the original to die

It does not remove the original variable. Use UNSET as its own AFTER step.

Treating AND inside IF as a parallel fork, or AND between statements as a boolean

Position determines the role. Between complete condition phrases, the keyword is logical; between statements, it is a chain keyword.

A Method for Reading Any Script

When a script is difficult to interpret, ignore its visual layout and identify the chain joins.

  1. Ignore every (* comment *) and every blank line. Neither forms part of the statement sequence.
  2. Find each OPEN and its matching CLOSE. That block represents one statement in the enclosing chain and begins a new chain inside the block.
  3. Inside each block, the first statement is bare. Every subsequent statement in that block requires AFTER, AND, or OR.
  4. Outside the blocks, the first statement of the script is bare. Every subsequent statement — including each IF, WHILE, LOOP, and SWITCH that owns a block — requires a chain keyword.
  5. An OR or OR IF adjacent to a CLOSE belongs to the IF (or WHILE) that opened the block, not to the final statement inside it.
  6. An AFTER adjacent to a CLOSE waits for the entire block before continuing the outer chain.
  7. An AFTER following an AND fork waits for every branch in that fork.

Applying these seven steps allows you to read the statement structure in the same way as the runtime.

The following simplified example summarizes the rule:

Example
FIRST LINE....
AFTER SECOND LINE....

(* comment *)
AFTER THIRD LINE (OR FIFTH)
AFTER IF CONDITION....
OPEN
FIRST BLOCK LINE
AFTER SECOND BLOCK LINE
CLOSE

AFTER FIRST LINE AFTER BLOCK...

A line break never establishes a new statement or a new chain. The chain continues until the syntax explicitly connects or terminates it.

Where to Go Next

The next page covers the OcaltQL API and client, including how to connect to the runtime and send your first script. The chaining reference then examines AFTER, AND, and OR in greater detail, including multivariables and parallel execution.

OcaltQL API & Client →