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:
[CHAIN] [SUBJECT] VERB [MODE] [ARGS] [PREPOSITION TARGET] [SET ?var]
The elements of this pattern are described below.
- CHAIN —
AFTER,AND, orOR. Absent on the first statement of a chain and present on every subsequent statement. - SUBJECT — optional. A
?variablethat the verb operates on, written before the verb. - VERB — the operation to perform. Examples include
EMIT,STRING,CALCULATE,IF,WHILE, andFILE READ. - MODE — optional modifier. Examples include
ALL,ONCE, andFROM … TO. - ARGS — the inputs supplied to the verb. These may be literals or
?variables. - PREPOSITION TARGET — verb-specific. Possible forms include
TO,INTO,WITH,WHERE,AS, andFROM. The verb determines which form is valid. - SET ?var — optional capture of a return value. This is valid only for verbs that return a value.
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.
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.
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:
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.
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:
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:
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:
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:
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:
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:
EMIT "start"
AFTER EMIT "middle"
AFTER EMIT "end"
- The first statement has no chain keyword:
EMIT "start". The runtime writesstartto the response, and the statement completes successfully. - The runtime encounters
AFTERand waits for step 1 to finish. It has finished, soEMIT "middle"executes. The response becomesstartmiddle. - The runtime encounters the next
AFTERand waits for step 2 to finish. It has finished, soEMIT "end"executes. The response becomesstartmiddleend. - 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:
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:
EMIT "Hello World"
AFTER EMIT "Done"
The same chain may also be written on a single line:
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:
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:
- At the beginning of a script.
- At the first statement inside an
OPEN … CLOSEblock. - At the first statement inside a
NEW OPERATIONbody, aSWITCHcase body, or any other block that opens a nested chain.
In every other position, the script is already inside a chain, so the next statement requires a chain keyword.
Remember the following distinction:
- First statement of a chain → bare.
- Every subsequent statement in that chain →
AFTER/AND/OR.
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.
(* 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:
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:
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.
OPENandCLOSEdelimit the body of the block. They are not chain keywords and are not statements themselves.CLOSEterminates the block body, not the outer chain.- The complete
IF ... OPEN ... CLOSEconstruct behaves as a single statement within the outer sequence, just as anEMITorSETdoes. - If the
IFwas introduced withAFTER, anAFTERplaced afterCLOSEcontinues the same outer chain and waits for the entire block to finish first.
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:
EMIT "before"— the first outer statement. It is bare.AFTER IF …— the second outer statement. TheIFand its body together form the statement.OPEN … CLOSE— the body of statement 2, not a separate outer statement.AFTER EMIT "after the block"— the third outer statement.AFTERwaits for the entireIFstatement 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.
- First statement inside
OPEN: bare. - Every subsequent statement inside that
OPEN … CLOSEblock: keyworded.
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:
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:
EMIT ?i— the first inner statement. It is bare and writes the current value of?i.AFTER CALCULATE ?i + 1 SET ?i— the second inner statement. It waits for the precedingEMIT, 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.
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.
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.
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:
- In the outer script,
LOOPis a statement and its body is opened. - Inside the loop body, the first statement is
IF ?i IS EQUAL TO 2. It is bare because it is the first statement in thatOPENblock. - That
IFopens its own chain. Its first, and only, statement isEMIT "matched: 2", which is therefore bare. - The inner
CLOSEreturns execution to the loop body. TheIFwas the first statement in that body. AFTER EMIT "processed 2"is the second statement in the loop body, so it requiresAFTERand runs after theIFfinishes.- The loop
CLOSEis reached after all iterations, returning execution to the outer chain. AFTER EMIT "loop finished"is the next outer statement, so it requiresAFTERand 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.
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:
IF … OPEN … CLOSE— the condition and its true branch together form one statement.OR OPEN … CLOSE— the fallback body. It executes only if theIFreturns false, null, or a soft error. It remains part of the same outer chain.AFTER EMIT "always"— waits for whichever branch executes and then continues the outer chain.
OR IF is used in the same way:
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.
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:
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:
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.
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.
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.
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.
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:
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:
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:
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.
EMIT "primary" OR EMIT "fallback"
(* Output: primary if it succeeds; fallback if primary returns false, null, or a soft error *)
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:
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.
- A soft failure (
ERROR, andWARNINGwhen execution continues) sets the previous statement's success flag to false. ORexamines that flag. If the previous statement failed,ORexecutes; if it succeeded,ORis skipped.AFTERdoes not examine the success flag. It waits for the previous statement to finish and then executes, regardless of whether that statement succeeded.- A FATAL error terminates the script. No subsequent
OR,AFTER, orCATCHexecutes.
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.
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.
EMIT "setup"
AFTER EMIT "try"
OR EMIT "fallback"
The chain is unchanged; the second form simply makes its structure easier to read.
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.
- Between statements,
ANDandORare chain keywords that define parallel execution or fallback behavior. - Inside
IForWHILE, between complete condition phrases,ANDandORare logical operators. They do not begin new statements.
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.
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.
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:
- Ordering — statements that appear on different lines may still be sequential, while statements on the same line may not be sequential at all.
EMIT "A" AFTER EMIT "B"is ordered. WritingEMIT "A"andEMIT "B"on separate lines without a chain keyword does not create two valid sequential steps.EMIT "A" AND EMIT "B"creates a parallel fork rather than an ordered sequence. The keyword determines the behavior; the line break does not. - Waiting behavior —
AFTERfollowing a block waits for the entire block to finish.AFTERfollowing parallelANDbranches waits for every branch.ORexecutes only when the preceding statement fails softly. These distinctions determine when subsequent statements can safely consume values produced by earlier statements.
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
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
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
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
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
OPEN
EMIT "one"
EMIT "two"
CLOSE
The same rule applies inside the block: the second inner statement requires AFTER.
Missing AFTER after CLOSE
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.
- Ignore every
(* comment *)and every blank line. Neither forms part of the statement sequence. - Find each
OPENand its matchingCLOSE. That block represents one statement in the enclosing chain and begins a new chain inside the block. - Inside each block, the first statement is bare. Every subsequent statement in that block requires
AFTER,AND, orOR. - Outside the blocks, the first statement of the script is bare. Every subsequent statement — including each
IF,WHILE,LOOP, andSWITCHthat owns a block — requires a chain keyword. - An
ORorOR IFadjacent to aCLOSEbelongs to theIF(orWHILE) that opened the block, not to the final statement inside it. - An
AFTERadjacent to aCLOSEwaits for the entire block before continuing the outer chain. - An
AFTERfollowing anANDfork 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:
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.