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"
34 #include "SC_DirUtils.h"
37 # include "SC_Win32Utils.h"
39 # include <sys/param.h>
42 #include <boost/regex.hpp>
43 #include <boost/intrusive/list.hpp>
44 #include <boost/intrusive/unordered_set.hpp>
51 int prStringAsSymbol(struct VMGlobals
*g
, int numArgsPushed
);
52 int prStringAsSymbol(struct VMGlobals
*g
, int numArgsPushed
)
55 char str
[1024], *strp
=0;
59 len
= slotRawObject(a
)->size
;
60 strp
= len
> 1023 ? (char*)malloc(len
+1) : str
;
62 memcpy(strp
, slotRawString(a
)->s
, len
);
65 SetSymbol(a
, getsym(strp
));
67 if (len
> 1023) free(strp
);
72 int prString_AsInteger(struct VMGlobals
*g
, int numArgsPushed
);
73 int prString_AsInteger(struct VMGlobals
*g
, int numArgsPushed
)
78 int err
= slotStrVal(a
, str
, 255);
86 int prString_AsFloat(struct VMGlobals
*g
, int numArgsPushed
);
87 int prString_AsFloat(struct VMGlobals
*g
, int numArgsPushed
)
92 int err
= slotStrVal(a
, str
, 255);
95 SetFloat(a
, atof(str
));
100 int prString_AsCompileString(struct VMGlobals
*g
, int numArgsPushed
)
103 PyrString
* scstr
= slotRawString(a
);
104 char *chars1
= scstr
->s
;
105 int newSize
= scstr
->size
+ 2;
106 for (int i
=0; i
<scstr
->size
; ++i
) {
107 if (chars1
[i
] == '"' || chars1
[i
] == '\\') newSize
++;
109 PyrString
*newString
= newPyrStringN(g
->gc
, newSize
, 0, true);
110 char *chars2
= newString
->s
;
112 chars2
[newSize
- 1] = '"';
114 for (int i
=0; i
<scstr
->size
; ++i
) {
116 if (c
== '"' || c
== '\\') chars2
[k
++] = '\\';
119 SetObject(a
, newString
);
123 int prString_Format(struct VMGlobals
*g
, int numArgsPushed
)
125 PyrSlot
*a
= g
->sp
- 1;
128 if (!isKindOfSlot(b
, class_array
)) return errWrongType
;
130 char *fmt
= slotRawString(a
)->s
;
132 int asize
= slotRawObject(a
)->size
;
133 int bsize
= slotRawObject(b
)->size
;
136 PyrSlot
*slots
= slotRawObject(b
)->slots
;
137 for (int i
=0; i
<bsize
; ++i
) {
138 PyrSlot
*slot
= slots
+ i
;
139 if (!isKindOfSlot(slot
, class_string
)) return errWrongType
;
140 csize
+= slotRawString(slot
)->size
;
142 PyrString
*newString
= newPyrStringN(g
->gc
, csize
, 0, true);
143 char* buf
= newString
->s
;
147 for (int i
=0; i
<asize
;) {
151 PyrString
* bstring
= slotRawString(&slots
[index
]);
152 memcpy(buf
+k
, bstring
->s
, bstring
->size
);
156 } else if (ch
== '\\') {
157 if (i
>= asize
) break;
169 SetObject(a
, newString
);
175 namespace bin
= boost::intrusive
;
177 class regex_lru_cache
182 bin::list_base_hook
<>,
183 bin::unordered_set_base_hook
<>
186 regex_node(const char * str
, size_t size
, int regex_flags
):
187 pattern(str
, size
, regex_flags
)
190 boost::regex
const & get (void) const
196 boost::regex pattern
;
201 bool operator()(regex_node
const & lhs
, regex_node
const & rhs
) const
203 return lhs
.get() == rhs
.get();
206 bool operator()(const char * lhs
, regex_node
const & rhs
) const
208 return strcmp(lhs
, rhs
.get().str().c_str()) == 0;
212 static inline std::size_t string_hash(const char * str
)
219 ret
= c
+ (ret
<< 6) + (ret
<< 16) - ret
;
226 size_t operator()(regex_node
const & arg
) const
228 return string_hash(arg
.get().str().c_str());
231 size_t operator()(const char * arg
) const
233 return string_hash(arg
);
237 typedef bin::unordered_set
<regex_node
, bin::equal
<regex_equal
>, bin::hash
<regex_hash
>,
238 bin::power_2_buckets
<true>, bin::constant_time_size
<false> >
240 typedef re_set_t::bucket_type bucket_type
;
241 typedef re_set_t::bucket_traits bucket_traits
;
242 bucket_type buckets
[128];
245 bin::list
<regex_node
> re_list
;
249 regex_node
& rlu
= re_list
.back();
256 regex_lru_cache(int regex_flags
= boost::regex_constants::ECMAScript
):
257 re_set(bucket_traits(buckets
, 128))
262 while (!re_list
.empty()) {
267 boost::regex
const & get_regex(const char * str
, size_t size
)
269 re_set_t::iterator re_in_cache
= re_set
.find(str
, regex_hash(), regex_equal());
270 if (re_in_cache
!= re_set
.end()) {
271 regex_node
& node
= *re_in_cache
;
272 bin::list
<regex_node
>::iterator re_in_list
= bin::list
<regex_node
>::s_iterator_to(node
);
274 re_list
.splice(re_list
.begin(), re_list
, re_in_list
); // move to the begin of the list
275 assert(&re_list
.front() == &node
);
279 if (re_list
.size() >= 64)
282 regex_node
* new_node
= new regex_node(str
, size
, regex_flags
);
283 re_set
.insert(*new_node
);
284 re_list
.push_front(*new_node
);
285 return new_node
->get();
291 int prString_Regexp(struct VMGlobals
*g
, int numArgsPushed
)
294 static detail::regex_lru_cache
regex_lru_cache(boost::regex_constants::ECMAScript
| boost::regex_constants::nosubs
);
296 using namespace boost
;
298 int err
, start
, end
, ret
, len
;
300 PyrSlot
*a
= g
->sp
- 3;
301 PyrSlot
*b
= g
->sp
- 2;
302 PyrSlot
*c
= g
->sp
- 1;
305 if (!isKindOfSlot(b
, class_string
)) return errWrongType
;
306 if (NotInt(c
) || (NotInt(d
) && NotNil(d
))) return errWrongType
;
307 start
= slotRawInt(c
);
309 len
= slotRawObject(b
)->size
; // last char index instead of size
320 if(end
- start
<= 0) {
325 int stringlen
= end
- start
;
328 regex
const & pattern
= regex_lru_cache
.get_regex(slotRawString(a
)->s
, slotRawObject(a
)->size
);
329 match_flag_type flags
= match_nosubs
| match_any
;
331 const char * stringStart
= slotRawString(b
)->s
+ start
;
332 const char * stringEnd
= stringStart
+ stringlen
;
333 bool res
= regex_search(stringStart
, stringEnd
, pattern
, flags
);
341 } catch (std::exception
const & e
) {
342 postfl("Warning: Exception in _String_Regexp - %s\n", e
.what());
347 struct sc_regexp_match
{
353 static int prString_FindRegexp(struct VMGlobals
*g
, int numArgsPushed
)
356 static detail::regex_lru_cache
regex_lru_cache(boost::regex_constants::ECMAScript
);
358 using namespace boost
;
360 PyrSlot
*a
= g
->sp
- 2; // source string
361 PyrSlot
*b
= g
->sp
- 1; // pattern
362 PyrSlot
*c
= g
->sp
; // offset
364 if (!isKindOfSlot(b
, class_string
) || (NotInt(c
))) return errWrongType
;
366 int offset
= slotRawInt(c
);
367 int stringlen
= std::max(slotRawObject(a
)->size
- offset
, 0);
368 int patternsize
= slotRawObject(b
)->size
+ 1;
370 std::vector
<sc_regexp_match
> matches
;
371 const char* const stringBegin
= slotRawString(a
)->s
+ offset
;
373 regex
const & pattern
= regex_lru_cache
.get_regex(slotRawString(b
)->s
, slotRawObject(b
)->size
);
374 match_flag_type flags
= match_default
;
376 match_results
<const char*> what
;
377 const char* start
= stringBegin
;
378 const char* end
= start
+ stringlen
;
379 while (start
<= end
&& regex_search(start
, end
, what
, pattern
, flags
))
381 for (int i
= 0; i
< what
.size(); ++i
)
383 sc_regexp_match match
;
384 if (what
[i
].matched
) {
385 match
.pos
= what
[i
].first
- stringBegin
;
386 match
.len
= what
[i
].second
- what
[i
].first
;
391 matches
.push_back(match
);
393 start
= what
[0].second
;
394 if(what
[0].first
== what
[0].second
) ++start
;
396 } catch (std::exception
const & e
) {
397 postfl("Warning: Exception in _String_FindRegexp - %s\n", e
.what());
401 int match_count
= matches
.size();
403 PyrObject
*result_array
= newPyrArray(g
->gc
, match_count
, 0, true);
404 result_array
->size
= 0;
405 SetObject(a
, result_array
);
407 if( !match_count
) return errNone
;
409 for (int i
= 0; i
< match_count
; ++i
)
411 int pos
= matches
[i
].pos
;
412 int len
= matches
[i
].len
;
414 PyrObject
*array
= newPyrArray(g
->gc
, 2, 0, true);
415 SetObject(result_array
->slots
+ i
, array
);
416 result_array
->size
++;
417 g
->gc
->GCWrite(result_array
, array
);
419 PyrString
*matched_string
= newPyrStringN(g
->gc
, len
, 0, true);
420 memcpy(matched_string
->s
, stringBegin
+ pos
, len
);
423 SetInt(array
->slots
, pos
+ offset
);
424 SetObject(array
->slots
+1, matched_string
);
425 g
->gc
->GCWrite(array
, matched_string
);
431 int memcmpi(char *a
, char *b
, int len
)
433 for (int i
=0; i
<len
; ++i
) {
434 char aa
= toupper(a
[i
]);
435 char bb
= toupper(b
[i
]);
436 if (aa
< bb
) return -1;
437 if (aa
> bb
) return 1;
442 int prStringCompare(struct VMGlobals
*g
, int numArgsPushed
);
443 int prStringCompare(struct VMGlobals
*g
, int numArgsPushed
)
452 if (NotObj(b
) || !isKindOf(slotRawObject(b
), class_string
)) {
456 length
= sc_min(slotRawObject(a
)->size
, slotRawObject(b
)->size
);
457 if (IsTrue(c
)) cmp
= memcmpi(slotRawString(a
)->s
, slotRawString(b
)->s
, length
);
458 else cmp
= memcmp(slotRawString(a
)->s
, slotRawString(b
)->s
, length
);
460 if (slotRawObject(a
)->size
< slotRawObject(b
)->size
) cmp
= -1;
461 else if (slotRawObject(a
)->size
> slotRawObject(b
)->size
) cmp
= 1;
467 int prStringHash(struct VMGlobals
*g
, int numArgsPushed
);
468 int prStringHash(struct VMGlobals
*g
, int numArgsPushed
)
471 int hash
= Hash(slotRawString(a
)->s
, slotRawString(a
)->size
);
479 int prStringPathMatch(struct VMGlobals
*g
, int numArgsPushed
);
480 int prStringPathMatch(struct VMGlobals
*g
, int numArgsPushed
)
485 int err
= slotStrVal(a
, pattern
, 1023);
490 int gflags
= GLOB_MARK
| GLOB_TILDE
;
492 gflags
|= GLOB_QUOTE
;
495 int gerr
= glob(pattern
, gflags
, NULL
, &pglob
);
499 PyrObject
* array
= newPyrArray(g
->gc
, pglob
.gl_pathc
, 0, true);
501 if (gerr
) return errNone
;
503 for (unsigned int i
=0; i
<pglob
.gl_pathc
; ++i
) {
504 PyrObject
*string
= (PyrObject
*)newPyrString(g
->gc
, pglob
.gl_pathv
[i
], 0, true);
505 SetObject(array
->slots
+i
, string
);
506 g
->gc
->GCWrite(array
, string
);
514 #else //#ifndef SC_WIN32
515 int prStringPathMatch(struct VMGlobals
*g
, int numArgsPushed
);
517 int prStringPathMatch(struct VMGlobals
*g
, int numArgsPushed
)
522 int err
= slotStrVal(a
, pattern
, 1023);
525 win32_ReplaceCharInString(pattern
,1024,'/','\\');
526 // Remove trailing slash if found, to allow folders to be matched
527 if(pattern
[strlen(pattern
)-1]=='\\'){
528 pattern
[strlen(pattern
)-1] = 0;
530 // extract the containing folder, including backslash
532 win32_ExtractContainingFolder(folder
,pattern
,1024);
536 WIN32_FIND_DATA findData
;
540 hFind
= ::FindFirstFile(pattern
, &findData
);
541 if (hFind
== INVALID_HANDLE_VALUE
) {
545 if (hFind
== INVALID_HANDLE_VALUE
) {
546 // This is what happens when no matches. So we create an empty array to return.
547 PyrObject
* array
= newPyrArray(g
->gc
, 0, 0, true);
553 if(strcmp(findData
.cFileName
, "..")!=0 && strcmp(findData
.cFileName
, "..")!=0){
556 } while( ::FindNextFile(hFind
, &findData
));
561 hFind
= ::FindFirstFile(pattern
, &findData
);
562 if (hFind
== INVALID_HANDLE_VALUE
) {
566 PyrObject
* array
= newPyrArray(g
->gc
, nbPaths
, 0, true);
568 if (hFind
== INVALID_HANDLE_VALUE
) {
574 if(strcmp(findData
.cFileName
, "..")!=0 && strcmp(findData
.cFileName
, ".")!=0){
575 std::string
strPath(folder
);
576 strPath
+= std::string(findData
.cFileName
);
577 if(findData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
){
578 strPath
+= std::string("\\"); // Append trailing slash, to match behaviour on unix (used by sclang to detect folderness)
580 const char* fullPath
= strPath
.c_str();
581 PyrObject
*string
= (PyrObject
*)newPyrString(g
->gc
, fullPath
, 0, true);
582 SetObject(array
->slots
+i
, string
);
583 g
->gc
->GCWrite(array
, string
);
587 } while( ::FindNextFile(hFind
, &findData
));
591 #endif //#ifndef SC_WIN32
593 int prString_Getenv(struct VMGlobals
* g
, int numArgsPushed
);
594 int prString_Getenv(struct VMGlobals
* g
, int /* numArgsPushed */)
596 PyrSlot
* arg
= g
->sp
;
601 err
= slotStrVal(arg
, key
, 256);
607 PyrString
* pyrString
= newPyrString(g
->gc
, value
, 0, true);
608 if (!pyrString
) return errFailed
;
609 SetObject(arg
, pyrString
);
617 int prString_Setenv(struct VMGlobals
* g
, int numArgsPushed
);
618 int prString_Setenv(struct VMGlobals
* g
, int /* numArgsPushed */)
620 PyrSlot
* args
= g
->sp
- 1;
624 err
= slotStrVal(args
+0, key
, 256);
629 SetEnvironmentVariable(key
,NULL
);
635 err
= slotStrVal(args
+1, value
, 1024);
638 SetEnvironmentVariable(key
, value
);
640 setenv(key
, value
, 1);
647 int prStripRtf(struct VMGlobals
*g
, int numArgsPushed
);
648 int prStripRtf(struct VMGlobals
*g
, int numArgsPushed
)
651 int len
= slotRawObject(a
)->size
;
652 char * chars
= (char*)malloc(len
+ 1);
653 memcpy(chars
, slotRawString(a
)->s
, len
);
657 PyrString
* string
= newPyrString(g
->gc
, chars
, 0, false);
658 SetObject(a
, string
);
664 int prStripHtml(struct VMGlobals
*g
, int numArgsPushed
);
665 int prStripHtml(struct VMGlobals
*g
, int numArgsPushed
)
668 int len
= slotRawObject(a
)->size
;
669 char * chars
= (char*)malloc(len
+ 1);
670 memcpy(chars
, slotRawString(a
)->s
, len
);
674 PyrString
* string
= newPyrString(g
->gc
, chars
, 0, false);
675 SetObject(a
, string
);
682 int prString_GetResourceDirPath(struct VMGlobals
*g
, int numArgsPushed
);
683 int prString_GetResourceDirPath(struct VMGlobals
*g
, int numArgsPushed
)
687 char * chars
= (char*)malloc(MAXPATHLEN
- 32);
688 sc_GetResourceDirectory(chars
, MAXPATHLEN
- 32);
690 PyrString
* string
= newPyrString(g
->gc
, chars
, 0, false);
691 SetObject(a
, string
);
698 int prString_Find(struct VMGlobals
*g
, int numArgsPushed
);
699 int prString_Find(struct VMGlobals
*g
, int numArgsPushed
)
701 PyrSlot
*a
= g
->sp
- 3; // source string
702 PyrSlot
*b
= g
->sp
- 2; // search string
703 PyrSlot
*c
= g
->sp
- 1; // ignoreCase
704 PyrSlot
*d
= g
->sp
; // offset
707 int err
= slotIntVal(d
, &offset
);
710 if (!isKindOfSlot(b
, class_string
)) {
715 int alength
= slotRawObject(a
)->size
- offset
;
716 int blength
= slotRawObject(b
)->size
;
720 // should also return nil if search string is longer than source
721 || (blength
> alength
))
727 int cmp
= 1; // assume contains will be false
728 char *achar
= slotRawString(a
)->s
+ offset
;
729 char *bchar
= slotRawString(b
)->s
;
730 char bchar0
= bchar
[0];
731 int scanlength
= alength
- blength
;
733 bchar0
= toupper(bchar0
);
734 for (int i
=0; i
<= scanlength
; ++i
, ++achar
) {
735 if (toupper(*achar
) == bchar0
) {
736 cmp
= memcmpi(achar
+1, bchar
+1, blength
-1);
741 for (int i
=0; i
<= scanlength
; ++i
, ++achar
) {
742 if (*achar
== bchar0
) {
743 cmp
= memcmp(achar
+1, bchar
+1, blength
-1);
749 SetInt(a
, achar
- slotRawString(a
)->s
);
756 int prString_FindBackwards(struct VMGlobals
*g
, int numArgsPushed
);
757 int prString_FindBackwards(struct VMGlobals
*g
, int numArgsPushed
)
759 PyrSlot
*a
= g
->sp
- 3; // source string
760 PyrSlot
*b
= g
->sp
- 2; // search string
761 PyrSlot
*c
= g
->sp
- 1; // ignoreCase
762 PyrSlot
*d
= g
->sp
; // offset
765 int err
= slotIntVal(d
, &offset
);
768 if (!isKindOfSlot(b
, class_string
)) {
773 int alength
= sc_min(offset
+ 1, slotRawObject(a
)->size
);
774 int blength
= slotRawObject(b
)->size
;
778 // should also return nil if search string is longer than source
779 || (blength
> alength
))
785 int cmp
= 1; // assume contains will be false
786 char *achar
= slotRawString(a
)->s
+ (alength
- blength
);
787 char *bchar
= slotRawString(b
)->s
;
788 char bchar0
= bchar
[0];
789 int scanlength
= alength
- blength
;
791 bchar0
= toupper(bchar0
);
792 for (int i
=scanlength
; i
>= 0; --i
, --achar
) {
793 if (toupper(*achar
) == bchar0
) {
794 cmp
= memcmpi(achar
+1, bchar
+1, blength
-1);
799 for (int i
=scanlength
; i
>= 0; --i
, --achar
) {
800 if (*achar
== bchar0
) {
801 cmp
= memcmp(achar
+1, bchar
+1, blength
-1);
807 SetInt(a
, achar
- slotRawString(a
)->s
);
815 # include <CoreFoundation/CoreFoundation.h>
818 int prString_StandardizePath(struct VMGlobals
* g
, int numArgsPushed
);
819 int prString_StandardizePath(struct VMGlobals
* g
, int /* numArgsPushed */)
821 PyrSlot
* arg
= g
->sp
;
822 char ipath
[PATH_MAX
];
823 char opathbuf
[PATH_MAX
];
824 char* opath
= opathbuf
;
827 err
= slotStrVal(arg
, ipath
, PATH_MAX
);
830 if (!sc_StandardizePath(ipath
, opath
)) {
835 CFStringRef cfstring
=
836 CFStringCreateWithCString(NULL
,
838 kCFStringEncodingUTF8
);
839 err
= !CFStringGetFileSystemRepresentation(cfstring
, opath
, PATH_MAX
);
841 if (err
) return errFailed
;
844 PyrString
* pyrString
= newPyrString(g
->gc
, opath
, 0, true);
845 SetObject(arg
, pyrString
);
850 int prString_EscapeChar(struct VMGlobals
* g
, int numArgsPushed
)
852 PyrSlot
* arg
= g
->sp
- 1;
853 PyrSlot
* charToEscapeSlot
= g
->sp
;
855 assert (isKindOfSlot(arg
, class_string
));
857 if (!IsChar(charToEscapeSlot
))
860 char charToEscape
= slotRawChar(charToEscapeSlot
);
862 PyrString
* argString
= slotRawString(arg
);
863 int length
= argString
->size
;
864 PyrString
* resultString
= newPyrStringN(g
->gc
, length
*2 + 1, 0, 1); // pressimize
866 char * original
= argString
->s
;
867 char * result
= resultString
->s
;
869 int resultLength
= length
;
870 for (int i
= 0; i
!= length
; ++i
) {
871 char current
= *original
++;
872 if (current
== charToEscape
) {
880 resultString
->size
= resultLength
;
882 SetRaw(arg
, (PyrObject
*)resultString
);
888 void initStringPrimitives();
889 void initStringPrimitives()
893 base
= nextPrimitiveIndex();
895 definePrimitive(base
, index
++, "_StringCompare", prStringCompare
, 3, 0);
896 definePrimitive(base
, index
++, "_StringHash", prStringHash
, 1, 0);
897 definePrimitive(base
, index
++, "_StringPathMatch", prStringPathMatch
, 1, 0);
898 definePrimitive(base
, index
++, "_StringAsSymbol", prStringAsSymbol
, 1, 0);
899 definePrimitive(base
, index
++, "_String_AsInteger", prString_AsInteger
, 1, 0);
900 definePrimitive(base
, index
++, "_String_AsFloat", prString_AsFloat
, 1, 0);
901 definePrimitive(base
, index
++, "_String_AsCompileString", prString_AsCompileString
, 1, 0);
902 definePrimitive(base
, index
++, "_String_Getenv", prString_Getenv
, 1, 0);
903 definePrimitive(base
, index
++, "_String_Setenv", prString_Setenv
, 2, 0);
904 definePrimitive(base
, index
++, "_String_Find", prString_Find
, 4, 0);
905 definePrimitive(base
, index
++, "_String_FindBackwards", prString_FindBackwards
, 4, 0);
906 definePrimitive(base
, index
++, "_String_Format", prString_Format
, 2, 0);
907 definePrimitive(base
, index
++, "_String_Regexp", prString_Regexp
, 4, 0);
908 definePrimitive(base
, index
++, "_String_FindRegexp", prString_FindRegexp
, 3, 0);
909 definePrimitive(base
, index
++, "_StripRtf", prStripRtf
, 1, 0);
910 definePrimitive(base
, index
++, "_StripHtml", prStripHtml
, 1, 0);
911 definePrimitive(base
, index
++, "_String_GetResourceDirPath", prString_GetResourceDirPath
, 1, 0);
912 definePrimitive(base
, index
++, "_String_StandardizePath", prString_StandardizePath
, 1, 0);
913 definePrimitive(base
, index
++, "_String_EscapeChar", prString_EscapeChar
, 2, 0);
919 #include "SCPlugin.h"
921 // export the function that SC will call to load the plug in.
923 extern "C" { SCPlugIn
* loadPlugIn(void); }
927 // define plug in object
928 class APlugIn
: public SCPlugIn
934 virtual void AboutToCompile();
939 // constructor for plug in
944 // destructor for plug in
947 void APlugIn::AboutToCompile()
949 // this is called each time the class library is compiled.
950 initStringPrimitives();
953 // This function is called when the plug in is loaded into SC.
954 // It returns an instance of APlugIn.
955 SCPlugIn
* loadPlugIn()
957 return new APlugIn();