statement --> term_statement. statement --> compound_statement. statement --> if_statement. statement --> loop_statement. statement --> try_statement. statement --> switch_statement.
Almost any Plang term can be used as a statement, as long as the outermost operator has a priority less than (,)/2, or is bracketed. This is typically referred to as an "argument term" because such terms normally appear as the arguments to predicate calls.
term_statement --> argument_term, ";".
The following are some examples of term statements:
X = f(Y);
stdout::writeln("Hello World!");
parent(P, C);
(V = 1, W = a || V = 2, W = b);
At runtime, the term is evaluated as a predicate call, which will either succeed, fail, or throw an error. If the term succeeds, then execution continues with the next statement. If the term fails, then Plang will backtrack and find an alternative solution. If the term throws an error, then control passes to the try statement that has a catch clause matching the error.
There is a special term statement, consisting of only a semi-colon, that is equivalent to true/0:
term_statement --> ";".
Compound statements are surrounded with curly braces, as in C:
compound_statement --> "{", statements, "}". compound_statement --> "{", "}". statements --> statement. statements --> statements, statement.
Conditional statements can be expressed with the if statement:
Goal1 is executed, and if it succeeds then Goal2 is executed. If Goal1 fails, then Goal3 is executed. If Goal3 is omitted, then the statement succeeds if Goal1 fails.
Goal1 is only executed once. An implicit commit/0 is performed if Goal1 succeeds. Goal2 and Goal3 may involve alternatives.
The formal syntax is as follows:
if_statement --> "if", "(", term, ")", statement. if_statement --> "if", "(", term, ")", statement, "else", statement.
Conditional statements can also be written using the (->)/2 and (||)/2 predicates:
The if statement form is recommended unless the conditional appears within an argument to call/1.
Loops are expressed using the do, for, and while keywords.
The semantics is similar to the corresponding loop constructs in C. The do statement executes Statements until Condition succeeds. The for statement executes Statement for each element of List, binding Variable to each in turn. The while statement executes Statement while Condition succeeds. The following code prints the first five powers of 2:
for (X in [2, 4, 8, 16, 32]) {
stdout::writeln(X);
}
We may instead want to express this loop as:
for (X in [1, 2, 3, 4, 5]) {
Y is 2 ** X;
stdout::writeln(Y);
}
However, there is a problem. After the first loop iteration, Y will be bound to 2. In the second loop iteration, 2 will not unify with 4 (the new value of Y) and the loop fails. The solution is to list the local variables in the loop that must be unbound each time around the loop:
for [Y] (X in [1, 2, 3, 4, 5]) {
Y is 2 ** X;
stdout::writeln(Y);
}
The X
variable in the for loop is implicitly added to the list of variables to unbind.
All of the loop statements can take a list of variables to unbind each time around the loop:
The formal syntax for loop statements is as follows:
loop_statement --> "do", unbind_vars, compound_statement, "while" "(", term, ")", ";". loop_statement --> "for", unbind_vars, "(", variable, "in", term, ")", statement. loop_statement --> "while", unbind_vars, "(", term, ")", statement. unbind_vars --> []. unbind_vars --> "[", "]". unbind_vars --> "[", unbind_var_list, "]". unbind_var_list --> variable. unbind_var_list --> unbind_var_list, ",", variable.
Try/catch statements are used to catch errors that were thrown by the throw/1 predicate.
The try statement executes Statements and succeeds or fails accordingly. If one of the Statements throws an error with throw/1, and the error can be unified with PatternN, then RecoveryN will be executed. If the error does not unify with any of the patterns, then the error will continue to be thrown further up the call chain.
It is possible for Statements to succeed and then for execution to backtrack into the body of Statements seeking another solution. When that happens, a thrown error the second time may be caught by the catch clauses.
try_statement --> "try", compound_statement, catch_clauses. catch_clauses --> catch_clause. catch_clauses --> catch_clauses, ",", catch_clause. catch_clause --> "catch", "(", argument_term, ")", compound_statement.
The switch statement is used to choose between a number of labels, seeking the first that unifies with a term.
Finds the first LabelM term in the case list that unifies with Term, and executes the associated StatementM. If none of the case labels match, then executes the DefaultStatement associated with the default label. If there is no default label, then the switch statement fails.
Multiple case labels can be specified for the same statement with case Label1: case Label2: ... case LabelN: Statement. The default label can be mixed with regular case labels.
Unlike C, execution does not fall through from StatementM to the following StatementM+1.
Once a LabelM is found that unifies with Term, the switch statement does an implicit commit/0 to commit the clause to that choice. Backtracking does not select later case labels even if they may have otherwise unified with Term.
The following is an example of an expression evaluator using switch:
eval(Term, Answer) { switch (Term) { case X + Y: { eval(X, XAnswer); eval(Y, YAnswer); Answer is XAnswer + YAnswer; } case X - Y: { eval(X, XAnswer); eval(Y, YAnswer); Answer is XAnswer - YAnswer; } case X * Y: { eval(X, XAnswer); eval(Y, YAnswer); Answer is XAnswer * YAnswer; } case X / Y: { eval(X, XAnswer); eval(Y, YAnswer); Answer is XAnswer / YAnswer; } case -X: { eval(X, XAnswer); Answer is -XAnswer; } default: { if (number(Term)) Answer = Term; else lookup_variable(Term, Answer); } } } eval(2 * x + y, Answer)
The formal syntax for switch statements is as follows:
switch_statement --> "switch", "(", argument_term, ")", switch_body. switch_body --> "{", switch_cases, "}". switch_body --> "{", "}". switch_cases --> switch_case. switch_cases --> switch_cases, ",", switch_case. switch_case --> case_labels, statement. case_labels --> case_label. case_labels --> case_labels, case_label. case_label --> "case", argument_term, ":". case_label --> "default", ":".