#include "CLuaPreProcessor.h" #include "U_String.h" #include "CScopeExit.h" #include "U_Log.h" #include 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> CLuaPreProcessor::FindProtectedRanges(const std::wstring& str) { if(str.empty()) { return {}; } enum class State { Normal, InSingleQuote, InDoubleQuote, InLongString, InSingleLineComment, InMultiLineComment }; std::vector> 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 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(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"; }