fix codetest failure - ASSERT_ARGS does not have a ; after and
[parrot.git] / src / pmc / codestring.pmc
blobd1dd65878458365e76c9fdb585d2e615bb238f5a
1 /*
2 Copyright (C) 2007-2010, Parrot Foundation.
3 $Id$
5 =head1 NAME
7 src/pmc/codestring.pmc - CodeString PMC Class
9 =head1 DESCRIPTION
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
14 me.
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
23 C<emit> method.
25 =head2 Methods
27 =over 4
29 =cut
33 #if PARROT_HAS_ICU
34 #  include <unicode/uchar.h>
35 #endif
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 */
46 =item C<init()>
48 Initialize the CodeString.
50 =cut
54     VTABLE void init() {
55         SUPER();
56         SET_ATTR_linepos(INTERP, SELF, PMCNULL);
57         PObj_custom_mark_SET(SELF);
58     }
62 =item C<mark()>
64 Mark the CodeString as live.
66 =cut
70     VTABLE void mark() {
71         SUPER();
72         if (PMC_data(SELF)) {
73             PMC *linepos;
75             GET_ATTR_linepos(INTERP, SELF, linepos);
77             Parrot_gc_mark_PMC_alive(INTERP, linepos);
78         }
79     }
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
95     %%               a percent sign
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.
102 =cut
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;
111     INTVAL pos          = 0;
112     INTVAL replen       = 0;
114     while (pos >= 0) {
115         pos += replen;
116         pos = Parrot_str_find_index(INTERP, fmt, percent, pos);
117         if (pos < 0)
118             break;
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);
124         }
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);
128         }
129         else if (Parrot_str_equal(INTERP, key, comma)) {
130             repl = Parrot_str_join(INTERP, comma_space, args);
131         }
132         else if (Parrot_str_equal(INTERP, key, percent)) {
133             repl = percent;
134         }
135         else {
136             /* No substitution is necessary */
137             replen = 2;
138             continue;
139         }
141         fmt    = Parrot_str_replace(INTERP, fmt, pos, 2, repl);
142         replen = Parrot_str_length(INTERP, repl);
143     }
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);
155     }
156     else
157         S1 = Parrot_str_concat(INTERP, S1, fmt);
159     VTABLE_set_string_native(INTERP, SELF, S1);
161     RETURN(PMC *SELF);
162   }
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.
172 =cut
176   METHOD lineof(INTVAL pos) {
177     PMC    *linepos;
178     INTVAL  count;
179     INTVAL  line = 0;
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)) {
185         STRING *str             = NULL;
186         INTVAL  eos;
187         INTVAL  jpos;
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,
195                                       str, 0, eos);
196         while (jpos < eos) {
197             ++jpos;
198             /* add the start of line position */
199             VTABLE_push_integer(INTERP, linepos, jpos);
201             /* treat \r\n as a single newline */
202             if (jpos < eos
203             && string_ord(INTERP, str, jpos - 1)     == 13
204             && string_ord(INTERP, str, jpos)         == 10) {
205                 ++jpos;
206             }
207             /* search for the next newline */
208             jpos = Parrot_str_find_cclass(INTERP, enum_cclass_newline,
209                                           str, jpos, eos);
210         }
211         /* save the array of line positions */
212         SET_ATTR_linepos(INTERP, SELF, linepos);
213     }
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.
218      */
219     count = VTABLE_elements(INTERP, linepos);
220     while (line < count
221           && VTABLE_get_integer_keyed_int(INTERP, linepos, line) <= pos)
222         ++line;
224     RETURN(INTVAL line);
225   }
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").
238 =cut
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);
245     UNUSED(SELF);
247     ++counter;
249     if (!has_fmt) {
250         RETURN(STRING *counter_as_string);
251     }
252     else {
253         STRING *result = Parrot_str_concat(INTERP, format, counter_as_string);
254         RETURN(STRING *result);
255     }
256   }
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:'.
266 =cut
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");
274     INTVAL x_pos;
275     INTVAL is_unicode = 0;
276     UNUSED(SELF);
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);
282     if (x_pos != -1) {
283         is_unicode = 1;
284     }
285     else {
286         STRING * const u = CONST_STRING(INTERP, "\\u");
287         const INTVAL u_pos = Parrot_str_find_index(INTERP, escaped_str, u, 0);
288         if (u_pos != -1)
289             is_unicode = 1;
290     }
292     if (is_unicode) {
293         STRING * const unicode = CONST_STRING(INTERP, "unicode:");
294         escaped_str            = Parrot_str_concat(INTERP, unicode, escaped_str);
295     }
297     RETURN(STRING *escaped_str);
298   }
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.
307 =cut
312   METHOD charname_to_ord(STRING *name) {
313 #if PARROT_HAS_ICU
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);
320     }
321     RETURN(INTVAL -1);
322 #else
323     Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_LIBRARY_ERROR,
324         "no ICU lib loaded");
325 #endif
326   }
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"]>.
335 =cut
339   METHOD key(PMC *args :slurpy) {
340     INTVAL         index;
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);
355                 INTVAL index2;
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);
360                     if (prefix)
361                         out = Parrot_str_concat(INTERP, out, prefix);
362                     out    = Parrot_str_concat(INTERP, out, S0);
363                     prefix = semi;
364                 }
365             }
366             else {
367                 STRING *S0   = VTABLE_get_string_keyed_int(INTERP, args, index);
368                 (STRING *S0) = PCCINVOKE(INTERP, SELF, "escape", STRING *S0);
369                 if (prefix)
370                     out = Parrot_str_concat(INTERP, out, prefix);
371                 out    = Parrot_str_concat(INTERP, out, S0);
372                 prefix = semi;
373             }
374         }
375     }
377     out = Parrot_str_concat(INTERP, out, close_bracket);
379     RETURN(STRING *out);
384 =back
386 =cut
394  * Local variables:
395  *   c-file-style: "parrot"
396  * End:
397  * vim: expandtab shiftwidth=4:
398  */