mirror of
https://github.com/vczh-libraries/Release.git
synced 2026-03-24 17:03:49 +08:00
442 lines
11 KiB
C++
442 lines
11 KiB
C++
#include "WfMergeCpp.h"
|
|
#include <VlppRegex.h>
|
|
|
|
namespace vl
|
|
{
|
|
namespace workflow
|
|
{
|
|
namespace cppcodegen
|
|
{
|
|
using namespace collections;
|
|
using namespace stream;
|
|
using namespace regex;
|
|
|
|
/***********************************************************************
|
|
MergeCpp
|
|
***********************************************************************/
|
|
|
|
WString RemoveSpacePrefix(const WString& s)
|
|
{
|
|
for (vint i = 0; i < s.Length(); i++)
|
|
{
|
|
if (s[i] != L' '&&s[i] != L'\t')
|
|
{
|
|
return s.Sub(i, s.Length() - i);
|
|
}
|
|
}
|
|
return WString::Empty;
|
|
}
|
|
|
|
const vint NORMAL = 0;
|
|
const vint WAIT_HEADER = 1;
|
|
const vint WAIT_OPEN = 2;
|
|
const vint WAIT_CLOSE = 3;
|
|
const vint USER_CONTENT = 4;
|
|
const vint UNUSED_USER_CONTENT = 5;
|
|
|
|
template<typename TCallback>
|
|
void ProcessCppContent(const WString& code, const TCallback& callback)
|
|
{
|
|
Regex regexUserContentBegin(L"/.*?(?/{)?///* USER_CONTENT_BEGIN/((<name>[^)]*?)/) /*//");
|
|
vint _name = regexUserContentBegin.CaptureNames().IndexOf(L"name");
|
|
|
|
vint state = NORMAL;
|
|
vint counter = 0;
|
|
WString previousContent;
|
|
|
|
StringReader reader(code);
|
|
while (!reader.IsEnd())
|
|
{
|
|
auto line = reader.ReadLine();
|
|
if (reader.IsEnd() && line == L"")
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (line == L"// UNUSED_USER_CONTENT:")
|
|
{
|
|
state = UNUSED_USER_CONTENT;
|
|
}
|
|
|
|
if (state == UNUSED_USER_CONTENT)
|
|
{
|
|
callback(state, state, line, line);
|
|
}
|
|
else
|
|
{
|
|
auto content = RemoveSpacePrefix(line);
|
|
auto previousState = state;
|
|
switch (state)
|
|
{
|
|
case NORMAL:
|
|
if (auto match = regexUserContentBegin.MatchHead(content))
|
|
{
|
|
content = L"USERIMPL(/* " + match->Groups()[_name][0].Value() + L" */)";
|
|
if (match->Captures().Count() > 0)
|
|
{
|
|
content += previousContent;
|
|
}
|
|
state = USER_CONTENT;
|
|
}
|
|
else if (INVLOC.StartsWith(content, L"USERIMPL(",Locale::None))
|
|
{
|
|
state = WAIT_HEADER;
|
|
}
|
|
break;
|
|
case WAIT_HEADER:
|
|
state = WAIT_OPEN;
|
|
break;
|
|
case WAIT_OPEN:
|
|
if (INVLOC.StartsWith(content, L"{", Locale::None))
|
|
{
|
|
state = WAIT_CLOSE;
|
|
}
|
|
break;
|
|
case WAIT_CLOSE:
|
|
if (INVLOC.StartsWith(content, L"{", Locale::None))
|
|
{
|
|
counter++;
|
|
}
|
|
else if (INVLOC.StartsWith(content, L"}", Locale::None))
|
|
{
|
|
if (counter == 0)
|
|
{
|
|
state = NORMAL;
|
|
}
|
|
else
|
|
{
|
|
counter--;
|
|
}
|
|
}
|
|
break;
|
|
case USER_CONTENT:
|
|
if (INVLOC.EndsWith(content, L"/* USER_CONTENT_END() */", Locale::None))
|
|
{
|
|
state = NORMAL;
|
|
}
|
|
break;
|
|
}
|
|
callback(previousState, state, line, content);
|
|
}
|
|
previousContent = RemoveSpacePrefix(line);
|
|
}
|
|
}
|
|
|
|
template<typename TCallback>
|
|
void SplitCppContent(const WString& code, Dictionary<WString, WString>& userContents, Dictionary<WString, WString>& userContentsFull, const TCallback& callback)
|
|
{
|
|
WString name;
|
|
WString userImpl;
|
|
WString userImplFull;
|
|
ProcessCppContent(code, [&](vint previousState, vint state, const WString& line, const WString& content)
|
|
{
|
|
if (state == UNUSED_USER_CONTENT)
|
|
{
|
|
callback(line);
|
|
}
|
|
else
|
|
{
|
|
switch (previousState)
|
|
{
|
|
case NORMAL:
|
|
switch (state)
|
|
{
|
|
case WAIT_HEADER:
|
|
case USER_CONTENT:
|
|
name = content;
|
|
userImpl = L"";
|
|
userImplFull = L"";
|
|
break;
|
|
}
|
|
break;
|
|
case WAIT_HEADER:
|
|
name += content;
|
|
break;
|
|
case WAIT_CLOSE:
|
|
case USER_CONTENT:
|
|
switch (state)
|
|
{
|
|
case WAIT_CLOSE:
|
|
case USER_CONTENT:
|
|
userImpl += line + L"\r\n";
|
|
break;
|
|
case NORMAL:
|
|
userImplFull += L"//" + line + L"\r\n";
|
|
userContents.Add(name, userImpl);
|
|
userContentsFull.Add(name, userImplFull);
|
|
name = L"";
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (name != L"")
|
|
{
|
|
userImplFull += L"//" + line + L"\r\n";
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
MergeCppMultiPlatformException::MergeCppMultiPlatformException(vint _row32, vint _column32, vint _row64, vint _column64)
|
|
:Exception(L"The difference at "
|
|
L"x86 file(row:" + itow(_row32 + 1) + L", column:" + itow(_column32 + 1) + L") and "
|
|
L"x64 file(row:" + itow(_row64 + 1) + L", column:" + itow(_column64 + 1) + L") are not "
|
|
L"\"vint32_t\" and \"vint64_t\", "
|
|
L"\"vuint32_t\" and \"vuint64_t\", "
|
|
L"\"<number>\" and \"<number>L\", "
|
|
L"\"<number>\" and \"<number>UL\".")
|
|
, row32(_row32)
|
|
, column32(_column32)
|
|
, row64(_row64)
|
|
, column64(_column64)
|
|
{
|
|
}
|
|
|
|
void CountRowAndColumn(const wchar_t* start, const wchar_t* reading, vint& row, vint& column)
|
|
{
|
|
row = 0;
|
|
column = 0;
|
|
while (start < reading)
|
|
{
|
|
if (*start++ == L'\n')
|
|
{
|
|
row++;
|
|
column = 0;
|
|
}
|
|
else
|
|
{
|
|
column++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
WString MergeCppMultiPlatform(const WString& code32, const WString& code64)
|
|
{
|
|
static wchar_t stringCast32[] = L"static_cast<::vl::vint32_t>(";
|
|
const vint lengthCast32 = sizeof(stringCast32) / sizeof(*stringCast32) - 1;
|
|
|
|
static wchar_t stringCast64[] = L"static_cast<::vl::vint64_t>(";
|
|
const vint lengthCast64 = sizeof(stringCast64) / sizeof(*stringCast64) - 1;
|
|
|
|
return GenerateToStream([&](StreamWriter& writer)
|
|
{
|
|
const wchar_t* reading32 = code32.Buffer();
|
|
const wchar_t* reading64 = code64.Buffer();
|
|
const wchar_t* start32 = reading32;
|
|
const wchar_t* start64 = reading64;
|
|
while (true)
|
|
{
|
|
vint length = 0;
|
|
while (reading32[length] && reading64[length])
|
|
{
|
|
if (reading32[length] == reading64[length])
|
|
{
|
|
length++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
writer.WriteString(reading32, length);
|
|
reading32 += length;
|
|
reading64 += length;
|
|
|
|
if (*reading32 == 0 && *reading64 == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
#define IS_DIGIT(C) (L'0' <= C && C <= L'9')
|
|
|
|
if (reading32[0] == L'3' && reading32[1] == L'2' && reading64[0] == L'6' && reading64[1] == L'4')
|
|
{
|
|
if (length >= 4)
|
|
{
|
|
if (wcsncmp(reading32 - 4, L"vint32_t", 8) == 0 && wcsncmp(reading64 - 4, L"vint64_t", 8) == 0)
|
|
{
|
|
reading32 += 4;
|
|
reading64 += 4;
|
|
goto NEXT_ROUND;
|
|
}
|
|
}
|
|
if (length >= 5)
|
|
{
|
|
if (wcsncmp(reading32 - 5, L"vuint32_t", 9) == 0 && wcsncmp(reading64 - 5, L"vuint64_t", 9) == 0)
|
|
{
|
|
reading32 += 4;
|
|
reading64 += 4;
|
|
goto NEXT_ROUND;
|
|
}
|
|
}
|
|
}
|
|
else if (reading64[0] == L'L')
|
|
{
|
|
if (reading32[0] == reading64[1] && length >= 1)
|
|
{
|
|
if (IS_DIGIT(reading32[-1]) && !IS_DIGIT(reading32[0]))
|
|
{
|
|
if (IS_DIGIT(reading64[-1]) && !IS_DIGIT(reading64[1]))
|
|
{
|
|
reading64 += 1;
|
|
goto NEXT_ROUND;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (reading64[0] == L'U' && reading64[1] == L'L')
|
|
{
|
|
if (reading32[0] == reading64[2] && length >= 1)
|
|
{
|
|
if (IS_DIGIT(reading32[-1]) && !IS_DIGIT(reading32[0]))
|
|
{
|
|
if (IS_DIGIT(reading64[-1]) && !IS_DIGIT(reading64[2]))
|
|
{
|
|
reading64 += 2;
|
|
goto NEXT_ROUND;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (wcsncmp(reading32, stringCast32, lengthCast32) == 0 && IS_DIGIT(reading32[lengthCast32]) && IS_DIGIT(reading64[0]))
|
|
{
|
|
reading32 += lengthCast32;
|
|
vint digitCount = 0;
|
|
while (IS_DIGIT(reading32[digitCount])) digitCount++;
|
|
if (wcsncmp(reading32, reading64, digitCount) == 0 && reading64[digitCount] == L'L' && reading32[digitCount] == L')')
|
|
{
|
|
writer.WriteString(L"static_cast<::vl::vint>(");
|
|
writer.WriteString(WString::CopyFrom(reading32, digitCount));
|
|
writer.WriteChar(L')');
|
|
reading64 += digitCount + 1;
|
|
reading32 += digitCount + 1;
|
|
goto NEXT_ROUND;
|
|
}
|
|
}
|
|
else if (wcsncmp(reading64, stringCast64, lengthCast64) == 0 && IS_DIGIT(reading64[lengthCast64]) && IS_DIGIT(reading32[0]))
|
|
{
|
|
reading64 += lengthCast64;
|
|
vint digitCount = 0;
|
|
while (IS_DIGIT(reading64[digitCount])) digitCount++;
|
|
if (wcsncmp(reading64, reading32, digitCount) == 0 && reading64[digitCount] == L'L' && reading64[digitCount + 1] == L')')
|
|
{
|
|
writer.WriteString(L"static_cast<::vl::vint>(");
|
|
writer.WriteString(WString::CopyFrom(reading64, digitCount));
|
|
writer.WriteChar(L')');
|
|
reading64 += digitCount + 2;
|
|
reading32 += digitCount;
|
|
goto NEXT_ROUND;
|
|
}
|
|
}
|
|
|
|
{
|
|
vint row32 = 0;
|
|
vint column32 = 0;
|
|
vint row64 = 0;
|
|
vint column64 = 0;
|
|
CountRowAndColumn(start32, reading32, row32, column32);
|
|
CountRowAndColumn(start64, reading64, row64, column64);
|
|
throw MergeCppMultiPlatformException(row32, column32, row64, column64);
|
|
}
|
|
NEXT_ROUND:;
|
|
#undef IS_DIGIT
|
|
}
|
|
});
|
|
}
|
|
|
|
WString MergeCppFileContent(const WString& dst, const WString& src)
|
|
{
|
|
Dictionary<WString, WString> userContents, userContentsFull;
|
|
WString unusedUserContent = GenerateToStream([&](StreamWriter& writer)
|
|
{
|
|
SplitCppContent(dst, userContents, userContentsFull, [&](const WString& line)
|
|
{
|
|
writer.WriteLine(line);
|
|
});
|
|
});
|
|
|
|
WString processedUnusedUserContent = GenerateToStream([&](StreamWriter& writer)
|
|
{
|
|
StringReader reader(unusedUserContent);
|
|
while (!reader.IsEnd())
|
|
{
|
|
auto line = reader.ReadLine();
|
|
if (line != L"// UNUSED_USER_CONTENT:")
|
|
{
|
|
if (INVLOC.StartsWith(line, L"//", Locale::None))
|
|
{
|
|
line = line.Right(line.Length() - 2);
|
|
}
|
|
writer.WriteLine(line);
|
|
}
|
|
}
|
|
});
|
|
|
|
SplitCppContent(processedUnusedUserContent, userContents, userContentsFull, [&](const WString& line) {});
|
|
|
|
return GenerateToStream([&](StreamWriter& writer)
|
|
{
|
|
WString name;
|
|
WString userImpl;
|
|
ProcessCppContent(src, [&](vint previousState, vint state, const WString& line, const WString& content)
|
|
{
|
|
switch (previousState)
|
|
{
|
|
case NORMAL:
|
|
switch (state)
|
|
{
|
|
case WAIT_HEADER:
|
|
case USER_CONTENT:
|
|
name = content;
|
|
userImpl = L"";
|
|
break;
|
|
}
|
|
break;
|
|
case WAIT_HEADER:
|
|
name += content;
|
|
break;
|
|
case WAIT_CLOSE:
|
|
case USER_CONTENT:
|
|
switch (state)
|
|
{
|
|
case WAIT_CLOSE:
|
|
case USER_CONTENT:
|
|
userImpl += line + L"\r\n";
|
|
return;
|
|
case NORMAL:
|
|
{
|
|
vint index = userContents.Keys().IndexOf(name);
|
|
if (index == -1)
|
|
{
|
|
writer.WriteString(userImpl);
|
|
}
|
|
else
|
|
{
|
|
writer.WriteString(userContents.Values()[index]);
|
|
userContentsFull.Remove(name);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
writer.WriteLine(line);
|
|
});
|
|
|
|
if (userContentsFull.Count() > 0)
|
|
{
|
|
writer.WriteLine(L"// UNUSED_USER_CONTENT:");
|
|
for (auto content : userContentsFull.Values())
|
|
{
|
|
writer.WriteString(content);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|