File Coverage

blib/lib/Syntax/Keyword/Match.pm
Criterion Covered Total %
statement 28 30 93.3
branch 7 10 70.0
condition n/a
subroutine 10 10 100.0
pod 0 3 0.0
total 45 53 84.9


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2021-2023 -- leonerd@leonerd.org.uk
5              
6             package Syntax::Keyword::Match 0.13;
7              
8 17     17   3766621 use v5.14;
  17         229  
9 17     17   96 use warnings;
  17         36  
  17         449  
10              
11 17     17   88 use Carp;
  17         33  
  17         8779  
12              
13             require XSLoader;
14             XSLoader::load( __PACKAGE__, our $VERSION );
15              
16             =head1 NAME
17              
18             C - a C syntax for perl
19              
20             =head1 SYNOPSIS
21              
22             use v5.14;
23             use Syntax::Keyword::Match;
24              
25             my $n = ...;
26              
27             match($n : ==) {
28             case(1) { say "It's one" }
29             case(2) { say "It's two" }
30             case(3) { say "It's three" }
31             case(4), case(5)
32             { say "It's four or five" }
33             case if($n < 10)
34             { say "It's less than ten" }
35             default { say "It's something else" }
36             }
37              
38             =head1 DESCRIPTION
39              
40             This module provides a syntax plugin that implements a control-flow block
41             called C, which executes at most one of a choice of different
42             blocks depending on the value of its controlling expression.
43              
44             This is similar to C's C syntax (copied into many other
45             languages), or syntax provided by L.
46              
47             This is an initial, experimental implementation. Furthermore, it is built as
48             a non-trivial example use-case on top of L, which is also
49             experimental. No API or compatibility guarantees are made at this time.
50              
51             =head1 Experimental Features
52              
53             Some of the features of this module are currently marked as experimental (even
54             within the context that the module itself is experimental). They will provoke
55             warnings in the C category, unless silenced.
56              
57             use Syntax::Keyword::Match qw( match :experimental(dispatch) );
58              
59             use Syntax::Keyword::Match qw( match :experimental ); # all of the above
60              
61             =cut
62              
63             =head1 KEYWORDS
64              
65             =head2 match
66              
67             match( EXPR : OP ) {
68             ...
69             }
70              
71             A C statement provides the controlling expression, comparison operator,
72             and sequence of C statements for a match operation. The expression is
73             evaluated to yield a scalar value, which is then compared, using the
74             comparison operator, against each of the C labels in the order they are
75             written, topmost first. If a match is found then the body of the labelled
76             block is executed. If no label matches but a C block is present, that
77             will be executed instead. After a single inner block has been executed, no
78             further tests are performed and execution continues from the statement
79             following the C statement.
80              
81             The braces following the C block must only contain C or
82             C statements. Arbitrary code is not supported here.
83              
84             Even though a C statement is a full statement and not an expression, it
85             can still yield a value if it appears as the final statment in its containing
86             C or C block. For example:
87              
88             my $result = do {
89             match( $topic : == ) {
90             case(1) { ... }
91             }
92             };
93              
94             If the controlling expression introduces a new variable, that variable will be
95             visible within any of the C blocks, and will go out of scope after the
96             C statement finishes. This may be useful for temporarily storing the
97             result of a more complex expression.
98              
99             match( my $x = some_function_call() : == ) {
100             case ...
101             }
102              
103             =head3 Comparison Operators
104              
105             The comparison operator must be either C (to compare cases as strings) or
106             C<==> (to compare them as numbers), or C<=~> (to compare cases using regexps).
107              
108             I, or previous versions on Perl
109             releases 5.32 onwards, the C operator is also supported, allowing
110             dispatch based on what type of object the controlling expression gives.
111              
112             match( $obj : isa ) {
113             case(A::Package) { ... }
114             case(Another::Package) { ... }
115             }
116              
117             Remember that comparisons are made in the order they are written, from the top
118             downwards. Therefore, if you list a derived class as well as a base class,
119             make sure to put the derived class B the base class, or instances of
120             that type will also match the base class C block and the derived one
121             will never match.
122              
123             class TheBase {}
124             class Derived :isa(TheBase) {}
125              
126             match( $obj : isa ) {
127             case(TheBase) { ... }
128             case(Derived) {
129             # This case will never match as the one above will always happen first
130             }
131             }
132              
133             I the operator syntax is parsed using L,
134             meaning that custom infix operators can be recognised, even on versions of
135             perl that do not support the full C mechanism.
136              
137             =head2 case
138              
139             case(VAL) { STATEMENTS... }
140              
141             case(VAL), case(VAL), ... { STATEMENTS... }
142              
143             A C statement must only appear inside the braces of a C. It
144             provides a block of code to run if the controlling expression's value matches
145             the value given in the C statement, according to the comparison
146             operator.
147              
148             Multiple C statements are permitted for a single block. A value matching
149             any of them will run the code inside the block.
150              
151             If the value is a non-constant expression, such as a variable or function
152             call, it will be evaluated as part of performing the comparison every time the
153             C statement is executed. For best performance it is advised to extract
154             values that won't need computing again into a variable or C that
155             can be calculated just once at program startup; for example:
156              
157             use constant CONDITION => a_function("with", "arguments");
158              
159             match( $var : eq ) {
160             case(CONDITION) { ... }
161             ...
162             }
163              
164             The C<:experimental(dispatch)> feature selects a more efficient handling of
165             sequences of multiple C blocks with constant expressions. This handling
166             is implemented with a custom operator that will entirely confuse modules like
167             C or optree inspectors like coverage tools so is not selected by
168             default, but can be enabled for extra performance in critical sections.
169              
170             =head2 case if
171              
172             case if(EXPR) { STATEMENTS... }
173              
174             case(VAL), case if(EXPR) { STATEMENTS... }
175              
176             A C statement may also be written C with a boolean predicate
177             expression in parentheses. This inserts a direct boolean test into the
178             comparison logic, allowing for other logical tests that aren't easily
179             expressed as uses of the comparison operator. As C is an alternative
180             to a regular C, they can be combined on a single code block if required.
181              
182             For example, when testing an inequality in a selection of numerical C<==>
183             tests, or a single regexp test among some string C tests.
184              
185             match( $num : == ) {
186             case(0) { ... }
187             case(1), case(2) { ... }
188             case if($num < 5) { ... }
189             }
190              
191             Z<>
192              
193             match( $str : eq ) {
194             case("abc") { ... }
195             case("def") { ... }
196             case if($str =~ m/g/) { ... }
197             }
198              
199             By default the match value is not assigned into a variable that is visible
200             to C expressions, but if needed a new lexical can be constructed by
201             using a regular C assignment.
202              
203             match( my $v = some_expression() : eq ) {
204             case if($v =~ m/pattern/) { ... }
205             }
206              
207             =head2 default
208              
209             A C statement must only appear inside the braces of a C. If
210             present, it must be the final choice, and there must only be one of them. It
211             provides a block of code to run if the controlling expression's value did not
212             match any of the given C labels.
213              
214             =cut
215              
216             =head1 COMPARISONS
217              
218             As this syntax is fairly similar to a few other ideas, the following
219             comparisons may be useful.
220              
221             =head2 Core perl's given/when syntax
222              
223             Compared to core perl's C syntax (available with
224             C), this syntax is initially visually very similar but
225             actually behaves very differently. Core's C uses the smartmatch
226             (C<~~>) operator for its comparisons, which is complex, subtle, and hard to
227             use correctly - doubly-so when comparisons against values stored in variables
228             rather than literal constants are involved. It can be unpredictable whether
229             string or numerical comparison are being used, for example. By comparison,
230             this module requires the programmer to specify the comparison operator. The
231             choice of string or numerical comparison is given in the source code - there
232             can be no ambiguity.
233              
234             Additionally, the C operator is also permitted, which has no equivalent
235             ability in smartmatch.
236              
237             Also, the C syntax permits mixed code within a C block
238             which is run unconditionally, or at least, until the first successful C
239             statement is encountered. The syntax provided by this module requires that the
240             only code inside a C block be a sequence of C statements. No
241             other code is permitted.
242              
243             =head2 Switch::Plain
244              
245             Like this module, L also provides a syntax where the programmer
246             specifies whether the comparison is made using stringy or numerical semantics.
247             C also permits additional conditions to be placed on C
248             blocks, whereas this module does not.
249              
250             Additionally, the C operator is also permitted, which has no equivalent
251             ability in C.
252              
253             =head2 C's switch/case
254              
255             The C programming language provides a similar sort of syntax, using keywords
256             named C and C. One key difference between that and the syntax
257             provided for Perl by this module is that in C the C labels really are
258             just labels. The C part of the statement effectively acts as a sort of
259             computed C. This often leads to bugs caused by forgetting to put a
260             C at the end of a sequence of statements before the next C label;
261             a situation called "fallthrough". Such a mistake is impossible with this
262             module, because every C is provided by a block. Once execution has
263             finished with the block, the entire C statement is finished. There is
264             no possibility of accidental fallthrough.
265              
266             C's syntax only permits compiletime constants for C labels, whereas this
267             module will also allow the result of any runtime expression.
268              
269             Code written in C will perform identically even if any of the C labels
270             and associated code are moved around into a different order. The syntax
271             provided by this module notionally performs all of its tests in the order they
272             are written in, and any changes of that order might cause a different result.
273              
274             =cut
275              
276             sub import
277             {
278 13     13   97 my $pkg = shift;
279 13         33 my $caller = caller;
280              
281 13         68 $pkg->import_into( $caller, @_ );
282             }
283              
284             sub unimport
285             {
286 1     1   10 my $pkg = shift;
287 1         3 my $caller = caller;
288              
289 1         3 $pkg->unimport_into( $caller, @_ );
290             }
291              
292             my @EXPERIMENTAL = qw( dispatch );
293              
294 16     16 0 113 sub import_into { shift->apply( sub { $^H{ $_[0] }++ }, @_ ) }
  13     13   67  
295 1     1 0 5 sub unimport_into { shift->apply( sub { delete $^H{ $_[0] } }, @_ ) }
  1     1   5  
296              
297             sub apply
298             {
299 14     14 0 30 my $pkg = shift;
300 14         38 my ( $cb, $caller, @syms ) = @_;
301              
302 14 100       102 @syms or @syms = ( "match" );
303              
304 14         66 my %syms = map { $_ => 1 } @syms;
  17         119  
305              
306 14 50       138 $cb->( "Syntax::Keyword::Match/match" ) if delete $syms{match};
307              
308 14         39 foreach ( @EXPERIMENTAL ) {
309 14 100       88 $cb->( "Syntax::Keyword::Match/experimental($_)" ) if delete $syms{":experimental($_)"};
310             }
311              
312 14 50       48 if( delete $syms{":experimental"} ) {
313 0         0 $cb->( "Syntax::Keyword::Match/experimental($_)" ) for @EXPERIMENTAL;
314             }
315              
316 14 50       22147 croak "Unrecognised import symbols @{[ keys %syms ]}" if keys %syms;
  0            
317             }
318              
319             =head1 TODO
320              
321             This is clearly an early experimental work. There are many features to add,
322             and design decisions to make. Rather than attempt to list them all here it
323             would be best to check the RT bug queue at
324              
325             L
326              
327             =head1 AUTHOR
328              
329             Paul Evans
330              
331             =cut
332              
333             0x55AA;