1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 19:48:20 +02:00

String interpolation

This commit is contained in:
Brian Fiete 2020-11-11 05:46:52 -08:00
parent 22cc81862b
commit 281f19e04c
15 changed files with 379 additions and 59 deletions

View file

@ -166,6 +166,11 @@ void BfStructuralVisitor::Visit(BfLiteralExpression* literalExpr)
Visit(literalExpr->ToBase()); Visit(literalExpr->ToBase());
} }
void BfStructuralVisitor::Visit(BfStringInterpolationExpression* stringInterpolationExpression)
{
Visit(stringInterpolationExpression->ToBase());
}
void BfStructuralVisitor::Visit(BfIdentifierNode* identifierNode) void BfStructuralVisitor::Visit(BfIdentifierNode* identifierNode)
{ {
Visit(identifierNode->ToBase()); Visit(identifierNode->ToBase());

View file

@ -261,6 +261,7 @@ class BfExpressionStatement;
class BfAttributedExpression; class BfAttributedExpression;
class BfAttributedStatement; class BfAttributedStatement;
class BfLiteralExpression; class BfLiteralExpression;
class BfStringInterpolationExpression;
class BfBlock; class BfBlock;
class BfBlockExtension; class BfBlockExtension;
class BfRootNode; class BfRootNode;
@ -441,8 +442,9 @@ public:
virtual void Visit(BfEmptyStatement* emptyStmt); virtual void Visit(BfEmptyStatement* emptyStmt);
virtual void Visit(BfTokenNode* tokenNode); virtual void Visit(BfTokenNode* tokenNode);
virtual void Visit(BfTokenPairNode* tokenPairNode); virtual void Visit(BfTokenPairNode* tokenPairNode);
virtual void Visit(BfLiteralExpression* literalExpr); virtual void Visit(BfLiteralExpression* literalExpr);
virtual void Visit(BfStringInterpolationExpression* stringInterpolationExpression);
virtual void Visit(BfIdentifierNode* identifierNode); virtual void Visit(BfIdentifierNode* identifierNode);
virtual void Visit(BfAttributedIdentifierNode* attrIdentifierNode); virtual void Visit(BfAttributedIdentifierNode* attrIdentifierNode);
virtual void Visit(BfQualifiedNameNode* nameNode); virtual void Visit(BfQualifiedNameNode* nameNode);
@ -2099,6 +2101,16 @@ public:
BfVariant mValue; BfVariant mValue;
}; BF_AST_DECL(BfLiteralExpression, BfExpression); }; BF_AST_DECL(BfLiteralExpression, BfExpression);
class BfStringInterpolationExpression : public BfExpression
{
public:
BF_AST_TYPE(BfStringInterpolationExpression, BfExpression);
BfAstNode* mAllocNode;
String* mString;
BfSizedArray<ASTREF(BfBlock*)> mExpressions;
}; BF_AST_DECL(BfStringInterpolationExpression, BfExpression);
class BfInitializerExpression : public BfExpression class BfInitializerExpression : public BfExpression
{ {
public: public:

View file

@ -208,6 +208,16 @@ void BfElementVisitor::Visit(BfLiteralExpression* literalExpr)
Visit(literalExpr->ToBase()); Visit(literalExpr->ToBase());
} }
void BfElementVisitor::Visit(BfStringInterpolationExpression* stringInterpolationExpression)
{
Visit(stringInterpolationExpression->ToBase());
VisitChild(stringInterpolationExpression->mAllocNode);
for (auto block : stringInterpolationExpression->mExpressions)
{
VisitChild(block);
}
}
void BfElementVisitor::Visit(BfIdentifierNode* identifierNode) void BfElementVisitor::Visit(BfIdentifierNode* identifierNode)
{ {
Visit(identifierNode->ToBase()); Visit(identifierNode->ToBase());

View file

@ -37,6 +37,7 @@ public:
virtual void Visit(BfTokenNode* tokenNode); virtual void Visit(BfTokenNode* tokenNode);
virtual void Visit(BfTokenPairNode* tokenPairNode); virtual void Visit(BfTokenPairNode* tokenPairNode);
virtual void Visit(BfLiteralExpression* literalExpr); virtual void Visit(BfLiteralExpression* literalExpr);
virtual void Visit(BfStringInterpolationExpression* stringInterpolationExpression);
virtual void Visit(BfIdentifierNode* identifierNode); virtual void Visit(BfIdentifierNode* identifierNode);
virtual void Visit(BfAttributedIdentifierNode* attrIdentifierNode); virtual void Visit(BfAttributedIdentifierNode* attrIdentifierNode);
virtual void Visit(BfQualifiedNameNode* nameNode); virtual void Visit(BfQualifiedNameNode* nameNode);

View file

@ -3200,6 +3200,47 @@ void BfExprEvaluator::Visit(BfLiteralExpression* literalExpr)
GetLiteral(literalExpr, literalExpr->mValue); GetLiteral(literalExpr, literalExpr->mValue);
} }
void BfExprEvaluator::Visit(BfStringInterpolationExpression* stringInterpolationExpression)
{
if ((mBfEvalExprFlags & BfEvalExprFlags_StringInterpolateFormat) != 0)
{
BfVariant variant;
variant.mTypeCode = BfTypeCode_CharPtr;
variant.mString = stringInterpolationExpression->mString;
GetLiteral(stringInterpolationExpression, variant);
return;
}
if (stringInterpolationExpression->mAllocNode != NULL)
{
auto stringType = mModule->ResolveTypeDef(mModule->mCompiler->mStringTypeDef)->ToTypeInstance();
BfTokenNode* newToken = NULL;
BfAllocTarget allocTarget = ResolveAllocTarget(stringInterpolationExpression->mAllocNode, newToken);
CreateObject(NULL, stringInterpolationExpression->mAllocNode, stringType);
BfTypedValue newString = mResult;
BF_ASSERT(newString);
SizedArray<BfExpression*, 2> argExprs;
argExprs.Add(stringInterpolationExpression);
BfSizedArray<BfExpression*> sizedArgExprs(argExprs);
BfResolvedArgs argValues(&sizedArgExprs);
ResolveArgValues(argValues, BfResolveArgFlag_InsideStringInterpolationAlloc);
MatchMethod(stringInterpolationExpression, NULL, newString, false, false, "AppendF", argValues, NULL);
mResult = newString;
return;
}
mModule->Fail("Invalid use of string interpolation expression. Consider adding an allocation specifier such as 'scope'.", stringInterpolationExpression);
for (auto block : stringInterpolationExpression->mExpressions)
{
VisitChild(block);
}
}
BfTypedValue BfExprEvaluator::LoadLocal(BfLocalVariable* varDecl, bool allowRef) BfTypedValue BfExprEvaluator::LoadLocal(BfLocalVariable* varDecl, bool allowRef)
{ {
if (!mModule->mIsInsideAutoComplete) if (!mModule->mIsInsideAutoComplete)
@ -4413,23 +4454,44 @@ void BfExprEvaluator::ResolveArgValues(BfResolvedArgs& resolvedArgs, BfResolveAr
autoComplete->mIgnoreFixits = true; autoComplete->mIgnoreFixits = true;
} }
for (int argIdx = 0; argIdx < argCount ; argIdx++) int deferredArgIdx = 0;
SizedArray<BfExpression*, 8> deferredArgs;
int argIdx = 0;
//for (int argIdx = 0; argIdx < argCount ; argIdx++)
while (true)
{ {
//printf("Args: %p %p %d\n", resolvedArgs.mArguments, resolvedArgs.mArguments->mVals, resolvedArgs.mArguments->mSize); //printf("Args: %p %p %d\n", resolvedArgs.mArguments, resolvedArgs.mArguments->mVals, resolvedArgs.mArguments->mSize);
BfExpression* argExpr = NULL; BfExpression* argExpr = NULL;
if (argIdx < resolvedArgs.mArguments->size())
argExpr = (*resolvedArgs.mArguments)[argIdx]; int curArgIdx = -1;
if (deferredArgIdx < deferredArgs.size())
{
argExpr = deferredArgs[deferredArgIdx++];
}
else if (argIdx >= argCount)
{
break;
}
else
{
curArgIdx = argIdx++;
if (curArgIdx < resolvedArgs.mArguments->size())
argExpr = (*resolvedArgs.mArguments)[curArgIdx];
}
if (argExpr == NULL) if (argExpr == NULL)
{ {
if (argIdx == 0) if (curArgIdx == 0)
{ {
if (resolvedArgs.mOpenToken != NULL) if (resolvedArgs.mOpenToken != NULL)
mModule->FailAfter("Expression expected", resolvedArgs.mOpenToken); mModule->FailAfter("Expression expected", resolvedArgs.mOpenToken);
} }
else if (resolvedArgs.mCommas != NULL) else if (resolvedArgs.mCommas != NULL)
mModule->FailAfter("Expression expected", (*resolvedArgs.mCommas)[argIdx - 1]); mModule->FailAfter("Expression expected", (*resolvedArgs.mCommas)[curArgIdx - 1]);
} }
if (auto typedValueExpr = BfNodeDynCast<BfTypedValueExpression>(argExpr)) if (auto typedValueExpr = BfNodeDynCast<BfTypedValueExpression>(argExpr))
@ -4440,7 +4502,7 @@ void BfExprEvaluator::ResolveArgValues(BfResolvedArgs& resolvedArgs, BfResolveAr
resolvedArgs.mResolvedArgs.push_back(resolvedArg); resolvedArgs.mResolvedArgs.push_back(resolvedArg);
continue; continue;
} }
BfResolvedArg resolvedArg; BfResolvedArg resolvedArg;
BfExprEvaluator exprEvaluator(mModule); BfExprEvaluator exprEvaluator(mModule);
exprEvaluator.mResolveGenericParam = (flags & BfResolveArgFlag_AllowUnresolvedTypes) == 0; exprEvaluator.mResolveGenericParam = (flags & BfResolveArgFlag_AllowUnresolvedTypes) == 0;
@ -4448,6 +4510,16 @@ void BfExprEvaluator::ResolveArgValues(BfResolvedArgs& resolvedArgs, BfResolveAr
bool handled = false; bool handled = false;
bool evaluated = false; bool evaluated = false;
if (auto interpolateExpr = BfNodeDynCastExact<BfStringInterpolationExpression>(argExpr))
{
if ((interpolateExpr->mAllocNode == NULL) || ((flags & BfResolveArgFlag_InsideStringInterpolationAlloc) != 0))
{
resolvedArg.mArgFlags = (BfArgFlags)(resolvedArg.mArgFlags | BfArgFlag_StringInterpolateFormat);
for (auto innerExpr : interpolateExpr->mExpressions)
deferredArgs.Add(innerExpr);
}
}
bool deferParamEval = false; bool deferParamEval = false;
if ((flags & BfResolveArgFlag_DeferParamEval) != 0) if ((flags & BfResolveArgFlag_DeferParamEval) != 0)
{ {
@ -4542,6 +4614,10 @@ void BfExprEvaluator::ResolveArgValues(BfResolvedArgs& resolvedArgs, BfResolveAr
if (!evaluated) if (!evaluated)
{ {
exprEvaluator.mBfEvalExprFlags = (BfEvalExprFlags)(exprEvaluator.mBfEvalExprFlags | BfEvalExprFlags_AllowParamsExpr); exprEvaluator.mBfEvalExprFlags = (BfEvalExprFlags)(exprEvaluator.mBfEvalExprFlags | BfEvalExprFlags_AllowParamsExpr);
if ((resolvedArg.mArgFlags & BfArgFlag_StringInterpolateFormat) != 0)
exprEvaluator.mBfEvalExprFlags = (BfEvalExprFlags)(exprEvaluator.mBfEvalExprFlags | BfEvalExprFlags_StringInterpolateFormat);
exprEvaluator.Evaluate(argExpr, false, false, true); exprEvaluator.Evaluate(argExpr, false, false, true);
} }
@ -4595,7 +4671,7 @@ void BfExprEvaluator::ResolveArgValues(BfResolvedArgs& resolvedArgs, BfResolveAr
} }
resolvedArg.mExpression = argExpr; resolvedArg.mExpression = argExpr;
resolvedArgs.mResolvedArgs.push_back(resolvedArg); resolvedArgs.mResolvedArgs.push_back(resolvedArg);
} }
if (autoComplete != NULL) if (autoComplete != NULL)
autoComplete->mIgnoreFixits = hadIgnoredFixits; autoComplete->mIgnoreFixits = hadIgnoredFixits;
@ -12035,14 +12111,19 @@ void BfExprEvaluator::CheckObjectCreateTypeRef(BfType* expectingType, BfAstNode*
} }
void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr) void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr)
{
CreateObject(objCreateExpr, objCreateExpr->mNewNode, NULL);
}
void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAstNode* allocNode, BfType* wantAllocType)
{ {
auto autoComplete = GetAutoComplete(); auto autoComplete = GetAutoComplete();
if ((autoComplete != NULL) && (objCreateExpr->mTypeRef != NULL)) if ((autoComplete != NULL) && (objCreateExpr != NULL) && (objCreateExpr->mTypeRef != NULL))
{ {
autoComplete->CheckTypeRef(objCreateExpr->mTypeRef, false, true); autoComplete->CheckTypeRef(objCreateExpr->mTypeRef, false, true);
} }
if ((autoComplete != NULL) && (objCreateExpr->mOpenToken != NULL) && (objCreateExpr->mCloseToken != NULL) && if ((autoComplete != NULL) && (objCreateExpr != NULL) && (objCreateExpr->mOpenToken != NULL) && (objCreateExpr->mCloseToken != NULL) &&
(objCreateExpr->mOpenToken->mToken == BfToken_LBrace) && (autoComplete->CheckFixit(objCreateExpr->mOpenToken))) (objCreateExpr->mOpenToken->mToken == BfToken_LBrace) && (autoComplete->CheckFixit(objCreateExpr->mOpenToken)))
{ {
auto refNode = objCreateExpr->mOpenToken; auto refNode = objCreateExpr->mOpenToken;
@ -12055,28 +12136,28 @@ void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr)
} }
} }
CheckObjectCreateTypeRef(mExpectingType, objCreateExpr->mNewNode); CheckObjectCreateTypeRef(mExpectingType, allocNode);
BfAttributeState attributeState; BfAttributeState attributeState;
attributeState.mTarget = BfAttributeTargets_Alloc; attributeState.mTarget = BfAttributeTargets_Alloc;
SetAndRestoreValue<BfAttributeState*> prevAttributeState(mModule->mAttributeState, &attributeState); SetAndRestoreValue<BfAttributeState*> prevAttributeState(mModule->mAttributeState, &attributeState);
BfTokenNode* newToken = NULL; BfTokenNode* newToken = NULL;
BfAllocTarget allocTarget = ResolveAllocTarget(objCreateExpr->mNewNode, newToken, &attributeState.mCustomAttributes); BfAllocTarget allocTarget = ResolveAllocTarget(allocNode, newToken, &attributeState.mCustomAttributes);
bool isScopeAlloc = newToken->GetToken() == BfToken_Scope; bool isScopeAlloc = newToken->GetToken() == BfToken_Scope;
bool isAppendAlloc = newToken->GetToken() == BfToken_Append; bool isAppendAlloc = newToken->GetToken() == BfToken_Append;
bool isStackAlloc = (newToken->GetToken() == BfToken_Stack) || (isScopeAlloc); bool isStackAlloc = (newToken->GetToken() == BfToken_Stack) || (isScopeAlloc);
bool isArrayAlloc = false;// (objCreateExpr->mArraySizeSpecifier != NULL); bool isArrayAlloc = false;// (objCreateExpr->mArraySizeSpecifier != NULL);
bool isRawArrayAlloc = (objCreateExpr->mStarToken != NULL); bool isRawArrayAlloc = (objCreateExpr != NULL) && (objCreateExpr->mStarToken != NULL);
if (isScopeAlloc) if (isScopeAlloc)
{ {
if ((mBfEvalExprFlags & BfEvalExprFlags_FieldInitializer) != 0) if ((mBfEvalExprFlags & BfEvalExprFlags_FieldInitializer) != 0)
{ {
mModule->Warn(0, "This allocation will only be in scope during the constructor. Consider using a longer-term allocation such as 'new'", objCreateExpr->mNewNode); mModule->Warn(0, "This allocation will only be in scope during the constructor. Consider using a longer-term allocation such as 'new'", allocNode);
} }
if (objCreateExpr->mNewNode == newToken) // Scope, no target specified if (allocNode == newToken) // Scope, no target specified
{ {
if (mModule->mParentNodeEntry != NULL) if (mModule->mParentNodeEntry != NULL)
{ {
@ -12086,7 +12167,7 @@ void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr)
{ {
// If we are assigning this to a property then it's possible the property setter can actually deal with a temporary allocation so no warning in that case // If we are assigning this to a property then it's possible the property setter can actually deal with a temporary allocation so no warning in that case
if ((mBfEvalExprFlags & BfEvalExprFlags_PendingPropSet) == 0) if ((mBfEvalExprFlags & BfEvalExprFlags_PendingPropSet) == 0)
mModule->Warn(0, "This allocation will immediately go out of scope. Consider specifying a wider scope target such as 'scope::'", objCreateExpr->mNewNode); mModule->Warn(0, "This allocation will immediately go out of scope. Consider specifying a wider scope target such as 'scope::'", allocNode);
} }
} }
} }
@ -12102,7 +12183,12 @@ void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr)
BfType* unresolvedTypeRef = NULL; BfType* unresolvedTypeRef = NULL;
BfType* resolvedTypeRef = NULL; BfType* resolvedTypeRef = NULL;
if (objCreateExpr->mTypeRef == NULL) if (wantAllocType != NULL)
{
unresolvedTypeRef = wantAllocType;
resolvedTypeRef = wantAllocType;
}
else if (objCreateExpr->mTypeRef == NULL)
{ {
if ((!mExpectingType) || (!mExpectingType->IsArray())) if ((!mExpectingType) || (!mExpectingType->IsArray()))
{ {
@ -12118,7 +12204,7 @@ void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr)
} }
} }
else else
{ {
if ((objCreateExpr->mTypeRef->IsExact<BfDotTypeReference>()) && (mExpectingType != NULL)) if ((objCreateExpr->mTypeRef->IsExact<BfDotTypeReference>()) && (mExpectingType != NULL))
{ {
//mModule->SetElementType(objCreateExpr->mTypeRef, BfSourceElementType_TypeRef); //mModule->SetElementType(objCreateExpr->mTypeRef, BfSourceElementType_TypeRef);
@ -12287,12 +12373,12 @@ void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr)
{ {
if (!mModule->mCurTypeInstance->IsObject()) if (!mModule->mCurTypeInstance->IsObject())
{ {
mModule->Fail("Append allocations are only allowed in classes", objCreateExpr->mNewNode); mModule->Fail("Append allocations are only allowed in classes", allocNode);
isAppendAlloc = false; isAppendAlloc = false;
} }
else if ((mBfEvalExprFlags & BfEvalExprFlags_VariableDeclaration) == 0) else if ((mBfEvalExprFlags & BfEvalExprFlags_VariableDeclaration) == 0)
{ {
mModule->Fail("Append allocations are only allowed as local variable initializers in constructor body", objCreateExpr->mNewNode); mModule->Fail("Append allocations are only allowed as local variable initializers in constructor body", allocNode);
isAppendAlloc = false; isAppendAlloc = false;
} }
else else
@ -12300,22 +12386,22 @@ void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr)
auto methodDef = mModule->mCurMethodInstance->mMethodDef; auto methodDef = mModule->mCurMethodInstance->mMethodDef;
if (methodDef->mMethodType == BfMethodType_CtorCalcAppend) if (methodDef->mMethodType == BfMethodType_CtorCalcAppend)
{ {
mModule->Fail("Append allocations are only allowed as local variable declarations in the main method body", objCreateExpr->mNewNode); mModule->Fail("Append allocations are only allowed as local variable declarations in the main method body", allocNode);
isAppendAlloc = false; isAppendAlloc = false;
} }
else if (!methodDef->mHasAppend) else if (!methodDef->mHasAppend)
{ {
mModule->Fail("Append allocations can only be used on constructors with [AllowAppend] specified", objCreateExpr->mNewNode); mModule->Fail("Append allocations can only be used on constructors with [AllowAppend] specified", allocNode);
isAppendAlloc = false; isAppendAlloc = false;
} }
else if (methodDef->mMethodType != BfMethodType_Ctor) else if (methodDef->mMethodType != BfMethodType_Ctor)
{ {
mModule->Fail("Append allocations are only allowed in constructors", objCreateExpr->mNewNode); mModule->Fail("Append allocations are only allowed in constructors", allocNode);
isAppendAlloc = false; isAppendAlloc = false;
} }
else if (methodDef->mIsStatic) else if (methodDef->mIsStatic)
{ {
mModule->Fail("Append allocations are only allowed in non-static constructors", objCreateExpr->mNewNode); mModule->Fail("Append allocations are only allowed in non-static constructors", allocNode);
isAppendAlloc = false; isAppendAlloc = false;
} }
} }
@ -12731,7 +12817,7 @@ void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr)
if ((isStackAlloc) && (mModule->mCurMethodState == NULL)) if ((isStackAlloc) && (mModule->mCurMethodState == NULL))
{ {
mModule->Fail("Cannot use 'stack' here", objCreateExpr->mNewNode); mModule->Fail("Cannot use 'stack' here", allocNode);
isStackAlloc = false; isStackAlloc = false;
isScopeAlloc = false; isScopeAlloc = false;
} }
@ -12810,8 +12896,12 @@ void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr)
BfIRValue appendSizeValue; BfIRValue appendSizeValue;
BfTypedValue emtpyThis(mModule->mBfIRBuilder->GetFakeVal(), resolvedTypeRef, resolvedTypeRef->IsStruct()); BfTypedValue emtpyThis(mModule->mBfIRBuilder->GetFakeVal(), resolvedTypeRef, resolvedTypeRef->IsStruct());
BfResolvedArgs argValues(objCreateExpr->mOpenToken, &objCreateExpr->mArguments, &objCreateExpr->mCommas, objCreateExpr->mCloseToken); BfResolvedArgs argValues;
ResolveArgValues(argValues, BfResolveArgFlag_DeferParamEval); //// if (objCreateExpr != NULL)
{
argValues.Init(objCreateExpr->mOpenToken, &objCreateExpr->mArguments, &objCreateExpr->mCommas, objCreateExpr->mCloseToken);
ResolveArgValues(argValues, BfResolveArgFlag_DeferParamEval); ////
}
if (typeInstance == NULL) if (typeInstance == NULL)
{ {
@ -12821,7 +12911,7 @@ void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr)
mModule->Fail(StrFormat("Only default parameterless constructors can be called on primitive type '%s'", mModule->TypeToString(resolvedTypeRef).c_str()), objCreateExpr->mTypeRef); mModule->Fail(StrFormat("Only default parameterless constructors can be called on primitive type '%s'", mModule->TypeToString(resolvedTypeRef).c_str()), objCreateExpr->mTypeRef);
} }
} }
else if ((autoComplete != NULL) && (objCreateExpr->mOpenToken != NULL)) else if ((autoComplete != NULL) && (objCreateExpr != NULL) && (objCreateExpr->mOpenToken != NULL))
{ {
auto wasCapturingMethodInfo = autoComplete->mIsCapturingMethodMatchInfo; auto wasCapturingMethodInfo = autoComplete->mIsCapturingMethodMatchInfo;
autoComplete->CheckInvocation(objCreateExpr, objCreateExpr->mOpenToken, objCreateExpr->mCloseToken, objCreateExpr->mCommas); autoComplete->CheckInvocation(objCreateExpr, objCreateExpr->mOpenToken, objCreateExpr->mCloseToken, objCreateExpr->mCommas);
@ -12836,9 +12926,13 @@ void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr)
} }
else if (!resolvedTypeRef->IsFunction()) else if (!resolvedTypeRef->IsFunction())
{ {
MatchConstructor(objCreateExpr->mTypeRef, objCreateExpr, emtpyThis, typeInstance, argValues, false, true); auto refNode = allocNode;
if (objCreateExpr != NULL)
refNode = objCreateExpr->mTypeRef;
MatchConstructor(refNode, objCreateExpr, emtpyThis, typeInstance, argValues, false, true);
} }
mModule->ValidateAllocation(typeInstance, objCreateExpr->mTypeRef); if (objCreateExpr != NULL)
mModule->ValidateAllocation(typeInstance, objCreateExpr->mTypeRef);
prevBindResult.Restore(); prevBindResult.Restore();
@ -12998,7 +13092,7 @@ void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr)
BF_ASSERT(removeStackObjMethod); BF_ASSERT(removeStackObjMethod);
if (removeStackObjMethod) if (removeStackObjMethod)
{ {
mModule->AddDeferredCall(removeStackObjMethod, irArgs, allocTarget.mScopeData, objCreateExpr); mModule->AddDeferredCall(removeStackObjMethod, irArgs, allocTarget.mScopeData, allocNode);
} }
} }
} }
@ -15079,7 +15173,7 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
copiedArgs.push_back(arg); copiedArgs.push_back(arg);
BfSizedArray<BfExpression*> sizedCopiedArgs(copiedArgs); BfSizedArray<BfExpression*> sizedCopiedArgs(copiedArgs);
BfResolvedArgs argValues(&sizedCopiedArgs); BfResolvedArgs argValues(&sizedCopiedArgs);
if (mModule->mParentNodeEntry != NULL) if (mModule->mParentNodeEntry != NULL)
{ {
if (auto invocationExpr = BfNodeDynCast<BfInvocationExpression>(mModule->mParentNodeEntry->mNode)) if (auto invocationExpr = BfNodeDynCast<BfInvocationExpression>(mModule->mParentNodeEntry->mNode))
@ -15088,7 +15182,7 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
argValues.mCommas = &invocationExpr->mCommas; argValues.mCommas = &invocationExpr->mCommas;
argValues.mCloseToken = invocationExpr->mCloseParen; argValues.mCloseToken = invocationExpr->mCloseParen;
} }
} }
BfResolveArgFlags resolveArgsFlags = (BfResolveArgFlags)(BfResolveArgFlag_DeferFixits | BfResolveArgFlag_AllowUnresolvedTypes); BfResolveArgFlags resolveArgsFlags = (BfResolveArgFlags)(BfResolveArgFlag_DeferFixits | BfResolveArgFlag_AllowUnresolvedTypes);
resolveArgsFlags = (BfResolveArgFlags)(resolveArgsFlags | BfResolveArgFlag_DeferParamEval); resolveArgsFlags = (BfResolveArgFlags)(resolveArgsFlags | BfResolveArgFlag_DeferParamEval);

View file

@ -18,7 +18,8 @@ enum BfArgFlags
BfArgFlag_ExpectedTypeCast = 0x80, BfArgFlag_ExpectedTypeCast = 0x80,
BfArgFlag_VariableDeclaration = 0x100, BfArgFlag_VariableDeclaration = 0x100,
BfArgFlag_ParamsExpr = 0x200, BfArgFlag_ParamsExpr = 0x200,
BfArgFlag_UninitializedExpr = 0x400 BfArgFlag_UninitializedExpr = 0x400,
BfArgFlag_StringInterpolateFormat = 0x800
}; };
enum BfResolveArgFlags enum BfResolveArgFlags
@ -28,6 +29,7 @@ enum BfResolveArgFlags
BfResolveArgFlag_DeferParamValues = 2, // We still evaluate but don't generate code until the method is selected (for SkipCall support) BfResolveArgFlag_DeferParamValues = 2, // We still evaluate but don't generate code until the method is selected (for SkipCall support)
BfResolveArgFlag_DeferParamEval = 4, BfResolveArgFlag_DeferParamEval = 4,
BfResolveArgFlag_AllowUnresolvedTypes = 8, BfResolveArgFlag_AllowUnresolvedTypes = 8,
BfResolveArgFlag_InsideStringInterpolationAlloc = 0x10
}; };
class BfResolvedArg class BfResolvedArg
@ -91,6 +93,14 @@ public:
mCloseToken = closeToken; mCloseToken = closeToken;
} }
void Init(BfTokenNode* openToken, BfSizedArray<BfExpression*>* args, BfSizedArray<BfTokenNode*>* commas, BfTokenNode* closeToken)
{
mOpenToken = openToken;
mArguments = args;
mCommas = commas;
mCloseToken = closeToken;
}
void Init(const BfSizedArray<BfExpression*>* args) void Init(const BfSizedArray<BfExpression*>* args)
{ {
mOpenToken = NULL; mOpenToken = NULL;
@ -99,7 +109,8 @@ public:
mCloseToken = NULL; mCloseToken = NULL;
} }
void HandleFixits(BfModule* module); void HandleFixits(BfModule* module);
}; };
class BfGenericInferContext class BfGenericInferContext
@ -441,6 +452,7 @@ public:
void InitializedSizedArray(BfSizedArrayType* sizedArrayType, BfTokenNode* openToken, const BfSizedArray<BfExpression*>& values, const BfSizedArray<BfTokenNode*>& commas, BfTokenNode* closeToken, BfTypedValue* receivingValue = NULL); void InitializedSizedArray(BfSizedArrayType* sizedArrayType, BfTokenNode* openToken, const BfSizedArray<BfExpression*>& values, const BfSizedArray<BfTokenNode*>& commas, BfTokenNode* closeToken, BfTypedValue* receivingValue = NULL);
void CheckDotToken(BfTokenNode* tokenNode); void CheckDotToken(BfTokenNode* tokenNode);
void DoMemberReference(BfMemberReferenceExpression* memberRefExpr, BfTypedValue* outCascadeValue); void DoMemberReference(BfMemberReferenceExpression* memberRefExpr, BfTypedValue* outCascadeValue);
void CreateObject(BfObjectCreateExpression* objCreateExpr, BfAstNode* allocNode, BfType* allocType);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -452,6 +464,7 @@ public:
virtual void Visit(BfCaseExpression* caseExpr) override; virtual void Visit(BfCaseExpression* caseExpr) override;
virtual void Visit(BfTypedValueExpression* typedValueExpr) override; virtual void Visit(BfTypedValueExpression* typedValueExpr) override;
virtual void Visit(BfLiteralExpression* literalExpr) override; virtual void Visit(BfLiteralExpression* literalExpr) override;
virtual void Visit(BfStringInterpolationExpression* stringInterpolationExpression) override;
virtual void Visit(BfIdentifierNode* identifierNode) override; virtual void Visit(BfIdentifierNode* identifierNode) override;
virtual void Visit(BfAttributedIdentifierNode* attrIdentifierNode) override; virtual void Visit(BfAttributedIdentifierNode* attrIdentifierNode) override;
virtual void Visit(BfQualifiedNameNode* nameNode) override; virtual void Visit(BfQualifiedNameNode* nameNode) override;

