tagged release 0.7.1
[parrot.git] / languages / pipp / src / pct / quote_expression.pir
bloba310c7e87abac4bd4a25529a4cf81495f7a7ef20
1 # Copyright (C) 2008, The Perl Foundation.
2 # $Id$
4 =for comment
6 PHP 5.3 has four kinds of literal strings.
8 =over 4
10 =item single quoted
12 Neither variable nor backslash interpolation, besides B<\'> and B<\\> is done within single quotes.
13 Single quotes need to be escaped with a backslash in order to be not taken for the delimiter.
14 A backslash escapes a following backslash.
15 A literal backslash needs to be escaped at end of string, as otherwise the delimiting single quote
16 would be recognised as a literal single quote.
17 In contrast to Perl 5, backslashes that preceede any other character are literal.
19 =item double quoted
21 The escape sequences
22 \n, \r, \t, \v, \f, \\, \$, \"
23 are recognised.
25 Charactes can alse be written in octal notation, \[0-7]{1,3},
26 and hexadecimal notation, \x[0-9A-Fa-f]{1,2}.
27 The octal notation allows to specify values greater 256. In theses
28 cases the value is taken as mod 256.
30 =item heredoc
32    $param = 'dummy';
33    $example = <<<EXAMPLE
34    Variables are interpolated.
35    $param
36    EXAMPLE
38 Double quotes are literal.
39 The backslashes before a double quote are literal.
40 Unlike in Perl 5, the newline before the delimiter is not part of the string.
42 =item nowdoc
44 A heredoc with single quotes.
46    $param = 'dummy';
47    $example = <<<'EXAMPLE'
48    Variables are not interpolated.
49    $param
50    EXAMPLE
52 Single quotes are literal.
53 Backslashes are literal.
54 Unlike in Perl 5, the newline before the delimiter is not part of the string.
56 =cut
58 .namespace ['Pipp::Grammar']
60 ## called from code in grammar.pg
61 .sub 'quote_expression' :method
62     .param string flags
63     .param pmc    options    :slurpy :named
65     ##  create a new match object
66     .local pmc mob
67     .local int pos
68     .local string target
69     (mob, pos, target) = self.'new'(self)
71     ##  get action object
72     .local pmc action
73     action = options['action']
75     ##  set up options based on flags
76     .local pmc flagarray, iter
77     flagarray = split ' ', flags
78     iter = new 'Iterator', flagarray
79   iter_loop:
80     unless iter goto iter_end
81     .local string oname
82     oname = shift iter
83     oname = substr oname, 1   # remove the leading colon
84     options[oname] = 1
85     if oname == 'qq' goto opt_qq
86     goto iter_loop
87   opt_qq:
88     options['s'] = 1        # interpolate variables
89     options['c'] = 1        # interpolate stuff in '{ }', when there is a $ right after the '{'
90     options['b'] = 1        # Interpolate \n, \t, etc. 'b' stands for backslash
91     options['q'] = 1        # Interpolate \\, \q and \' (or whatever)
92     goto iter_loop
93   iter_end:
95     ## there is no heredoc-support yet, so the delimiter are either single or double quotes
96     .local string start, stop
97     start = substr target, pos, 1
98     stop  = start
99     options['stop'] = stop
100     pos = pos + 1
102     ##  determine lastpos
103     .local int lastpos
104     lastpos = length target
105     lastpos -= 1
107     .local string key
108     ##  handle word parsing
109   word_start:
110     ##  set up escapes based on flags
111     .local string escapes
112     escapes = ''
113     $I0 = options['s']
114     unless $I0 goto escape_s_done
115     escapes = '$'
116   escape_s_done:
117     $I0 = options['c']
118     unless $I0 goto escape_c_done
119     escapes .= '{'
120   escape_c_done:
121   have_escapes:
122     options['escapes'] = escapes
124     .local pmc quote_concat
125     quote_concat = new 'ResizablePMCArray'
127     goto word_plain
128   word_loop:
129     $S0 = substr target, pos, 1
130     if $S0 == stop goto word_succeed
131     if pos >= lastpos goto fail
132     goto word_plain
134   word_plain:
135     mob.'to'(pos)
136     $P0 = mob.'quote_concat'(options)
137     unless $P0 goto fail
138     push quote_concat, $P0
139     pos = $P0.'to'()
140     goto word_loop
141   word_succeed:
142     key = 'quote_concat'
143     mob[key] = quote_concat
145   succeed:
146     pos += 1
147     mob.'to'(pos)
148     if null action goto succeed_done
149     $I0 = can action, 'quote_expression'
150     unless $I0 goto succeed_done
151     action.'quote_expression'(mob, key)
152   succeed_done:
153     .return (mob)
154   fail:
155     mob.'to'(-1)
156     .return (mob)
157 .end
160 .sub 'quote_concat' :method
161     .param pmc options
163     ##  create a new match object
164     .local pmc mob
165     .local int pos
166     .local string target
167     (mob, pos, target) = self.'new'(self)
169     ##  determine pos, lastpos
170     .local string stop
171     .local int lastpos
172     stop = options['stop']
173     lastpos = length target
174     lastpos -= 1
176     .local string escapes
177     escapes = options['escapes']
179     .local pmc quote_term
180     quote_term = new 'ResizablePMCArray'
182   term_loop:
183     mob.'to'(pos)
184     $P0 = mob.'quote_term'(options)
185     unless $P0 goto fail
186     push quote_term, $P0
187     pos = $P0.'to'()
188     if pos > lastpos goto fail
189     $S0 = substr target, pos, 1
190     if $S0 == stop goto succeed
191     goto term_loop
192   succeed:
193     ##  save the array of captured terms
194     mob['quote_term'] = quote_term
195     mob.'to'(pos)
196     ##  call any related {*} actions
197     .local pmc action
198     action = options['action']
199     if null action goto succeed_done
200     $I0 = can action, 'quote_concat'
201     unless $I0 goto succeed_done
202     action.'quote_concat'(mob)
203   succeed_done:
204     .return (mob)
205   fail:
206     mob.'to'(-1)
207     .return (mob)
208 .end
211 .sub 'quote_term' :method
212     .param pmc options
214     .local pmc action
215     action = options['action']
217     .local pmc mob
218     .local int pos
219     .local string target
220     (mob, pos, target) = self.'new'(self)
222     .local int dollar_is_literal
223     dollar_is_literal = 0
225     .local string leadchar, escapes
226     escapes = options['escapes']
227     leadchar = substr target, pos, 1
228     $I0 = index escapes, leadchar
229     if $I0 < 0 goto term_literal
230     if leadchar == '$' goto term_scalar
231     if leadchar == '{' goto term_closure
232   term_literal:
233     mob.'to'(pos)
234     $P0 = mob.'quote_literal'(options, dollar_is_literal)
235     unless $P0 goto fail
236     pos = $P0.'to'()
237     mob['quote_literal'] = $P0
238     .local string key
239     key = 'literal'
240     goto succeed
242   term_scalar:
243     mob.'to'(pos)
244     $P0 = mob.'var'('action'=>action)
245     unless $P0 goto var_did_not_match
246     pos = $P0.'to'()
247     key = 'var'
248     mob[key] = $P0
249     goto succeed
250   var_did_not_match:
251     dollar_is_literal = 1
252     goto term_literal
254   term_closure:
255     mob.'to'(pos)
256     $P0 = mob.'curly_interpolation'('action'=>action)
257     unless $P0 goto term_literal
258     pos = $P0.'to'()
259     key = 'curly_interpolation'
260     mob[key] = $P0
261     goto succeed
263   succeed:
264     mob.'to'(pos)
265     if null action goto succeed_done
266     $I0 = can action, 'quote_term'
267     unless $I0 goto succeed_done
268     action.'quote_term'(mob, key)
269   succeed_done:
270     .return (mob)
272   fail:
273     mob.'to'(-1)
274     .return (mob)
275 .end
278 .sub 'quote_literal' :method
279     .param pmc options
280     .param int dollar_is_literal
282     .local pmc mob
283     .local int pos
284     .local string target
285     (mob, pos, target) = self.'new'(self)
287     .local string stop, stop1
288     .local int lastpos
289     stop = options['stop']
290     stop1 = substr stop, 0, 1
291     lastpos = length target
292     lastpos -= 1
294     .local string escapes
295     .local int optq, optb
296     escapes = options['escapes']
297     optq = options['q']
298     optb = options['b']
300     .local string literal
301     literal = ''
302   scan_loop:
303     if pos > lastpos goto fail
304     $S0 = substr target, pos, 1
305     if $S0 == stop goto succeed
306     goto scan_loop_1
307   scan_loop_1:
308     if pos >= lastpos goto fail
310   scan_char:
311     .local string litchar
312     litchar = substr target, pos, 1
313     ##  if we've reached an escape char, we're done
314     if litchar == '{' goto add_litchar
315     unless dollar_is_literal goto dollar_is_not_a_literal
316         if litchar == '$' goto add_litchar
317     dollar_is_not_a_literal:
318     $I0 = index escapes, litchar
319     if $I0 >= 0 goto succeed
320     ##  if this isn't an interpolation, add the char
321     unless optq goto add_litchar
322     if litchar != "\\" goto add_litchar
323     ##  okay, we have a backslash, let's process it
324     .local string backchar
325     $I0 = pos + 1
326     backchar = substr target, $I0, 1
327     ##  handle :q options, \\ and \+stop
328     if backchar == "\\" goto add_backchar
329     if backchar == stop1 goto add_backchar
330     unless optb goto add_litchar
331     ##  handle :b options
332     $I0 = index "vfnrt\\$\"x0123456789", backchar
333     if $I0 < 0 goto add_backslash_and_backchar
334     if $I0 >  8 goto scan_octal
335     if $I0 == 8 goto scan_hex
336     litchar = substr "\v\f\n\r\t\\$\"", $I0, 1
337   add_litchar2:
338     pos += 2
339     literal .= litchar
340     goto scan_loop
341   add_backslash_and_backchar:
342     literal .= '\'
343   add_backchar:
344     pos += 2
345     literal .= backchar
346     goto scan_loop
347   add_litchar:
348     pos += 1
349     literal .= litchar
350     goto scan_loop
353     .local int base, decnum, max_digits, cnt_digit
354   scan_octal:
355     base = 8
356     max_digits = 3
357     pos += 1     # octal digits come right after the '\'
358     goto got_base
359   scan_hex:
360     base = 16
361     max_digits = 2
362     pos += 2     # skip the 'x'
363   got_base:
364     ##  Handle hex and octal escapes.
365     ##  The base is either 8 or 16.
366     ##  Then loop through the characters
367     ##  that follow to compute the decimal value of codepoints,
368     ##  and add the codepoints to our literal.
369     decnum = 0
370     cnt_digit = 1
371     $S0 = substr target, pos, 1
372   scan_xo_char_loop:
373     if cnt_digit > max_digits goto scan_xo_char_end
374     inc cnt_digit
375     $S0 = substr target, pos, 1
376     $I0 = index '0123456789abcdef0123456789ABCDEF', $S0
377     if $I0 < 0 goto scan_xo_char_end
378     $I0 %= 16
379     if $I0 >= base goto scan_xo_char_end
380     decnum *= base
381     decnum += $I0
382     inc pos
383     goto scan_xo_char_loop
384   scan_xo_char_end:
385     $S1 = chr decnum
386     concat literal, $S1
387   scan_xo_end:
388     goto scan_loop
390   succeed:
391     mob.'result_object'(literal)
392     mob.'to'(pos)
393     .return (mob)
394   fail_backchar_digit:
395     self.panic('encountered invalid octal digit')
396   fail:
397     mob.'to'(-1)
398     .return (mob)
399 .end
402 # Local Variables:
403 #   mode: pir
404 #   fill-column: 100
405 # End:
406 # vim: expandtab shiftwidth=4 ft=pir: