You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: doc/Language/grammar_tutorial.pod6
+36-34Lines changed: 36 additions & 34 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,7 +13,7 @@ perfectly.
13
13
14
14
=SUBTITLEWhen Would I Use Grammars?
15
15
16
-
Grammars are great at taking stings, trying to make sense of them, and then saving them into a data structure you can actually work with. If you have strings of some kind that you need to bring order to, or interpret, grammars give you some great tools to make it easier.
16
+
Grammars are great at taking strings, trying to make sense of them, and then saving them into a data structure you can actually work with. If you have strings of some kind that you need to bring order to, or interpret, grammars give you some great tools to make it easier.
17
17
18
18
Your string might be a whole file that you need to break into sections. Or
19
19
perhaps line by line. Maybe you have a protocol like SMTP you're working with,
@@ -38,15 +38,15 @@ For example, in the case of HTML, you could define a grammar that would recogniz
38
38
39
39
Grammars are defined as an object, just like everything else in Perl. Technically, they are normal classes with a little extra magic thrown in, which we'll get to later -- and a few limitations. You name and define a grammar exactly as you would a class, except using the "grammar" keyword instead of "class".
40
40
41
-
=begincode :skip-test
42
-
grammar My::Gram { ..methods 'n stuff... }
41
+
=begincode
42
+
grammar My::Gram { ...methods 'n stuff... }
43
43
=endcode
44
44
45
45
Grammars contain elements like methods, called I<regex, token or rule>. These elements are named, just like methods are named. And each one defines a regex, token or rule (which are mostly the same thing (not really) but we'll get to that later).
46
46
47
47
Once you have your grammar defined, in your program call it by name and pass in the string you want to parse, and that string will be run through the rules you defined based upon your regex, token and rule "methods". When done, you get back a I<Match> object that has been populated with data structured and stored by the names you used to define your methods.
48
48
49
-
=begincode :skip-test
49
+
=begincode
50
50
my $matchObject = My::Gram.parse($what-a-big-string-you-have);
51
51
=endcode
52
52
@@ -110,14 +110,14 @@ But in our big string we get, we don't know what order these regex matches will
110
110
111
111
You could actually use this to extract your data from the URI for basic CRUD that has all 3 parameters included:
112
112
113
-
=begincode :skip-test
113
+
=begincode
114
114
my $match = REST.parse('/product/update/7/notify');
115
115
say $match;
116
116
117
-
# 「/product/update/7/notify」
118
-
# subject => 「product」
119
-
# command => 「update」
120
-
# data => 「7/notify」
117
+
「/product/update/7/notify」
118
+
subject => 「product」
119
+
command => 「update」
120
+
data => 「7/notify」
121
121
=endcode
122
122
123
123
Of course, the data can be accessed directly by using $match<subject> or $match<command> or $match<data> to return the values parsed. They each contain match objects you can work further with, or coerce into a string ( $match<command>.Str )
@@ -140,7 +140,7 @@ The REST grammar so far will handle retrieves, deletes and updates ok. However,
140
140
# 「product」「create」
141
141
=endcode
142
142
143
-
Let's imagine, for the sake of demonstration, that we might want to allow these same URI's to be entered in by a user from the terminal. In that case, they might put spaces between the '/'s, since users are prone to break things. If we wanted to accommodate this possibility, we could replace the '/'s in TOP with another token that allowed for spaces on either side of it.
143
+
Let's imagine, for the sake of demonstration, that we might want to allow these same URIs to be entered in by a user from the terminal. In that case, they might put spaces between the '/'s, since users are prone to break things. If we wanted to accommodate this possibility, we could replace the '/'s in TOP with another token that allowed for spaces on either side of it.
144
144
145
145
=begincode
146
146
grammar REST {
@@ -155,13 +155,14 @@ Let's imagine, for the sake of demonstration, that we might want to allow these
155
155
my $m = REST.parse('/ product / update /7 /notify');
156
156
say $m;
157
157
158
-
# 「/ product / update /7 /notify」
159
-
# slash => 「/ 」
160
-
# subject => 「product」
161
-
# slash => 「 / 」
162
-
# command => 「update」
163
-
# slash => 「 /」
164
-
# data => 「7 /notify」
158
+
###
159
+
「/ product / update /7 /notify」
160
+
slash => 「/ 」
161
+
subject => 「product」
162
+
slash => 「 / 」
163
+
command => 「update」
164
+
slash => 「 /」
165
+
data => 「7 /notify」
165
166
=endcode
166
167
167
168
We're getting some extra junk in our match object now, with those slashes, but there are some very nice ways to make a tidy return value that we'll get to.
@@ -172,10 +173,10 @@ We want our RESTful grammar to allow for CRUD operations only. Anything else we
172
173
173
174
There are several ways to accomplish this. For example, you could change the command method:
@@ -184,7 +185,7 @@ This results in any URI coming in getting checked; where the second string betwe
184
185
185
186
There is another way, though, that can give you far greater flexibility to do interesting or unholy things with your regexes, and provide some better readability when options grow large. These are proto-regexes.
186
187
187
-
To utilize these multimethods (here called protoregexes) to constrain our command to the same values we had above, we'll replace "token command" with the following:
188
+
To utilize these multi-methods (here called protoregexes) to constrain our command to the same values we had above, we'll replace "token command" with the following:
188
189
189
190
=begincode
190
191
proto token command {*}
@@ -217,9 +218,9 @@ This is what we've come up for processing our RESTful URIs so far:
217
218
}
218
219
=endcode
219
220
220
-
Let's look at various URI's and how they behave being passed through our grammar.
221
+
Let's look at various URIs and how they behave being passed through our grammar.
221
222
222
-
=begincode :skip-test
223
+
=begincode
223
224
my @uris = ['/product/update/7/notify',
224
225
'/product/create',
225
226
'/item/delete/4'];
@@ -229,14 +230,15 @@ Let's look at various URI's and how they behave being passed through our grammar
229
230
say "Sub: $m<subject> Cmd: $m<command> Dat: $m<data>";
230
231
}
231
232
232
-
# Sub: product Cmd: update Dat: 7/notify
233
-
# Sub: product Cmd: create Dat:
234
-
# Sub: item Cmd: delete Dat: 4
233
+
###
234
+
Sub: product Cmd: update Dat: 7/notify
235
+
Sub: product Cmd: create Dat:
236
+
Sub: item Cmd: delete Dat: 4
235
237
=endcode
236
238
237
-
So with just this part of a grammar, we're getting almost everything we need. Our URI's get efficiently parsed and we're given a nice little data structure for the variables we need to work with.
239
+
So with just this part of a grammar, we're getting almost everything we need. Our URIs get efficiently parsed and we're given a nice little data structure for the variables we need to work with.
238
240
239
-
But look at that first line returned -- the <em>data</em> token is returning the entire end of the URI as just one string. We need to be able to work with that 7 there. And that 4! Well, the 4 is easy... But the 7 had the extra /notify on the end, to signal the system to notify someone that a product was updated (perhaps).
241
+
But look at that first line returned -- the I<data> token is returning the entire end of the URI as just one string. We need to be able to work with that 7 there. And that 4! Well, the 4 is easy... But the 7 had the extra /notify on the end, to signal the system to notify someone that a product was updated (perhaps).
240
242
241
243
So let's make sure we can do stuff with our regex tokens that were matched, such as that I<data> token that returned a "7/notify". And to do so, we'll take advantage of another characteristic of these Grammar classes -- a thing called actions.
242
244
@@ -248,10 +250,10 @@ You can think of grammar actions as a kind of plug-in expansion module for gramm
248
250
249
251
You do this when you first create your grammar. In addition to passing in the actual string you want to parse, you can pass in a named parameter called "actions" which should contain an instance of your actions class. From our example above, if our actions class were called REST-actions we would parse our grammar string like this
250
252
251
-
=begincode :skip-test
253
+
=begincode
252
254
my $matchObj = REST.parse($uri, actions => REST-actions.new);
253
255
254
-
#…or if you prefer…
256
+
...or if you prefer...
255
257
256
258
my $matchObj = REST.parse($uri, :actions(REST-actions.new));
257
259
=endcode
@@ -302,7 +304,7 @@ But not really.
302
304
303
305
=head2Keeping grammars with actions tidy with "make" and "made"
304
306
305
-
If our grammar calls our action above on data, the data method will be called, but nothing will show up in the big TOP grammar match result returned to our program. In order to make our action results show up, we need to call "make" on that result, and that result can be many things, including strings, array or hash structures.
307
+
If our grammar calls our action above on data, the "data" method will be called, but nothing will show up in the big TOP grammar match result returned to our program. In order to make our action results show up, we need to call "make" on that result, and that result can be many things, including strings, array or hash structures.
306
308
307
309
You can imagine that the "make" we put on our action results, places that result in a special, contained area in our whole grammar. Everything that we "make" for data structures, can be accessed later by "made".
308
310
@@ -319,7 +321,7 @@ When we add "make" to our match split (which returns a list), our action will re
319
321
320
322
Now if we want to access just our id of 7 from that long URL, we can access the first element of the list returned from the "data" action we "made" with "make":
321
323
322
-
=begincode :skip-test
324
+
=begincode
323
325
my $uri = '/product/update/7/notify';
324
326
325
327
my $match = REST.parse($uri, actions => REST-actions.new);
@@ -353,7 +355,7 @@ But what we want to be certain to do, is to use the "made" method on our $<data>
353
355
354
356
After we "make" something in the TOP method of a grammar action, we can then access all of our custom-made stuff by calling the "made" method on our grammar result object. From our earlier example, it now becomes
355
357
356
-
=begincode :skip-test
358
+
=begincode
357
359
my $uri = '/product/update/7/notify';
358
360
359
361
my $match = REST.parse($uri, actions => REST-actions.new);
@@ -366,7 +368,7 @@ After we "make" something in the TOP method of a grammar action, we can then acc
366
368
367
369
Of course you could also shorten it if you knew you wouldn't need the full grammar return match object, and only wanted your custom-made data from your action's TOP.
368
370
369
-
=begincode :skip-test
371
+
=begincode
370
372
my $uri = '/product/update/7/notify';
371
373
372
374
my $rest = REST.parse($uri, actions => REST-actions.new).made;
@@ -394,7 +396,7 @@ Oh, did we forget to get rid of that ugly array element number? Hmm. Let's make
394
396
395
397
Now we can do this instead
396
398
397
-
=begincode :skip-test
399
+
=begincode
398
400
my $uri = '/product/update/7/notify';
399
401
400
402
my $rest = REST.parse($uri, actions => REST-actions.new).made;
0 commit comments