View file

@ -43,7 +43,7 @@ enum BfPopulateType
BfPopulateType_Interfaces, BfPopulateType_Interfaces,
BfPopulateType_Data, BfPopulateType_Data,
BfPopulateType_DataAndMethods, BfPopulateType_DataAndMethods,
BfPopulateType_Full = BfPopulateType_DataAndMethods , BfPopulateType_Full = BfPopulateType_DataAndMethods,
BfPopulateType_Full_Force BfPopulateType_Full_Force
}; };
@ -66,7 +66,8 @@ enum BfEvalExprFlags
BfEvalExprFlags_FieldInitializer = 0x2000, BfEvalExprFlags_FieldInitializer = 0x2000,
BfEvalExprFlags_VariableDeclaration = 0x4000, BfEvalExprFlags_VariableDeclaration = 0x4000,
BfEvalExprFlags_NoAutoComplete = 0x8000, BfEvalExprFlags_NoAutoComplete = 0x8000,
BfEvalExprFlags_AllowNonConst = 0x10000 BfEvalExprFlags_AllowNonConst = 0x10000,
BfEvalExprFlags_StringInterpolateFormat = 0x20000
}; };
enum BfCastFlags enum BfCastFlags

View file

@ -357,7 +357,8 @@ BfParser::BfParser(BfSystem* bfSystem, BfProject* bfProject) : BfSource(bfSystem
mLineStart = 0; mLineStart = 0;
//mCurToken = (BfSyntaxToken)0; //mCurToken = (BfSyntaxToken)0;
mToken = BfToken_None; mToken = BfToken_None;
mSyntaxToken = BfSyntaxToken_None; mSyntaxToken = BfSyntaxToken_None;
mTokenStart = 0; mTokenStart = 0;
mTokenEnd = 0; mTokenEnd = 0;
mLineNum = 0; mLineNum = 0;
@ -1360,7 +1361,7 @@ double BfParser::ParseLiteralDouble()
return strtod(buf, NULL); return strtod(buf, NULL);
} }
void BfParser::NextToken(int endIdx) void BfParser::NextToken(int endIdx, bool outerIsInterpolate)
{ {
auto prevToken = mToken; auto prevToken = mToken;
@ -1372,11 +1373,13 @@ void BfParser::NextToken(int endIdx)
bool isLineStart = true; bool isLineStart = true;
bool isVerbatim = false; bool isVerbatim = false;
int verbatimStart = -1; bool isInterpolate = false;
int stringStart = -1;
while (true) while (true)
{ {
bool setVerbatim = false; bool setVerbatim = false;
bool setInterpolate = false;
uint32 checkTokenHash = 0; uint32 checkTokenHash = 0;
if ((endIdx != -1) && (mSrcIdx >= endIdx)) if ((endIdx != -1) && (mSrcIdx >= endIdx))
@ -1389,6 +1392,15 @@ void BfParser::NextToken(int endIdx)
mTokenEnd = mSrcIdx + 1; mTokenEnd = mSrcIdx + 1;
char c = mSrc[mSrcIdx++]; char c = mSrc[mSrcIdx++];
if (outerIsInterpolate)
{
if (c == '"')
{
mSyntaxToken = BfSyntaxToken_StringQuote;
return;
}
}
if ((mPreprocessorIgnoreDepth > 0) && (endIdx == -1)) if ((mPreprocessorIgnoreDepth > 0) && (endIdx == -1))
{ {
if (c == 0) if (c == 0)
@ -1668,7 +1680,7 @@ void BfParser::NextToken(int endIdx)
case '@': case '@':
setVerbatim = true; setVerbatim = true;
c = mSrc[mSrcIdx]; c = mSrc[mSrcIdx];
if (c == '\"') if ((c == '\"') || (c == '$'))
{ {
setVerbatim = true; setVerbatim = true;
} }
@ -1679,19 +1691,29 @@ void BfParser::NextToken(int endIdx)
else else
{ {
mSyntaxToken = BfSyntaxToken_Identifier; mSyntaxToken = BfSyntaxToken_Identifier;
//mToken = BfToken_At;
//mSyntaxToken = BfSyntaxToken_Token;
//Fail("Keyword, identifier, or string expected after verbatim specifier: @"); // CS1646
return; return;
} }
break; break;
case '$':
setInterpolate = true;
c = mSrc[mSrcIdx];
if ((c == '\"') || (c == '@'))
{
setInterpolate = true;
}
else
Fail("Expected to precede string");
break;
case '"': case '"':
case '\'': case '\'':
{ {
SizedArray<BfBlock*, 4> interpolateExpressions;
String lineHeader; String lineHeader;
String strLiteral; String strLiteral;
char startChar = c; char startChar = c;
bool isMultiline = false; bool isMultiline = false;
int triviaStart = mTriviaStart;
if ((mSrc[mSrcIdx] == '"') && (mSrc[mSrcIdx + 1] == '"')) if ((mSrc[mSrcIdx] == '"') && (mSrc[mSrcIdx + 1] == '"'))
{ {
@ -1982,16 +2004,64 @@ void BfParser::NextToken(int endIdx)
Fail("Unrecognized escape sequence"); Fail("Unrecognized escape sequence");
strLiteral += c; strLiteral += c;
} }
} }
else else
{
strLiteral += c; strLiteral += c;
if (isInterpolate)
{
if (c == '{')
{
BfBlock* newBlock = mAlloc->Alloc<BfBlock>();
mTokenStart = mSrcIdx - 1;
mTriviaStart = mTokenStart;
mTokenEnd = mTokenStart + 1;
mToken = BfToken_LBrace;
newBlock->mOpenBrace = (BfTokenNode*)CreateNode();
newBlock->Init(this);
ParseBlock(newBlock, 1, true);
if (mToken == BfToken_RBrace)
{
newBlock->mCloseBrace = (BfTokenNode*)CreateNode();
newBlock->SetSrcEnd(mSrcIdx);
strLiteral += "}";
}
else if ((mSyntaxToken == BfSyntaxToken_EOF) || (mSyntaxToken == BfSyntaxToken_StringQuote))
{
mSrcIdx--;
mPassInstance->FailAfterAt("Expected '}'", mSourceData, newBlock->GetSrcEnd() - 1);
}
mInAsmBlock = false;
interpolateExpressions.Add(newBlock);
}
else if (c == '}')
{
if (!interpolateExpressions.IsEmpty())
{
auto block = interpolateExpressions.back();
if (block->mCloseBrace == NULL)
{
mTokenStart = mSrcIdx - 1;
mTriviaStart = mTokenStart;
mTokenEnd = mTokenStart + 1;
mToken = BfToken_RBrace;
block->mCloseBrace = (BfTokenNode*)CreateNode();
block->SetSrcEnd(mSrcIdx);
}
}
}
}
}
} }
if (isVerbatim) if (stringStart != -1)
{ {
mTokenStart--; mTokenStart = stringStart;
} stringStart = -1;
}
mTriviaStart = triviaStart;
mTokenEnd = mSrcIdx; mTokenEnd = mSrcIdx;
mSyntaxToken = BfSyntaxToken_Literal; mSyntaxToken = BfSyntaxToken_Literal;
if (startChar == '\'') if (startChar == '\'')
@ -2046,6 +2116,20 @@ void BfParser::NextToken(int endIdx)
mLiteral.mTypeCode = BfTypeCode_CharPtr; mLiteral.mTypeCode = BfTypeCode_CharPtr;
mLiteral.mString = strLiteralPtr; mLiteral.mString = strLiteralPtr;
} }
if (isInterpolate)
{
auto interpolateExpr = mAlloc->Alloc<BfStringInterpolationExpression>();
interpolateExpr->mString = mLiteral.mString;
interpolateExpr->mTriviaStart = mTriviaStart;
interpolateExpr->mSrcStart = mTokenStart;
interpolateExpr->mSrcEnd = mSrcIdx;
BfSizedArrayInitIndirect(interpolateExpr->mExpressions, interpolateExpressions, mAlloc);
mGeneratedNode = interpolateExpr;
mSyntaxToken = BfSyntaxToken_GeneratedNode;
mToken = BfToken_None;
}
return; return;
} }
break; break;
@ -3098,8 +3182,11 @@ void BfParser::NextToken(int endIdx)
((c >= 'a') && (c <= 'z')) || ((c >= 'a') && (c <= 'z')) ||
(c == '_')) (c == '_'))
{ {
if (isVerbatim) if (stringStart != -1)
mTokenStart = verbatimStart; {
mTokenStart = stringStart;
stringStart = -1;
}
while (true) while (true)
{ {
@ -3163,7 +3250,12 @@ void BfParser::NextToken(int endIdx)
if ((setVerbatim) && (!isVerbatim)) if ((setVerbatim) && (!isVerbatim))
{ {
isVerbatim = true; isVerbatim = true;
verbatimStart = mTokenStart; stringStart = mTokenStart;
}
if ((setInterpolate) && (!isInterpolate))
{
isInterpolate = true;
stringStart = mTokenStart;
} }
} }
} }
@ -3171,7 +3263,7 @@ void BfParser::NextToken(int endIdx)
static int gParseBlockIdx = 0; static int gParseBlockIdx = 0;
static int gParseMemberIdx = 0; static int gParseMemberIdx = 0;
void BfParser::ParseBlock(BfBlock* astNode, int depth) void BfParser::ParseBlock(BfBlock* astNode, int depth, bool isInterpolate)
{ {
gParseBlockIdx++; gParseBlockIdx++;
int startParseBlockIdx = gParseBlockIdx; int startParseBlockIdx = gParseBlockIdx;
@ -3180,6 +3272,8 @@ void BfParser::ParseBlock(BfBlock* astNode, int depth)
SizedArray<BfAstNode*, 32> childArr; SizedArray<BfAstNode*, 32> childArr;
int parenDepth = 0;
while (true) while (true)
{ {
if ((mSyntaxToken == BfSyntaxToken_Token) && (mToken == BfToken_Asm)) if ((mSyntaxToken == BfSyntaxToken_Token) && (mToken == BfToken_Asm))
@ -3190,7 +3284,8 @@ void BfParser::ParseBlock(BfBlock* astNode, int depth)
isAsmBlock = true; isAsmBlock = true;
} }
NextToken(); NextToken(-1, isInterpolate);
if (mPreprocessorIgnoredSectionNode != NULL) if (mPreprocessorIgnoredSectionNode != NULL)
{ {
if (mSyntaxToken != BfSyntaxToken_EOF) if (mSyntaxToken != BfSyntaxToken_EOF)
@ -3256,7 +3351,7 @@ void BfParser::ParseBlock(BfBlock* astNode, int depth)
mInAsmBlock = false; mInAsmBlock = false;
astNode->Add(newBlock); astNode->Add(newBlock);
childArr.push_back(newBlock); childArr.push_back(newBlock);
} }
else if (mToken == BfToken_RBrace) else if (mToken == BfToken_RBrace)
{ {
if (depth == 0) if (depth == 0)
@ -3265,6 +3360,20 @@ void BfParser::ParseBlock(BfBlock* astNode, int depth)
} }
else else
{ {
if (mToken == BfToken_LParen)
parenDepth++;
else if (mToken == BfToken_RParen)
parenDepth--;
if ((isInterpolate) && (parenDepth == 0))
{
if ((mToken == BfToken_Colon) || (mToken == BfToken_Comma))
{
mSrcIdx = mTokenStart;
break;
}
}
astNode->Add(childNode); astNode->Add(childNode);
childArr.push_back(childNode); childArr.push_back(childNode);
@ -3310,6 +3419,8 @@ BfAstNode* BfParser::CreateNode()
mLiteral.mWarnType = 0; mLiteral.mWarnType = 0;
return bfLiteralExpression; return bfLiteralExpression;
} }
case BfSyntaxToken_GeneratedNode:
return mGeneratedNode;
default: break; default: break;
} }

