-
Notifications
You must be signed in to change notification settings - Fork 138
/
codestring.pmc
397 lines (296 loc) · 10.7 KB
/
codestring.pmc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
/*
Copyright (C) 2007-2009, Parrot Foundation.
$Id$
=head1 NAME
src/pmc/codestring.pmc - CodeString PMC Class
=head1 DESCRIPTION
C<CodeString> is a class intended to simplify the process of emitting code
strings. Ideally this will eventually become a form of "CodeBuffer" that is
more efficient than string concatenation, but for now it works well enough for
me.
The primary method for C<CodeString> objects is C<emit>, which appends a line
(or lines) of code to the string according to a format parameter. The line can
contain substitution markers (ala printf) that indicate where other parameters
to the call should be placed.
Note that C<CodeString> is just a subclass of Parrot's native C<String> class,
so it's easy to combine CodeString objects with other strings outside of the
C<emit> method.
=head2 Methods
=over 4
=cut
*/
#if PARROT_HAS_ICU
# include <unicode/uchar.h>
#endif
pmclass CodeString extends String provides string auto_attrs {
ATTR PMC *linepos; /* start of line positions */
/*
=item C<init()>
Initialize the CodeString.
=cut
*/
VTABLE void init() {
SUPER();
SET_ATTR_linepos(INTERP, SELF, PMCNULL);
PObj_custom_mark_SET(SELF);
}
/*
=item C<mark()>
Mark the CodeString as live.
=cut
*/
VTABLE void mark() {
PMC *linepos;
SUPER();
if (!PMC_data(SELF))
return;
GET_ATTR_linepos(INTERP, SELF, linepos);
Parrot_gc_mark_PMC_alive(INTERP, linepos);
}
/*
=item C<emit(string fmt [, pmc args ] [, pmc hash ])>
Add a line to a C<CodeString> object according to C<fmt>.
The C<fmt> string can contain any number of "%-replacements"
which are replaced by the corresponding values from C<args>
or C<hash> prior to being appended to the string. (Here
C<args> is a slurpy array, and C<hash> is a slurpy hash.)
The currently defined replacements include:
%0 %1 ... %9 the value from the args array at index 0..9
%, the values of the args array separated by commas
%% a percent sign
A percent-sign followed by any other character that is a hash
key receives the value of the hash element.
A newline is automatically added to the end of the fmt.
=cut
*/
METHOD emit(STRING *fmt, PMC *args :slurpy, PMC *hash :slurpy :named) {
STRING * const percent = CONST_STRING(INTERP, "%");
STRING * const comma = CONST_STRING(INTERP, ",");
STRING * const comma_space = CONST_STRING(INTERP, ", ");
STRING * const newline = CONST_STRING(INTERP, "\n");
STRING *key, *repl, *S0, *S1;
INTVAL pos = 0;
INTVAL replen = 0;
INTVAL I0, I1;
fmt = Parrot_str_new_COW(INTERP, fmt);
while (pos >= 0) {
pos += replen;
pos = Parrot_str_find_index(INTERP, fmt, percent, pos);
if (pos < 0) break;
key = Parrot_str_substr(INTERP, fmt, pos+1, 1, &key, 0);
if (VTABLE_exists_keyed_str(INTERP, hash, key)) {
repl = VTABLE_get_string_keyed_str(INTERP, hash, key);
}
else if (Parrot_str_is_cclass(INTERP, enum_cclass_numeric, fmt,
(UINTVAL)pos + 1)) {
I0 = Parrot_str_to_int(INTERP, key);
repl = VTABLE_get_string_keyed_int(INTERP, args, I0);
}
else if (Parrot_str_equal(INTERP, key, comma)) {
repl = VTABLE_get_string_keyed_int(INTERP, args, 0);
repl = Parrot_str_new_COW(INTERP, repl);
I1 = VTABLE_elements(INTERP, args);
I0 = 1;
while (I0 < I1) {
S0 = VTABLE_get_string_keyed_int(INTERP, args, I0);
repl = Parrot_str_append(INTERP, repl, comma_space);
repl = Parrot_str_append(INTERP, repl, S0);
I0++;
}
}
else if (Parrot_str_equal(INTERP, key, percent)) {
repl = percent;
}
else {
/* No substitution is necessary */
replen = 2;
continue;
}
(void) Parrot_str_replace(INTERP, fmt, pos, 2, repl, NULL);
replen = Parrot_str_byte_length(INTERP, repl);
}
/* Add a newline if necessary */
if ('\n' != Parrot_str_indexed(INTERP, fmt, Parrot_str_byte_length(INTERP, fmt) - 1))
fmt = Parrot_str_concat(INTERP, fmt, newline, 0);
GET_ATTR_str_val(INTERP, SELF, S1);
S1 = Parrot_str_concat(INTERP, S1, fmt, 0);
VTABLE_set_string_native(INTERP, SELF, S1);
RETURN(PMC *SELF);
}
/*
=item lineof(INTVAL pos)
Return the line number of the line at offset C<pos>. This code assumes that
the first line is line number zero.
=cut
*/
METHOD lineof(INTVAL pos) {
PMC *linepos;
INTVAL count;
INTVAL line = 0;
GET_ATTR_linepos(INTERP, SELF, linepos);
/* build the linepos array if we haven't already done so */
if (!linepos || PMC_IS_NULL(linepos)) {
STRING *str = NULL;
INTVAL eos;
INTVAL jpos;
linepos = pmc_new(INTERP, enum_class_ResizableIntegerArray);
/* get the string itself */
GET_ATTR_str_val(INTERP, SELF, str);
eos = Parrot_str_byte_length(INTERP, str);
/* find the first newline, if any */
jpos = Parrot_str_find_cclass(INTERP, enum_cclass_newline,
str, 0, eos);
while (jpos < eos) {
jpos++;
/* add the start of line position */
VTABLE_push_integer(INTERP, linepos, jpos);
/* treat \r\n as a single newline */
if (jpos < eos
&& string_ord(INTERP, str, jpos - 1) == 13
&& string_ord(INTERP, str, jpos) == 10) {
jpos++;
}
/* search for the next newline */
jpos = Parrot_str_find_cclass(INTERP, enum_cclass_newline,
str, jpos, eos);
}
/* save the array of line positions */
SET_ATTR_linepos(INTERP, SELF, linepos);
}
/* Find the line from the array, stop at the first index that is
* greater than the position we're looking for. We do a linear
* search for now, * perhaps a binary search would be better someday.
*/
count = VTABLE_elements(INTERP, linepos);
while (line < count
&& VTABLE_get_integer_keyed_int(INTERP, linepos, line) <= pos)
line++;
RETURN(INTVAL line);
}
/*
=item C<unique([string fmt])>
Each call to C<unique> returns a unique number, or if a C<fmt>
parameter is given it returns a unique string beginning with
C<fmt>. (This may eventually be generalized to allow
uniqueness anywhere in the string.) The function starts
counting at 10 (so that the values 0..9 can be considered "safe").
=cut
*/
METHOD unique(STRING *format :optional, int has_fmt :opt_flag) {
static INTVAL counter = 10;
STRING *counter_as_string = Parrot_str_from_int(INTERP, counter);
UNUSED(SELF);
counter++;
if (!has_fmt) {
RETURN(STRING *counter_as_string);
}
else {
STRING *result = Parrot_str_copy(INTERP, format);
result = Parrot_str_concat(INTERP, result, counter_as_string, 1);
RETURN(STRING *result);
}
}
/*
=item C<escape(string str)>
Returns an escaped value of C<str> suitable for including in PIR.
If the string contains any non-ASCII characters, then it's
prefixed with 'unicode:'.
=cut
*/
METHOD escape(STRING *str) {
STRING *escaped_str = Parrot_str_escape(INTERP, str);
STRING * const quote = CONST_STRING(INTERP, "\x22");
STRING * const x = CONST_STRING(INTERP, "\\x");
INTVAL x_pos;
INTVAL is_unicode = 0;
UNUSED(SELF);
escaped_str = Parrot_str_concat(INTERP, quote, escaped_str, 1);
escaped_str = Parrot_str_concat(INTERP, escaped_str, quote, 1);
x_pos = Parrot_str_find_index(INTERP, escaped_str, x, 0);
if (x_pos != -1) {
is_unicode = 1;
}
else {
STRING * const u = CONST_STRING(INTERP, "\\u");
INTVAL u_pos = Parrot_str_find_index(INTERP, escaped_str, u, 0);
if (u_pos != -1)
is_unicode = 1;
}
if (is_unicode) {
STRING * const unicode = CONST_STRING(INTERP, "unicode:");
escaped_str = Parrot_str_concat(INTERP, unicode, escaped_str, 1);
}
RETURN(STRING *escaped_str);
}
/*
=item C<charname_to_ord(string name)>
Converts the Unicode character name given by C<name> to its
codepoint value. Returns -1 if an error occurs in conversion.
=cut
*/
METHOD charname_to_ord(STRING *name) {
#if PARROT_HAS_ICU
UErrorCode err = U_ZERO_ERROR;
char * const cstr = Parrot_str_to_cstring(INTERP, name);
UChar32 codepoint = u_charFromName(U_EXTENDED_CHAR_NAME, cstr, &err);
Parrot_str_free_cstring(cstr);
if (U_SUCCESS(err)) {
RETURN(INTVAL codepoint);
}
RETURN(INTVAL -1);
#else
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_LIBRARY_ERROR,
"no ICU lib loaded");
#endif
}
/*
=item C<key( string name1 [, string name2, ...] )>
Constructs a PIR key using the strings passed as arguments.
For example, C<key('Foo', 'Bar')> returns C<["Foo";"Bar"]>.
=cut
*/
METHOD key(PMC *args :slurpy) {
INTVAL index;
INTVAL elements = VTABLE_elements(INTERP, args);
STRING * const open_bracket = CONST_STRING(INTERP, "[");
STRING * const semi = CONST_STRING(INTERP, ";");
STRING * const close_bracket = CONST_STRING(INTERP, "]");
STRING * const s_array = CONST_STRING(INTERP, "array");
STRING * prefix = NULL;
STRING * out = open_bracket;
for (index = 0; index < elements; index++) {
PMC *P0 = VTABLE_get_pmc_keyed_int(INTERP, args, index);
if (PMC_IS_NULL(P0)) continue;
else if (VTABLE_does(INTERP, P0, s_array)) {
INTVAL elements2, index2;
elements2 = VTABLE_elements(INTERP, P0);
for (index2 = 0; index2 < elements2; index2++) {
STRING *S0 = VTABLE_get_string_keyed_int(INTERP, P0, index2);
(STRING *S0) = PCCINVOKE(INTERP, SELF, "escape", STRING *S0);
if (prefix) out = Parrot_str_append(INTERP, out, prefix);
out = Parrot_str_append(INTERP, out, S0);
prefix = semi;
}
}
else {
STRING *S0 = VTABLE_get_string_keyed_int(INTERP, args, index);
(STRING *S0) = PCCINVOKE(INTERP, SELF, "escape", STRING *S0);
if (prefix) out = Parrot_str_append(INTERP, out, prefix);
out = Parrot_str_append(INTERP, out, S0);
prefix = semi;
}
}
out = Parrot_str_append(INTERP, out, close_bracket);
RETURN(STRING *out);
}
/*
=back
=cut
*/
}
/*
* Local variables:
* c-file-style: "parrot"
* End:
* vim: expandtab shiftwidth=4:
*/