In the documentation for an implementation of Pascal for a Soviet computer, I've encountered a very weird language extension. I'll try to translate it:
Branching operator (branch)
Syntax
<Branching operator>::= branch <Expression> of <Operator> { ; <Operator> }* end
Semantics
When control reaches a branching operator, the Expression, which should be of a simple type, is evaluated, then control is passed to first Operator in the branch.
[Forall j ls likely missing here. - LB] If the j-th operator of the branch has concluded, the branch is exited, and control is passed beyond end. During the execution of the j-th Operator, the back operator can be called.Syntax of the back operator
<back operator> ::= back <Expression>
During the execution of the back operator, the Expression is evaluated, and a search is performed for the closest dynamically preceding branch with a compatible value of its Expression. The values are compatible, when they are either equal, or one of them is equal to zero.
If such a branch is found, control is passed to the next operator in the branch, or, if the last operator in the branch was being executed, beyond the end of the branch.
If a branch with a compatible expression is not found, then it is deemed that the whole program is enclosed within
branch <Program>; begin <Printing "exit on ALT"> end end
TL;DR: The branch operator implements exceptions, allowing to represent concepts similar to C++ try, catch (including catch (...)), throw and re-throw, albeit using values of expressions rather than types to distinguish the exceptions.I notice two peculiarities: there are references to the "j-th operator" without explaining what "j" refers to (it turns out that in the context it simply means "any"); also the diagnostic "exit on ALT" — the equivalent of C++ terminate()
— had ALT in Latin letters, and the turn of the phrase is somewhat unnatural in Russian.
(OK, three peculiarities: the last line is syntactically incorrect: the expression and the keyword of are missing. Should have been something like branch 0 of ...)
This leads me to think that this branch operator was not of an original design, and that part of the documentation is a direct translation from a foreign language.
Does it ring a bell? Has anyone seen this kind of proto-exceptions in any programming language?
An example (simplified from the example in the documentation):
program t;
procedure m(i: integer);
begin
branch i of
begin
if i<20 then begin
m(i+1);
back i-2
end
end;
writeln(i)
end
end;
begin
m(0)
end.
It prints
17
14
11
8
5
2
0
The equivalent C++ code would be:
#include <iostream>
void m(int i) {
try {
if (i < 20) {
m(i+1);
throw i-2;
}
} catch (int k) {
if (i && k && i != k)
throw;
std::cout << i << '\n';
}
}
main() {
m(0);
}
Additional examples:
branch expr of
writeln(1);
writeln(2);
writeln(3)
end
will print 1
regardless of the value of expr
, because the execution of the branch
operator always starts from the first enclosed operator and finishes as soon as an operator concludes.
branch 0 of
begin
branch 1 of
begin write(1); back 0 end;
begin write(2); back 1 end;
begin write(3); back 99 end
write(4);
end;
write(5)
end;
writeln(99)
end
will print 1 2 3 99
: back 0
matches the closest enclosing branch
with expression value 1 (because one of the expressions is 0), the first item of branch 1
has been executed; so control is passed to the next item of that branch; in the second item, back 1
matches branch 1
, so control is passed to the third item. back 99
doesn't match branch 1
but it matches branch 0
, so control is passed to the next item of branch 0
which prints 99
. Without back 99
, 1 2 3 5
would be printed. With back 1
instead of back 99
, 1 2 3 4 5
would be printed.
The question of different types of expressions is not covered in the documentation. Experimentation shows that 1.0 doesn't match 1, but chr(1) does. This indicates that the type information was not saved, and that matching was done by bit pattern.
j
" refers to. The example complicates things even a bit more by being recursive (is this indeed needed). And the whole thing reminds me a bit ofsetjmp/longjmp
in Csetjmp/longjmp
, but the devil's in the details.