Move criteria.matches() to C to gain back some performance lost to filter prefixes

This commit is contained in:
Jason Perkins 2014-04-08 15:07:09 -04:00
parent 28cfa55886
commit 0f713d5c39
4 changed files with 300 additions and 105 deletions

View File

@ -8,7 +8,7 @@
-- Copyright (c) 2012-2014 Jason Perkins and the Premake project -- Copyright (c) 2012-2014 Jason Perkins and the Premake project
-- --
premake.criteria = {} premake.criteria = criteria
local criteria = premake.criteria local criteria = premake.criteria
@ -43,15 +43,18 @@
end end
local parts = path.wildcards(term) local parts = path.wildcards(term)
local isWildcard = (parts ~= term)
parts = parts:explode(" or ") parts = parts:explode(" or ")
for i, part in ipairs(parts) do for i, part in ipairs(parts) do
if part:startswith("not ") then if part:startswith("not ") then
table.insert(pattern, "not") table.insert(pattern, "not")
table.insert(pattern, part:sub(5)) part = part:sub(5)
else
table.insert(pattern, part)
end end
if isWildcard then
table.insert(pattern, "%%")
end
table.insert(pattern, part)
end end
table.insert(patterns, pattern) table.insert(patterns, pattern)
@ -62,101 +65,3 @@
crit.patterns = patterns crit.patterns = patterns
return crit return crit
end end
---
-- Determine if this criteria is met by the provided list of context terms.
--
-- @param crit
-- The criteria to be tested.
-- @param context
-- The list of context terms to test against, provided as a list of
-- lowercase strings.
-- @return
-- True if all criteria are satisfied by the context.
---
function criteria.matches(crit, context)
-- If the context specifies a filename, I should only match against
-- blocks targeted at that file specifically. This way, files only
-- pick up the settings that a different from the main project.
local filename = context.files
local filematched = false
-- Test one value from the context against a part of a pattern
function testValue(value, part)
if type(value) == "table" then
for i = 1, #value do
if testValue(value[i], part) then
return true
end
end
else
if value and value:match(part) == value then
return true;
end
end
return false
end
-- Test one part of one pattern against the provided context
function testContext(prefix, part, assertion)
if prefix then
local result = testValue(context[prefix], part)
if prefix == "files" and result == assertion then
filematched = true
end
if result then
return assertion
end
else
if filename and assertion and filename:match(part) == filename then
filematched = true
return assertion
end
for prefix, value in pairs(context) do
if testValue(value, part) then
return assertion
end
end
end
return not assertion
end
-- Test an individual pattern in this criteria's list of patterns
function testPattern(pattern)
local n = #pattern
local assertion = true
for i = 1, n do
local part = pattern[i]
if part == "not" then
assertion = false
else
if testContext(pattern.prefix, part, assertion) then
return true
end
assertion = true
end
end
end
-- Iterate the list of patterns and test each in turn
local n = #crit.patterns
for i = 1, n do
local pattern = crit.patterns[i]
if not testPattern(pattern) then
return false
end
end
if filename and not filematched then
return false
end
return true
end

283
src/host/criteria_matches.c Normal file
View File

@ -0,0 +1,283 @@
/**
* \file criteria_matches.c
* \brief Determine if this criteria is met by the provided filter terms.
* \author Copyright (c) 2002-2013 Jason Perkins and the Premake project
*/
#include "premake.h"
#include <string.h>
/*
* return value:match(pattern) == value
*/
static int match(lua_State* L, const char* value, const char* pattern, int wildcard)
{
if (wildcard) {
const char* result;
int matched = 0;
int top = lua_gettop(L);
lua_pushvalue(L, 4);
lua_pushstring(L, value);
lua_pushstring(L, pattern);
lua_call(L, 2, 1);
if (lua_isstring(L, -1)) {
result = lua_tostring(L, -1);
matched = (strcmp(value, result) == 0);
}
lua_settop(L, top);
return matched;
}
else {
return (strcmp(value, pattern) == 0);
}
}
/*
* Compares the value on the top of the stack to the provided
* part, which is a Lua pattern string.
*/
static int testValue(lua_State* L, const char* part, const int wildcard)
{
const char* value;
size_t i, n;
int result;
/*
if type(value) == "table" then
for i = 1, #value do
if testValue(value[i], part) then
return true
end
end
else
if value and value:match(part) == value then
return true;
end
end
*/
if (lua_istable(L, -1)) {
n = lua_objlen(L, -1);
for (i = 1; i <= n; ++i) {
lua_rawgeti(L, -1, i);
result = testValue(L, part, wildcard);
lua_pop(L, 1);
if (result) {
return 1;
}
}
return 0;
}
value = lua_tostring(L, -1);
if (value && match(L, value, part, wildcard)) {
return 1;
}
return 0;
}
/*
* The context is a set of key-value pairs, something like:
* {
* action = "vs2010",
* configurations = "Debug",
* system = "windows",
* files = "/absolute/path/to/hello.cpp",
* -- and so on...
* }
*/
static int testContext(lua_State* L, const char* prefix, const char* part,
const int assertion, const int wildcard,
const char* filename, int* fileMatched)
{
/*
if prefix then
local result = testValue(context[prefix], part, wildcard)
if result == assertion and prefix == "files" then
filematched = true
end
if result then
return assertion
end
else
if filename and assertion and filename:match(part) == filename then
filematched = true
return assertion
end
for prefix, value in pairs(context) do
if testValue(value, part, wildcard) then
return assertion
end
end
end
*/
int result;
if (prefix) {
lua_getfield(L, 2, prefix);
result = testValue(L, part, wildcard);
lua_pop(L, 1);
if (result == assertion && strcmp(prefix, "files") == 0) {
(*fileMatched) = 1;
}
if (result) {
return assertion;
}
}
else {
if (filename && assertion && match(L, filename, part, wildcard)) {
(*fileMatched) = 1;
return assertion;
}
lua_pushnil(L);
while (lua_next(L, 2)) {
if (testValue(L, part, wildcard)) {
lua_pop(L, 2);
return assertion;
}
lua_pop(L, 1);
}
}
return (!assertion);
}
/*
* Patterns are represented as an array of string values.
* "windows" = { "windows" }
* "not windows" = { "not", "windows" }
* "windows or linux" = { "windows", "linux" }
*
* If the patterns is targeted at a specific prefix, that is stored
* as a keyed value.
*
* "files:**.c" = { prefix="files", "**.c" }
*/
static int testPattern(lua_State* L, const char* filename, int* fileMatched)
{
const char* prefix;
const char* part;
size_t i, n;
int assertion = 1;
int wildcard = 0;
int result = 0;
/* prefix = pattern.prefix */
lua_getfield(L, -1, "prefix");
prefix = lua_tostring(L, -1);
/*
for i = 1, #pattern do
part = pattern[i]
if part == "not" then
assertion = false
else
if testContext(pattern.prefix, part, assertion) then
result = true
break
end
assertion = true
end
end
*/
n = lua_objlen(L, -2);
for (i = 1; i <= n; ++i) {
lua_rawgeti(L, -2, i);
part = lua_tostring(L, -1);
if (strcmp(part, "not") == 0) {
assertion = 0;
}
else if (part[0] == '%' && part[1] == '%') {
wildcard = 1;
}
else {
if (testContext(L, prefix, part, assertion, wildcard, filename, fileMatched)) {
lua_pop(L, 1);
result = 1;
break;
}
assertion = 1;
wildcard = 0;
}
lua_pop(L, 1);
}
lua_pop(L, 1);
return result;
}
int criteria_matches(lua_State* L)
{
/* stack [1] = criteria */
/* stack [2] = context */
const char* filename;
int top = lua_gettop(L);
int matched = 1;
int fileMatched = 0;
/*
Cache string.match for a quicker lookup in match() above
stack[3] = string
stack[4] = string.match
*/
lua_getglobal(L, "string");
lua_getfield(L, -1, "match");
/* filename = context.files */
lua_getfield(L, 2, "files");
filename = lua_tostring(L, -1);
/*
for i, pattern in pairs(criteria.patterns) do
if not testPattern(pattern) then
matched = false
break
end
end
*/
lua_getfield(L, 1, "patterns");
lua_pushnil(L);
while (lua_next(L, -2)) {
if (!testPattern(L, filename, &fileMatched)) {
matched = 0;
break;
}
lua_pop(L, 1);
}
/*
if matched and filename and not filematched then
matched = false
end
return matched
*/
if (matched && filename && !fileMatched) {
matched = 0;
}
lua_settop(L, top);
lua_pushboolean(L, matched);
return 1;
}

View File

@ -35,6 +35,11 @@ extern const char* builtin_scripts[];
/* Built-in functions */ /* Built-in functions */
static const luaL_Reg criteria_functions[] = {
{ "matches", criteria_matches },
{ NULL, NULL }
};
static const luaL_Reg path_functions[] = { static const luaL_Reg path_functions[] = {
{ "getabsolute", path_getabsolute }, { "getabsolute", path_getabsolute },
{ "getrelative", path_getrelative }, { "getrelative", path_getrelative },
@ -79,6 +84,7 @@ static const luaL_Reg string_functions[] = {
*/ */
int premake_init(lua_State* L) int premake_init(lua_State* L)
{ {
luaL_register(L, "criteria", criteria_functions);
luaL_register(L, "path", path_functions); luaL_register(L, "path", path_functions);
luaL_register(L, "os", os_functions); luaL_register(L, "os", os_functions);
luaL_register(L, "string", string_functions); luaL_register(L, "string", string_functions);

View File

@ -62,6 +62,7 @@ void do_translate(char* value, const char sep);
/* Built-in functions */ /* Built-in functions */
int criteria_matches(lua_State* L);
int path_getabsolute(lua_State* L); int path_getabsolute(lua_State* L);
int path_getrelative(lua_State* L); int path_getrelative(lua_State* L);
int path_isabsolute(lua_State* L); int path_isabsolute(lua_State* L);