Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / lang / LangPrimSource / PyrStringPrim.cpp
blob365ae848781f9658ac1249b4a6015560e7e34e0e
1 /*
2 SuperCollider real time audio synthesis system
3 Copyright (c) 2002 James McCartney. All rights reserved.
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 Primitives for String.
26 #include "PyrPrimitive.h"
27 #include "PyrKernel.h"
28 #include "GC.h"
29 #include "Hash.h"
30 #include <string.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33 #include "PyrLexer.h"
34 #include "SC_DirUtils.h"
35 #ifdef SC_WIN32
36 # include <direct.h>
37 # include "SC_Win32Utils.h"
38 #else
39 # include <sys/param.h>
40 #endif
42 #include <boost/regex.hpp>
43 #include <boost/intrusive/list.hpp>
44 #include <boost/intrusive/unordered_set.hpp>
46 #include <fstream>
47 #include "yaml-cpp/yaml.h"
49 #include <string>
50 #include <vector>
52 using namespace std;
54 int prStringAsSymbol(struct VMGlobals *g, int numArgsPushed);
55 int prStringAsSymbol(struct VMGlobals *g, int numArgsPushed)
57 PyrSlot *a;
58 char str[1024], *strp=0;
59 int len;
61 a = g->sp;
62 len = slotRawObject(a)->size;
63 strp = len > 1023 ? (char*)malloc(len+1) : str;
65 memcpy(strp, slotRawString(a)->s, len);
66 strp[len] = 0;
68 SetSymbol(a, getsym(strp));
70 if (len > 1023) free(strp);
72 return errNone;
75 int prString_AsInteger(struct VMGlobals *g, int numArgsPushed);
76 int prString_AsInteger(struct VMGlobals *g, int numArgsPushed)
78 PyrSlot *a = g->sp;
80 char str[256];
81 int err = slotStrVal(a, str, 255);
82 if (err) return err;
84 SetInt(a, atoi(str));
86 return errNone;
89 int prString_AsFloat(struct VMGlobals *g, int numArgsPushed);
90 int prString_AsFloat(struct VMGlobals *g, int numArgsPushed)
92 PyrSlot *a = g->sp;
94 char str[256];
95 int err = slotStrVal(a, str, 255);
96 if (err) return err;
98 SetFloat(a, atof(str));
100 return errNone;
103 int prString_AsCompileString(struct VMGlobals *g, int numArgsPushed)
105 PyrSlot *a = g->sp;
106 PyrString* scstr = slotRawString(a);
107 char *chars1 = scstr->s;
108 int newSize = scstr->size + 2;
109 for (int i=0; i<scstr->size; ++i) {
110 if (chars1[i] == '"' || chars1[i] == '\\') newSize++;
112 PyrString *newString = newPyrStringN(g->gc, newSize, 0, true);
113 char *chars2 = newString->s;
114 chars2[0] = '"';
115 chars2[newSize - 1] = '"';
116 int k = 1;
117 for (int i=0; i<scstr->size; ++i) {
118 int c = chars1[i];
119 if (c == '"' || c == '\\') chars2[k++] = '\\';
120 chars2[k++] = c;
122 SetObject(a, newString);
123 return errNone;
126 int prString_Format(struct VMGlobals *g, int numArgsPushed)
128 PyrSlot *a = g->sp - 1;
129 PyrSlot *b = g->sp;
131 if (!isKindOfSlot(b, class_array)) return errWrongType;
133 char *fmt = slotRawString(a)->s;
135 int asize = slotRawObject(a)->size;
136 int bsize = slotRawObject(b)->size;
137 int csize = asize;
139 PyrSlot *slots = slotRawObject(b)->slots;
140 for (int i=0; i<bsize; ++i) {
141 PyrSlot *slot = slots + i;
142 if (!isKindOfSlot(slot, class_string)) return errWrongType;
143 csize += slotRawString(slot)->size;
145 PyrString *newString = newPyrStringN(g->gc, csize, 0, true);
146 char* buf = newString->s;
148 int k=0;
149 int index = 0;
150 for (int i=0; i<asize;) {
151 char ch = fmt[i++];
152 if (ch == '%') {
153 if (index < bsize) {
154 PyrString* bstring = slotRawString(&slots[index]);
155 memcpy(buf+k, bstring->s, bstring->size);
156 k += bstring->size;
157 index++;
159 } else if (ch == '\\') {
160 if (i >= asize) break;
161 ch = fmt[i++];
162 if (ch == '%') {
163 buf[k++] = '%';
164 } else {
165 i--;
167 } else {
168 buf[k++] = ch;
171 newString->size = k;
172 SetObject(a, newString);
173 return errNone;
176 namespace detail {
178 namespace bin = boost::intrusive;
180 class regex_lru_cache
182 int regex_flags;
184 struct regex_node:
185 bin::list_base_hook<>,
186 bin::unordered_set_base_hook<>
188 public:
189 regex_node(const char * str, size_t size, int regex_flags):
190 pattern(str, size, regex_flags)
193 boost::regex const & get (void) const
195 return pattern;
198 private:
199 boost::regex pattern;
202 struct regex_equal
204 bool operator()(regex_node const & lhs, regex_node const & rhs) const
206 return lhs.get() == rhs.get();
209 bool operator()(const char * lhs, regex_node const & rhs) const
211 return strcmp(lhs, rhs.get().str().c_str()) == 0;
215 static inline std::size_t string_hash(const char * str)
217 std::size_t ret = 0;
219 // sdbm hash
220 int c;
221 while ((c = *str++))
222 ret = c + (ret << 6) + (ret << 16) - ret;
224 return ret;
227 struct regex_hash
229 size_t operator()(regex_node const & arg) const
231 return string_hash(arg.get().str().c_str());
234 size_t operator()(const char * arg) const
236 return string_hash(arg);
240 typedef bin::unordered_set<regex_node, bin::equal<regex_equal>, bin::hash<regex_hash>,
241 bin::power_2_buckets<true>, bin::constant_time_size<false> >
242 re_set_t;
243 typedef re_set_t::bucket_type bucket_type;
244 typedef re_set_t::bucket_traits bucket_traits;
245 bucket_type buckets[128];
246 re_set_t re_set;
248 bin::list<regex_node> re_list;
250 void pop_lru()
252 regex_node & rlu = re_list.back();
253 re_list.pop_back();
254 re_set.erase(rlu);
255 delete &rlu;
258 public:
259 regex_lru_cache(int regex_flags = boost::regex_constants::ECMAScript):
260 re_set(bucket_traits(buckets, 128))
263 ~regex_lru_cache()
265 while (!re_list.empty()) {
266 pop_lru();
270 boost::regex const & get_regex(const char * str, size_t size)
272 re_set_t::iterator re_in_cache = re_set.find(str, regex_hash(), regex_equal());
273 if (re_in_cache != re_set.end()) {
274 regex_node & node = *re_in_cache;
275 bin::list<regex_node>::iterator re_in_list = bin::list<regex_node>::s_iterator_to(node);
277 re_list.splice(re_list.begin(), re_list, re_in_list); // move to the begin of the list
278 assert(&re_list.front() == &node);
279 return node.get();
282 if (re_list.size() >= 64)
283 pop_lru();
285 regex_node * new_node = new regex_node(str, size, regex_flags);
286 re_set.insert(*new_node);
287 re_list.push_front(*new_node);
288 return new_node->get();
294 int prString_Regexp(struct VMGlobals *g, int numArgsPushed)
296 /* not reentrant */
297 static detail::regex_lru_cache regex_lru_cache(boost::regex_constants::ECMAScript | boost::regex_constants::nosubs);
299 using namespace boost;
301 int start, end, len;
303 PyrSlot *a = g->sp - 3;
304 PyrSlot *b = g->sp - 2;
305 PyrSlot *c = g->sp - 1;
306 PyrSlot *d = g->sp;
308 if (!isKindOfSlot(b, class_string)) return errWrongType;
309 if (NotInt(c) || (NotInt(d) && NotNil(d))) return errWrongType;
310 start = slotRawInt(c);
312 len = slotRawObject(b)->size; // last char index instead of size
314 if(IsNil(d)) {
315 end = len;
316 } else {
317 end = slotRawInt(d);
320 if(end > len)
321 end = len;
323 if(end - start <= 0) {
324 SetFalse(a);
325 return errNone;
328 int stringlen = end - start;
330 try {
331 regex const & pattern = regex_lru_cache.get_regex(slotRawString(a)->s, slotRawObject(a)->size);
332 match_flag_type flags = match_nosubs | match_any;
334 const char * stringStart = slotRawString(b)->s + start;
335 const char * stringEnd = stringStart + stringlen;
336 bool res = regex_search(stringStart, stringEnd, pattern, flags);
338 if(res)
339 SetTrue(a);
340 else
341 SetFalse(a);
343 return errNone;
344 } catch (std::exception const & e) {
345 postfl("Warning: Exception in _String_Regexp - %s\n", e.what());
346 return errFailed;
350 struct sc_regexp_match {
351 int pos;
352 int len;
356 static int prString_FindRegexp(struct VMGlobals *g, int numArgsPushed)
358 /* not reentrant */
359 static detail::regex_lru_cache regex_lru_cache(boost::regex_constants::ECMAScript);
361 using namespace boost;
363 PyrSlot *a = g->sp - 2; // source string
364 PyrSlot *b = g->sp - 1; // pattern
365 PyrSlot *c = g->sp; // offset
367 if (!isKindOfSlot(b, class_string) || (NotInt(c))) return errWrongType;
369 int offset = slotRawInt(c);
370 int stringlen = std::max(slotRawObject(a)->size - offset, 0);
372 std::vector<sc_regexp_match> matches;
373 const char* const stringBegin = slotRawString(a)->s + offset;
374 try {
375 regex const & pattern = regex_lru_cache.get_regex(slotRawString(b)->s, slotRawObject(b)->size);
376 match_flag_type flags = match_default;
378 match_results<const char*> what;
379 const char* start = stringBegin;
380 const char* end = start + stringlen;
381 while (start <= end && regex_search(start, end, what, pattern, flags))
383 for (int i = 0; i < what.size(); ++i )
385 sc_regexp_match match;
386 if (what[i].matched) {
387 match.pos = what[i].first - stringBegin;
388 match.len = what[i].second - what[i].first;
389 } else {
390 match.pos = 0;
391 match.len = 0;
393 matches.push_back(match);
395 start = what[0].second;
396 if(what[0].first == what[0].second) ++start;
398 } catch (std::exception const & e) {
399 postfl("Warning: Exception in _String_FindRegexp - %s\n", e.what());
400 return errFailed;
403 int match_count = matches.size();
405 PyrObject *result_array = newPyrArray(g->gc, match_count, 0, true);
406 result_array->size = 0;
407 SetObject(a, result_array);
409 if( !match_count ) return errNone;
411 for (int i = 0; i < match_count; ++i )
413 int pos = matches[i].pos;
414 int len = matches[i].len;
416 PyrObject *array = newPyrArray(g->gc, 2, 0, true);
417 SetObject(result_array->slots + i, array);
418 result_array->size++;
419 g->gc->GCWrite(result_array, array);
421 PyrString *matched_string = newPyrStringN(g->gc, len, 0, true);
422 memcpy(matched_string->s, stringBegin + pos, len);
424 array->size = 2;
425 SetInt(array->slots, pos + offset);
426 SetObject(array->slots+1, matched_string);
427 g->gc->GCWrite(array, matched_string);
430 return errNone;
433 int memcmpi(char *a, char *b, int len)
435 for (int i=0; i<len; ++i) {
436 char aa = toupper(a[i]);
437 char bb = toupper(b[i]);
438 if (aa < bb) return -1;
439 if (aa > bb) return 1;
441 return 0;
444 int prStringCompare(struct VMGlobals *g, int numArgsPushed);
445 int prStringCompare(struct VMGlobals *g, int numArgsPushed)
447 PyrSlot *a, *b, *c;
448 int cmp, length;
450 a = g->sp - 2;
451 b = g->sp - 1;
452 c = g->sp;
454 if (NotObj(b) || !isKindOf(slotRawObject(b), class_string)) {
455 SetNil(a);
456 return errNone;
458 length = sc_min(slotRawObject(a)->size, slotRawObject(b)->size);
459 if (IsTrue(c)) cmp = memcmpi(slotRawString(a)->s, slotRawString(b)->s, length);
460 else cmp = memcmp(slotRawString(a)->s, slotRawString(b)->s, length);
461 if (cmp == 0) {
462 if (slotRawObject(a)->size < slotRawObject(b)->size) cmp = -1;
463 else if (slotRawObject(a)->size > slotRawObject(b)->size) cmp = 1;
465 SetInt(a, cmp);
466 return errNone;
469 int prStringHash(struct VMGlobals *g, int numArgsPushed);
470 int prStringHash(struct VMGlobals *g, int numArgsPushed)
472 PyrSlot *a = g->sp;
473 int hash = Hash(slotRawString(a)->s, slotRawString(a)->size);
474 SetInt(a, hash);
475 return errNone;
478 #ifndef SC_WIN32
479 #include <glob.h>
481 int prStringPathMatch(struct VMGlobals *g, int numArgsPushed);
482 int prStringPathMatch(struct VMGlobals *g, int numArgsPushed)
484 PyrSlot *a = g->sp;
486 char pattern[1024];
487 int err = slotStrVal(a, pattern, 1023);
488 if (err) return err;
490 glob_t pglob;
492 int gflags = GLOB_MARK | GLOB_TILDE;
493 #ifdef SC_DARWIN
494 gflags |= GLOB_QUOTE;
495 #endif
497 int gerr = glob(pattern, gflags, NULL, &pglob);
498 if (gerr) {
499 pglob.gl_pathc = 0;
501 PyrObject* array = newPyrArray(g->gc, pglob.gl_pathc, 0, true);
502 SetObject(a, array);
503 if (gerr) return errNone;
505 for (unsigned int i=0; i<pglob.gl_pathc; ++i) {
506 PyrObject *string = (PyrObject*)newPyrString(g->gc, pglob.gl_pathv[i], 0, true);
507 SetObject(array->slots+i, string);
508 g->gc->GCWrite(array, string);
509 array->size++;
512 globfree(&pglob);
514 return errNone;
516 #else //#ifndef SC_WIN32
517 int prStringPathMatch(struct VMGlobals *g, int numArgsPushed);
519 int prStringPathMatch(struct VMGlobals *g, int numArgsPushed)
521 PyrSlot *a = g->sp;
523 char pattern[1024];
524 int err = slotStrVal(a, pattern, 1023);
525 if (err) return err;
527 win32_ReplaceCharInString(pattern,1024,'/','\\');
528 // Remove trailing slash if found, to allow folders to be matched
529 if(pattern[strlen(pattern)-1]=='\\'){
530 pattern[strlen(pattern)-1] = 0;
532 // extract the containing folder, including backslash
533 char folder[1024];
534 win32_ExtractContainingFolder(folder,pattern,1024);
536 ///////// PASS 1
538 WIN32_FIND_DATA findData;
539 HANDLE hFind;
540 int nbPaths = 0;
542 hFind = ::FindFirstFile(pattern, &findData);
543 if (hFind == INVALID_HANDLE_VALUE) {
544 nbPaths = 0;
547 if (hFind == INVALID_HANDLE_VALUE) {
548 // This is what happens when no matches. So we create an empty array to return.
549 PyrObject* array = newPyrArray(g->gc, 0, 0, true);
550 SetObject(a, array);
551 return errNone;
554 do {
555 if(strcmp(findData.cFileName, "..")!=0 && strcmp(findData.cFileName, "..")!=0){
556 nbPaths++;
558 } while( ::FindNextFile(hFind, &findData));
559 ::FindClose(hFind);
561 // PASS 2
563 hFind = ::FindFirstFile(pattern, &findData);
564 if (hFind == INVALID_HANDLE_VALUE) {
565 nbPaths = 0;
568 PyrObject* array = newPyrArray(g->gc, nbPaths , 0, true);
569 SetObject(a, array);
570 if (hFind == INVALID_HANDLE_VALUE) {
571 return errNone;
574 int i = 0;
575 do {
576 if(strcmp(findData.cFileName, "..")!=0 && strcmp(findData.cFileName, ".")!=0){
577 std::string strPath(folder);
578 strPath += std::string(findData.cFileName);
579 if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
580 strPath += std::string("\\"); // Append trailing slash, to match behaviour on unix (used by sclang to detect folderness)
582 const char* fullPath = strPath.c_str();
583 PyrObject *string = (PyrObject*)newPyrString(g->gc, fullPath, 0, true);
584 SetObject(array->slots+i, string);
585 g->gc->GCWrite(array, string);
586 array->size++;
587 i++;
589 } while( ::FindNextFile(hFind, &findData));
590 ::FindClose(hFind);
591 return errNone;
593 #endif //#ifndef SC_WIN32
595 int prString_Getenv(struct VMGlobals* g, int numArgsPushed);
596 int prString_Getenv(struct VMGlobals* g, int /* numArgsPushed */)
598 PyrSlot* arg = g->sp;
599 char key[256];
600 char* value;
601 int err;
603 err = slotStrVal(arg, key, 256);
604 if (err) return err;
606 value = getenv(key);
608 if (value) {
609 PyrString* pyrString = newPyrString(g->gc, value, 0, true);
610 if (!pyrString) return errFailed;
611 SetObject(arg, pyrString);
612 } else {
613 SetNil(arg);
616 return errNone;
619 int prString_Setenv(struct VMGlobals* g, int numArgsPushed);
620 int prString_Setenv(struct VMGlobals* g, int /* numArgsPushed */)
622 PyrSlot* args = g->sp - 1;
623 char key[256];
624 int err;
626 err = slotStrVal(args+0, key, 256);
627 if (err) return err;
629 if (IsNil(args+1)) {
630 #ifdef SC_WIN32
631 SetEnvironmentVariable(key,NULL);
632 #else
633 unsetenv(key);
634 #endif
635 } else {
636 char value[1024];
637 err = slotStrVal(args+1, value, 1024);
638 if (err) return err;
639 #ifdef SC_WIN32
640 SetEnvironmentVariable(key, value);
641 #else
642 setenv(key, value, 1);
643 #endif
646 return errNone;
649 int prStripRtf(struct VMGlobals *g, int numArgsPushed);
650 int prStripRtf(struct VMGlobals *g, int numArgsPushed)
652 PyrSlot *a = g->sp;
653 int len = slotRawObject(a)->size;
654 char * chars = (char*)malloc(len + 1);
655 memcpy(chars, slotRawString(a)->s, len);
656 chars[len] = 0;
657 rtf2txt(chars);
659 PyrString* string = newPyrString(g->gc, chars, 0, false);
660 SetObject(a, string);
661 free(chars);
663 return errNone;
666 int prStripHtml(struct VMGlobals *g, int numArgsPushed);
667 int prStripHtml(struct VMGlobals *g, int numArgsPushed)
669 PyrSlot *a = g->sp;
670 int len = slotRawObject(a)->size;
671 char * chars = (char*)malloc(len + 1);
672 memcpy(chars, slotRawString(a)->s, len);
673 chars[len] = 0;
674 html2txt(chars);
676 PyrString* string = newPyrString(g->gc, chars, 0, false);
677 SetObject(a, string);
678 free(chars);
680 return errNone;
683 int prString_Find(struct VMGlobals *g, int numArgsPushed);
684 int prString_Find(struct VMGlobals *g, int numArgsPushed)
686 PyrSlot *a = g->sp - 3; // source string
687 PyrSlot *b = g->sp - 2; // search string
688 PyrSlot *c = g->sp - 1; // ignoreCase
689 PyrSlot *d = g->sp; // offset
691 int offset;
692 int err = slotIntVal(d, &offset);
693 if (err) return err;
695 if (!isKindOfSlot(b, class_string)) {
696 SetNil(a);
697 return errNone;
700 int alength = slotRawObject(a)->size - offset;
701 int blength = slotRawObject(b)->size;
703 if ((alength <= 0)
704 || (blength == 0)
705 // should also return nil if search string is longer than source
706 || (blength > alength))
708 SetNil(a);
709 return errNone;
712 int cmp = 1; // assume contains will be false
713 char *achar = slotRawString(a)->s + offset;
714 char *bchar = slotRawString(b)->s;
715 char bchar0 = bchar[0];
716 int scanlength = alength - blength;
717 if (IsTrue(c)) {
718 bchar0 = toupper(bchar0);
719 for (int i=0; i <= scanlength; ++i, ++achar) {
720 if (toupper(*achar) == bchar0) {
721 cmp = memcmpi(achar+1, bchar+1, blength-1);
722 if (cmp == 0) break;
725 } else {
726 for (int i=0; i <= scanlength; ++i, ++achar) {
727 if (*achar == bchar0) {
728 cmp = memcmp(achar+1, bchar+1, blength-1);
729 if (cmp == 0) break;
733 if (cmp == 0) {
734 SetInt(a, achar - slotRawString(a)->s);
735 } else {
736 SetNil(a);
738 return errNone;
741 int prString_FindBackwards(struct VMGlobals *g, int numArgsPushed);
742 int prString_FindBackwards(struct VMGlobals *g, int numArgsPushed)
744 PyrSlot *a = g->sp - 3; // source string
745 PyrSlot *b = g->sp - 2; // search string
746 PyrSlot *c = g->sp - 1; // ignoreCase
747 PyrSlot *d = g->sp; // offset
749 int offset;
750 int err = slotIntVal(d, &offset);
751 if (err) return err;
753 if (!isKindOfSlot(b, class_string)) {
754 SetNil(a);
755 return errNone;
758 int alength = sc_min(offset + 1, slotRawObject(a)->size);
759 int blength = slotRawObject(b)->size;
761 if ((alength <= 0)
762 || (blength == 0)
763 // should also return nil if search string is longer than source
764 || (blength > alength))
766 SetNil(a);
767 return errNone;
770 int cmp = 1; // assume contains will be false
771 char *achar = slotRawString(a)->s + (alength - blength);
772 char *bchar = slotRawString(b)->s;
773 char bchar0 = bchar[0];
774 int scanlength = alength - blength;
775 if (IsTrue(c)) {
776 bchar0 = toupper(bchar0);
777 for (int i=scanlength; i >= 0; --i, --achar) {
778 if (toupper(*achar) == bchar0) {
779 cmp = memcmpi(achar+1, bchar+1, blength-1);
780 if (cmp == 0) break;
783 } else {
784 for (int i=scanlength; i >= 0; --i, --achar) {
785 if (*achar == bchar0) {
786 cmp = memcmp(achar+1, bchar+1, blength-1);
787 if (cmp == 0) break;
791 if (cmp == 0) {
792 SetInt(a, achar - slotRawString(a)->s);
793 } else {
794 SetNil(a);
796 return errNone;
799 #if SC_DARWIN
800 # include <CoreFoundation/CoreFoundation.h>
801 #endif // SC_DARWIN
803 int prString_StandardizePath(struct VMGlobals* g, int numArgsPushed);
804 int prString_StandardizePath(struct VMGlobals* g, int /* numArgsPushed */)
806 PyrSlot* arg = g->sp;
807 char ipath[PATH_MAX];
808 char opathbuf[PATH_MAX];
809 char* opath = opathbuf;
810 int err;
812 err = slotStrVal(arg, ipath, PATH_MAX);
813 if (err) return err;
815 if (!sc_StandardizePath(ipath, opath)) {
816 opath = ipath;
819 #if SC_DARWIN
820 CFStringRef cfstring =
821 CFStringCreateWithCString(NULL,
822 opath,
823 kCFStringEncodingUTF8);
824 err = !CFStringGetFileSystemRepresentation(cfstring, opath, PATH_MAX);
825 CFRelease(cfstring);
826 if (err) return errFailed;
827 #endif // SC_DARWIN
829 PyrString* pyrString = newPyrString(g->gc, opath, 0, true);
830 SetObject(arg, pyrString);
832 return errNone;
835 int prString_EscapeChar(struct VMGlobals* g, int numArgsPushed)
837 PyrSlot* arg = g->sp - 1;
838 PyrSlot* charToEscapeSlot = g->sp;
840 assert (isKindOfSlot(arg, class_string));
842 if (!IsChar(charToEscapeSlot))
843 return errWrongType;
845 char charToEscape = slotRawChar(charToEscapeSlot);
847 PyrString* argString = slotRawString(arg);
848 int length = argString->size;
849 PyrString* resultString = newPyrStringN(g->gc, length*2 + 1, 0, 1); // pressimize
851 char * original = argString->s;
852 char * result = resultString->s;
854 int resultLength = length;
855 for (int i = 0; i != length; ++i) {
856 char current = *original++;
857 if (current == charToEscape) {
858 *result++ = '\\';
859 resultLength += 1;
861 *result++ = current;
863 *result = 0;
865 resultString->size = resultLength;
867 SetRaw(arg, (PyrObject*)resultString);
869 return errNone;
872 static void yaml_traverse(struct VMGlobals* g, const YAML::Node & node, PyrObject *parent, PyrSlot *slot) {
873 YAML::NodeType::value type = node.Type();
874 string out;
875 PyrObject *result = NULL;
877 switch (type)
879 case YAML::NodeType::Scalar:
880 node >> out;
881 result = (PyrObject*)newPyrString(g->gc, out.c_str(), 0, true);
882 SetObject(slot, result);
883 if(parent) g->gc->GCWrite(parent, result);
884 break;
886 case YAML::NodeType::Sequence:
887 result = newPyrArray(g->gc, node.size(), 0, true);
888 result->size = 0;
889 SetObject(slot, result);
890 if(parent) g->gc->GCWrite(parent, result);
891 for (unsigned int i = 0; i < node.size(); i++) {
892 const YAML::Node & subnode = node[i];
893 result->size++;
894 yaml_traverse(g, subnode, result, result->slots+i);
896 break;
898 case YAML::NodeType::Map:
900 result = instantiateObject( g->gc, s_dictionary->u.classobj, 0, false, true );
901 SetObject(slot, result);
902 if(parent) g->gc->GCWrite(parent, result);
904 PyrObject *array = newPyrArray(g->gc, node.size()*2, 0, true);
905 array->size = 0;
906 result->size = 2; // ?
907 SetObject(result->slots, array); // array
908 SetInt(result->slots+1, node.size()); // size
909 g->gc->GCWrite(result, array);
911 int j = 0;
912 for (YAML::Iterator i = node.begin(); i != node.end(); ++i) {
913 const YAML::Node & key = i.first();
914 const YAML::Node & value = i.second();
915 key >> out;
916 PyrObject *pkey = (PyrObject*)newPyrString(g->gc, out.c_str(), 0, true);
917 SetObject(array->slots+j, pkey);
918 array->size++;
919 g->gc->GCWrite(array, pkey);
921 array->size++;
922 yaml_traverse(g, value, array, array->slots+j+1);
924 j += 2;
926 break;
929 case YAML::NodeType::Null:
930 SetNil(slot);
931 break;
933 default:
934 postfl("WARNING: yaml_traverse(): unknown/unsupported node type\n");
935 SetNil(slot);
939 int prString_ParseYAML(struct VMGlobals* g, int numArgsPushed)
941 PyrSlot* arg = g->sp;
943 if (!isKindOfSlot(arg, class_string)) return errWrongType;
945 string str((const char*)slotRawString(arg)->s,slotRawString(arg)->size);
947 std::istringstream fin(str);
948 YAML::Parser parser(fin);
949 YAML::Node doc;
950 // while(parser.GetNextDocument(doc)) {
951 // yaml_traverse(doc, 0);
952 // }
953 parser.GetNextDocument(doc);
954 yaml_traverse(g, doc, NULL, arg);
956 return errNone;
959 int prString_ParseYAMLFile(struct VMGlobals* g, int numArgsPushed)
961 PyrSlot* arg = g->sp;
963 if (!isKindOfSlot(arg, class_string)) return errWrongType;
965 string str((const char*)slotRawString(arg)->s,slotRawString(arg)->size);
967 std::ifstream fin(str.c_str());
968 YAML::Parser parser(fin);
969 YAML::Node doc;
970 // while(parser.GetNextDocument(doc)) {
971 // yaml_traverse(doc, 0);
972 // }
973 parser.GetNextDocument(doc);
974 yaml_traverse(g, doc, NULL, arg);
976 return errNone;
979 void initStringPrimitives();
980 void initStringPrimitives()
982 int base, index = 0;
984 base = nextPrimitiveIndex();
986 definePrimitive(base, index++, "_StringCompare", prStringCompare, 3, 0);
987 definePrimitive(base, index++, "_StringHash", prStringHash, 1, 0);
988 definePrimitive(base, index++, "_StringPathMatch", prStringPathMatch, 1, 0);
989 definePrimitive(base, index++, "_StringAsSymbol", prStringAsSymbol, 1, 0);
990 definePrimitive(base, index++, "_String_AsInteger", prString_AsInteger, 1, 0);
991 definePrimitive(base, index++, "_String_AsFloat", prString_AsFloat, 1, 0);
992 definePrimitive(base, index++, "_String_AsCompileString", prString_AsCompileString, 1, 0);
993 definePrimitive(base, index++, "_String_Getenv", prString_Getenv, 1, 0);
994 definePrimitive(base, index++, "_String_Setenv", prString_Setenv, 2, 0);
995 definePrimitive(base, index++, "_String_Find", prString_Find, 4, 0);
996 definePrimitive(base, index++, "_String_FindBackwards", prString_FindBackwards, 4, 0);
997 definePrimitive(base, index++, "_String_Format", prString_Format, 2, 0);
998 definePrimitive(base, index++, "_String_Regexp", prString_Regexp, 4, 0);
999 definePrimitive(base, index++, "_String_FindRegexp", prString_FindRegexp, 3, 0);
1000 definePrimitive(base, index++, "_StripRtf", prStripRtf, 1, 0);
1001 definePrimitive(base, index++, "_StripHtml", prStripHtml, 1, 0);
1002 definePrimitive(base, index++, "_String_StandardizePath", prString_StandardizePath, 1, 0);
1003 definePrimitive(base, index++, "_String_EscapeChar", prString_EscapeChar, 2, 0);
1004 definePrimitive(base, index++, "_String_ParseYAML", prString_ParseYAML, 1, 0);
1005 definePrimitive(base, index++, "_String_ParseYAMLFile", prString_ParseYAMLFile, 1, 0);
1008 #if _SC_PLUGINS_
1011 #include "SCPlugin.h"
1013 // export the function that SC will call to load the plug in.
1014 #pragma export on
1015 extern "C" { SCPlugIn* loadPlugIn(void); }
1016 #pragma export off
1019 // define plug in object
1020 class APlugIn : public SCPlugIn
1022 public:
1023 APlugIn();
1024 virtual ~APlugIn();
1026 virtual void AboutToCompile();
1029 APlugIn::APlugIn()
1031 // constructor for plug in
1034 APlugIn::~APlugIn()
1036 // destructor for plug in
1039 void APlugIn::AboutToCompile()
1041 // this is called each time the class library is compiled.
1042 initStringPrimitives();
1045 // This function is called when the plug in is loaded into SC.
1046 // It returns an instance of APlugIn.
1047 SCPlugIn* loadPlugIn()
1049 return new APlugIn();
1052 #endif