tagged release 0.7.1
[parrot.git] / languages / perl6 / src / parser / quote_expression.pir
blob9de9e36d08d457653f80e31b4d065ebbcf68c9d0
1 # Copyright (C) 2007-2008, The Perl Foundation.
2 # $Id$
4 .include 'cclass.pasm'
6 .namespace ['Perl6::Grammar']
8 .sub 'peek_brackets' :method
9     .param string target
10     .param int pos
11     .local string brackets, start, stop
12     brackets = unicode:"<>[](){}\xab\xbb"
13     start = substr target, pos, 1
14     stop = start
15     $I0 = index brackets, start
16     if $I0 < 0 goto end
17     $I1 = $I0 % 2
18     unless $I1 goto bracket_valid
19     self.'panic'("Using a closing delimiter for an opener is reserved")
20     goto end
21   bracket_valid:
22     inc $I0
23     stop = substr brackets, $I0, 1
24   end:
25     .return (start, stop)
26 .end
29 .sub 'quote_expression' :method
30     .param string flags
31     .param pmc options         :slurpy :named
33     ##  create a new match object
34     .local pmc mob
35     .local int pos
36     .local string target
37     (mob, pos, target) = self.'new'(self)
39     ##  get action object
40     .local pmc action
41     action = options['action']
43     ##  set up options based on flags
44     .local pmc flagarray, iter
45     flagarray = split ' ', flags
46     iter = new 'Iterator', flagarray
47   iter_loop:
48     unless iter goto iter_end
49     .local string oname
50     oname = shift iter
51     oname = substr oname, 1
52     options[oname] = 1
53     if oname == 'ww' goto opt_ww
54     if oname == 'w' goto opt_w
55     if oname == 'qq' goto opt_qq
56     if oname == 'b' goto opt_b
57     goto iter_loop
58   opt_ww:
59   opt_w:
60     options['wsstop'] = 1
61     goto iter_loop
62   opt_qq:
63     options['s'] = 1
64     options['a'] = 1
65     options['h'] = 1
66     options['f'] = 1
67     options['c'] = 1
68     options['b'] = 1
69   opt_b:
70     options['q'] = 1
71     goto iter_loop
72   iter_end:
74     .local string start, stop
75     (start, stop) = self.'peek_brackets'(target, pos)
77     ##  determine pos, lastpos
78     $I0 = length start
79     pos += $I0
80     .local int stoplen, lastpos, wsstop
81     stoplen = length stop
82     wsstop = options['wsstop']
83     lastpos = length target
84     lastpos -= stoplen
85     options['stop'] = stop
87     ##  handle :regex parsing
88     .local pmc p6regex, quote_regex
89     $I0 = options['regex']
90     unless $I0 goto word_start
91   regex_start:
92     p6regex = get_root_global ['parrot';'PGE::Perl6Regex'], 'regex'
93     mob.'to'(pos)
94     quote_regex = p6regex(mob, options :flat :named)
95     unless quote_regex goto fail
96     pos = quote_regex.'to'()
97     .local string key
98     key = 'quote_regex'
99     mob[key] = quote_regex
100     goto succeed
102     ##  handle word parsing
103   word_start:
104     ##  set up escapes based on flags
105     .local string escapes
106     escapes = ''
107     $I0 = options['s']
108     unless $I0 goto escape_s_done
109     escapes = '$'
110   escape_s_done:
111     $I0 = options['c']
112     unless $I0 goto escape_c_done
113     escapes .= '{'
114   escape_c_done:
115   have_escapes:
116     options['escapes'] = escapes
118     .local int optww
119     optww = options['ww']
120     unless optww goto have_wwopts
121     .local pmc wwsingleopts, wwdoubleopts
122     wwsingleopts = new 'Hash'
123     wwsingleopts['q'] = 1
124     wwsingleopts['stop'] = "'"
125     wwsingleopts['action'] = action
126     ##  FIXME: RT#48112  -- currently 'clone' on a Hash can't
127     ##  handle null entries (and does a deepcopy), so we're
128     ##  using an iterator to do it.
129     ##  wwdoubleopts = clone options
130             wwdoubleopts = new 'Hash'
131             .local pmc iter2
132             iter2 = new 'Iterator', options
133           iter2_loop:
134             unless iter2 goto iter2_end
135             $S0 = shift iter2
136             $P0 = options[$S0]
137             wwdoubleopts[$S0] = $P0
138             goto iter2_loop
139           iter2_end:
140     wwdoubleopts['stop'] = '"'
141     wwdoubleopts['wsstop'] = 0
142   have_wwopts:
144     .local pmc quote_concat
145     quote_concat = new 'ResizablePMCArray'
147     unless wsstop goto word_plain
148   word_loop:
149     pos = find_not_cclass .CCLASS_WHITESPACE, target, pos, lastpos
150     if pos > lastpos goto fail
151     $S0 = substr target, pos, stoplen
152     if $S0 == stop goto word_succeed
153     if pos >= lastpos goto fail
154     unless optww goto word_plain
155   word_shell:
156     $S0 = substr target, pos, 1
157     if $S0 == '"' goto word_shell_double
158     if $S0 != "'" goto word_plain
159   word_shell_single:
160     inc pos
161     mob.'to'(pos)
162     $P0 = mob.'quote_concat'(wwsingleopts)
163     unless $P0 goto fail
164     push quote_concat, $P0
165     pos = $P0.'to'()
166     inc pos
167     goto word_loop
168   word_shell_double:
169     inc pos
170     mob.'to'(pos)
171     $P0 = mob.'quote_concat'(wwdoubleopts)
172     unless $P0 goto fail
173     push quote_concat, $P0
174     pos = $P0.'to'()
175     inc pos
176     goto word_loop
177   word_plain:
178     mob.'to'(pos)
179     $P0 = mob.'quote_concat'(options)
180     unless $P0 goto fail
181     push quote_concat, $P0
182     pos = $P0.'to'()
183     goto word_loop
184   word_succeed:
185     key = 'quote_concat'
186     mob[key] = quote_concat
188   succeed:
189     pos += stoplen
190     mob.'to'(pos)
191     if null action goto succeed_done
192     $I0 = can action, 'quote_expression'
193     unless $I0 goto succeed_done
194     action.'quote_expression'(mob, key)
195   succeed_done:
196     .return (mob)
197   fail:
198     mob.'to'(-1)
199     .return (mob)
200 .end
203 .sub 'quote_concat' :method
204     .param pmc options
206     ##  create a new match object
207     .local pmc mob
208     .local int pos
209     .local string target
210     (mob, pos, target) = self.'new'(self)
212     ##  determine pos, lastpos
213     .local string stop
214     .local int stoplen, lastpos, wsstop
215     stop = options['stop']
216     wsstop = options['wsstop']
217     stoplen = length stop
218     lastpos = length target
219     lastpos -= stoplen
221     .local string escapes
222     escapes = options['escapes']
224     .local pmc quote_term
225     quote_term = new 'ResizablePMCArray'
227   term_loop:
228     mob.'to'(pos)
229     $P0 = mob.'quote_term'(options)
230     unless $P0 goto fail
231     push quote_term, $P0
232     pos = $P0.'to'()
233     if pos > lastpos goto fail
234     $S0 = substr target, pos, stoplen
235     if $S0 == stop goto succeed
236     unless wsstop goto term_loop
237     $I0 = is_cclass .CCLASS_WHITESPACE, target, pos
238     unless $I0 goto term_loop
239   succeed:
240     ##  save the array of captured terms
241     mob['quote_term'] = quote_term
242     mob.'to'(pos)
243     ##  call any related {*} actions
244     .local pmc action
245     action = options['action']
246     if null action goto succeed_done
247     $I0 = can action, 'quote_concat'
248     unless $I0 goto succeed_done
249     action.'quote_concat'(mob)
250   succeed_done:
251     .return (mob)
252   fail:
253     mob.'to'(-1)
254     .return (mob)
255 .end
258 .sub 'quote_term' :method
259     .param pmc options
261     .local pmc action
262     action = options['action']
264     .local pmc mob
265     .local int pos
266     .local string target
267     (mob, pos, target) = self.'new'(self)
269     .local string leadchar, escapes
270     escapes = options['escapes']
271     leadchar = substr target, pos, 1
272     $I0 = index escapes, leadchar
273     if $I0 < 0 goto term_literal
274     if leadchar == '$' goto term_scalar
275     if leadchar == '{' goto term_closure
276   term_literal:
277     mob.'to'(pos)
278     $P0 = mob.'quote_literal'(options)
279     unless $P0 goto fail
280     pos = $P0.'to'()
281     mob['quote_literal'] = $P0
282     .local string key
283     key = 'literal'
284     goto succeed
286   term_scalar:
287     mob.'to'(pos)
288     $P0 = mob.'variable'('action'=>action)
289     unless $P0 goto err_scalar
290     pos = $P0.'to'()
291     key = 'variable'
292     mob[key] = $P0
293     goto succeed
295   term_closure:
296     mob.'to'(pos)
297     $P0 = mob.'circumfix'('action'=>action)
298     unless $P0 goto fail
299     pos = $P0.'to'()
300     key = 'circumfix'
301     mob[key] = $P0
302     goto succeed
304   succeed:
305     mob.'to'(pos)
306     if null action goto succeed_done
307     $I0 = can action, 'quote_term'
308     unless $I0 goto succeed_done
309     action.'quote_term'(mob, key)
310   succeed_done:
311     .return (mob)
313   fail:
314     mob.'to'(-1)
315     .return (mob)
317   err_scalar:
318     mob.'to'(pos)
319     mob.'panic'("Can't use $ as non-variable in interpolated string")
320     .return (mob)
321 .end
324 .sub 'quote_literal' :method
325     .param pmc options
327     .local pmc mob
328     .local int pos
329     .local string target
330     (mob, pos, target) = self.'new'(self)
332     .local string stop, stop1
333     .local int stoplen, lastpos, wsstop
334     stop = options['stop']
335     wsstop = options['wsstop']
336     stop1 = substr stop, 0, 1
337     stoplen = length stop
338     lastpos = length target
339     lastpos -= stoplen
341     .local string escapes
342     .local int optq, optb
343     escapes = options['escapes']
344     optq = options['q']
345     optb = options['b']
347     .local string literal
348     literal = ''
350   scan_loop:
351     if pos > lastpos goto fail
352     $S0 = substr target, pos, stoplen
353     if $S0 == stop goto succeed
354     unless wsstop goto scan_loop_1
355     $I0 = is_cclass .CCLASS_WHITESPACE, target, pos
356     if $I0 goto succeed
357   scan_loop_1:
358     if pos >= lastpos goto fail
360   scan_char:
361     .local string litchar
362     litchar = substr target, pos, 1
363     ##  if we've reached an escape char, we're done
364     $I0 = index escapes, litchar
365     if $I0 >= 0 goto succeed
366     ##  if this isn't an interpolation, add the char
367     unless optq goto add_litchar
368     if litchar != "\\" goto add_litchar
369     ##  okay, we have a backslash, let's process it
370     .local string backchar
371     $I0 = pos + 1
372     backchar = substr target, $I0, 1
373     ##  handle :q options, \\ and \+stop
374     if backchar == "\\" goto add_backchar
375     if backchar == stop1 goto add_backchar
376     unless optb goto add_litchar
377     ##  handle :b options
378     $I0 = index "0abefnrtxdo123456789", backchar
379     if $I0 < 0 goto add_backchar
380     if $I0 >= 11 goto fail_backchar_digit
381     if $I0 >= 8 goto scan_xdo
382     litchar = substr "\0\a\b\e\f\n\r\t", $I0, 1
383     if $I0 >= 1 goto add_litchar2
384     ##  peek ahead for octal digits after \0
385     $I0 = pos + 2
386     $S0 = substr target, $I0, 1
387     $I0 = index "01234567", $S0
388     if $I0 >= 0 goto fail_backchar_digit
389   add_litchar2:
390     pos += 2
391     literal .= litchar
392     goto scan_loop
393   add_backchar:
394     literal .= backchar
395     pos += 2
396     goto scan_loop
397   add_litchar:
398     literal .= litchar
399     inc pos
400     goto scan_loop
402   scan_xdo:
403     ##  handle \x, \d, and \o escapes.  start by converting
404     ##  the backchar into 8, 10, or 16 (yes, it's a hack
405     ##  but it works).  Then loop through the characters
406     ##  that follow to compute the decimal value of codepoints,
407     ##  and add the codepoints to our literal.
408     .local int base, decnum, isbracketed
409     base = index '        o d     x', backchar
410     decnum = 0
411     pos += 2
412     $S0 = substr target, pos, 1
413     isbracketed = iseq $S0, '['
414     pos += isbracketed
415   scan_xdo_char_loop:
416     $S0 = substr target, pos, 1
417     $I0 = index '0123456789abcdef0123456789ABCDEF', $S0
418     if $I0 < 0 goto scan_xdo_char_end
419     $I0 %= 16
420     if $I0 >= base goto scan_xdo_char_end
421     decnum *= base
422     decnum += $I0
423     inc pos
424     goto scan_xdo_char_loop
425   scan_xdo_char_end:
426     $S1 = chr decnum
427     concat literal, $S1
428     unless isbracketed goto scan_xdo_end
429     if $S0 == ']' goto scan_xdo_end
430     if $S0 != ',' goto fail
431     inc pos
432     decnum = 0
433     goto scan_xdo_char_loop
434   scan_xdo_end:
435     pos += isbracketed
436     goto scan_loop
438   succeed:
439     mob.'result_object'(literal)
440     mob.'to'(pos)
441     .return (mob)
442   fail_backchar_digit:
443     self.panic('\123 form deprecated, use \o123 instead')
444   fail:
445     mob.'to'(-1)
446     .return (mob)
447 .end
450 # Local Variables:
451 #   mode: pir
452 #   fill-column: 100
453 # End:
454 # vim: expandtab shiftwidth=4 ft=pir: