Skip to content

Commit

Permalink
#62 Implement conditional statement "if", comparison function "$(=", …
Browse files Browse the repository at this point in the history
…and "FileNameExt"

property in TestLexers to allow varying lexer properties over different files.
  • Loading branch information
nyamatongwe committed Mar 15, 2022
1 parent ac76206 commit 1499a42
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 13 deletions.
5 changes: 5 additions & 0 deletions doc/LexillaHistory.html
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,11 @@ <h3>
Released 9 February 2022.
</li>
<li>
Implement conditional statement "if", comparison function "$(=", and "FileNameExt"
property in TestLexers to allow varying lexer properties over different files.
<a href="https://github.com/ScintillaOrg/lexilla/issues/62">Issue #62</a>.
</li>
<li>
Add LexAccessor::BufferStyleAt to retrieve style values to simplify logic and
improve performance.
<a href="https://github.com/ScintillaOrg/lexilla/issues/54">Issue #54</a>.
Expand Down
8 changes: 6 additions & 2 deletions test/README
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,12 @@ Other settings are treated as lexer or folder properties and forwarded to the le

It is often necessary to set 'fold' in SciTE.properties to cause folding.

If there is a need to test additional configurations of keywords or properties then
create another subdirectory with the different settings in a new SciTE.properties.
Properties can be set for a particular file with an "if $(=" expression like so:
if $(= $(FileNameExt);HeaderEOLFill_1.md)
lexer.markdown.header.eolfill=1

More complex tests with additional configurations of keywords or properties can be performed
by creating another subdirectory with the different settings in a new SciTE.properties.

There is some support for running benchmarks on lexers and folders. The properties
testlexers.repeat.lex and testlexers.repeat.fold specify the number of times example
Expand Down
115 changes: 104 additions & 11 deletions test/TestLexers.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ namespace {
constexpr std::string_view suffixStyled = ".styled";
constexpr std::string_view suffixFolded = ".folded";

constexpr std::string_view prefixIf = "if ";
constexpr std::string_view prefixMatch = "match ";
constexpr std::string_view prefixEqual = "= ";
constexpr std::string_view prefixComment = "#";

std::string ReadFile(std::filesystem::path path) {
std::ifstream ifs(path, std::ios::binary);
std::string content((std::istreambuf_iterator<char>(ifs)),
Expand Down Expand Up @@ -94,12 +99,106 @@ std::pair<std::string, std::string> MarkedAndFoldedDocument(const Scintilla::IDo
return { MarkedDocument(pdoc), FoldedDocument(pdoc) };
}

std::vector<std::string> StringSplit(const std::string_view &text, int separator) {
std::vector<std::string> vs(text.empty() ? 0 : 1);
for (std::string_view::const_iterator it = text.begin(); it != text.end(); ++it) {
if (*it == separator) {
vs.push_back(std::string());
} else {
vs.back() += *it;
}
}
return vs;
}

static constexpr bool IsSpaceOrTab(char ch) noexcept {
return (ch == ' ') || (ch == '\t');
}

class PropertyMap {

std::string Evaluate(std::string_view text) {
if (text.find(' ') != std::string_view::npos) {
if (text.starts_with(prefixEqual)) {
const std::string_view sExpressions = text.substr(prefixEqual.length());
std::vector<std::string> parts = StringSplit(sExpressions, ';');
if (parts.size() > 1) {
for (size_t part = 1; part < parts.size(); part++) {
if (parts[part] != parts[0]) {
return "0";
}
}
return "1";
}
}
return {};
} else {
std::optional<std::string> value = GetProperty(text);
if (value) {
return *value;
}
return {};
}
}

std::string Expand(std::string withVars) {
constexpr size_t maxVars = 100;
size_t varStart = withVars.rfind("$(");
for (size_t count = 0; (count < maxVars) && (varStart != std::string::npos); count++) {
const size_t varEnd = withVars.find(')', varStart + 2);
if (varEnd == std::string::npos) {
break;
}

const std::string_view whole = withVars;
const std::string_view var = whole.substr(varStart + 2, varEnd - (varStart + 2));
const std::string val = Evaluate(var);

withVars.erase(varStart, varEnd - varStart + 1);
withVars.insert(varStart, val);

varStart = withVars.rfind("$(");
}
return withVars;
}

bool ProcessLine(std::string_view text, bool ifIsTrue) {
// If clause ends with first non-indented line
if (!ifIsTrue && (text.empty() || IsSpaceOrTab(text[0]))) {
return false;
}
ifIsTrue = true;
if (text.starts_with(prefixIf)) {
const std::string value = Expand(std::string(text.substr(prefixIf.length())));
if (value == "0" || value == "") {
ifIsTrue = false;
}
} else if (text.starts_with(prefixMatch)) {
std::optional<std::string> fileNameExt = GetProperty("FileNameExt");
ifIsTrue = fileNameExt == text.substr(prefixMatch.length());
} else {
while (!text.empty() && IsSpaceOrTab(text.at(0))) {
text.remove_prefix(1);
}
if (text.starts_with(prefixComment)) {
return ifIsTrue;
}
const size_t positionEquals = text.find("=");
if (positionEquals != std::string::npos) {
const std::string key(text.substr(0, positionEquals));
const std::string_view value = text.substr(positionEquals + 1);
properties[key] = value;
}
}
return ifIsTrue;
}

public:
using PropMap = std::map<std::string, std::string>;
PropMap properties;

void ReadFromFile(std::filesystem::path path) {
bool ifIsTrue = true;
std::ifstream ifs(path);
std::string line;
std::string logicalLine;
Expand All @@ -112,12 +211,7 @@ class PropertyMap {
if (logicalLine.ends_with("\\")) {
logicalLine.pop_back();
} else {
const size_t positionEquals = logicalLine.find("=");
if (positionEquals != std::string::npos) {
const std::string key = logicalLine.substr(0, positionEquals);
const std::string value = logicalLine.substr(positionEquals + 1);
properties[key] = value;
}
ifIsTrue = ProcessLine(logicalLine, ifIsTrue);
logicalLine.clear();
}
}
Expand Down Expand Up @@ -374,9 +468,7 @@ void SetProperties(Scintilla::ILexer5 *plex, const PropertyMap &propertyMap, std

// Set parameters of lexer
for (auto const &[key, val] : propertyMap.properties) {
if (key.starts_with("#")) {
// Ignore comments
} else if (key.starts_with("lexer.*")) {
if (key.starts_with("lexer.*")) {
// Ignore as processed earlier
} else if (key.starts_with("keywords")) {
// Ignore as processed earlier
Expand Down Expand Up @@ -463,8 +555,6 @@ bool TestFile(const std::filesystem::path &path, const PropertyMap &propertyMap)
}

bool TestDirectory(std::filesystem::path directory, std::filesystem::path basePath) {
PropertyMap properties;
properties.ReadFromFile(directory / "SciTE.properties");
bool success = true;
for (auto &p : std::filesystem::directory_iterator(directory)) {
if (!p.is_directory()) {
Expand All @@ -473,6 +563,9 @@ bool TestDirectory(std::filesystem::path directory, std::filesystem::path basePa
extension != suffixFolded) {
const std::filesystem::path relativePath = p.path().lexically_relative(basePath);
std::cout << "Lexing " << relativePath.string() << '\n';
PropertyMap properties;
properties.properties["FileNameExt"] = p.path().filename().string();
properties.ReadFromFile(directory / "SciTE.properties");
if (!TestFile(p, properties)) {
success = false;
}
Expand Down

0 comments on commit 1499a42

Please sign in to comment.