← Back to file list Raw

src/CLuaPreProcessor.cpp

#include "CLuaPreProcessor.h"
#include "U_String.h"
#include "CScopeExit.h"
#include "U_Log.h"
#include <fstream>
void CLuaPreProcessor::UpdateInternalRanges()
{
Ranges = FindProtectedRanges(CodeString);
FindSkipRanges();
}
void CLuaPreProcessor::FindSkipRanges()
{
if(CodeString.empty()) { return; }
for(size_t i = 0; i < CodeString.size(); ++i)
{
if(IsInProtectedRange(i) || IsInProtectedRange(i + 1) || i + 1 >= CodeString.size()) { continue; }
std::wstring sub = CodeString.substr(i, 2);
if(sub == L"..") { SkipRanges.push_back({ i, i + 1 }); ++i; }
}
}
std::vector<std::pair<size_t, size_t>> CLuaPreProcessor::FindProtectedRanges(const std::wstring& str)
{
if(str.empty()) { return {}; }
enum class State
{
Normal,
InSingleQuote,
InDoubleQuote,
InLongString,
InSingleLineComment,
InMultiLineComment
};
std::vector<std::pair<size_t, size_t>> protectedRanges;
State state = State::Normal;
size_t i = 0;
size_t start = 0;
auto startsWith = [&](const std::wstring& prefix, size_t pos) -> bool
{
return str.compare(pos, prefix.length(), prefix) == 0;
};
while (i < str.length())
{
switch (state)
{
case State::Normal:
if (startsWith(L"--[[", i))
{
state = State::InMultiLineComment;
start = i;
i += 4;
}
else if (startsWith(L"--", i))
{
state = State::InSingleLineComment;
start = i;
i += 2;
}
else if (startsWith(L"[[", i))
{
state = State::InLongString;
start = i;
i += 2;
}
else if (str[i] == L'"')
{
state = State::InDoubleQuote;
start = i++;
}
else if (str[i] == L'\'')
{
state = State::InSingleQuote;
start = i++;
}
else
{
++i;
}
break;
case State::InSingleLineComment:
if (str[i] == L'\n')
{
protectedRanges.emplace_back(start, i);
state = State::Normal;
}
++i;
break;
case State::InMultiLineComment:
if (startsWith(L"]]", i))
{
i += 2;
protectedRanges.emplace_back(start, i);
state = State::Normal;
}
else
{
++i;
}
break;
case State::InSingleQuote:
if (str[i] == L'\\' && i + 1 < str.size())
{
i += 2;
}
else if (str[i] == L'\'')
{
++i;
protectedRanges.emplace_back(start, i);
state = State::Normal;
}
else
{
++i;
}
break;
case State::InDoubleQuote:
if (str[i] == L'\\' && i + 1 < str.size())
{
i += 2;
}
else if (str[i] == L'"')
{
++i;
protectedRanges.emplace_back(start, i);
state = State::Normal;
}
else
{
++i;
}
break;
case State::InLongString:
if (startsWith(L"]]", i))
{
i += 2;
protectedRanges.emplace_back(start, i);
state = State::Normal;
}
else
{
++i;
}
break;
}
}
if (state != State::Normal)
{
protectedRanges.emplace_back(start, str.length());
}
return protectedRanges;
}
bool CLuaPreProcessor::IsInProtectedRange(size_t pos)
{
for (const auto& [start, end] : Ranges)
{
if (pos >= start && pos < end) { return true; }
}
return false;
}
bool CLuaPreProcessor::IsInProtectedRange(size_t rangeStart, size_t rangeEnd)
{
for (const auto& [protectedStart, protectedEnd] : Ranges)
{
if (rangeStart < protectedEnd && rangeEnd > protectedStart)
{
return true;
}
}
return false;
}
bool CLuaPreProcessor::IsInSkipRange(size_t pos)
{
for (const auto& [start, end] : SkipRanges)
{
if (pos >= start && pos < end) { return true; }
}
return false;
}
bool CLuaPreProcessor::IsInSkipRange(size_t rangeStart, size_t rangeEnd)
{
for (const auto& [protectedStart, protectedEnd] : SkipRanges)
{
if (rangeStart < protectedEnd && rangeEnd > protectedStart)
{
return true;
}
}
return false;
}
void CLuaPreProcessor::UpdateInternalDirectives()
{
Directives = FindDirectives(CodeString);
}
CLuaPreProcessor::DirectiveResult CLuaPreProcessor::FindDirectives(const std::wstring& str)
{
DirectiveResult result;
size_t i = 0;
bool additiveMode = false;
bool atLineStart = true;
std::wstring directiveString;
size_t directiveStart = 0;
auto flushDirective = [&](size_t endPos)
{
if (!directiveString.empty())
{
auto splitted = StringUtils::split_str(directiveString, L' ');
if (!splitted.empty())
{
std::wstring merged_arg;
if (splitted.size() > 1)
{
merged_arg = StringUtils::merge_arg(splitted, 1, splitted.size() - 1);
}
result.directives.insert({ splitted[0], merged_arg });
result.directiveRanges.emplace_back(directiveStart, endPos);
}
directiveString.clear();
}
additiveMode = false;
};
while (i < str.length())
{
if (str[i] == L'\n')
{
flushDirective(i + 1);
atLineStart = true;
++i;
continue;
}
if (IsInProtectedRange(i, i + 1))
{
flushDirective(i);
++i;
continue;
}
if (atLineStart)
{
size_t lineStart = i;
while (i < str.length() && (str[i] == L' ' || str[i] == L'\t'))
{
++i;
}
if (i < str.length() && str[i] == L'#')
{
additiveMode = true;
directiveString.clear();
directiveStart = lineStart;
directiveString += str[i++];
atLineStart = false;
continue;
}
atLineStart = false;
}
if (additiveMode)
{
directiveString += str[i];
}
++i;
}
flushDirective(i);
return result;
}
std::wstring CLuaPreProcessor::DeleteDirectives()
{
std::wstring result;
size_t current = 0;
for (const auto& [start, end] : Directives.directiveRanges)
{
if (current < start)
{
result.append(CodeString.begin() + current, CodeString.begin() + start);
}
current = end;
}
if (current < CodeString.size())
{
result.append(CodeString.begin() + current, CodeString.end());
}
return result;
}
std::wstring CLuaPreProcessor::GetIdentifierBackwards(const std::wstring& text, size_t pos)
{
int brackets = 0;
bool dotFound = false;
std::wstring identifier;
size_t i = pos;
auto isSpace = [](wchar_t _char) -> bool { return _char == L'\t' || _char == L' ' || _char == L'\n'; };
auto isBracket = [](wchar_t _char) -> bool
{
return _char == L'.' || _char == L':' || _char == L'(' ||
_char == L')' || _char == L'[' || _char == L']';
};
for(;;)
{
if(IsInProtectedRange(i)) { break; }
if((text.at(i) == L'\t' || text.at(i) == L' ' || text.at(i) == L'\n') && (brackets == 0 && !dotFound) && !identifier.empty() && !isBracket(identifier.front()))
{
bool furtherWillBeBracket = false;
bool expectWidening = isBracket(identifier.front());
if(!expectWidening)
{
break;
}
size_t j = i;
for(;;)
{
if(text.at(j) == L'\t' || text.at(j) == L' ' || text.at(j) == L'\n') { if(j == 0) { break; } --j; continue; }
if(text.at(j) == L'.' || text.at(j) == L':' || text.at(j) == L'(' ||
text.at(j) == L')' || text.at(j) == L'[' || text.at(j) == L']')
{
furtherWillBeBracket = true;
break;
}
else
{
break;
}
if(j == 0) { break; }
--j;
}
if(!furtherWillBeBracket)
{
break;
}
}
else
{
identifier.insert(identifier.begin(), text.at(i));
if(IsInSkipRange(i)) { --i; continue; }
if(text.at(i) == L']' || text.at(i) == L')')
{
++brackets;
}
else if(text.at(i) == L'[' || text.at(i) == L'(')
{
--brackets;
}
else if(text.at(i) == '.' || text.at(i) == L':')
{
dotFound = true;
}
else
{
dotFound = false;
}
}
--i;
}
return identifier;
}
std::wstring CLuaPreProcessor::GetIdentifierForward(const std::wstring& text, size_t pos)
{
int brackets = 0;
bool dotFound = false;
std::wstring identifier;
size_t i = pos;
auto isDot = [&text](size_t pos) -> bool { return text.at(pos) == L'.' && (pos == text.size() - 1 || text.at(pos + 1) != L'.'); };
auto isSpace = [](wchar_t _char) -> bool { return _char == L'\t' || _char == L' ' || _char == L'\n'; };
auto isBracket = [](wchar_t _char) -> bool
{
return _char == L'.' || _char == L':' || _char == L'(' ||
_char == L')' || _char == L'[' || _char == L']';
};
for(;;)
{
if(IsInProtectedRange(i)) { break; }
if((text.at(i) == L'\t' || text.at(i) == L' ' || text.at(i) == L'\n') && (brackets == 0 && !dotFound) && !identifier.empty() && !isBracket(identifier.front()))
{
bool furtherWillBeBracket = false;
size_t j = i;
for(;;)
{
if(text.at(j) == L'\t' || text.at(j) == L' ' || text.at(j) == L'\n') { if(j >= text.size()) { break; } ++j; continue; }
if(isDot(j) || text.at(j) == L':' || text.at(j) == L'(' ||
text.at(j) == L')' || text.at(j) == L'[' || text.at(j) == L']')
{
furtherWillBeBracket = true;
break;
}
else
{
break;
}
if(j >= text.size()) { break; }
++j;
}
if(!furtherWillBeBracket)
{
break;
}
}
else
{
identifier.push_back(text.at(i));
if(IsInSkipRange(i)) { ++i; continue; }
if(text.at(i) == L']' || text.at(i) == L')')
{
--brackets;
}
else if(text.at(i) == L'[' || text.at(i) == L'(')
{
++brackets;
}
else if(isDot(i) || text.at(i) == L':')
{
dotFound = true;
}
else
{
dotFound = false;
}
}
++i;
}
return identifier;
}
std::wstring CLuaPreProcessor::ApplyCustomOperators(const std::wstring& source)
{
if(source.empty()) { return {}; }
std::wstring ret = source;
std::vector<OperatorPattern> patterns =
{
{L"+=", L"+"},
{L"-=", L"-"},
{L"*=", L"*"},
{L"/=", L"/"},
{L"%=", L"%"}
};
size_t i = 0;
while (i < ret.length() - 1)
{
if (!IsInProtectedRange(i, i + 2))
{
std::wstring oper;
if(i < ret.size() - 2)
{
oper = ret.substr(i, 2);
}
auto it = std::find_if(patterns.begin(), patterns.end(), [&oper](const OperatorPattern& p) { return p.symbol == oper; });
if (it != patterns.end())
{
std::wstring backwards = GetIdentifierBackwards(source, i - 1);
std::wstring forward = GetIdentifierForward(source, i + 2);
std::wstring finalString = backwards + L" = " + backwards + L" " + it->replacementOp + L" " + forward;
auto isSpace = [](wchar_t _char) -> bool { return (_char == L' ' || _char == L'\t'); };
auto newEnd = std::unique(finalString.begin(), finalString.end(), [&isSpace](wchar_t a, wchar_t b) { return isSpace(a) && isSpace(b); });
finalString.erase(newEnd, finalString.end());
Log::Instance() << "Found identifiers:\nBackward: \"" << backwards << "\"\nForward: \"" << forward <<
"\"\nFinal string: \"" << finalString << "\"\n\n";
++i;
continue;
size_t lhs_end = i;
size_t lhs_start = lhs_end;
while (lhs_start > 0 && iswspace(ret[lhs_start - 1]))
{
--lhs_start;
}
while (lhs_start > 0 && (iswalnum(ret[lhs_start - 1]) || ret[lhs_start - 1] == L'_'))
{
--lhs_start;
}
//auto range = FindLeftExpression(ret, i);
//std::wstring lhs = ret.substr(range.first, range.second - range.first);
std::wstring lhs = ret.substr(lhs_start, lhs_end - lhs_start);
size_t rhs_start = i + 2;
while (rhs_start < ret.length() && iswspace(ret[rhs_start]))
{
++rhs_start;
}
size_t rhs_end = rhs_start;
while (rhs_end < ret.length() && (iswalnum(ret[rhs_end]) || ret[rhs_end] == L'_'))
{
++rhs_end;
}
std::wstring rhs = ret.substr(rhs_start, rhs_end - rhs_start);
std::wstring replacement = lhs + L" = " + lhs + L" " + it->replacementOp + L" " + rhs;
//auto isSpace = [](wchar_t _char) -> bool { return (_char == L' ' || _char == L'\t'); };
//auto newEnd = std::unique(replacement.begin(), replacement.end(), [&isSpace](wchar_t a, wchar_t b) { return isSpace(a) && isSpace(b); });
//replacement.erase(newEnd, replacement.end());
ret.replace(lhs_start, rhs_end - lhs_start, replacement);
i = lhs_start + replacement.length();
continue;
}
}
++i;
}
return ret;
}
bool CLuaPreProcessor::IsDirectivePresent(const std::wstring& name)
{
return Directives.directives.find(name) != Directives.directives.end();
}
std::wstring CLuaPreProcessor::GetDirectiveValue(const std::wstring& name)
{
auto it = Directives.directives.find(name);
return it != Directives.directives.end() ? it->second : std::wstring();
}
bool CLuaPreProcessor::IsDirectivePresent(const std::string& name) { return IsDirectivePresent(StringUtils::StrToWstr(name)); }
std::wstring CLuaPreProcessor::GetDirectiveValue(const std::string& name) { return GetDirectiveValue(StringUtils::StrToWstr(name)); }
void CLuaPreProcessor::ProcessText(const std::wstring& str)
{
size_t oldLines = 0;
CScopeExit _scope([&oldLines, this]()
{
size_t newLines = 0;
for(const wchar_t& _char : CodeString) { if(_char == L'\n') { newLines++; } }
Log::Instance() << "CLuaPreProcessor::oldLines: " << oldLines << "\nCLuaPreProcessor::newLines: " << newLines << Log::Endl;
});
CodeString = str;
StringUtils::replace<std::wstring>(CodeString, L"\r\n", L"\n");
for(const wchar_t& _char : CodeString) { if(_char == L'\n') { oldLines++; } }
UpdateInternalRanges();
UpdateInternalDirectives();
if(!Directives.directives.empty())
{
CodeString = DeleteDirectives();
UpdateInternalRanges();
}
if(IsDirectivePresent(L"#nopreprocess")) { return; }
if(IsDirectivePresent(L"#disable"))
{
CodeString.clear();
UpdateInternalRanges();
return;
}
CodeString = ApplyCustomOperators(CodeString);
UpdateInternalRanges();
}
void CLuaPreProcessor::Dump(const std::wstring& outName)
{
std::wostream* output;
std::wofstream file;
CScopeExit guard([&file]() { if(file.is_open()) { file.close(); } });
if(outName == L"$cerr")
{
output = &std::wcerr;
}
else if(outName == L"$cout")
{
output = &std::wcout;
}
else
{
file.open(outName.c_str());
if(!file.is_open()) { return; }
}
(*output) << L"-- Processed code: --\n\n" << CodeString << L"\n\n---------------------\n\n-- Protected ranges: --\n\n";
for(auto& pair : Ranges)
{
(*output) << pair.first << L" : " << pair.second << L'\n';
}
(*output) << L"\n-----------------------\n\n-- Directives list: --\n\n";
size_t index = 0;
for(auto& dirct : Directives.directives)
{
(*output) << L'[' << index << L"] \"" << dirct.first << L"\": \"" << dirct.second << L"\"\n";
index++;
}
(*output) << L"\n-----------------------\n\n-- Directives ranges: --\n\n";
index = 0;
for(auto& dirct : Directives.directiveRanges)
{
(*output) << L'[' << index << L"] " << dirct.first << L" : " << dirct.second << L'\n';
index++;
}
(*output) << L"\n-----------------------\n";
}