2 Copyright (C) 2007-2010, Parrot Foundation.
7 src/pmc/codestring.pmc - CodeString PMC Class
11 C<CodeString> is a class intended to simplify the process of emitting code
12 strings. Ideally this will eventually become a form of "CodeBuffer" that is
13 more efficient than string concatenation, but for now it works well enough for
16 The primary method for C<CodeString> objects is C<emit>, which appends a line
17 (or lines) of code to the string according to a format parameter. The line can
18 contain substitution markers (ala printf) that indicate where other parameters
19 to the call should be placed.
21 Note that C<CodeString> is just a subclass of Parrot's native C<String> class,
22 so it's easy to combine CodeString objects with other strings outside of the
34 # include <unicode/uchar.h>
37 /* HEADERIZER HFILE: none */
38 /* HEADERIZER BEGIN: static */
39 /* HEADERIZER END: static */
41 pmclass CodeString extends String provides string auto_attrs {
42 ATTR PMC *linepos; /* start of line positions */
48 Initialize the CodeString.
56 SET_ATTR_linepos(INTERP, SELF, PMCNULL);
57 PObj_custom_mark_SET(SELF);
64 Mark the CodeString as live.
75 GET_ATTR_linepos(INTERP, SELF, linepos);
77 Parrot_gc_mark_PMC_alive(INTERP, linepos);
83 =item C<emit(string fmt [, pmc args ] [, pmc hash ])>
85 Add a line to a C<CodeString> object according to C<fmt>.
86 The C<fmt> string can contain any number of "%-replacements"
87 which are replaced by the corresponding values from C<args>
88 or C<hash> prior to being appended to the string. (Here
89 C<args> is a slurpy array, and C<hash> is a slurpy hash.)
91 The currently defined replacements include:
93 %0 %1 ... %9 the value from the args array at index 0..9
94 %, the values of the args array separated by commas
97 A percent-sign followed by any other character that is a hash
98 key receives the value of the hash element.
100 A newline is automatically added to the end of the fmt.
106 METHOD emit(STRING *fmt, PMC *args :slurpy, PMC *hash :slurpy :named) {
107 STRING * const percent = CONST_STRING(INTERP, "%");
108 STRING * const comma = CONST_STRING(INTERP, ",");
109 STRING * const comma_space = CONST_STRING(INTERP, ", ");
110 STRING *key, *repl, *S1;
116 pos = Parrot_str_find_index(INTERP, fmt, percent, pos);
120 key = Parrot_str_substr(INTERP, fmt, pos+1, 1);
122 if (VTABLE_exists_keyed_str(INTERP, hash, key)) {
123 repl = VTABLE_get_string_keyed_str(INTERP, hash, key);
125 else if (Parrot_str_is_cclass(INTERP, enum_cclass_numeric, fmt, (UINTVAL)pos + 1)) {
126 const INTVAL I0 = Parrot_str_to_int(INTERP, key);
127 repl = VTABLE_get_string_keyed_int(INTERP, args, I0);
129 else if (Parrot_str_equal(INTERP, key, comma)) {
130 repl = Parrot_str_join(INTERP, comma_space, args);
132 else if (Parrot_str_equal(INTERP, key, percent)) {
136 /* No substitution is necessary */
141 fmt = Parrot_str_replace(INTERP, fmt, pos, 2, repl);
142 replen = Parrot_str_length(INTERP, repl);
145 GET_ATTR_str_val(INTERP, SELF, S1);
147 /* Add a newline if necessary */
148 if ('\n' != Parrot_str_indexed(INTERP, fmt, Parrot_str_length(INTERP, fmt) - 1)) {
149 STRING * const newline = CONST_STRING(INTERP, "\n");
150 PMC *parts = Parrot_pmc_new_init_int(INTERP, enum_class_FixedStringArray, 3);
151 VTABLE_set_string_keyed_int(INTERP, parts, 0, S1);
152 VTABLE_set_string_keyed_int(INTERP, parts, 1, fmt);
153 VTABLE_set_string_keyed_int(INTERP, parts, 2, newline);
154 S1 = Parrot_str_join(INTERP, STRINGNULL, parts);
157 S1 = Parrot_str_concat(INTERP, S1, fmt);
159 VTABLE_set_string_native(INTERP, SELF, S1);
167 =item lineof(INTVAL pos)
169 Return the line number of the line at offset C<pos>. This code assumes that
170 the first line is line number zero.
176 METHOD lineof(INTVAL pos) {
181 GET_ATTR_linepos(INTERP, SELF, linepos);
183 /* build the linepos array if we haven't already done so */
184 if (!linepos || PMC_IS_NULL(linepos)) {
189 linepos = Parrot_pmc_new(INTERP, enum_class_ResizableIntegerArray);
190 /* get the string itself */
191 GET_ATTR_str_val(INTERP, SELF, str);
192 eos = Parrot_str_length(INTERP, str);
193 /* find the first newline, if any */
194 jpos = Parrot_str_find_cclass(INTERP, enum_cclass_newline,
198 /* add the start of line position */
199 VTABLE_push_integer(INTERP, linepos, jpos);
201 /* treat \r\n as a single newline */
203 && string_ord(INTERP, str, jpos - 1) == 13
204 && string_ord(INTERP, str, jpos) == 10) {
207 /* search for the next newline */
208 jpos = Parrot_str_find_cclass(INTERP, enum_cclass_newline,
211 /* save the array of line positions */
212 SET_ATTR_linepos(INTERP, SELF, linepos);
215 /* Find the line from the array, stop at the first index that is
216 * greater than the position we're looking for. We do a linear
217 * search for now, * perhaps a binary search would be better someday.
219 count = VTABLE_elements(INTERP, linepos);
221 && VTABLE_get_integer_keyed_int(INTERP, linepos, line) <= pos)
230 =item C<unique([string fmt])>
232 Each call to C<unique> returns a unique number, or if a C<fmt>
233 parameter is given it returns a unique string beginning with
234 C<fmt>. (This may eventually be generalized to allow
235 uniqueness anywhere in the string.) The function starts
236 counting at 10 (so that the values 0..9 can be considered "safe").
242 METHOD unique(STRING *format :optional, int has_fmt :opt_flag) {
243 static INTVAL counter = 10;
244 STRING * const counter_as_string = Parrot_str_from_int(INTERP, counter);
250 RETURN(STRING *counter_as_string);
253 STRING *result = Parrot_str_concat(INTERP, format, counter_as_string);
254 RETURN(STRING *result);
260 =item C<escape(string str)>
262 Returns an escaped value of C<str> suitable for including in PIR.
263 If the string contains any non-ASCII characters, then it's
264 prefixed with 'unicode:'.
270 METHOD escape(STRING *str) {
271 STRING *escaped_str = Parrot_str_escape(INTERP, str);
272 STRING * const quote = CONST_STRING(INTERP, "\x22");
273 STRING * const x = CONST_STRING(INTERP, "\\x");
275 INTVAL is_unicode = 0;
278 escaped_str = Parrot_str_concat(INTERP, quote, escaped_str);
279 escaped_str = Parrot_str_concat(INTERP, escaped_str, quote);
280 x_pos = Parrot_str_find_index(INTERP, escaped_str, x, 0);
286 STRING * const u = CONST_STRING(INTERP, "\\u");
287 const INTVAL u_pos = Parrot_str_find_index(INTERP, escaped_str, u, 0);
293 STRING * const unicode = CONST_STRING(INTERP, "unicode:");
294 escaped_str = Parrot_str_concat(INTERP, unicode, escaped_str);
297 RETURN(STRING *escaped_str);
302 =item C<charname_to_ord(string name)>
304 Converts the Unicode character name given by C<name> to its
305 codepoint value. Returns -1 if an error occurs in conversion.
312 METHOD charname_to_ord(STRING *name) {
314 UErrorCode err = U_ZERO_ERROR;
315 char * const cstr = Parrot_str_to_cstring(INTERP, name);
316 UChar32 codepoint = u_charFromName(U_EXTENDED_CHAR_NAME, cstr, &err);
317 Parrot_str_free_cstring(cstr);
318 if (U_SUCCESS(err)) {
319 RETURN(INTVAL codepoint);
323 Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_LIBRARY_ERROR,
324 "no ICU lib loaded");
330 =item C<key( string name1 [, string name2, ...] )>
332 Constructs a PIR key using the strings passed as arguments.
333 For example, C<key('Foo', 'Bar')> returns C<["Foo";"Bar"]>.
339 METHOD key(PMC *args :slurpy) {
341 const INTVAL elements = VTABLE_elements(INTERP, args);
342 STRING * const open_bracket = CONST_STRING(INTERP, "[");
343 STRING * const semi = CONST_STRING(INTERP, ";");
344 STRING * const close_bracket = CONST_STRING(INTERP, "]");
345 STRING * const s_array = CONST_STRING(INTERP, "array");
346 STRING * prefix = NULL;
347 STRING * out = open_bracket;
349 for (index = 0; index < elements; ++index) {
350 PMC * const P0 = VTABLE_get_pmc_keyed_int(INTERP, args, index);
352 if (!PMC_IS_NULL(P0)) {
353 if (VTABLE_does(INTERP, P0, s_array)) {
354 const INTVAL elements2 = VTABLE_elements(INTERP, P0);
357 for (index2 = 0; index2 < elements2; ++index2) {
358 STRING *S0 = VTABLE_get_string_keyed_int(INTERP, P0, index2);
359 (STRING *S0) = PCCINVOKE(INTERP, SELF, "escape", STRING *S0);
361 out = Parrot_str_concat(INTERP, out, prefix);
362 out = Parrot_str_concat(INTERP, out, S0);
367 STRING *S0 = VTABLE_get_string_keyed_int(INTERP, args, index);
368 (STRING *S0) = PCCINVOKE(INTERP, SELF, "escape", STRING *S0);
370 out = Parrot_str_concat(INTERP, out, prefix);
371 out = Parrot_str_concat(INTERP, out, S0);
377 out = Parrot_str_concat(INTERP, out, close_bracket);
395 * c-file-style: "parrot"
397 * vim: expandtab shiftwidth=4: