yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Yong HeAdd Slang Byte Code generation and interpreter. (#6896)c39c29bf4

master
23.5 KiB978 linesraw
1#include "slang-string-util.h"
2
3#include "slang-blob.h"
4#include "slang-char-util.h"
5#include "slang-text-io.h"
6
7namespace Slang
8{
9
10// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! StringUtil !!!!!!!!!!!!!!!!!!!!!!!!!!!
11
12/* static */ bool StringUtil::areAllEqual(
13    const List<UnownedStringSlice>& a,
14    const List<UnownedStringSlice>& b,
15    EqualFn equalFn)
16{
17    if (a.getCount() != b.getCount())
18    {
19        return false;
20    }
21
22    const Index count = a.getCount();
23    for (Index i = 0; i < count; ++i)
24    {
25        if (!equalFn(a[i], b[i]))
26        {
27            return false;
28        }
29    }
30    return true;
31}
32
33/* static */ bool StringUtil::areAllEqualWithSplit(
34    const UnownedStringSlice& a,
35    const UnownedStringSlice& b,
36    char splitChar,
37    EqualFn equalFn)
38{
39    List<UnownedStringSlice> slicesA, slicesB;
40    StringUtil::split(a, splitChar, slicesA);
41    StringUtil::split(b, splitChar, slicesB);
42    return areAllEqual(slicesA, slicesB, equalFn);
43}
44
45/* static */ void StringUtil::appendSplitOnWhitespace(
46    const UnownedStringSlice& in,
47    List<UnownedStringSlice>& outSlices)
48{
49    const char* start = in.begin();
50    const char* end = in.end();
51
52    // Skip any at the start
53    while (start < end && CharUtil::isWhitespace(*start))
54        start++;
55
56    while (start < end)
57    {
58        // Find all the non white space in a run
59        const char* cur = start;
60        while (cur < end && !CharUtil::isWhitespace(*cur))
61        {
62            cur++;
63        }
64
65        // Add to output
66        outSlices.add(UnownedStringSlice(start, cur));
67
68        // Find the next start
69        start = cur + 1;
70
71        // Skip the split
72        while (start < end && CharUtil::isWhitespace(*start))
73            start++;
74    }
75}
76
77/* static */ void StringUtil::appendSplit(
78    const UnownedStringSlice& in,
79    char splitChar,
80    List<UnownedStringSlice>& outSlices)
81{
82    const char* start = in.begin();
83    const char* end = in.end();
84
85    while (start < end)
86    {
87        // Move cur so it's either at the end or at next split character
88        const char* cur = start;
89        while (cur < end && *cur != splitChar)
90        {
91            cur++;
92        }
93
94        // Add to output
95        outSlices.add(UnownedStringSlice(start, cur));
96
97        // Skip the split character, if at end we are okay anyway
98        start = cur + 1;
99    }
100}
101
102/* static */ void StringUtil::appendSplit(
103    const UnownedStringSlice& in,
104    const UnownedStringSlice& splitSlice,
105    List<UnownedStringSlice>& outSlices)
106{
107    const Index splitLen = splitSlice.getLength();
108
109    if (splitLen == 1)
110    {
111        return appendSplit(in, splitSlice[0], outSlices);
112    }
113
114    SLANG_ASSERT(splitLen > 0);
115    if (splitLen <= 0)
116    {
117        return;
118    }
119
120    const char* start = in.begin();
121    const char* end = in.end();
122
123    const char splitChar = splitSlice[0];
124
125    while (start < end)
126    {
127        // Move cur so it's either at the end or at next splitSlice
128        const char* cur = start;
129        while (cur < end)
130        {
131            if (*cur == splitChar &&
132                (cur + splitLen <= end && UnownedStringSlice(cur, splitLen) == splitSlice))
133            {
134                // We hit a split
135                break;
136            }
137
138            cur++;
139        }
140
141        // Add to output
142        outSlices.add(UnownedStringSlice(start, cur));
143
144        // Skip the split, if at end we are okay anyway
145        start = cur + splitLen;
146    }
147}
148
149/* static */ void StringUtil::split(
150    const UnownedStringSlice& in,
151    char splitChar,
152    List<UnownedStringSlice>& outSlices)
153{
154    outSlices.clear();
155    appendSplit(in, splitChar, outSlices);
156}
157
158/* static */ void StringUtil::split(
159    const UnownedStringSlice& in,
160    const UnownedStringSlice& splitSlice,
161    List<UnownedStringSlice>& outSlices)
162{
163    outSlices.clear();
164    appendSplit(in, splitSlice, outSlices);
165}
166
167/* static */ void StringUtil::splitOnWhitespace(
168    const UnownedStringSlice& in,
169    List<UnownedStringSlice>& outSlices)
170{
171    outSlices.clear();
172    appendSplitOnWhitespace(in, outSlices);
173}
174
175/* static */ Index StringUtil::split(
176    const UnownedStringSlice& in,
177    char splitChar,
178    Index maxSlices,
179    UnownedStringSlice* outSlices)
180{
181    Index index = 0;
182
183    const char* start = in.begin();
184    const char* end = in.end();
185
186    while (start < end && index < maxSlices)
187    {
188        // Move cur so it's either at the end or at next split character
189        const char* cur = start;
190        while (cur < end && *cur != splitChar)
191        {
192            cur++;
193        }
194
195        // Add to output
196        outSlices[index++] = UnownedStringSlice(start, cur);
197
198        // Skip the split character, if at end we are okay anyway
199        start = cur + 1;
200    }
201
202    return index;
203}
204
205/* static */ SlangResult StringUtil::split(
206    const UnownedStringSlice& in,
207    char splitChar,
208    Index maxSlices,
209    UnownedStringSlice* outSlices,
210    Index& outSlicesCount)
211{
212    const Index sliceCount = split(in, splitChar, maxSlices, outSlices);
213    if (sliceCount == maxSlices && sliceCount > 0)
214    {
215        // To succeed must have parsed all of the input
216        if (in.end() != outSlices[sliceCount - 1].end())
217        {
218            return SLANG_FAIL;
219        }
220    }
221    outSlicesCount = sliceCount;
222    return SLANG_OK;
223}
224
225/* static */ void StringUtil::join(const List<String>& values, char separator, StringBuilder& out)
226{
227    join(values, UnownedStringSlice(&separator, 1), out);
228}
229
230/* static */ void StringUtil::join(
231    const List<String>& values,
232    const UnownedStringSlice& separator,
233    StringBuilder& out)
234{
235    const Index count = values.getCount();
236    if (count <= 0)
237    {
238        return;
239    }
240    out.append(values[0]);
241    for (Index i = 1; i < count; i++)
242    {
243        out.append(separator);
244        out.append(values[i]);
245    }
246}
247
248/* static */ void StringUtil::join(
249    const UnownedStringSlice* values,
250    Index valueCount,
251    char separator,
252    StringBuilder& out)
253{
254    join(values, valueCount, UnownedStringSlice(&separator, 1), out);
255}
256
257/* static */ void StringUtil::join(
258    const UnownedStringSlice* values,
259    Index valueCount,
260    const UnownedStringSlice& separator,
261    StringBuilder& out)
262{
263    if (valueCount <= 0)
264    {
265        return;
266    }
267    out.append(values[0]);
268    for (Index i = 1; i < valueCount; i++)
269    {
270        out.append(separator);
271        out.append(values[i]);
272    }
273}
274
275/* static */ Index StringUtil::indexOfInSplit(
276    const UnownedStringSlice& in,
277    char splitChar,
278    const UnownedStringSlice& find)
279{
280    const char* start = in.begin();
281    const char* end = in.end();
282
283    for (Index i = 0; start < end; ++i)
284    {
285        // Move cur so it's either at the end or at next split character
286        const char* cur = start;
287        while (cur < end && *cur != splitChar)
288        {
289            cur++;
290        }
291
292        // See if we have a match
293        if (UnownedStringSlice(start, cur) == find)
294        {
295            return i;
296        }
297
298        // Skip the split character, if at end we are okay anyway
299        start = cur + 1;
300    }
301    return -1;
302}
303
304UnownedStringSlice StringUtil::getAtInSplit(
305    const UnownedStringSlice& in,
306    char splitChar,
307    Index index)
308{
309    const char* start = in.begin();
310    const char* end = in.end();
311
312    for (Index i = 0; start < end; ++i)
313    {
314        // Move cur so it's either at the end or at next split character
315        const char* cur = start;
316        while (cur < end && *cur != splitChar)
317        {
318            cur++;
319        }
320
321        if (i == index)
322        {
323            return UnownedStringSlice(start, cur);
324        }
325
326        // Skip the split character, if at end we are okay anyway
327        start = cur + 1;
328    }
329
330    return UnownedStringSlice();
331}
332
333/* static */ size_t StringUtil::calcFormattedSize(const char* format, va_list args)
334{
335#if SLANG_WINDOWS_FAMILY
336    return _vscprintf(format, args);
337#else
338    return vsnprintf(nullptr, 0, format, args);
339#endif
340}
341
342/* static */ void StringUtil::calcFormatted(
343    const char* format,
344    va_list args,
345    size_t numChars,
346    char* dst)
347{
348#if SLANG_WINDOWS_FAMILY
349    vsnprintf_s(dst, numChars + 1, _TRUNCATE, format, args);
350#else
351    vsnprintf(dst, numChars + 1, format, args);
352#endif
353}
354
355/* static */ void StringUtil::append(const char* format, va_list args, StringBuilder& buf)
356{
357    // Calculate the size required (not including terminating 0)
358    size_t numChars;
359    {
360        // Create a copy of args, as will be consumed by calcFormattedSize
361        va_list argsCopy;
362        va_copy(argsCopy, args);
363        numChars = calcFormattedSize(format, argsCopy);
364        va_end(argsCopy);
365    }
366
367    // Requires + 1 , because calcFormatted appends a terminating 0
368    char* dst = buf.prepareForAppend(numChars + 1);
369    calcFormatted(format, args, numChars, dst);
370    buf.appendInPlace(dst, numChars);
371}
372
373/* static */ void StringUtil::appendFormat(StringBuilder& buf, const char* format, ...)
374{
375    va_list args;
376    va_start(args, format);
377    append(format, args, buf);
378    va_end(args);
379}
380
381/* static */ String StringUtil::makeStringWithFormat(const char* format, ...)
382{
383    StringBuilder builder;
384
385    va_list args;
386    va_start(args, format);
387    append(format, args, builder);
388    va_end(args);
389
390    return builder;
391}
392
393template<typename T>
394static T readValue(ArrayView<const void*> ptrToArgs, Count& argIndex)
395{
396    if (argIndex < ptrToArgs.getCount())
397    {
398        T value;
399        memcpy(&value, ptrToArgs[argIndex], sizeof(T));
400        argIndex++;
401        return value;
402    }
403    return T();
404}
405
406String StringUtil::makeStringWithFormatFromArgArray(
407    const char* format,
408    ArrayView<const void*> ptrToArgs)
409{
410    if (!format)
411    {
412        return String();
413    }
414    StringBuilder builder;
415    const char* ptr = format;
416    Count argIndex = 0;
417    auto consumeString = [&]()
418    {
419        if (argIndex < ptrToArgs.getCount())
420        {
421            const char* strPtr = *(const char**)ptrToArgs[argIndex];
422            argIndex++;
423            if (strPtr)
424            {
425                // Append the string to the builder
426                builder.append(strPtr);
427            }
428        }
429    };
430#define ADVANCE_PTR                     \
431    ptr++;                              \
432    if (!*ptr)                          \
433    {                                   \
434        return builder.produceString(); \
435    }
436
437    while (*ptr)
438    {
439        if (*ptr == '%')
440        {
441            const char* formatStart = ptr;
442            ADVANCE_PTR;
443            if (*ptr == 's')
444            {
445                // If we have a %s, then we want to append the data
446                consumeString();
447                // Move past the 's'
448                ADVANCE_PTR;
449                continue;
450            }
451            if (*ptr == '-')
452            {
453                // If we have a %- then we want to continue parsing format string.
454                ADVANCE_PTR;
455            }
456            while (CharUtil::isDigit(*ptr))
457            {
458                // Skip the digits after the '.'
459                ADVANCE_PTR;
460            }
461            if (*ptr == '.')
462            {
463                ADVANCE_PTR;
464                while (CharUtil::isDigit(*ptr))
465                {
466                    // Skip the digits after the '.'
467                    ADVANCE_PTR;
468                }
469            }
470            int isLong = 0;
471            if (*ptr == 'l' || *ptr == 'L')
472            {
473                // If we have a 'l' or 'L', then we want to skip it.
474                ADVANCE_PTR;
475                isLong = 1;
476                if (*ptr == 'l' || *ptr == 'L')
477                {
478                    // If we have another 'l' or 'L', then we want to skip it too.
479                    ADVANCE_PTR;
480                    isLong = 2;
481                }
482            }
483            const char typeChar = *ptr;
484            ADVANCE_PTR;
485            String formatStr = UnownedStringSlice(formatStart, ptr);
486            switch (CharUtil::toLower(typeChar))
487            {
488            case 'd':
489            case 'x':
490            case 'i':
491            case 'u':
492            case 'o':
493            case 'c':
494                if (isLong == 2)
495                {
496                    StringUtil::appendFormat(
497                        builder,
498                        formatStr.getBuffer(),
499                        readValue<int64_t>(ptrToArgs, argIndex));
500                }
501                else
502                {
503                    StringUtil::appendFormat(
504                        builder,
505                        formatStr.getBuffer(),
506                        readValue<int>(ptrToArgs, argIndex));
507                }
508                break;
509            case 'e':
510            case 'f':
511            case 'g':
512                if (isLong != 0)
513                {
514                    StringUtil::appendFormat(
515                        builder,
516                        formatStr.getBuffer(),
517                        readValue<double>(ptrToArgs, argIndex));
518                }
519                else
520                {
521                    StringUtil::appendFormat(
522                        builder,
523                        formatStr.getBuffer(),
524                        readValue<float>(ptrToArgs, argIndex));
525                }
526                break;
527            case 'n':
528                break;
529            case '%':
530                // If we have a '%%' then we want to append a single '%'
531                builder.appendChar('%');
532                continue;
533            }
534        }
535        else
536        {
537            // Just append the character
538            builder.appendChar(*ptr);
539            ptr++;
540        }
541    }
542    return builder.produceString();
543}
544
545
546/* static */ UnownedStringSlice StringUtil::getSlice(ISlangBlob* blob)
547{
548    if (blob)
549    {
550        size_t size = blob->getBufferSize();
551        if (size > 0)
552        {
553            const char* contents = (const char*)blob->getBufferPointer();
554            // Check it has terminating 0, if it has we skip it, because slices do not need zero
555            // termination
556            if (contents[size - 1] == 0)
557            {
558                size--;
559            }
560            return UnownedStringSlice(contents, contents + size);
561        }
562    }
563    return UnownedStringSlice();
564}
565
566/* static */ String StringUtil::getString(ISlangBlob* blob)
567{
568    return getSlice(blob);
569}
570
571ComPtr<ISlangBlob> StringUtil::createStringBlob(const String& string)
572{
573    return StringBlob::create(string);
574}
575
576/* static */ String StringUtil::calcCharReplaced(
577    const UnownedStringSlice& slice,
578    char fromChar,
579    char toChar)
580{
581    if (fromChar == toChar)
582    {
583        return slice;
584    }
585
586    const Index numChars = slice.getLength();
587    const char* srcChars = slice.begin();
588
589    StringBuilder builder;
590    char* dstChars = builder.prepareForAppend(numChars);
591
592    for (Index i = 0; i < numChars; ++i)
593    {
594        char c = srcChars[i];
595        dstChars[i] = (c == fromChar) ? toChar : c;
596    }
597
598    builder.appendInPlace(dstChars, numChars);
599    return builder;
600}
601
602/* static */ String StringUtil::calcCharReplaced(const String& string, char fromChar, char toChar)
603{
604    return (fromChar == toChar || string.indexOf(fromChar) == Index(-1))
605               ? string
606               : calcCharReplaced(string.getUnownedSlice(), fromChar, toChar);
607}
608
609String StringUtil::replaceAll(
610    UnownedStringSlice text,
611    UnownedStringSlice subStr,
612    UnownedStringSlice replacement)
613{
614    StringBuilder builder;
615    for (Index i = 0; i < text.getLength();)
616    {
617        if (i + subStr.getLength() > text.getLength())
618        {
619            builder.append(text.subString(i, text.getLength() - i));
620            break;
621        }
622        if (text.subString(i, subStr.getLength()) == subStr)
623        {
624            builder.append(replacement);
625            i += subStr.getLength();
626        }
627        else
628        {
629            builder.append(text[i]);
630            i++;
631        }
632    }
633    return builder.produceString();
634}
635
636
637/* static */ void StringUtil::appendStandardLines(
638    const UnownedStringSlice& text,
639    StringBuilder& out)
640{
641    const char* cur = text.begin();
642    const char* start = cur;
643    const char* const end = text.end();
644
645    while (cur < end)
646    {
647        const char c = *cur;
648        switch (c)
649        {
650        case '\n':
651            {
652                ++cur;
653                if (cur < end && *cur == '\r')
654                {
655                    // If we have following \r, we should append with \n
656                    // Append (including \n)
657                    out.append(start, cur);
658                    // Skip the \r
659                    start = ++cur;
660                }
661                else
662                {
663                    // If not, we don't need to append because just \n is 'standard', and everything
664                    // remaining is appended at the end
665                }
666                break;
667            }
668        case '\r':
669            {
670                out.append(start, cur);
671                out.appendChar('\n');
672
673                ++cur;
674                // If next is \n, we want to skip that
675                cur += Index(cur < end && *cur == '\n');
676                start = cur;
677                break;
678            }
679        default:
680            {
681                cur++;
682                break;
683            }
684        }
685    }
686
687    if (start < end)
688    {
689        out.append(start, end);
690    }
691}
692
693/* static */ bool StringUtil::extractLine(UnownedStringSlice& ioText, UnownedStringSlice& outLine)
694{
695    char const* const begin = ioText.begin();
696    char const* const end = ioText.end();
697
698    // If we have hit the end then return the 'special' terminator
699    if (begin == nullptr)
700    {
701        outLine = UnownedStringSlice(nullptr, nullptr);
702        return false;
703    }
704
705    char const* cursor = begin;
706    while (cursor < end)
707    {
708        int c = *cursor++;
709        switch (c)
710        {
711        case '\r':
712        case '\n':
713            {
714                // Remember the end of the line
715                const char* const lineEnd = cursor - 1;
716
717                // When we see a line-break character we need
718                // to record the line break, but we also need
719                // to deal with the annoying issue of encodings,
720                // where a multi-byte sequence might encode
721                // the line break.
722                if (cursor < end)
723                {
724                    int d = *cursor;
725                    if ((c ^ d) == ('\r' ^ '\n'))
726                        cursor++;
727                }
728
729                ioText = UnownedStringSlice(cursor, end);
730                outLine = UnownedStringSlice(begin, lineEnd);
731                return true;
732            }
733        default:
734            break;
735        }
736    }
737
738    // There is nothing remaining
739    ioText = UnownedStringSlice(nullptr, nullptr);
740
741    // Could be empty, or the remaining line (without line end terminators of)
742    SLANG_ASSERT(begin <= cursor);
743
744    outLine = UnownedStringSlice(begin, cursor);
745    return true;
746}
747
748/* static */ void StringUtil::calcLines(
749    const UnownedStringSlice& textIn,
750    List<UnownedStringSlice>& outLines)
751{
752    outLines.clear();
753    UnownedStringSlice text(textIn), line;
754    while (extractLine(text, line))
755    {
756        outLines.add(line);
757    }
758}
759
760/* static */ UnownedStringSlice StringUtil::trimEndOfLine(const UnownedStringSlice& line)
761{
762    // Strip CR/LF from end of line if present
763
764    const char* begin = line.begin();
765    const char* end = line.end();
766
767    if (end > begin)
768    {
769        const char c = end[-1];
770        // If last char is CR/LF move back a char
771        if (c == '\n' || c == '\r')
772        {
773            --end;
774            // If next char is a match for the CR/LF pair move back an extra char.
775            end -= Index((end > begin) && (c ^ end[-1]) == ('\r' ^ '\n'));
776        }
777    }
778
779    return line.head(Index(end - begin));
780}
781
782/* static */ bool StringUtil::areLinesEqual(
783    const UnownedStringSlice& inA,
784    const UnownedStringSlice& inB)
785{
786    UnownedStringSlice a(inA), b(inB), lineA, lineB;
787
788    while (true)
789    {
790        const auto hasLineA = extractLine(a, lineA);
791        const auto hasLineB = extractLine(b, lineB);
792
793        if (!(hasLineA && hasLineB))
794        {
795            return hasLineA == hasLineB;
796        }
797
798        // The lines must be equal
799        if (lineA != lineB)
800        {
801            return false;
802        }
803    }
804}
805
806/* static */ SlangResult StringUtil::parseDouble(const UnownedStringSlice& text, double& out)
807{
808    const Index bufSize = 32;
809
810    const auto len = text.getLength();
811
812    if (len > bufSize - 1)
813    {
814        List<char> work;
815        work.setCount(len + 1);
816        char* dst = work.getBuffer();
817
818        ::memcpy(dst, text.begin(), len * sizeof(char));
819        dst[len] = 0;
820
821        out = atof(dst);
822    }
823    else
824    {
825        char buf[bufSize];
826        ::memcpy(buf, text.begin(), len * sizeof(char));
827        buf[len] = 0;
828        out = atof(buf);
829    }
830    return SLANG_OK;
831}
832
833/* static */ SlangResult StringUtil::parseInt(const UnownedStringSlice& in, Int& outValue)
834{
835    const char* cur = in.begin();
836    const char* end = in.end();
837
838    bool negate = false;
839    if (cur < end && *cur == '-')
840    {
841        negate = true;
842        cur++;
843    }
844
845    int radix = 10;
846    auto getDigit = CharUtil::getDecimalDigitValue;
847    if (cur + 1 < end && *cur == '0' && (*(cur + 1) == 'x' || *(cur + 1) == 'X'))
848    {
849        radix = 16;
850        getDigit = CharUtil::getHexDigitValue;
851        cur += 2;
852    }
853
854    // We need at least one digit
855    if (cur >= end || !CharUtil::isDigit(*cur))
856    {
857        return SLANG_FAIL;
858    }
859
860    Int value = 0;
861    // Do the digits
862    for (; cur < end; ++cur)
863    {
864        const auto d = getDigit(*cur);
865        if (d == -1)
866            return SLANG_FAIL;
867        value = value * radix + d;
868    }
869
870    value = negate ? -value : value;
871
872    outValue = value;
873    return SLANG_OK;
874}
875
876/* static */ SlangResult StringUtil::parseInt64(const UnownedStringSlice& text, int64_t& out)
877{
878    bool negate = false;
879
880    const char* cur = text.begin();
881    const char* end = text.end();
882
883    if (cur < end)
884    {
885        if (*cur == '-')
886        {
887            negate = true;
888            cur++;
889        }
890        else if (*cur == '+')
891        {
892            cur++;
893        }
894    }
895
896    // Must have at least one digit
897    if (cur >= end || !CharUtil::isDigit(*cur))
898    {
899        return SLANG_FAIL;
900    }
901
902    uint64_t value = 0;
903    // We can have 20 digits, but the last digit can cause overflow.
904    // Lets do the easy first digits first
905    Index numSimple = 19;
906    for (; cur < end && CharUtil::isDigit(*cur) && numSimple > 0; ++cur, --numSimple)
907    {
908        value = value * 10 + (*cur - '0');
909    }
910
911    if (cur < end && CharUtil::isDigit(*cur))
912    {
913        const auto prevValue = value;
914        value = value * 10 + (*cur - '0');
915        cur++;
916
917        if (value < prevValue)
918        {
919            // We have overflow
920            return SLANG_FAIL;
921        }
922    }
923
924    if (negate)
925    {
926        if (value > ~((~uint64_t(0)) >> 1))
927        {
928            // Overflow
929            return SLANG_FAIL;
930        }
931        out = -int64_t(value);
932    }
933    else
934    {
935        if (value > ((~uint64_t(0)) >> 1))
936        {
937            // Overflow
938            return SLANG_FAIL;
939        }
940        out = value;
941    }
942
943    return (cur == end) ? SLANG_OK : SLANG_FAIL;
944}
945
946int StringUtil::parseIntAndAdvancePos(UnownedStringSlice text, Index& pos)
947{
948    int result = 0;
949    while (text[pos] == ' ' && pos < text.getLength())
950    {
951        pos++;
952        continue;
953    }
954    bool isNeg = false;
955    if (pos < text.getLength() && text[pos] == '-')
956    {
957        pos++;
958        isNeg = true;
959    }
960    while (pos < text.getLength())
961    {
962        if (text[pos] >= '0' && text[pos] <= '9')
963        {
964            result *= 10;
965            result += text[pos] - '0';
966            pos++;
967        }
968        else
969        {
970            break;
971        }
972    }
973    if (isNeg)
974        result = -result;
975    return result;
976}
977
978} // namespace Slang