Move criteria.matches() to C to gain back some performance lost to filter prefixes
This commit is contained in:
parent
28cfa55886
commit
0f713d5c39
@ -8,7 +8,7 @@
|
||||
-- Copyright (c) 2012-2014 Jason Perkins and the Premake project
|
||||
--
|
||||
|
||||
premake.criteria = {}
|
||||
premake.criteria = criteria
|
||||
local criteria = premake.criteria
|
||||
|
||||
|
||||
@ -43,15 +43,18 @@
|
||||
end
|
||||
|
||||
local parts = path.wildcards(term)
|
||||
local isWildcard = (parts ~= term)
|
||||
parts = parts:explode(" or ")
|
||||
|
||||
for i, part in ipairs(parts) do
|
||||
if part:startswith("not ") then
|
||||
table.insert(pattern, "not")
|
||||
table.insert(pattern, part:sub(5))
|
||||
else
|
||||
table.insert(pattern, part)
|
||||
part = part:sub(5)
|
||||
end
|
||||
if isWildcard then
|
||||
table.insert(pattern, "%%")
|
||||
end
|
||||
table.insert(pattern, part)
|
||||
end
|
||||
|
||||
table.insert(patterns, pattern)
|
||||
@ -62,101 +65,3 @@
|
||||
crit.patterns = patterns
|
||||
return crit
|
||||
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
283
src/host/criteria_matches.c
Normal 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;
|
||||
}
|
@ -35,6 +35,11 @@ extern const char* builtin_scripts[];
|
||||
|
||||
|
||||
/* Built-in functions */
|
||||
static const luaL_Reg criteria_functions[] = {
|
||||
{ "matches", criteria_matches },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const luaL_Reg path_functions[] = {
|
||||
{ "getabsolute", path_getabsolute },
|
||||
{ "getrelative", path_getrelative },
|
||||
@ -79,9 +84,10 @@ static const luaL_Reg string_functions[] = {
|
||||
*/
|
||||
int premake_init(lua_State* L)
|
||||
{
|
||||
luaL_register(L, "path", path_functions);
|
||||
luaL_register(L, "os", os_functions);
|
||||
luaL_register(L, "string", string_functions);
|
||||
luaL_register(L, "criteria", criteria_functions);
|
||||
luaL_register(L, "path", path_functions);
|
||||
luaL_register(L, "os", os_functions);
|
||||
luaL_register(L, "string", string_functions);
|
||||
|
||||
/* push the application metadata */
|
||||
lua_pushstring(L, LUA_COPYRIGHT);
|
||||
|
@ -62,6 +62,7 @@ void do_translate(char* value, const char sep);
|
||||
|
||||
|
||||
/* Built-in functions */
|
||||
int criteria_matches(lua_State* L);
|
||||
int path_getabsolute(lua_State* L);
|
||||
int path_getrelative(lua_State* L);
|
||||
int path_isabsolute(lua_State* L);
|
||||
|
Loading…
Reference in New Issue
Block a user