void VM::Concat(Value *dst, Value *op1, Value *op2) { if (op1->type_ == ValueT_String && op2->type_ == ValueT_String) { dst->str_ = state_->GetString(op1->str_->GetStdString() + op2->str_->GetCStr()); } else if (op1->type_ == ValueT_String && op2->type_ == ValueT_Number) { dst->str_ = state_->GetString(op1->str_->GetCStr() + NumberToStr(op2)); } else if (op1->type_ == ValueT_Number && op2->type_ == ValueT_String) { dst->str_ = state_->GetString(NumberToStr(op1) + op2->str_->GetCStr()); } else { auto pos = GetCurrentInstructionPos(); throw RuntimeException(pos.first, pos.second, op1, op2, "concat"); } dst->type_ = ValueT_String; }
// binary string to hexadecimal string char* BinStrToHexStr(char* binStr, char* hexStr) { int i, j, t; Number binNum; Number hexNum; StrToNumber(binStr, &binNum); hexNum.sign = binNum.sign; hexNum.len = (int)ceil(binNum.len / 4.0); for (i = 0; i < hexNum.len; i++) { j = 4 * i; t = binNum.value[j]; if (j + 1 < binNum.len) t += 2 * binNum.value[j + 1]; if (j + 2 < binNum.len) t += 4 * binNum.value[j + 2]; if (j + 3 < binNum.len) t += 8 * binNum.value[j + 3]; hexNum.value[i] = t; } return NumberToStr(&hexNum, hexStr); }
// change type: BigInt to string(radix 10) char* BigIntToStr(BigInt* a, char* s) { char buf[BUFFER_SIZE]; Number binNum; BigIntToBinNum(a, &binNum); // BigInt to Number NumberToStr(&binNum, buf); // Number to string return ChangeStringRadix(buf, 2, 10, s); // string radix 2 to 10 }
// change string radix from srcRadix to dstRadix char* ChangeStringRadix(char* str, int srcRadix, int dstRadix, char* resultStr) { if (srcRadix < dstRadix) { char hexStr[BUFFER_SIZE]; ChangeStringRadix(str, srcRadix, 2, resultStr); // srcRadix to radix 2 BinStrToHexStr(resultStr, hexStr); // radix 2 to 16 // radix 16 to dstRadix return ChangeStringRadix(hexStr, 16, dstRadix, resultStr); } if (srcRadix == dstRadix) { return strcpy(resultStr, str); } else // srcRadix > dstRadix { int i, t; Number dividend; Number quotient; Number resultNum; // string to Number StrToNumber(str, ÷nd); resultNum.len = 0; resultNum.sign = dividend.sign; while (dividend.len > 0) { quotient.len = dividend.len; // simulate the way we do division // when the cycle is end, t is the remainder for (t = 0, i = dividend.len - 1; i >= 0; i--) { t = t * srcRadix + dividend.value[i]; quotient.value[i] = t / dstRadix; t = t % dstRadix; } // save the remainder resultNum.value[resultNum.len++] = t; // filter the unnecessary 0 in quotient for (i = quotient.len - 1; i >= 0 && quotient.value[i] == 0; i--); // set the next dividend length dividend.len = i + 1; // let the quotient be the next divident for (i = 0; i < dividend.len; i++) { dividend.value[i] = quotient.value[i]; } } return NumberToStr(&resultNum, resultStr); } }
/* Str format(...) Format a string based on a format string which may contain formatting sequences of form {...}; these correspond to the arguments of the method. The base string object represents the format string. */ AValue AFormat(AThread *t, AValue *frame) { int fi; /* Format string index */ int ai; /* Argument index */ int fmtLen; /* Format string length */ FormatOutput output; AExpectStr(t, frame[0]); output.t = t; output.index = 0; output.len = FORMAT_BUF_SIZE; output.str = &frame[2]; fmtLen = AStrLen(frame[0]); ai = 0; /* IDEA: Refactor this into multiple functions. */ /* Iterate over the format string. Handle { sequences and copy other characters directly to the output buffer (} may be duplicated). */ for (fi = 0; fi < fmtLen; fi++) { int ch = AStrItem(frame[0], fi); if (ch == '{') { if (fi == fmtLen - 1 || AStrItem(frame[0], fi + 1) == '{') { /* Literal '{'. */ fi++; Append(&output, ch); } else { /* A format sequence {...}. */ int negAlign = FALSE; int align = 0; int oldInd; int oldOutInd; AValue arg; fi++; oldInd = fi; /* Parse alignment */ if (fi < fmtLen && AStrItem(frame[0], fi) == '-' && AIsDigit(AStrItem(frame[0], fi + 1))) { negAlign = TRUE; fi++; } if (fi < fmtLen && AIsDigit(AStrItem(frame[0], fi))) { do { align = align * 10 + AStrItem(frame[0], fi) - '0'; fi++; } while (fi < fmtLen && AIsDigit(AStrItem(frame[0], fi))); if (AStrItem(frame[0], fi) != ':') { fi = oldInd; align = 0; } else fi++; } oldOutInd = output.index & ~WIDE_BUF_FLAG; if (ai >= AArrayLen(frame[1])) ARaiseValueError(t, "Too few arguments"); arg = AArrayItem(frame[1], ai); if (AIsInstance(arg) && AStrItem(frame[0], fi) != '}' && AMember(t, arg, "_format") != AError) { int i; oldInd = fi; while (fi <= fmtLen && AStrItem(frame[0], fi) != '}') fi++; if (fi >= fmtLen) ARaiseValueError(t, "Unterminated format"); frame[3] = AArrayItem(frame[1], ai); frame[4] = ASubStr(t, frame[0], oldInd, fi); frame[3] = ACallMethod(t, "_format", 1, frame + 3); if (AIsError(frame[3])) return AError; AExpectStr(t, frame[3]); for (i = 0; i < AStrLen(frame[3]); i++) Append(&output, AStrItem(frame[3], i)); } else { /* Either format an Int/Float or use the default conversion by calling std::Str. */ int minNumLen = 0; int fractionLen = 0; int optFractionLen = 0; int expLen = 0; int fraction = FALSE; int scientific = FALSE; int plusExp = FALSE; int expChar = ' '; while (fi < fmtLen && (ch = AStrItem(frame[0], fi)) != '}') { if (ch == '0') { if (scientific) expLen++; else if (fraction) fractionLen++; else minNumLen++; } else if (ch == '.') fraction = TRUE; else if (ch == 'e' || ch == 'E') { scientific = TRUE; expChar = ch; } else if (ch == '+' && scientific) plusExp = TRUE; else if (ch == '#' && fraction && !scientific) { fractionLen++; optFractionLen++; } else ARaiseValueError(t, "Invalid character in format"); fi++; } if (scientific) NumberToScientific(&output, arg, fractionLen, expLen, expChar, plusExp, optFractionLen); else if (minNumLen > 0 || fraction) NumberToStr(&output, arg, minNumLen, fractionLen, optFractionLen); else { /* Not a number formatting sequence. Use std::Str to convert the argument. */ int i; frame[3] = arg; frame[3] = AStdStr(t, &frame[3]); if (AIsError(frame[3])) return AError; for (i = 0; i < AStrLen(frame[3]); i++) Append(&output, AStrItem(frame[3], i)); } } if (fi >= fmtLen) ARaiseValueError(t, "Unterminated format"); align -= (output.index & ~WIDE_BUF_FLAG) - oldOutInd; if (align > 0) { int i; for (i = 0; i < align; i++) Append(&output, ' '); if (!negAlign) { if (output.index < FORMAT_BUF_SIZE) { AMoveMem(output.buf + oldOutInd + align, output.buf + oldOutInd, output.index - oldOutInd - align); for (i = 0; i < align; i++) output.buf[oldOutInd + i] = ' '; } else { for (i = (output.index & ~WIDE_BUF_FLAG) - 1; i >= oldOutInd + align; i--) ASetStrItem(frame[2], i, AStrItem(frame[2], i - align)); for (i = 0; i < align; i++) ASetStrItem(frame[2], oldOutInd + i, ' '); } } } ai++; } } else if (ch == '}') { /* Literal '}' can be duplicated (but does not need to be). */ if (fi < fmtLen - 1 && AStrItem(frame[0], fi + 1) == '}') fi++; Append(&output, ch); } else Append(&output, ch); } if (ai < AArrayLen(frame[1])) ARaiseValueError(t, "Too many arguments"); if (output.index <= FORMAT_BUF_SIZE) { /* The output contains only narrow characters (since the index does not have the WIDE_BUF_FLAG flag) and is short enough to not require a heap-allocated buffer. Create a string based on the internal buffer. */ AValue res = AMakeEmptyStr(t, output.index); ACopyMem(AGetStrElem(res), output.buf, output.index); return res; } else { /* The output is stored in a heap-allocated buffer (represented as a Str object). Return the initialized part of the buffer. */ return ASubStr(t, frame[2], 0, output.index & ~WIDE_BUF_FLAG); } }
/* Convert a number using a scientific format (e.g. 0.00e+00). The num argument is the number to convert. The argument numFrac is the number of digits in the fraction, expLen is the number of digits in the exponent, plusExp defines whether to prefix a positive exponent always with a '+', and optFrac speficies the number of fraction digits that are only generated if they are not zeroes. */ static void NumberToScientific(FormatOutput *output, AValue num, int numFrac, int expLen, int expChar, ABool plusExp, int optFrac) { /* FIX: Use sprintf for the conversion for better accuracy. */ double f = AGetFloat(output->t, num); int exp = 0; ABool sign = f < 0.0; int i; char s[NUM_BUF_SIZE]; int si; int s0; /* Handle infinities and NaN's as special cases. */ if (AIsNaN(f)) { AppendStr(output, "nan"); return; } else if (AIsInf(f)) { if (f > 0.0) AppendStr(output, "inf"); else AppendStr(output, "-inf"); return; } if (sign) f = -f; if (f != 0.0) { double max = 10.0 - 0.5 * pow(10.0, -numFrac); double min = 1.0 - 0.5 * pow(10.0, -numFrac); while (f >= max) { f /= 10.0; exp++; } while (f < min) { f *= 10.0; exp--; } } f = floor(f * pow(10.0, numFrac) + 0.5); if (numFrac >= NUM_BUF_SIZE - 2) ARaiseValueError(output->t, "Formatted Float is too long"); si = 0; for (i = 0; i <= numFrac; i++) { int d = (int)fmod(f, 10.0); s[si++] = '0' + d; f /= 10.0; } s0 = 0; while (optFrac > 0 && s0 < si && s[s0] == '0') { s0++; optFrac--; numFrac--; } if (sign) AppendCh(output, '-'); AppendCh(output, s[si - 1]); if (numFrac > 0) AppendCh(output, '.'); for (i = si - 2; i >= s0; i--) AppendCh(output, s[i]); AppendCh(output, expChar); if (plusExp && exp >= 0) AppendCh(output, '+'); NumberToStr(output, AMakeInt(output->t, exp), expLen, 0, 0); }