View file

@ -26,6 +26,7 @@ enum BfSyntaxToken
BfSyntaxToken_Literal, BfSyntaxToken_Literal,
BfSyntaxToken_CommentLine, BfSyntaxToken_CommentLine,
BfSyntaxToken_CommentBlock, BfSyntaxToken_CommentBlock,
BfSyntaxToken_GeneratedNode,
BfSyntaxToken_FAILED, BfSyntaxToken_FAILED,
BfSyntaxToken_HIT_END_IDX, BfSyntaxToken_HIT_END_IDX,
BfSyntaxToken_EOF BfSyntaxToken_EOF
@ -105,7 +106,7 @@ class BfParserCache
public: public:
struct LookupEntry struct LookupEntry
{ {
uint64 mHash; uint64 mHash;
String mFileName; String mFileName;
const char* mSrc; const char* mSrc;
int mSrcLength; int mSrcLength;
@ -173,7 +174,8 @@ public:
BfSyntaxToken mSyntaxToken; BfSyntaxToken mSyntaxToken;
int mTriviaStart; // mTriviaStart < mTokenStart when there's leading whitespace int mTriviaStart; // mTriviaStart < mTokenStart when there's leading whitespace
int mTokenStart; int mTokenStart;
int mTokenEnd; int mTokenEnd;
BfAstNode* mGeneratedNode;
BfVariant mLiteral; BfVariant mLiteral;
BfToken mToken; BfToken mToken;
BfPreprocesorIgnoredSectionNode* mPreprocessorIgnoredSectionNode; BfPreprocesorIgnoredSectionNode* mPreprocessorIgnoredSectionNode;
@ -203,7 +205,7 @@ public:
bool IsUnwarnedAt(BfAstNode* node); bool IsUnwarnedAt(BfAstNode* node);
bool SrcPtrHasToken(const char* name); bool SrcPtrHasToken(const char* name);
uint32 GetTokenHash(); uint32 GetTokenHash();
void ParseBlock(BfBlock* astNode, int depth); void ParseBlock(BfBlock* astNode, int depth, bool isInterpolate = false);
double ParseLiteralDouble(); double ParseLiteralDouble();
void AddErrorNode(int startIdx, int endIdx); void AddErrorNode(int startIdx, int endIdx);
BfCommentKind GetCommentKind(int startIdx); BfCommentKind GetCommentKind(int startIdx);
@ -223,7 +225,7 @@ public:
void SetSource(const char* data, int length); void SetSource(const char* data, int length);
void MoveSource(const char* data, int length); // Takes ownership of data ptr void MoveSource(const char* data, int length); // Takes ownership of data ptr
void RefSource(const char* data, int length); void RefSource(const char* data, int length);
void NextToken(int endIdx = -1); void NextToken(int endIdx = -1, bool outerIsInterpolate = false);
BfAstNode* CreateNode(); BfAstNode* CreateNode();
void Parse(BfPassInstance* passInstance); void Parse(BfPassInstance* passInstance);

View file

@ -1163,6 +1163,14 @@ void BfPrinter::Visit(BfLiteralExpression* literalExpr)
WriteSourceString(literalExpr); WriteSourceString(literalExpr);
} }
void BfPrinter::Visit(BfStringInterpolationExpression* stringInterpolationExpression)
{
Visit(stringInterpolationExpression->ToBase());
String str;
stringInterpolationExpression->ToString(str);
Write(str);
}
void BfPrinter::Visit(BfIdentifierNode* identifierNode) void BfPrinter::Visit(BfIdentifierNode* identifierNode)
{ {
Visit(identifierNode->ToBase()); Visit(identifierNode->ToBase());

View file

@ -138,6 +138,7 @@ public:
virtual void Visit(BfEmptyStatement* emptyStmt) override; virtual void Visit(BfEmptyStatement* emptyStmt) override;
virtual void Visit(BfTokenNode* tokenNode) override; virtual void Visit(BfTokenNode* tokenNode) override;
virtual void Visit(BfLiteralExpression* literalExpr) override; virtual void Visit(BfLiteralExpression* literalExpr) override;
virtual void Visit(BfStringInterpolationExpression* stringInterpolationExpression) override;
virtual void Visit(BfIdentifierNode* identifierNode) override; virtual void Visit(BfIdentifierNode* identifierNode) override;
virtual void Visit(BfQualifiedNameNode* nameNode) override; virtual void Visit(BfQualifiedNameNode* nameNode) override;
virtual void Visit(BfThisExpression* thisExpr) override; virtual void Visit(BfThisExpression* thisExpr) override;

View file

@ -1427,6 +1427,15 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
AssertCurrentNode(node); AssertCurrentNode(node);
if (auto interpolateExpr = BfNodeDynCastExact<BfStringInterpolationExpression>(node))
{
for (auto block : interpolateExpr->mExpressions)
{
HandleBlock(block, true);
}
return interpolateExpr;
}
if ((createExprFlags & (CreateExprFlags_AllowVariableDecl | CreateExprFlags_PermissiveVariableDecl)) != 0) if ((createExprFlags & (CreateExprFlags_AllowVariableDecl | CreateExprFlags_PermissiveVariableDecl)) != 0)
{ {
bool isLocalVariable = false; bool isLocalVariable = false;
@ -1712,6 +1721,8 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
bool isDelegateBind = false; bool isDelegateBind = false;
bool isLambdaBind = false; bool isLambdaBind = false;
bool isBoxing = false; bool isBoxing = false;
auto nextNode = mVisitorPos.GetNext(); auto nextNode = mVisitorPos.GetNext();
if (auto nextTokenNode = BfNodeDynCast<BfTokenNode>(nextNode)) if (auto nextTokenNode = BfNodeDynCast<BfTokenNode>(nextNode))
{ {
@ -1735,8 +1746,20 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
mVisitorPos.mReadPos--; mVisitorPos.mReadPos--;
} }
} }
if (auto interpExpr = BfNodeDynCastExact<BfStringInterpolationExpression>(nextNode))
{
mVisitorPos.MoveNext();
auto nextInterpExpr = CreateExpression(nextNode);
BF_ASSERT(nextInterpExpr == interpExpr);
if (isBoxing) interpExpr->mAllocNode = allocNode;
interpExpr->mTriviaStart = allocNode->mTriviaStart;
interpExpr->mSrcStart = allocNode->mSrcStart;
exprLeft = interpExpr;
}
else if (isBoxing)
{ {
auto boxExpr = mAlloc->Alloc<BfBoxExpression>(); auto boxExpr = mAlloc->Alloc<BfBoxExpression>();
ReplaceNode(allocNode, boxExpr); ReplaceNode(allocNode, boxExpr);

View file

@ -393,6 +393,20 @@ void BfSourceClassifier::Visit(BfLiteralExpression* literalExpr)
SetElementType(literalExpr, BfSourceElementType_Literal); SetElementType(literalExpr, BfSourceElementType_Literal);
} }
void BfSourceClassifier::Visit(BfStringInterpolationExpression* stringInterpolationExpression)
{
HandleLeafNode(stringInterpolationExpression);
Visit(stringInterpolationExpression->ToBase());
SetElementType(stringInterpolationExpression, BfSourceElementType_Literal);
VisitChild(stringInterpolationExpression->mAllocNode);
for (auto& expr : stringInterpolationExpression->mExpressions)
{
VisitChild(expr);
}
}
void BfSourceClassifier::Visit(BfTokenNode* tokenNode) void BfSourceClassifier::Visit(BfTokenNode* tokenNode)
{ {
HandleLeafNode(tokenNode); HandleLeafNode(tokenNode);

View file

@ -109,6 +109,7 @@ public:
virtual void Visit(BfGenericInstanceTypeRef* typeRef) override; virtual void Visit(BfGenericInstanceTypeRef* typeRef) override;
virtual void Visit(BfLocalMethodDeclaration * methodDecl) override; virtual void Visit(BfLocalMethodDeclaration * methodDecl) override;
virtual void Visit(BfLiteralExpression* literalExpr) override; virtual void Visit(BfLiteralExpression* literalExpr) override;
virtual void Visit(BfStringInterpolationExpression* stringInterpolationExpression) override;
virtual void Visit(BfTokenNode* tokenNode) override; virtual void Visit(BfTokenNode* tokenNode) override;
virtual void Visit(BfInvocationExpression* invocationExpr) override; virtual void Visit(BfInvocationExpression* invocationExpr) override;
virtual void Visit(BfIndexerExpression* indexerExpr) override; virtual void Visit(BfIndexerExpression* indexerExpr) override;

View file

@ -0,0 +1,24 @@
using System;
namespace Tests
{
class Strings
{
static void FormatString(String outString, String format, params Object[] args)
{
outString.AppendF(format, params args);
}
[Test]
public static void TestBasics()
{
var str0 = scope $@"AB\C";
Test.Assert(str0 == "AB\\C");
var str1 = scope @$"\A{100+200}B{100+200:X}";
Test.Assert(str1 == "\\A300B12C");
var str2 = scope String();
FormatString(str2, $"\a{200+300}B{200+300:X}");
Test.Assert(str2 == "\a500B1F4");
}
}
}